Reputation: 3
I am having a problem with a lisp macro. I would like to create a macro which generate a switch case according to an array.
Here is the code to generate the switch-case:
(defun split-elem(val)
`(,(car val) ',(cdr val)))
(defmacro generate-switch-case (var opts)
`(case ,var
,(mapcar #'split-elem opts)))
I can use it with a code like this:
(generate-switch-case onevar ((a . A) (b . B)))
But when I try to do something like this:
(defparameter *operators* '((+ . OPERATOR-PLUS)
(- . OPERATOR-MINUS)
(/ . OPERATOR-DIVIDE)
(= . OPERATOR-EQUAL)
(* . OPERATOR-MULT)))
(defmacro tokenize (data ops)
(let ((sym (string->list data)))
(mapcan (lambda (x) (generate-switch-case x ops)) sym)))
(tokenize data *operators*)
I got this error: *** - MAPCAR: A proper list must not end with OPS
, but I don't understand why.
When I print the type of ops
I get SYMBOL
I was expecting CONS
, is it related?
Also, for my function tokenize
, how many times is the lambda evaluated (or the macro expanded)?
Thanks.
Upvotes: 0
Views: 560
Reputation: 139311
This makes no sense. You trying to use macros, where functions are sufficient.
What you want is similar to this:
(defun tokenize (data ops)
(mapcar (lambda (d)
(cdr (assoc d ops)))
(string->list data)))
CASE is a macro that expects a bunch of fixed clauses. It does not take clauses that are computed at runtime. If list data should drive computation, then use functions like ASSOC.
GENERATE-SWITCH-CASE is also an odd name, since the macro IS a switch case.
GENERATE-SWITCH-CASE also does expect a list as a second argument. But in TOKENIZE you call it with a symbol OPS. Remember, macros compute with Lisp source code.
Next, there are also no ARRAYs involved. Lisp has arrays, but in your example is none.
Typical advice:
if you want to write a MACRO, think again. Write it as a function.
if you still want to write a macro, Go to 1.
Upvotes: 1