File: wrapping-reader.lisp

package info (click to toggle)
agnostic-lizard 0~git20201010.1.fe3a737-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 240 kB
  • sloc: lisp: 2,026; sh: 11; makefile: 2
file content (40 lines) | stat: -rw-r--r-- 1,508 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
(in-package :agnostic-lizard)

(defun wrap-every-form-reader (callback normal-readtable)
  "Prepare a special reader for the readtable that reads every top-level form 
  using normal-readtable, then applies the callback to the form"
  (lambda (stream char)
    (unread-char char stream)
    (let*
      ((*readtable* normal-readtable)
       (eof-marker (gensym))
       (form (read stream nil eof-marker)))
      (cond
        ((equal form eof-marker) nil)
        (t (funcall callback form))))))

(defun install-wrap-every-form-reader (callback)
  "Add an entry to the readtable that reads top-level forms normally, then 
  applies the callback to each form"
  (setf *readtable* (copy-readtable))
  (set-macro-character
    #\( (wrap-every-form-reader callback (copy-readtable)) 
    ; #\) will work fine on its own; this comment also closes the parenthesis
    ))

(defmacro wrap-rest-of-input (callback)
  "Modify each top-level form in the rest of the current file with the callback"
  `(eval-when
     (:compile-toplevel :load-toplevel :execute)
     (install-wrap-every-form-reader ,callback)))

(defmacro with-wrap-every-form-reader (callback &body body)
  "Execute body using a modified readtable so that every top-level form read by
  the reader is modified using the callback"
  `(let*
     ((old-readtable *readtable*))
     (setf *readtable* (copy-readtable))
     (install-wrap-every-form-reader ,callback)
     (unwind-protect
       (progn ,@ body)
       (setf *readtable* old-readtable))))