summaryrefslogtreecommitdiff
path: root/env-wrappers.lisp
blob: 50341c78fd350116721a230f7572f0c701ce1072 (plain)
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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
(in-package :agnostic-lizard)

; Simple helpers to wrap a chunk of code

(defun macrolet-wrap (name function body)
  "Wrap a chunk of code with a macrolet defined by a macro-function"
  `(macrolet
     ((,name (&whole whole &rest args &environment environment)
             (declare (ignorable args))
             (funcall ,function whole environment)))
     ,@ body))

(defun macrolet-code-wrap (name code body)
  "Wrap a chunk of code with a macrolet given a literal macrolet definition"
  `(macrolet
     ((,name ,@code))
     ,@ body))

(defun symbol-macrolet-wrap (name expansion body)
  "Wrap a chunk of code with a symbol-macrolet"
  `(symbol-macrolet ((,name ,expansion)) ,@body))

(defun flet-wrap (name body)
  "Wrap a chunk of code with a pseudo-flet;
it is for macroexpansion only, so there is no body"
  `(flet ((,name (&rest args) (declare (ignorable args)) nil))
     ,@ body))

(defun let-wrap (name body)
  "Wrap a chunk of code with a pseudo-let;
it is for macroexpansion only, so there is no body"
  `(let ((,name nil)) (declare (ignorable ,name)) ,@body))

; Wrappers with multiple names to define

(defun wrap-function-like-env (entries form)
  "Wrap a form using the function-like entries.
An entry can be:
(name nil) — not defined in this environment
(name (:plain)) — an flet
(name (:macro macro-function)) — a macrolet, defined by a macro-function
(name (:macro-from environment)) — a macrolet that should be copied from an environment object
(name (:macro-code)) — a macrolet, defined by literal macrolet code
(name (:macro-function-code)) — a macrolet, defined by the code to define the macro-function
"
  (loop
    for res := form then new-res
    for entry in entries
    for name := (first entry)
    for op := (second entry)
    for kind := (first op)
    for new-res := (ecase kind
                     ((nil) res)
                     ((:plain) (flet-wrap name (list res)))
                     ((:macro) (macrolet-wrap name (second op) (list res)))
                     ((:macro-from)
                      (macrolet-wrap
                        name (macro-function name (second op)) (list res)))
                     ((:macro-code)
                      (macrolet-code-wrap name (cdr op) (list res)))
                     ((:macro-function-code)
                      (macrolet-wrap name `(lambda ,@ (cdr op)) (list res)))
                     )
    finally (return res)))

(defun wrap-variable-like-env (entries form)
  "Wrap a form using variable-like entries.
An entry can be:
(name nil) — not defined in this environment
(name (:plain)) — a let
(name (:macro expansion)) — a symbol-macro, defined by the expansion
(name (:macro-from environment)) — a symbol-macro to be copied from an environment
"
  (loop
    for res := form then new-res
    for entry in entries
    for name := (first entry)
    for op := (second entry)
    for kind := (first op)
    for new-res := (ecase kind
                     ((nil) res)
                     ((:plain) (let-wrap name (list res)))
                     ((:macro) (symbol-macrolet-wrap name (second op) (list res)))
                     ((:macro-from)
                      (symbol-macrolet-wrap
                        name (macroexpand-1 name (second op)) (list res)))
                     )
    finally (return res)))

(defun wrap-block-env (entries form)
  "Wrap a form to make sure all the blocks listed in entries are defined"
  (loop
    for res := form then new-res
    for entry in entries
    for new-res := `(block ,entry ,res)
    finally (return res)))

(defun wrap-tag-env (entries form)
  "Wrap a form to make sure all the tags listed in entries are defined"
  (if entries
    (let
      ((s (gensym)))
      `(catch
         ',s
         (tagbody
           ,@ entries
           (throw ',s ,form))))
    form))