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)
source share