2013-09-02 18 views
6

Dlaczego następujące czynności nie działają?Dlaczego rozszerzenia makro czytnika nie są propagowane do środowiska wykonawczego (czytane)?

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

następnie:

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

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

Odpowiedz

8

CL: Odczytuje komunikaty na podstawie tabeli czytelnej związanej z CL: * READTABLE * w momencie uruchomienia wywołania READ. Pod maską ENABLE-INTERPOL-SYNTAX tworzy nowy readtable, ustawiając CL: * READTABLE *, aby go zatrzymać, i przechowuje starą wartość CL: * READTABLE *. DISABLE-INTERPOL-SYNTAX anuluje poprzedni odczyt i ustawia CL: * READTABLE * aby ponownie go przytrzymać. Minimalnie zmienia swoją pierwotną konfigurację, można zorganizować dla zachowania chciałeś brzmienie:

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

wywołanie wyłączyć składni może być umieszczony w dowolnym miejscu po defvar i odczytu i eval będzie nadal działać, ale jeśli chcesz bezpośrednio wprowadzić składnię interpolacji w pliku, którą składnia będzie musiała zostać umieszczona między włączaniem i wyłączaniem wywołań. W tym ostatnim celu ważne jest, że wywołania interpolowe rozszerzają się na KIERDĘ EVAL, z tego samego powodu, dla którego twoje wezwanie do POWTARZANIA musi znajdować się w punkcie GŁÓWNY; to znaczy, efekty muszą się już zdarzyć, gdy te ostatnie formularze są READ. Interfejs

CL-Interpolu abstrakty, co się dzieje, więc pokażę wam jak można ręcznie tworzyć i zmieniać 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

Ponieważ istnieje tylko jeden czytnik, ze stanem globalnej. Skutecznie włączasz i wyłączasz swoje makra. W tym przypadku makra czytnika są włączane tylko na czas odczytywania funkcji read-and-eval podczas kompilacji.

W takim przypadku należy ustawić makra w ramach funkcji read-and-eval, aby upewnić się, że czytnik jest w odpowiednim stanie, kiedy jest potrzebny.