I have a macro with-voice
:
(defmacro with-voice (tag body)
`(format nil "<span class=\"~a\">~%~a~%</span>" ',tag ,body))
which emits some text surrounded by a tag with some class. I know there are great libraries like CL-WHO - but I just need something tiny...
CL-USER> (with-voice narrator "foo")
"<span class=\"NARRATOR\">
foo
</span>"
which is is the desired outcome
I would like to be able to do this from a string
(let ((s (read-from-string "(with-voice narrator \"foo\")")))
(print (eval s)))
This works:
CL-USER> (let ((s (read-from-string "(with-voice narrator \"foo\")")))
(print (eval s)))
"<span class=\"NARRATOR\">
foo
</span>"
"<span class=\"NARRATOR\">
foo
</span>
but it has the dreaded eval
. I've tried to get this working using macros and lambdas, but I can't get it working.
I'd be grateful for some help
thanks!
You could funcall the symbol you expect in a certain position of the read form:
(destructuring-bind (operator class text)
(read-from-string "(with-voice narrator \"foo\")")
(funcall operator class text))
Or even just expect those forms to be funcallable:
(apply #'funcall (read-from-string "(with-voice narrator \"foo\")"))
If you have more different shapes of data, you might want to match those, e. g. using optima
, or using dispatch by eql
specializer. This can also help with validation if the read input might contain harmful intent.