I'm trying to understand why this small piece of code does not work as expected.
I would expect it to print out "foo", but in fact what I get is
CL-USER> (stringloop)
null output T
line output NIL
NIL
I expect I am using do wrong, but I've not be able to figure out what.
(defun stringloop ()
(with-input-from-string (s "foo" :index j )
(do ((line (read-line s nil) ;; var init-form
(read-line s nil))) ;; step=form
((null line) (progn (format t "null output ~a~% "(null line)) (format t "line output ~a~% " line))))))
You didn't put anything in the loop body. Your function reads a line ("foo"), does nothing with it, then reads another line (nil), your termination condition becomes true, and you print the null line.
Run this modified version to see what's happening:
(defun stringloop ()
(with-input-from-string (s "foo")
(do ((line (read-line s nil) ;; var init-form
(read-line s nil))) ;; step=form
((null line) (format t "termination condition - line: ~s~% " line))
(format t "in loop - line: ~s~%" line))))
Related
I am supposed to write a program that gets simple user input as a string and the code supposed to writes back a response (name, are you a person etc.) The program suppose to terminate when word 'bye' is typed. The code is below:
(defun x()
(setq words "empty")
(loop while (string/= words "BYE")
(setq words (read-delimited-list #\~)
(write-line words)
(format t "ROBBIE%: Hello, who are you?")
(case (string-include "I'm" words)
(format t "ROBBIE%: Nice to see you, how are you?")
((string-include "Hi" words)
(format t "ROBBIE%: How are you?")
(or (string-include "fine" words) (string-include "person" words))
(format t "ROBBIE%: No I'm a computer")))
(format t "BYE"))
(x)
However, when I compile this on program 2 errors pop up:
Line2:3 warning: undefined variable: COMMON-LISP-USER:: WORDS
Line3:3 error: during macroexpansion of (LOOP WHILE (STRING/= WORDS "BYE") ...). Use BREAK-ON-SIGNALS to intercept.
I've done programming in python but this is extremely complicated lang for me and I need some help understanding why this isn't working? Any advice is greatly appreciated!
When you do this:
(defun x ()
(setf words "foo"))
then words is not defined. It references some global variable, and if that doesn't exist, it will create it, but possibly with some strange behaviour regarding scope and extent. This is not portable code.
In order to introduce a local variable, use let:
(defun x ()
(let ((words "foo"))
;; words is is scope here
)
;; but not here
)
Loop (in the more usual »extended« form) uses loop keywords for all its clauses. There is no implicit body. In order to do something, you might use do, which allows multiple forms to follow:
(defun x ()
(let ((words "foo"))
(loop while (string/= words "bye")
do (setf words (read-line …))
(format …))))
Case uses compile-time values to compare using eql:
(case something
(:foo (do-a-foo))
((:bar :baz) (do-a-bell))
(t (moooh)))
This doesn't work with strings, because strings are not eql unless they are the same object (i. e. they are eq). In your case, you want a cond:
(cond ((string-include-p words "Whatever")
…)
((string-include-p words "yo man")
…))
For interaction with the user, you'd maybe want to use the bidirectional *query-io* stream:
(format *query-io* "Who am I?")
and
(read-line *query-io*)
Read-line gives you strings, and seems much better suited to your task than read-delimited-list, which has other use cases.
Let me focus on aspects of your code not already covered by other solutions.
Loop
Here is your loop structure:
(let ((words "empty"))
(loop
while (string/= words "BYE")
do
(progn
(setq words (read-line)))))
First of all, after do you don't need (progn ...). You could write equally:
(let ((words "empty"))
(loop
while (string/= words "BYE")
do (setq words (read-line))))
Having to initialize words to some arbitrary value (called sometime a sentinel value) is a code smell (not always a bad thing, but there might be better alternatives). Here you can simplify the loop by using a for clause.
(loop
for words = (read-line)
while (string/= words "BYE")
do ...)
Also, you may want to use until with a string= test, this might be more readable:
(loop
for words = (read-line)
until (string= words "BYE")
do ...)
Search
You can test for string inclusion with SEARCH. Here is a little commented snippet of code to showcase how string manipulation function could work:
(defun test-I-am (input)
(let ((start (search "I'am" input)))
(when start
;; we found an occurrence at position start
;; let's find the next space character
(let ((space (position #\space input :start start)))
(when space
;; we found a space character, the name starts just after
(format nil "Hello ~a!" (subseq input (1+ space))))))))
With this simple algorithm, here is a test (e.g. in your REPL):
> (test-i-am "I'am tired")
"Hello tired!"
Replace read-delimited-list with read-line, case with cond and balance some parentheses. Here is working solution, including some function for string-inclusion:
(defun string-include (string1 string2)
(let* ((string1 (string string1)) (length1 (length string1)))
(if (zerop length1)
nil
(labels ((sub (s)
(cond
((> length1 (length s)) nil)
((string= string1 s :end2 (length string1)) string1)
(t (sub (subseq s 1))))))
(sub (string string2))))))
(defun x ()
(let ((words "empty"))
(format t "ROBBIE%: Hello, who are you?~%")
(loop while (string/= words "BYE") do
(progn
(finish-output)
(setq words (read-line))
(cond ((string-include "I'm" words)
(format t "ROBBIE%: Nice to see you, how are you?~%"))
((string-include "Hi" words)
(format t "ROBBIE%: How are you?~%"))
((or (string-include "fine" words)
(string-include "person" words))
(format t "ROBBIE%: No I'm a computer~%")))))
(format t "BYE")))
Then you just call it:
(x)
I am trying to make a reader macro that would convert #this into "this".
This is what I currently have:
(defun string-reader (stream char)
(declare (ignore char))
(format nil "\"~a\"" (read-line stream t nil t))
)
(set-macro-character #\# #'string-reader )
The problem is that this requires that I put a newline after ever #this. I've also tried it with (read), but that just returns the variable test, which has not been set. I can't just hard-code the number of characters after the # symbol, because I don't know how many there would be. Is there any way to fix this?
Edit: is the only way to do this to loop over read-char and peek-char, reading until I get to #),#\space, or #\Newline?
You can try to use read and then look at what it returns:
(defun string-reader (stream char)
(declare (ignore char))
(let ((this (let ((*readtable* (copy-readtable)))
(setf (readtable-case *readtable*) :preserve)
(read stream t nil t))))
(etypecase this
(string this)
(symbol (symbol-name this)))))
(set-macro-character #\# #'string-reader)
Above would allow #This and #"This", but not #333.
This version just reads a string until whitespace:
(defun read-as-string-until-whitespace (stream)
(with-output-to-string (out-stream)
(loop for next = (peek-char nil stream t nil t)
until (member next '(#\space #\newline #\tab))
do (write-char (read-char stream t nil t) out-stream))))
(defun string-reader (stream char)
(declare (ignore char))
(read-as-string-until-whitespace stream))
(set-macro-character #\# #'string-reader)
Example:
CL-USER 21 > #this
"this"
CL-USER 22 > #42
"42"
CL-USER 23 > #FooBar
"FooBar"
I'm writing my own x86-64 assembler in Common Lisp and it produces correct binary code for a subset of x86-64. I use a custom reader macro to convert assembly code to a syntax tree, and it works as expected.
What I am attempting to accomplish is to allow using Lisp code inside assembly code, that way I could use Lisp as a macro language for my assembler. I use #a as the macro dispatch character and #e to signal end for the reader. Inside reader #l changes to Lisp mode and #a back to assembly mode, #e (to signal end for the reader macro) should work in both modes.
What I don't understand is how to output the results of the evaluated code back to the input stream (to be processed before the rest of the code), or otherwise how to get the Lisp code output be read again, so that the output of Lisp code (it would be assembly code) could be processed appropriately (the same way as the rest of the assembly code). How can I reach that goal?
A sidenote: this is my first reader macro, so there may be design flaws. I think my approach to read Lisp code into a string is not necessarily the best way, if there is some shorter and more idiomatic way to do it.
Here's a simplified version of my reader macro:
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun get-last-character-string (my-string)
"This function returns a string consisting of the last character of the input string."
(subseq my-string (1- (length my-string))))
(defun get-string-without-last-character (my-string)
"This function returns a string without the last character of the input string."
(subseq my-string 0 (1- (length my-string))))
(defun get-string-without-invalid-last-character (my-string invalid-last-characters)
"If the last character of the string is invalid, the string is returned without it, otherwise completely."
(loop for invalid-last-character in invalid-last-characters
do (if (equal (get-last-character-string my-string) invalid-last-character)
(setf my-string (get-string-without-last-character my-string))))
my-string)
(defun transform-code-to-string (stream sub-char numarg)
"This function converts assembly code into a string.
#l marks change to Lisp code. #a marks return to asm. #e marks end.
Partially based on: http://weitz.de/macros.lisp"
(declare (ignore sub-char numarg))
(let*
((invalid-last-characters (list "'" " " "(" ")"))
(current-mode "asm")
(is-there-code-on-this-line nil)
(current-phase "beginning-of-line")
(my-string "(list ")
(lisp-code-string ""))
;; loop through stream.
(loop for my-char = (coerce (list (read-char stream t nil t)) 'string)
do (cond
((equal current-mode "asm")
(cond
((equal current-phase "hash-sign-read")
;; is character e ?
;; if yes, we're done, fix closing parentheses and return.
(cond
((equal my-char "e")
(return-from transform-code-to-string
(concatenate 'string (get-string-without-invalid-last-character
(get-string-without-invalid-last-character
my-string invalid-last-characters)
invalid-last-characters) "))")))
;; is character l ?
;; if yes, change to Lisp mode.
((equal my-char "l")
;; could Lisp code could be read and evaluated here
;; without reading it into a string?
(progn
(setf current-mode "Lisp")
(setf is-there-code-on-this-line nil)
(setf lisp-code-string "")
(setf current-phase "beginning-of-line")))
;; otherwise, print error.
(t (error "in asm mode undefined control character after #"))))
;; is character # ?
;; if yes, mark hash sign read.
((equal my-char "#")
(setf current-phase "hash-sign-read"))
;; is character newline?
((equal my-char (coerce (list #\Newline) 'string))
(progn
(cond
;; is there _no_ code on this line?
;; if true, do not output anything.
((not is-there-code-on-this-line)
(setf current-phase "beginning-of-line"))
;; are we inside instruction or inside a parameter?
;; if true, output ")
((or (equal current-phase "inside-instruction")
(equal current-phase "inside-parameters"))
(progn
(setf current-phase "beginning-of-line")
(setf is-there-code-on-this-line nil)
(setf my-string (concatenate 'string my-string "\")"))))
;; otherwise output )
(t (progn
(setf current-phase "beginning-of-line")
(setf is-there-code-on-this-line nil)
(setf my-string (concatenate 'string my-string ")")))))))
;; are we inside a comment?
;; if yes, don't output anything.
((equal current-phase "inside-comment")
nil)
;; are we in the beginning of the line?
((equal current-phase "beginning-of-line")
(cond
;; is this a space in the beginning of the line?
;; if yes, do not output anything.
((equal my-char " ")
nil)
;; is this the first character of instruction and not ( or ) ?
;; if yes, mark there is code on this line, mark first character as printed, output " and current character.
((and
(not (equal my-char "("))
(not (equal my-char ")")))
(progn
(setf current-phase "inside-instruction")
(setf is-there-code-on-this-line t)
(setf my-string (concatenate 'string my-string "'(\"" my-char))))
(t nil)))
;; is character ; ?
;; if yes, don't output anything, begin comment.
((equal my-char ";")
(setf current-phase "inside-comment"))
;; is character space or comma?
((or (equal my-char " ")
(equal my-char ","))
(cond
;; is character space or comma, and last character was _not_ space, comma or opening parenthesis?
;; if yes, output " and space.
((and
(not (equal (get-last-character-string my-string) " "))
(not (equal (get-last-character-string my-string) ","))
(not (equal (get-last-character-string my-string) "(")))
(progn
(setf current-phase "in-space")
(setf my-string (concatenate 'string my-string "\" "))))
(t nil)))
;; is instruction printed and this is the 1st character of a parameter?
((and
(not (equal current-phase "inside-instruction"))
(or (equal (get-last-character-string my-string) " ")
(equal (get-last-character-string my-string) ",")))
(cond
;; mark we're inside parameters, output " and current character.
(t (progn
(setf current-phase "inside-parameters")
(setf my-string (concatenate 'string my-string "\"" my-char))))))
;; otherwise output the character.
(t (setf my-string (concatenate 'string my-string my-char)))))
((equal current-mode "Lisp")
;; in Lisp mode, read text until #e or #a is reached and eval it.
(cond
((equal current-phase "hash-sign-read")
(cond
;; is character e ?
;; if yes, we're done, fix closing parentheses and return.
((equal my-char "e")
(progn
(concatenate 'string "#a" (eval lisp-code-string) "#e") ; this should be something different.
(return-from transform-code-to-string
(concatenate 'string (get-string-without-invalid-last-character
(get-string-without-invalid-last-character
my-string invalid-last-characters)
invalid-last-characters) "))"))))
;; is character a ?
;; if yes, change to asm mode.
((equal my-char "a")
(progn
(setf current-mode "asm")
(setf is-there-code-on-this-line nil)
(setf current-phase "beginning-of-line")
(concatenate 'string "#a" (eval lisp-code-string) "#e") ; this should be something different.
;; otherwise, add # and the character to the Lisp code to be evaluated.
(t (progn
(setf current-phase "")
(setf my-string (concatenate 'string lisp-code-string "#" my-char))))))
;; is character # ?
;; if yes, mark hash sign read.
((equal my-char "#")
(setf current-phase "hash-sign-read"))
;; otherwise add the character to the Lisp code to be evaluated.
(t (setf my-string (concatenate 'string lisp-code-string my-char)))))
(t (error "invalid current mode"))))))
;;; #a is the input which starts the custom reader.
(set-dispatch-macro-character #\# #\a #'transform-code-to-string))
Here's some example assembly code without Lisp code inside, works:
(defparameter *example-code-x64*
#a
inc r10 ; increment register r10.
mov r11,r12 ; store value of r12 into r11.
#e)
And here's some assembly code with Lisp code inside, fails (see compiling error further below). In this one the Lisp code is after assembly code, but assembly and Lisp code should be allowed to be mixed freely using #a and #l as separators.
(defparameter *example-code-x64-with-lisp-fails*
#a
inc r10 ; increment register r10.
mov r11,r12 ; store value of r12 into r11.
#l
(loop for current-instruction in (list "inc" "dec")
do (loop for current-arg in (list "r13" "r14" "r15")
do (princ (concatenate 'string
current-instruction
" "
current-arg
(coerce (list #\Newline) 'string)))))
#e)
The Lisp part of the above code should be evaluated in the custom reader, so that it should produce identical results as the code below:
(defparameter *example-code-x64-with-lisp-fails*
#a
inc r10 ; increment register r10.
mov r11,r12 ; store value of r12 into r11.
inc r13
inc r14
inc r15
dec r13
dec r14
dec r15
#e)
But instead the compiling fails:
CL-USER> ; compiling file "/home/user/code/lisp/lisp-asm-reader-for-stackoverflow.lisp" (written 28 MAR 2014 10:11:29 PM):
;
; caught ERROR:
; READ error during COMPILE-FILE:
;
; The value -1 is not of type (MOD 4611686018427387901).
;
; (in form starting at line: 1, column: 0, file-position: 0)
;
; compilation unit aborted
; caught 1 fatal ERROR condition
; caught 1 ERROR condition
; compilation aborted after 0:00:00.004
1 compiler notes:
/home/user/code/lisp/lisp-asm-reader-for-stackoverflow.lisp:10487
read-error: READ error during COMPILE-FILE:
The value -1 is not of type (MOD 4611686018427387901).
(in form starting at line: 1, column: 0, file-position: 0)
CL-USER>
The idiomatic way to read lisp code from within a reader macro is to call cl:read. In your example, calling read after consuming #L will return the list whose car is loop, and that list can be passed to eval.
To collect the output created during the eval, you can bind *standard-output*. So an option is to use something akin to the following within your reader macro:
(let ((lisp-printed-string
(with-output-to-string (*standard-output*)
(eval (read stream t t t)))))
;; concatenate the lisp printed string onto your
;; hand parsed string here
)
An alternative is to have the user input a lisp form which returns a string {e.g. (concatenate "bar" "baz")}, and collect eval's return value instead of its printed output.
I want to write this program to find a keyword in a list. If found then print the list. But errors occur, i can't solve it. Please give me some suggestions. I am a newbie for lisp.
The main program is two dolist(two for in C) and find keyword in a list of list(two dimension array).
(defun kanna_find (key)
(let (
(result 0)
(kanna-table (list
(list "あ" "ア" "a")
(list "い" "イ" "i")
)
) ;; End of kanna table
) ;; End of let var define
(dolist (result kanna-table)
(dolist (item result)
(if (string= item key)
(print result))
) ;; End of the second dolist
) ;; End of the first dolist
) ;; End of let
)
(kanna_find "あ")
Below is the debug info
Debugger entered--Lisp error: (void-variable )
(list "い" "イ" "i")
(list (list "あ" "ア" "a") (list "い" "イ" "i"))
(let ((result 0) (kanna-table ...)) (dolist (result kanna-table) (dolist ... ...)))
kanna_find("あ")
eval((kanna_find "あ"))
eval-last-sexp-1(nil)
eval-last-sexp(nil)
call-interactively(eval-last-sexp nil nil)
recursive-edit()
(list "あ" "ア" "a")
(list "い" "イ" "i")
Do you notice how the spaces in the second line are longer than the ones on the first line? That's because they're not ASCII spaces, so emacs doesn't recognize them as spaces. Rather emacs thinks they're variable names, so it complains to you that there's no variable called " " (thus the space in the void-variable error).
You seem to be looking for this one-liner:
;; return a sublist tail of list-of-strings whose first element is string
;; or nil if string is not found
(member string list-of-strings)
Your code is terribly formatted. Try this style, which is used by about 99.5 million of the world's 100 million or so Lisp programmers:
(defun kanji_find (key)
(let ((result 0)
(kanji-table (list (list "あ" "ア" "a")
(list "い" "イ" "i"))))
(dolist (result kanji-table)
(dolist (item result)
(if (string= item key)
(print result))))))
Nobody writes comments about individual closing parentheses; that is ridiculous! You're turning Lisp into Ada.
Your text editor (being Emacs, after all) can show you the balancing pairs of parentheses and manage the indentation for you.
In the end, the indentation is your main visual clue about what goes with what.
The (result 0) binding in your code is useless and has nothing to do with the result variable in the outer dolist.
An mplayer tool (midentify) outputs "shell-ready" lines intended to be evaluated by a bash/sh/whatever interpreter.
How can I assign these var-names to their corresponding values as elisp var-names in emacs?
The data is in a string (via shell-command-to-string)
Here is the data
ID_AUDIO_ID=0
ID_FILENAME=/home/axiom/abc.wav
ID_DEMUXER=audio
ID_AUDIO_FORMAT=1
ID_AUDIO_BITRATE=512000
ID_AUDIO_RATE=0
ID_AUDIO_NCH=1
ID_LENGTH=3207.00
ID_SEEKABLE=1
ID_CHAPTERS=0
ID_AUDIO_BITRATE=512000
ID_AUDIO_RATE=32000
ID_AUDIO_NCH=1
ID_AUDIO_CODEC=pcm
ID_EXIT=EOF
Here's a routine that takes a string containing midentify output, and returns an association list of the key-value pairs (which is safer than setting Emacs variables willy-nilly). It also has the advantage that it parses numeric values into actual numbers:
(require 'cl) ; for "loop"
(defun midentify-output-to-alist (str)
(setq str (replace-regexp-in-string "\n+" "\n" str))
(setq str (replace-regexp-in-string "\n+\\'" "" str))
(loop for index = 0 then (match-end 0)
while (string-match "^\\(?:\\([A-Z_]+\\)=\\(?:\\([0-9]+\\(?:\\.[0-9]+\\)?\\)\\|\\(.*\\)\\)\\|\\(.*\\)\\)\n?" str index)
if (match-string 4 str)
do (error "Invalid line: %s" (match-string 4 str))
collect (cons (match-string 1 str)
(if (match-string 2 str)
(string-to-number (match-string 2 str))
(match-string 3 str)))))
You'd use this function like so:
(setq alist (midentify-output-to-alist my-output))
(if (assoc "ID_LENGTH" alist)
(setq id-length (cdr (assoc "ID_LENGTH" alist)))
(error "Didn't find an ID_LENGTH!"))
EDIT: Modified function to handle blank lines and trailing newlines correctly.
The regexp is indeed a beast; Emacs regexps are not known for their easiness on the eyes. To break it down a bit:
The outermost pattern is ^(?:valid-line)|(.*). It tries to match a valid line, or else matches the entire line (the .*) in match-group 4. If (match-group 4 str) is not nil, that indicates that an invalid line was encountered, and an error is raised.
valid-line is (word)=(?:(number)|(.*)). If this matches, then the name part of the name-value pair is in match-string 1, and if the rest of the line matches a number, then the number is in match-string 2, otherwise the entire rest of the line is in match-string 3.
There's probably a better way but this should do it:
(require 'cl)
(let ((s "ID_AUDIO_ID=0
ID_FILENAME=/home/axiom/abc.wav
ID_DEMUXER=audio
ID_AUDIO_FORMAT=1
ID_AUDIO_BITRATE=512000
ID_AUDIO_RATE=0
ID_AUDIO_NCH=1
ID_LENGTH=3207.00
ID_SEEKABLE=1
ID_CHAPTERS=0
ID_AUDIO_BITRATE=512000
ID_AUDIO_RATE=32000
ID_AUDIO_NCH=1
ID_AUDIO_CODEC=pcm
ID_EXIT=EOF"))
(loop for p in (split-string s "\n")
do
(let* ((elements (split-string p "="))
(key (elt elements 0))
(value (elt elements 1)))
(set (intern key) value))))
Here's a function you can run on the output buffer:
(defun set-variables-from-shell-assignments ()
(goto-char (point-min))
(while (< (point) (point-max))
(and (looking-at "\\([A-Z_]+\\)=\\(.*\\)$")
(set (intern (match-string 1)) (match-string 2)))
(forward-line 1)))
I don't think regexp is what really need. You need to split your string by \n and =, so you just say exactly the same to interpreter.
I think you can also use intern to get symbol from string(and set variables). I use it for the first time, so comment here if i am wrong. Anyways, if list is what you want, just remove top-level mapcar.
(defun set=(str)
(mapcar (lambda(arg)
(set
(intern (car arg))
(cadr arg)))
(mapcar (lambda(arg)
(split-string arg "=" t))
(split-string
str
"\n" t))))
(set=
"ID_AUDIO_ID=0
ID_FILENAME=/home/axiom/abc.wav
ID_DEMUXER=audio
ID_AUDIO_FORMAT=1
ID_AUDIO_BITRATE=512000
ID_AUDIO_RATE=0
ID_AUDIO_NCH=1
ID_LENGTH=3207.00
ID_SEEKABLE=1
ID_CHAPTERS=0
ID_AUDIO_BITRATE=512000
ID_AUDIO_RATE=32000
ID_AUDIO_NCH=1
ID_AUDIO_CODEC=pcm
ID_EXIT=EOF")