common lisp, how to mask keyboard input - keyboard

This is a console program in Common Lisp for a Hangman type game. The first player enters a string to be guessed by the second player. My input function is below --- unfortunately the characters typed by the first player remain visible.
With JavaScript it's simple, just use a password text entry box. With VB it's simple using the same sort of facility. Is there any way to do this using a native Common Lisp function?
Thanks, CC.
(defun get-answer ()
(format t "Enter the word or phrase to be guessed: ~%")
(coerce (string-upcase (read-line)) 'list))
(defun start-hangman ()
(setf tries 6)
(greeting)
(setf answer (get-answer))
(setf obscure (get-obscure answer))
(game-loop answer obscure))

Each implementation supports this differently.
You might want to use an auxiliary library like iolib.termios or cl-charms (interface to libcurses) if you want a portability layer above different implementations.
SBCL
I found a discussion thread about it for SBCL, and here is the code for that implementation, from Richard M. Kreuter:
(require :sb-posix)
(defun echo-off ()
(let ((tm (sb-posix:tcgetattr sb-sys:*tty*)))
(setf (sb-posix:termios-lflag tm)
(logandc2 (sb-posix:termios-lflag tm) sb-posix:echo))
(sb-posix:tcsetattr sb-sys:*tty* sb-posix:tcsanow tm)))
(defun echo-on ()
(let ((tm (sb-posix:tcgetattr sb-sys:*tty*)))
(setf (sb-posix:termios-lflag tm)
(logior (sb-posix:termios-lflag tm) sb-posix:echo))
(sb-posix:tcsetattr sb-sys:*tty* sb-posix:tcsanow tm)))
And so, here is finally an opportunity to talk about PROG2:
(defun read-silently ()
(prog2
(echo-off)
(read-line sb-sys:*tty*)
(echo-on)))
However, you might want to ensure that the echo is always reset when unwinding the stack, and clear the input before inputting things:
(defun read-silently ()
(echo-off)
(unwind-protect
(progn
(clear-input sb-sys:*tty*)
(read-line sb-sys:*tty*))
(echo-on)))
CL-CHARMS
Here is an alternative using libcurse. The following is sufficient to make a simple test work.
(defun read-silently ()
(let (input)
(charms:with-curses ()
(charms:disable-echoing)
(charms:enable-raw-input)
(clear-input *terminal-io*)
(setf input (read-line *terminal-io*))
(charms:disable-raw-input)
(charms:enable-echoing))
input))
Besides, using libcurse might help you implement a nice-looking hangman console game.

Are you printing to a console? That's an inherent limitation of standard consoles.
You'll need to print a ton of newlines to push the text off the screen.
Many consoles aren't capable of fancy things like selectively erasing parts of the screen.

Related

Simplifying complex setf expressions

For rapid prototyping purposes in common-lisp it would be convenient to be able to easily function-modify an object in an arbitrary data structure. This would seem to involve calling an arbitrary function on a place in the data structure, replacing the object at that place with the result of the function call. Common-lisp has a number of specialized modification macros (eg, incf, push, getf, etc) for particular types of objects, and setf for generalized place modification (eg, setf-second, setf-aref, setf-gethash, etc). But rather than inventing new specialized macros for other object types, or having to mentally consider the characteristics of each macro (slowing down development), it might be nice to have a generalized setf-like modification capability that was simpler to use than setf. For example, instead of (setf (second (getf plist indicator)) (1+ (second (getf plist indicator)))) or (incf (second (getf plist indicator))), one might write (callf (second (getf plist indicator)) #'1+), using any of the normal one argument functions (or lambda-expression) provided by common-lisp or the user. Here is an attempt at code:
(defun call (object function) (funcall function object))
(define-modify-macro callf (&rest args) call)
Will something like this work for all general cases, and can it actually simplify code in practice?
callf
I think what you are looking for is _f from OnLisp 12.4:
(defmacro _f (op place &rest args)
"Modify place using `op`, e.g., (incf a) == (_f a 1+)"
(multiple-value-bind (vars forms var set access)
(get-setf-expansion place)
`(let* (,#(mapcar #'list vars forms)
(,(car var) (,op ,access ,#args)))
,set)))
This uses get-setf-expansion - the workhorse of generalized reference handling.
Note that _f only works with single-value places.
IOW, (_f (values a b c) 1+) will not increment all 3 variables.
It is not all that hard to fix that though...
can it actually simplify code in practice?
This really depends on your coding style and the specific problem you are solving.

How do you securely parse untrusted input in Common Lisp?

How do you securely parse untrusted input in Common Lisp? Given that there is no parse-float etc, and that read-from-string will execute reader macros like #. (read time eval).
e.g.
(read-from-string "#.(+ 1 2)") => 3
I can't find the other question or comment that described some of the safe input handling procedures for Common Lisp (if someone else finds them, please post a comment!), but there are at least two important things that you might do:
Use with-standard-io-syntax to make sure that you're reading with the standard readtable, etc. Note that this will bind *read-eval* to true, so be sure to also:
Bind *read-eval* to false (within with-standard-io-syntax). This disables the sharpsign-dot (#.) macro mentioned in the question.
(let ((*readtable* (copy-readtable)))
(set-macro-character #\n (constantly 'injected))
(read-from-string "(#.(+ 2 5) n)"))
;;=> (7 INJECTED)
(let ((*readtable* (copy-readtable)))
(set-macro-character #\n (constantly 'injected))
(with-standard-io-syntax
(let ((*read-eval* nil))
(read-from-string "(#.(+ 2 5) n)"))))
;; Evaluation aborted on #<SB-INT:SIMPLE-READER-ERROR
;; "can't read #. while *READ-EVAL* is NIL" {1004DA3603}>.
(let ((*readtable* (copy-readtable)))
(set-macro-character #\n (constantly 'injected))
(list (read-from-string "(n)")
(with-standard-io-syntax
(let ((*read-eval* nil))
(read-from-string "(n)")))))
;; ((INJECTED) (N))
Generally, just that the standard code reader is so readily available and can read many kinds of input does not mean that you should use it to read anything but code.
There are many libraries for parsing a lot of things, e. g. parse-number for the Lisp number formats, fare-csv for CSV files (among many other CSV libraries), json-streams for JSON (again, many others). For most formats, you can just do a system-apropos lookup with Quicklisp.

How to play a song on repeat in Racket/gui?

I'm making a slideshow using racket and want a short song to play on loop behind the slide show. I'm currently using (play-sound) but Racket crashes with a segfault towards the end of the song, always at a different slide. If I run the show without the sound code it does not segfault.
I think the song ending is causing racket/gui to crash and looping might prevent this. Playing the song in the main function of the show as such
(define (run)
(play-sound "path" #t)
(slide (...)
(run))
Causes the song to continuously cut out, only playing the first two seconds or so before repeating. I now have the play-sound line outside of the main function, but it does crash towards the end, sometimes at the very end. I've tried increasing DrRacket's memory but it didn't help. Any advice is appreciated.
You might be interested in the RSound package (cf. http://pkgs.racket-lang.org). This package will have both advantages and disadvantages over your current approach. The most obvious disadvantage will be that it will require that you convert your song to a WAV. Here's some code:
#lang racket
(require rsound)
(define ishmael (rs-read "/Users/clements/Desktop/call-me-ishmael.wav"))
(define LEAD-TIME (* 1/10 44100))
(define (play-forever sound)
(define p (make-pstream))
(define len (rs-frames sound))
(let loop ([t 0])
(pstream-queue p sound (+ t LEAD-TIME))
(define next-t (+ t len))
(sleep (* 1/44100 (- next-t (pstream-current-frame p))))
(loop next-t)))
(play-forever ishmael)

My Lisp interpreter doesn't recognize defgrammar?

I'm new to Lisp, so I'm guessing I'm missing something simple here. I found some code online that I wanted to play with, and it uses the defgrammar macro. When I write it into my code file, copied exactly from the web to start with, save, and load, it says (with file name omitted):
;; Loading file ...
*** - EVAL: undefined function DEFGRAMMAR
I'm guessing there's some variable I have to flip, like for printing circular lists, or some add-on or something I have to install, but I don't really know what I'm looking for. Any suggestions?
By the way, I'm using GNU CLisp 2.49.
The code I'm trying to use is:
(load "rdp")
(use-package "COM.INFORMATIMAGO.RDP")
(defgrammar binary-tree
:terminals ((label "[^(),][^(),]*"))
:start tree
:rules ((--> tree
(opt node)
:action (if (null $1)
(make-empty-binary-tree)
$1)) ; it's identity, but make-empty-binary-tree
; could be defined otherwise.
(--> node
label (opt children)
:action (make-binary-tree :label (read-from-string (second $1))
:left (first $2) :right (second $2)))
(--> children
"(" tree "," tree ")"
:action (list $2 $4))))
(defun binary-tree-from-string (string)
(parse-binary-tree string))
Looking at it, I'm guessing I need "rdp"?
What's the code you want to play with, could you paste it here? defgrammar is not a built-in function. You should find the definition of defgrammar function. Load it first then re-evaluate your example.

Lisp format and force-output

I don't understand why this code behaves differently in different implementations:
(format t "asdf")
(setq var (read))
In CLISP it behaves as would be expected, with the prompt printed followed by the read, but in SBCL it reads, then outputs. I read a bit on the internet and changed it:
(format t "asdf")
(force-output t)
(setq var (read))
This, again, works fine in CLISP, but in SBCL it still reads, then outputs. I even tried separating it into another function:
(defun output (string)
(format t string)
(force-output t))
(output "asdf")
(setq var (read))
And it still reads, then outputs. Am I not using force-output correctly or is this just an idiosyncrasy of SBCL?
You need to use FINISH-OUTPUT.
In systems with buffered output streams, some output remains in the output buffer until the output buffer is full (then it will be automatically written to the destination) or the output buffer is explicity emptied.
Common Lisp has three functions for that:
FINISH-OUTPUT, attempts to ensure that all output is done and THEN returns.
FORCE-OUTPUT, starts the remaining output, but IMMEDIATELY returns and does NOT wait for all output being done.
CLEAR-OUTPUT, tries to delete any pending output.
Also the T in FORCE-OUTPUT and FORMAT are unfortunately not the same.
force-output / finish-output: T is *terminal-io* and NIL is *standard-output*
FORMAT: T is *standard-output*
this should work:
(format t "asdf")
(finish-output nil) ; note the NIL
(setq var (read))

Resources