Why are reader macro extensions not propagated at runtime (read)?

Why does the following not work?

;;;; foo.lisp (in-package :cl-user) (eval-when (:compile-toplevel :load-toplevel :execute) (require :cl-interpol)) (cl-interpol:enable-interpol-syntax) (defun read-and-eval (s) (eval (read-from-string s))) (cl-interpol:disable-interpol-syntax) 

then

 LISP> (load (compile-file "foo.lisp")) => T LISP> (read-and-eval "(let ((a \"foo\")) (princ #?\"${a}\"))") => no dispatch function defined for #\? 
+6
source share
2 answers

CL: READ sends based on the reading table associated with CL: * READTABLE * during the start of a READ call. Under the hood, ENABLE-INTERPOL-SYNTAX creates a new read table by setting CL: * READTABLE * to hold it and resetting the old value to CL: * READTABLE *. DISABLE-INTERPOL-SYNTAX locks the previous reading table and sets CL: * READTABLE * to hold it again. Minimally changing the initial setting, you can arrange the following as you wish:

 (in-package :cl-user) (eval-when (:compile-toplevel :load-toplevel :execute) (require :cl-interpol)) (cl-interpol:enable-interpol-syntax) (defvar *interpol-reader* *readtable*) (cl-interpol:disable-interpol-syntax) (defun read-and-eval (s) (let ((*readtable* *interpol-reader*)) (eval (read-from-string s)))) 

A call to disable syntax can be placed somewhere after defvar and read-and-eval still work, but if you want to directly enter the interpol syntax into a file, the syntax should be placed between allowing and disconnecting calls. For this latter purpose, it is important that interol calls expand into EVAL-WHENs for the same reason that you need to be in EVAL-WHEN for your REQUIRE call; that is, the effects should have happened when the last forms of READ.

The CL-INTERPOL interface abstracts what is happening, so I will show you how you can manually create and modify a reading:

 ;; Create a fresh readtable with standard syntax (defvar *not-readtable* (copy-readtable nil)) ;; A simple reader function (defun not-reader (stream char &optional count) "Like ' but for (not ...) instead of (quote ...)" (declare (ignore count char)) `(not ,(read stream t nil t))) ;; Mutate that readtable so that the dispatch character you want ;; calls the function you want (set-macro-character #\! 'not-reader nil *not-readtable*) ;; Try it out (let ((*readtable* *not-readtable*)) (read-from-string "(if !foo bar baz)")) => (IF (NOT FOO) BAR BAZ) 
+8
source

Because there is only one reader with global status. You effectively turn your macros on and off. In this case, reader macros are enabled only during the time that your read-and-eval function is read at compile time.

In this case, you will need to install the macros inside the read-and-eval function to make sure that the reader is in the correct state when you need it.

+10
source

Source: https://habr.com/ru/post/952981/


All Articles