SBCL -Common Lisp Error - lisp

I am trying to run the repository from the following link:
https://github.com/JafferWilson/test-program
I am getting this error:
sbcl --script paraphrasing.lisp
Unhandled SB-KERNEL:SIMPLE-PACKAGE-ERROR in thread #<SB-THREAD:THREAD
"main thread" RUNNING
{100399C6D3}>:
The name "USER" does not designate any package.
Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {100399C6D3}>
0: ((LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX))
1: (SB-IMPL::CALL-WITH-SANE-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX) {10039E3C1B}>)
2: (SB-IMPL::%WITH-STANDARD-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX) {10039E3BEB}>)
3: (PRINT-BACKTRACE :STREAM #<SB-SYS:FD-STREAM for "standard error" {10039A22B3}> :START 0 :FROM :INTERRUPTED-FRAME :COUNT NIL :PRINT-THREAD T :PRINT-FRAME-SOURCE NIL :METHOD-FRAME-STYLE NIL)
4: (SB-DEBUG::DEBUGGER-DISABLED-HOOK #<SB-KERNEL:SIMPLE-PACKAGE-ERROR "The name ~S does not designate any package." {10039E1643}> #<unavailable argument>)
5: (SB-DEBUG::RUN-HOOK *INVOKE-DEBUGGER-HOOK* #<SB-KERNEL:SIMPLE-PACKAGE-ERROR "The name ~S does not designate any package." {10039E1643}>)
6: (INVOKE-DEBUGGER #<SB-KERNEL:SIMPLE-PACKAGE-ERROR "The name ~S does not designate any package." {10039E1643}>)
7: (ERROR SB-KERNEL:SIMPLE-PACKAGE-ERROR :PACKAGE "USER" :FORMAT-CONTROL "The name ~S does not designate any package." :FORMAT-ARGUMENTS ("USER"))
8: (SB-INT:%FIND-PACKAGE-OR-LOSE "USER")
9: (SB-INT:FIND-UNDELETED-PACKAGE-OR-LOSE "USER")
10: ((LAMBDA NIL :IN "/home/aims/paraphrasing/paraphrasing.lisp"))
11: (SB-INT:SIMPLE-EVAL-IN-LEXENV (SETQ *PACKAGE* (SB-INT:FIND-UNDELETED-PACKAGE-OR-LOSE "USER")) #<NULL-LEXENV>)
12: (SB-INT:SIMPLE-EVAL-IN-LEXENV (IN-PACKAGE "USER") #<NULL-LEXENV>)
13: (EVAL-TLF (IN-PACKAGE "USER") 0 NIL)
14: ((LABELS SB-FASL::EVAL-FORM :IN SB-INT:LOAD-AS-SOURCE) (IN-PACKAGE "USER") 0)
15: ((LAMBDA (SB-KERNEL:FORM &KEY :CURRENT-INDEX &ALLOW-OTHER-KEYS) :IN SB-INT:LOAD-AS-SOURCE) (IN-PACKAGE "USER") :CURRENT-INDEX 0)
16: (SB-C::%DO-FORMS-FROM-INFO #<CLOSURE (LAMBDA (SB-KERNEL:FORM &KEY :CURRENT-INDEX &ALLOW-OTHER-KEYS) :IN SB-INT:LOAD-AS-SOURCE) {10039A7ECB}> #<SB-C::SOURCE-INFO {10039A7E83}> SB-C::INPUT-ERROR-IN-LOAD)
17: (SB-INT:LOAD-AS-SOURCE #<SB-SYS:FD-STREAM for "file /home/aims/paraphrasing/paraphrasing.lisp" {10039A4703}> :VERBOSE NIL :PRINT NIL :CONTEXT "loading")
18: ((FLET SB-FASL::LOAD-STREAM :IN LOAD) #<SB-SYS:FD-STREAM for "file /home/aims/paraphrasing/paraphrasing.lisp" {10039A4703}> NIL)
19: (LOAD #<SB-SYS:FD-STREAM for "file /home/aims/paraphrasing/paraphrasing.lisp" {10039A4703}> :VERBOSE NIL :PRINT NIL :IF-DOES-NOT-EXIST T :EXTERNAL-FORMAT :DEFAULT)
20: ((FLET SB-IMPL::LOAD-SCRIPT :IN SB-IMPL::PROCESS-SCRIPT) #<SB-SYS:FD-STREAM for "file /home/aims/paraphrasing/paraphrasing.lisp" {10039A4703}>)
21: ((FLET #:WITHOUT-INTERRUPTS-BODY-146 :IN SB-IMPL::PROCESS-SCRIPT))
22: (SB-IMPL::PROCESS-SCRIPT "paraphrasing.lisp")
23: (SB-IMPL::TOPLEVEL-INIT)
24: ((FLET #:WITHOUT-INTERRUPTS-BODY-82 :IN SAVE-LISP-AND-DIE))
25: ((LABELS SB-IMPL::RESTART-LISP :IN SAVE-LISP-AND-DIE))
unhandled condition in --disable-debugger mode, quitting
Kindly, help me to resolve this so the program runs effectively.

For SBCL:
Create a package and use only the package COMMON-LISP:
(defpackage "PARAPHRASING"
(:use "COMMON-LISP"))
(in-package "PARAPHRASING")
Replace all DEFCONSTANT defining lists with DEFPARAMETER
(defparameter +bad-single-pos-paraphrasings+
(mapcar #'intern '("TO" "DT" "CC" "IN" "WDT" "POS" "PRP")))
(defparameter +input-paren-types+
(list (list "[[" (intern "NN")) (list "((" (intern "VB"))))
;; Stuff with no alphabetic chars is ignored in any case
(defparameter +auxiliaries+ (mapcar #'intern '("DT" "IN" "TO")))
;; Lengths taken for rules
(defparameter +context-lens+ '(1))
Make sure the return value of NREVERSE is used
(defun extract-paraphrases1 (pair rule)
(labels (;; Find all positions where prfx is in seq.
(find-prefix-positions (prfx seq)
(if (null (first prfx))
;; Must be at the beginning.
(and (pos-match-prefix (rest prfx) seq) (list -1))
(pos-sub-positions prfx seq)))
;; Find all positions where sffx is in seq - after some point.
(find-suffix-positions (skip sffx seq)
(let ((lst (nthcdr skip seq)))
(if (null (first sffx))
;; a trick - nreverse both lists, not expensive
(let* ((slen (length (rest sffx)))
(sffx (nreverse (rest sffx)))
(lst (nreverse lst))
(m? (pos-match-prefix sffx lst)))
;; restore things
(setf sffx (nreverse sffx)) ; <- use the result
(setf lst (nreverse lst)) ; <- use the result
...
INTERN
All the calls to INTERN should use the package name "PARAPHRASING" as a second parameter. This way the symbols are always created in the correct package.
Use
* (paraphrasing::co-train "20000leagues-tagged-pairs-roots.txt")
Reading input...
Done, 6275 sentences read.
========== Iteration #1/10 ==========
Learning context rules...
...6250 left
...6000 left
...

Related

When I run my lisp code, it says undefined function NIL

The task was to Create an XLISP program that simulates the stack implementation of push and pop. Remember, the push and pop of a stack happens only on the top of stack (which is different from a queue)
In this case, we assume that the right most part of your list is the top portion.
Push operation description
Ask the user to enter a number
Insert the data into your stack
0 or negative number is not allowed. If so, simply print "Invalid Input"
Pop
Simply removes the top data from your stack.
Assumption:
You have a list called mystack initialized as an empty list.
Example Run:
(setq myStack())
NIL
(push)
*** When I try to run the code it says that undefined function NIL
(setq myStack(nil))
> (push)
> (pop)
; PUSH Function
(defun push ()
(let ((num (read)))
(if (and (numberp num) (> num 0))
(setq myStack (append myStack (list num)))
(print "Invalid Input"))))
; POP Function
(defun pop ()
(if (null myStack)
(print "Stack is empty")
(progn
(setq myStack (butlast myStack))
(print myStack))))
Your Problem first of all is
(setq myStack (nil))
In Common Lisp, one would write it:
(defparameter *my-stack* nil)
In Common Lisp, there is the equality: NIL == () == '() == 'NIL.
What you want is an empty list, which is one of those.
Remember, an empty list () or '() already contains an implicit NIL
as its last CDR. Proof:
(cdr ()) ;; => NIL
(cdr '()) ;; => NIL
(cdr NIL) ;; => NIL
(cdr 'NIL) ;; => NIL
At least in Common Lisp it is defined like this.
However, in Racket/Scheme this is not defined like this. Therefore, this is not universal to Lisps:
$ racket
Welcome to Racket v6.11.
> (cdr NIL)
; NIL: undefined;
; cannot reference undefined identifier
; [,bt for context]
> (cdr '())
; cdr: contract violation
; expected: pair?
; given: '()
; [,bt for context]
> (cdr ())
; readline-input:4:5: #%app: missing procedure expression;
; probably originally (), which is an illegal empty application
; in: (#%app)
; [,bt for context]
> (cdr 'NIL)
; cdr: contract violation
; expected: pair?
; given: 'NIL
; [,bt for context]
Therefore, in XLISP this must not be ...
However, for all lisps, it will be true that you need '() as an empty list.
So at least, your line must be:
(setq my-stack '())
If you forget the quote ', the interpreter/compiler thinks it is a function call and searches the function name nil and doesn't find it. Therefore your error.
Later you ask in your pop function: (null my-stack). If you start with '(nil), the problem is you don't start with an empty list.
Proof:
In Common Lisp:
(null '(nil)) ;;=> nil
In Racket:
(null? '(nil)) ;;=> #f
Why? because your list then contains still and element which has as value NIL.
In both languages, you can do:
(cdr '(nil)) ;;=> '() <-- Now the list is empty!
I would define your push und pop in Common Lisp:
(defparameter *my-stack* '())
(defun my-push (x)
(setf *my-stack* (cons x *my-stack*))
(defun my-pop ()
(let ((x (car *my-stack*)))
(setf *my-stack* (cdr *my-stack*))
x))
As you see I don't append it at the end because this is very inefficient (otherwise one has to traverse the entire list).
In Lisp, one adds at the start by cons-ing. And pops from the start by car-ing and cdr-ing.
Also, your pop doesn't return the pop-ed value.
Actually, your function to behave like the in-built push and pop in Common Lisp, must be:
(defmacro my-push (x lst)
`(setf ,lst (cons ,x ,lst)))
(defmacro my-pop (lst)
`(let ((x (car ,lst)))
(setf ,lst (cdr ,lst))
x))
Usage:
(defparameter *stack* '())
(my-push 1 *stack*) ;;=> (1)
(my-push 2 *stack*) ;;=> (2 1)
(my-push 3 *stack*) ;;=> (3 2 1)
*stack* ;;=> (3 2 1)
(my-pop *stack*) ;;=> 3
(my-pop *stack*) ;;=> 2
(my-pop *stack*) ;;=> 1
(my-pop *stack*) ;;=> NIL
(my-pop *stack*) ;;=> NIL
;; whether the list is empty or the first element's value is NIL
;; you can check by (length *stack*) => if that is 0, the stack is empty
In racket:
(define-syntax-rule (push x lst)
(set! lst (cons x lst)))
(define-syntax-rule (pop lst)
(let ((x (car lst)))
(set! lst (cdr lst))
x))
Usage:
> (define stack '())
> (push 1 stack)
> (push 2 stack)
> (push 3 stack)
> stack
'(3 2 1)
> (pop stack)
3
> (pop stack)
2
> (pop stack)
1
> (pop stack)
; car: contract violation
; expected: pair?
; given: '()
; [,bt for context]

How to do auto input in multiple read-lines?

How to do auto input in multiple read-line?
(let ((out (with-output-to-string (*standard-output*)
(let ((*standard-input* (make-string-input-stream "y y")))
(when (find (read-line) '("yes" "y" "t") :test #'string-equal)
(print "aaaaa"))
(when (find (read-line) '("yes" "y" "t") :test #'string-equal)
(print "bbbbbb"))
))))
out)
I try like this, and I get:
; Evaluation aborted on #<END-OF-FILE {10048FD503}>.
This code work with read, but I need with read-line.
Another possibility is to use the parameter of read-line that requires to return nil on end of file:
(let ((out (with-output-to-string (*standard-output*)
(let ((*standard-input* (make-string-input-stream "y y")))
(when (find (read-line *standard-input* nil) '("yes" "y" "t") :test #'string-equal)
(print "aaaaa"))
(when (find (read-line *standard-input* nil) '("yes" "y" "t") :test #'string-equal)
(print "bbbbbb"))))))
out)
I made it work like this:
(with-output-to-string (*standard-output*)
(with-input-from-string (*standard-input* (format nil "y~%y"))
(when (find (read-line) '("yes" "y" "t") :test #'string-equal)
(print "aaaaa"))
(when (find (read-line) '("yes" "y" "t") :test #'string-equal)
(print "bbbbbb"))))
The without-to-string is unnecessary for an example...
CL-USER 177 > (flet ((yes-p (input-string &aux (yes-words '("yes" "y" "t")))
"returns T when the input-string is one of yes, y or t."
(find input-string yes-words :test #'string-equal)))
(with-input-from-string (*standard-input* (format nil "y~%y"))
(when (yes-p (read-line))
(print "aaaaa"))
(when (yes-p (read-line))
(print "bbbbbb"))
(values)))
"aaaaa"
"bbbbbb"

How to define (defmacro) a macro inside a let binding in sbcl common lisp?

I am using SBCL Common Lisp. I am not an expert, but I like to think I understand it well enough to muddle along. However, I have recently encountered a strange problem with defmacro.
Why does the following code not compile, and how do I change it to make it compile?
(let ((a nil))
(defmacro testmacro ())
(testmacro))
The error is:
Unhandled UNDEFINED-FUNCTION in thread #<SB-THREAD:THREAD "main thread" RUNNING
{100399C9A3}>:
The function COMMON-LISP-USER::TESTMACRO is undefined.
Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {100399C9A3}>
0: ((LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX))
1: (SB-IMPL::CALL-WITH-SANE-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX) {1003A0EA8B}>)
2: (SB-IMPL::%WITH-STANDARD-IO-SYNTAX #<CLOSURE (LAMBDA NIL :IN SB-DEBUG::FUNCALL-WITH-DEBUG-IO-SYNTAX) {1003A0EA5B}>)
3: (PRINT-BACKTRACE :STREAM #<SB-SYS:FD-STREAM for "standard error" {10039A22B3}> :START 0 :FROM :INTERRUPTED-FRAME :COUNT NIL :PRINT-THREAD T :PRINT-FRAME-SOURCE NIL :METHOD-FRAME-STYLE NIL)
4: (SB-DEBUG::DEBUGGER-DISABLED-HOOK #<UNDEFINED-FUNCTION TESTMACRO {1003A0C193}> #<unavailable argument>)
5: (SB-DEBUG::RUN-HOOK *INVOKE-DEBUGGER-HOOK* #<UNDEFINED-FUNCTION TESTMACRO {1003A0C193}>)
6: (INVOKE-DEBUGGER #<UNDEFINED-FUNCTION TESTMACRO {1003A0C193}>)
7: (ERROR UNDEFINED-FUNCTION :NAME TESTMACRO)
8: ((LAMBDA (&REST SB-C::ARGS) :IN SB-C::INSTALL-GUARD-FUNCTION))
9: (SB-INT:SIMPLE-EVAL-IN-LEXENV (LET ((A NIL)) (DEFMACRO TESTMACRO NIL) (TESTMACRO)) #<NULL-LEXENV>)
10: (EVAL-TLF (LET ((A NIL)) (DEFMACRO TESTMACRO NIL) (TESTMACRO)) 0 NIL)
11: ((LABELS SB-FASL::EVAL-FORM :IN SB-INT:LOAD-AS-SOURCE) (LET ((A NIL)) (DEFMACRO TESTMACRO NIL) (TESTMACRO)) 0)
12: ((LAMBDA (SB-KERNEL:FORM &KEY :CURRENT-INDEX &ALLOW-OTHER-KEYS) :IN SB-INT:LOAD-AS-SOURCE) (LET ((A NIL)) (DEFMACRO TESTMACRO NIL) (TESTMACRO)) :CURRENT-INDEX 0)
13: (SB-C::%DO-FORMS-FROM-INFO #<CLOSURE (LAMBDA (SB-KERNEL:FORM &KEY :CURRENT-INDEX &ALLOW-OTHER-KEYS) :IN SB-INT:LOAD-AS-SOURCE) {10039B19FB}> #<SB-C::SOURCE-INFO {10039B19B3}> SB-C::INPUT-ERROR-IN-LOAD)
14: (SB-INT:LOAD-AS-SOURCE #<SB-SYS:FD-STREAM for "file /mnt/nas-data/Documents/Projects/SHARED PROJECTS - EVAN/pkcmd-lx/temp.lisp" {10039A5103}> :VERBOSE NIL :PRINT NIL :CONTEXT "loading")
15: ((FLET SB-FASL::LOAD-STREAM :IN LOAD) #<SB-SYS:FD-STREAM for "file /mnt/nas-data/Documents/Projects/SHARED PROJECTS - EVAN/pkcmd-lx/temp.lisp" {10039A5103}> NIL)
16: (LOAD #<SB-SYS:FD-STREAM for "file /mnt/nas-data/Documents/Projects/SHARED PROJECTS - EVAN/pkcmd-lx/temp.lisp" {10039A5103}> :VERBOSE NIL :PRINT NIL :IF-DOES-NOT-EXIST T :EXTERNAL-FORMAT :DEFAULT)
17: ((FLET SB-IMPL::LOAD-SCRIPT :IN SB-IMPL::PROCESS-SCRIPT) #<SB-SYS:FD-STREAM for "file /mnt/nas-data/Documents/Projects/SHARED PROJECTS - EVAN/pkcmd-lx/temp.lisp" {10039A5103}>)
18: ((FLET #:WITHOUT-INTERRUPTS-BODY-146 :IN SB-IMPL::PROCESS-SCRIPT))
19: (SB-IMPL::PROCESS-SCRIPT "temp.lisp")
20: (SB-IMPL::TOPLEVEL-INIT)
21: ((FLET #:WITHOUT-INTERRUPTS-BODY-82 :IN SAVE-LISP-AND-DIE))
22: ((LABELS SB-IMPL::RESTART-LISP :IN SAVE-LISP-AND-DIE))
The obvious answer is, of course, to put the defmacro outside of the let binding. But, for technical reasons, this would be very inconvenient for my project. Besides, I know of no valid reason why defining a macro under a let binding should fail.
Is 'let over macro' specifically prohibited in Common Lisp? Or am I missing something?
EDIT: The crux of my requirement is that the macro shares the same level as the function it calls, and subsequent code is not nested within it.
This is because I am attempting to write a macro that generates a function and a macro at the same time. The generated macro would call the function. So we end up with something like the following.
I write a line like:
(generate-stuff function-name)
And this resolves into:
(defun function-name-1 () ...)
(defmacro function-name (&rest args)
`(function-name-1 ,#args)
Therefore the macro and the function it calls must be at the same lexical level, adjacent to each other, and cannot create a new lexical environment (macrolet) for subsequent code to nest within.
This would all work fine, except that I happen to be within a let binding at the time; because the function in question has to refer to variables within this binding.
Note that the macro is accessible from outside the binding:
(let ...)
(defmacro my-macro ...)
(my-macro)
It seems absurd to me that a macro defined inside a let binding should only be accessible after the binding has ended. Nothing else in lisp behaves this way.
Local macros can be defined with MACROLET.
Besides, I know of no valid reason why defining a macro under a let binding should fail.
It doesn't fail. It's just not available during compile time, if you just compile the file or compile the expression. The compiler does not make defmacro definitions available in the compile-time environment, if the definition is not at top-level. Inside a progn it would be at top-level, but not inside a let.
Remember: SBCL compiles Lisp source code to machine code using a compiler. It executes machine code.
Let's look at your example:
(let ((a nil))
(defmacro testmacro ())
(testmacro))
Generally putting a global macro into a LET is bad practice and it's hard to understand what it actually should do. What should the influence of the binding of a be? Remember, a compiler compiles code before it executes.
Let's assume we have SBCL and SBCL loads a file with the above form:
(load "foo.lisp")
SBCL does now: READ the whole form, COMPILE the whole form, EXECUTE the whole form. In that order.
SBCL reads the first form. Which is the equivalent of:
CL-USER 155 > (read-from-string "(let ((a nil))
(defmacro testmacro ())
(testmacro))")
(LET ((A NIL)) (DEFMACRO TESTMACRO NIL) (TESTMACRO))
Thus we have data.
The next step is that SBCL compiles that code.
it sees a LET expression. It creates code for the LET bindings. It does not execute the LET. We are just compiling it.
it then compiles the body of the LET
it sees a DEFMACRO form: the macro gets expanded and it gets compiled to machine code. It does not execute the DEFMACRO for. We are just expanding and compiling it.
it sees a TESTMACRO form: It has no idea what it is. It compiles it as a function call to an undefined function and warns about an undefined function
The next step is that SBCL executes the compiled code
it runs the compiled LET form and creates the binding for a
it then executes the body of the LET
it executes the compiled DEFMACRO form: a global macro named TESTMACRO gets defined
it the executes the compiled TESTMACRO form: the function gets called. It is not defined. An error is signalled.
That's all logical and in no way absurd. SBCL first reads the LET, then compiles the LET to machine code and then runs the machine code.
These are three independent steps: read-time, compile-time, run-time.
Let's try it in the REPL:
First we are READing the form
* (read-from-string "(let ((a nil))
(defmacro testmacro ())
(testmacro))")
(LET ((A NIL))
(DEFMACRO TESTMACRO ())
(TESTMACRO))
129
Then we are compiling the form:
* (compile nil `(lambda () ,*))
; in: LAMBDA ()
; (LET ((A NIL))
; (DEFMACRO TESTMACRO ())
; (TESTMACRO))
;
; caught STYLE-WARNING:
; The variable A is defined but never used.
; in: LAMBDA ()
; (TESTMACRO)
;
; caught STYLE-WARNING:
; undefined function: COMMON-LISP-USER::TESTMACRO
;
; compilation unit finished
; Undefined function:
; TESTMACRO
; caught 2 STYLE-WARNING conditions
#<FUNCTION (LAMBDA ()) {226B025B}>
T
NIL
You can see that SBCL tells us about some problems. TESTMACRO is undefined.
Now we run the code:
* (funcall *)
STYLE-WARNING:
TESTMACRO is being redefined as a macro when it was previously assumed to be a function.
debugger invoked on a UNDEFINED-FUNCTION in thread
#<THREAD "main thread" RUNNING {10004F84C3}>:
The function COMMON-LISP-USER::TESTMACRO is undefined.
Type HELP for debugger help, or (SB-EXT:EXIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [CONTINUE ] Retry calling TESTMACRO.
1: [USE-VALUE ] Call specified function.
2: [RETURN-VALUE ] Return specified values.
3: [RETURN-NOTHING] Return zero values.
4: [ABORT ] Exit debugger, returning to top level.
("undefined function")
0]
As expected - the compiler did warn us: the function TESTMACRO is undefined.
If you want SBCL to compile a macro form, you have to make sure that the macro form is known at compile-time.

if clause nested in let inside a macro not working as expected

Alright first of all this is my first question so I apologize for any bad practice and appreciate if you tell me I'm doing something wrong.
I am trying to write a macro to reduce repetitive code, which is create a package, system or code file in Common Lisp with a chapter number in its name. The following code is what I have and it works perfectly when :chapter-number is passed as a string, but goes wrong when it is passed as number:
(defmacro with-open-chapter-file
((streamvar (component &key
(type "lisp")
(directory (sb-posix:getcwd))
chapter-number))
(&body body))
`(let ((chapter-number ,(if (numberp chapter-number) ; the problem is at this if clause.
(write-to-string chapter-number) ; My intention was to convert it to a string if it was a number or leave it as is otherwise.
chapter-number)))
(with-open-file (,streamvar (make-pathname
:name ,(if chapter-number ; the variable manipulated in the if clause is used in this expression
(concatenate 'string "chapter-" chapter-number "-" (string component))
component)
:type ,type
:defaults ,directory)
:direction :output)
,body)))
When I run the following test:
(macroexpand-1 '(with-open-chapter-file (out ("pack" :chapter-number 10))
(format t "Hey!")))
I get the error:
The value
10
is not of type
SEQUENCE
[Condition of type TYPE-ERROR]
And the backtrace:
0: (LENGTH 10)
1: (SB-KERNEL:%CONCATENATE-TO-STRING "chapter-" 10 "-" "pack")
2: ((MACRO-FUNCTION WITH-OPEN-CHAPTER-FILE) (WITH-OPEN-CHAPTER-FILE (OUT ("pack" :CHAPTER-NUMBER 10)) (FORMAT T "Hey!")) #<unused argument>)
3: ((FLET SB-IMPL::PERFORM-EXPANSION :IN MACROEXPAND-1) #<FUNCTION (MACRO-FUNCTION WITH-OPEN-CHAPTER-FILE) {2278173B}> NIL)
4: (SB-INT:SIMPLE-EVAL-IN-LEXENV (MACROEXPAND-1 (QUOTE (WITH-OPEN-CHAPTER-FILE # #))) #<NULL-LEXENV>)
5: (EVAL (MACROEXPAND-1 (QUOTE (WITH-OPEN-CHAPTER-FILE # #))))
I would be extremely grateful if you guys could help me.
In the code:
:name ,(if chapter-number ; the variable manipulated in the if clause is used in this expression
(concatenate 'string "chapter-" chapter-number "-" (string component))
component)
you're using the chapter-number parameter to the macro, not the variable that you bound with let in the expansion, because this code is after a comma.
You shouldn't be binding that variable in the expansion, you should just update the variable in the macro itself.
(defmacro with-open-chapter-file
((streamvar (component &key
(type "lisp") (directory (sb-posix:getcwd)) chapter-number))
(&body body))
(when (numberp chapter-number)
(setq chapter-number (write-to-string chapter-number)))
`(with-open-file (,streamvar (make-pathname
:name ,(if chapter-number
(concatenate 'string "chapter-" chapter-number "-" (string component))
component)
:type ,type
:defaults ,directory)
:direction :output)
,#body))
Another solution that doesn't require testing the type of chapter-number is to change the code that uses concatenate to use format:
(if chapter-number
(format nil "chapter-%A-%A" chapter-number component)
component)
An unrelated mistake is that you should use ,#body to substitute the body, since it's a list that must be spliced into the expression.
A typical problem with macros is to understand that in general they deal with code: they receive code and produce code. Generally they don't know the value of variables, because the code has not been run yet.
For example imagine:
(let ((n 10))
(with-open-chapter-file (out ("pack" :chapter-number n))
(format t "Hey!")))
Now there is no general way in the macro to know what the value of n is. When the macro form gets expanded during compilation, it sees a n and nothing more.
Now when you have an actual number in the code, the macro sees that number as part of the source:
(with-open-chapter-file (out ("pack" :chapter-number 10)
(format t "Hey!")))
Now we can ask us, if it would make sense for the macro to recognize the number during macro expansion and to compute something at macro expansion time? It's kind of an optimization and it might not be worth it. Now, the compiler might detect that it is a constant and could be converted at compile time...
Thus in your example it might be okay to at runtime convert the argument to a string, instead of doing it at macroexpansion time.
Now lets assume the code looks like this:
(defmacro with-open-chapter-file
((streamvar (component
&key
(type "lisp")
(directory "/foo/")
chapter-number))
(&body body))
(when (numberp chapter-number)
(setf chapter-number (write-to-string chapter-number)))
`(let ((component ,component)
(type ,type)
(directory ,directory)
(chapter-number ,chapter-number))
(when (numberp chapter-number)
(setf chapter-number (write-to-string chapter-number)))
(with-open-file
(,streamvar (make-pathname
:name (if chapter-number
(format nil
"chapter-~a-~a"
chapter-number
component)
component)
:type type
:defaults directory)
:direction :output)
,#body)))
Now we can do this:
a) with n
CL-USER 6 > (pprint (macroexpand-1 '(with-open-chapter-file (out ("pack" :chapter-number n))
(format t "Hey!"))))
(LET ((COMPONENT "pack") (TYPE "lisp") (DIRECTORY "/foo/") (CHAPTER-NUMBER N))
(WHEN (NUMBERP CHAPTER-NUMBER) (SETF CHAPTER-NUMBER (WRITE-TO-STRING CHAPTER-NUMBER)))
(WITH-OPEN-FILE (OUT
(MAKE-PATHNAME :NAME
(IF CHAPTER-NUMBER
(FORMAT NIL "chapter-~a-~a" CHAPTER-NUMBER COMPONENT)
COMPONENT)
:TYPE
TYPE
:DEFAULTS
DIRECTORY)
:DIRECTION
:OUTPUT)
FORMAT
T
"Hey!"))
and b) with 10
CL-USER 7 > (pprint (macroexpand-1 '(with-open-chapter-file (out ("pack" :chapter-number 10))
(format t "Hey!"))))
(LET ((COMPONENT "pack") (TYPE "lisp") (DIRECTORY "/foo/") (CHAPTER-NUMBER "10"))
(WHEN (NUMBERP CHAPTER-NUMBER) (SETF CHAPTER-NUMBER (WRITE-TO-STRING CHAPTER-NUMBER)))
(WITH-OPEN-FILE (OUT
(MAKE-PATHNAME :NAME
(IF CHAPTER-NUMBER
(FORMAT NIL "chapter-~a-~a" CHAPTER-NUMBER COMPONENT)
COMPONENT)
:TYPE
TYPE
:DEFAULTS
DIRECTORY)
:DIRECTION
:OUTPUT)
FORMAT
T
"Hey!"))
But since format does a conversion anyway during printing, we can remove all that conversion logic...
(defmacro with-open-chapter-file
((streamvar (component
&key
(type "lisp")
(directory "/foo/")
chapter-number))
(&body body))
`(let ((component ,component)
(type ,type)
(directory ,directory)
(chapter-number ,chapter-number))
(let ((name (if chapter-number
(format nil
"chapter-~a-~a"
chapter-number
component)
component)))
(with-open-file (,streamvar (make-pathname
:name name
:type type
:defaults directory)
:direction :output)
,#body))))
Now you need to make sure that component, type ... are not unwanted runtime variables which then were visible from the body code...

How to eval Lisp code inside a reader macro?

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.