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))
Related
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.
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.
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.
Out of practice to make myself more familiar with the scheme interface, I'm trying to write a procedure in Dr. Racket (though compatible with MIT Scheme) that checks between various different strings given and returns the appropriate strings depending on what is provided. What I have so far is this:
(define (conversation input)
(cond ((eq? (or "hello Racket" "hi Racket" "what's up, Racket?"
"hey Racket" "what's happening, Racket?") input) "hey coder")
(else "no hablo ingles.")))
*the space in between the strings is just so it will fit on here. It's one long statement in the interpreter.
The desired effect is that if I put in:
(conversation "hello Racket")
(conversation "hi Racket")
(conversation "hey Racket")
They will all return the same result, which is "hey coder". However, that is not what's happening. The only one that returns "hey coder" is (conversation "hello Racket"). All the rest return "no hable ingles." As with many other aspects of the language, I'm not all too savvy with strings in scheme. I'm fairly certain that the problem lies within the or statement, though I wouldn't know of the alternatives that would work in this context. I've tried looking up solutions, though I haven't run across anything that fits this type of description. Does anyone know of any alternatives to the code that would work?
or takes a list of arguments and checks whether any of its arguments are "truthy" (that is not #f). If so, it returns the first of its arguments that is truthy. If not, it returns #f. So (or "string1" "string2" ...) simply returns "string1". So all you're doing is checking whether the given string equals "hello Racket" and ignoring the other options (you might object here that it doesn't work for "hello Racket" either - I'll get to that). What you want to be doing is to give whole conditions to or, not just the strings. So it should look like (or (eq? "string1" input) (eq? "string2" input) ...).
However this doesn't work either. Why not? Because eq? is the wrong function to use to compare strings. It only returns true if two strings reside in the same location in memory. If two strings reside in different memory locations, but have the same contents, it returns #f. This is also why your current code returns #f for "hello Racket". What you should be using is equal?, which compares the contents of the strings.
This should work now, but it's a bit clunky - repeating the call to equal? for every possible string. A nicer approach would be to create a list with the valid strings and then check whether the input string is contained in the list using the member function.
You can achieve the effect that you intended using the member procedure and a list of possible options:
member locates the first element of lst that is equal? to v. If such an element exists, the tail of lst starting with that element is returned. Otherwise, the result is #f.
This is what I mean:
(define (conversation input)
(cond ((member input '("hello Racket" "hi Racket" "what's up, Racket?"
"hey Racket" "what's happening, Racket?"))
"hey coder")
(else "no hablo ingles.")))
I am new to elisp but I am trying to spice up my .emacs a little.
I am trying to define some paths, but having problems with creating a list of paths (and setting the list for YaSnippet more specifically).
When I evaluate the list I get a list of the symbol name (and not the symbol values as yassnippet want).
I got the code working but have feeling that there is a better way to do this?
Here is the working code:
;; some paths
(setq my-snippets-path "~/.emacs.d/snippets")
(setq default-snippets-path "~/.emacs.d/site-lisp/yasnippet/snippets")
;; set the yas/root-directory to a list of the paths
(setq yas/root-directory `(,my-snippets-path ,default-snippets-path))
;; load the directories
(mapc 'yas/load-directory yas/root-directory)
If you evaluate a list of strings, the result depends on the value of the list items. The best way to test that is to launch the ielm repl (M-x ielm), and enter:
ELISP> '("abc" "def" "ghi")
("abc" "def" "ghi")
The quoted list of string evaluates to the list value. If you store the value of the list in a variable, and then evaluate the variable, ELisp will complain that the function abc is unknown.
ELISP> (setq my-list '("abc" "def" "ghi"))
("abc" "def" "ghi")
ELISP> (eval my-list)
*** Eval error *** Invalid function: "abc"
For the yasnippet directory configuration, you should just set yas-snippet-dir instead, e.g.
(add-to-list 'load-path
"~/.emacs.d/plugins/yasnippet")
(require 'yasnippet)
(setq yas-snippet-dirs
'("~/.emacs.d/snippets" ;; personal snippets
"/path/to/yasnippet/snippets" ;; the default collection
"/path/to/other/snippets" ;; add any other folder with a snippet collection
))
(yas-global-mode 1)
Edit:
The use of yas/root-directory has been deprecated. From the documentation of yasnippet.el
`yas-snippet-dirs'
The directory where user-created snippets are to be
stored. Can also be a list of directories. In that case,
when used for bulk (re)loading of snippets (at startup or
via `yas-reload-all'), directories appearing earlier in
the list shadow other dir's snippets. Also, the first
directory is taken as the default for storing the user's
new snippets.
The deprecated `yas/root-directory' aliases this variable
for backward-compatibility.
I think you want
(setq yas/root-directory (list my-snippets-path default-snippets-path))