2013-09-02 4 views
6

Warum funktioniert das Folgende nicht?Warum werden Leser-Makroerweiterungen nicht zur Laufzeit propagiert (gelesen)?

;;;; 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) 

dann:

LISP> (load (compile-file "foo.lisp")) 
=> T 

LISP> (read-and-eval 
     "(let ((a \"foo\")) (prinC#?\"${a}\"))") 
=> no dispatch function defined for #\? 

Antwort

8

CL: READ-Versendungen basierend auf der lesbaren Tabelle an CL: * READTABLE * zum Zeitpunkt des Aufrufs von READ. Unter der Haube erstellt ENABLE-INTERPOL-SYNTAX eine neue lesbare Tabelle mit der Einstellung CL: * READTABLE *, um sie zu speichern und den alten Wert von CL zu speichern: * READTABLE *. DISABLE-INTERPOL-SYNTAX löscht die vorherige Tabelle und setzt CL: * READTABLE *, um sie erneut zu halten. Minimal Ihre ursprüngliche Setup ändern, können Sie für das Verhalten, das Sie durch die folgenden wollten arrangieren:

(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)))) 

Der Aufruf der Syntax deaktivieren überall nach dem defvar werden konnten platziert und Lese- und eval wird immer noch funktionieren, aber wenn Sie möchten direkt die Interpol-Syntax in die Datei eingeben, die zwischen den Aktivierungs- und Deaktivierungsaufrufen eingefügt werden muss. Zu diesem letztgenannten Zweck ist es von Bedeutung, dass die Interpol-Aufrufe zu EVAL-WHEN erweitert werden, aus dem gleichen Grund, aus dem es notwendig ist, dass Ihr Aufruf REQUIRE innerhalb eines EVAL-WHENs ist; Das heißt, die Effekte müssen bereits geschehen sein, wenn die letzteren Formulare READ sind.

CL-INTERPOL Schnittstelle abstrahiert, was geschieht, also werde ich Ihnen zeigen, wie Sie manuell erstellen könnte und ändern Readtable:

;; 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) 
10

Da es nur ein einziger Leser ist, mit dem globalen Zustand. Sie schalten Ihre Makros effektiv ein und aus. In diesem Fall werden die Lesermakros nur für die Dauer aktiviert, in der Ihre read-and-eval-Funktion zur Kompilierungszeit gelesen wird.

In diesem Fall müssten Sie die Makros innerhalb der read-and-eval Funktion einstellen, um sicherzustellen, dass der Leser im richtigen Zustand ist, wenn Sie ihn brauchen.