forked from paul7/lrefal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrefal-internal.lisp
286 lines (245 loc) · 6.35 KB
/
refal-internal.lisp
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
;;;; Refal internal data representation
;;;; (c) paul7, 2010
(defpackage :net.paul7.refal.internal
(:nicknames :ir)
(:use :common-lisp
:net.paul7.utility)
(:export shift-scope
active-scope
scopep
empty
bound
value
data
make-uniform-type
make-var
refal-var
refal-t-var
refal-s-var
refal-e-var
refal-scope
refal-pattern
refal-funcall
function-name
function-argument
module
bind-var
unbind-var
push-scope
pop-scope
data->scope
data->pattern
var-type
refal-module
reset-module
function-dict
module-name
refal-entry
interpolate
*main*
*global*))
(in-package :net.paul7.refal.internal)
;;; Refal variable hierarchy
(defclass refal-var ()
((name
:initform 0
:initarg :name
:accessor name)
(value
:initform nil
:initarg :value
:accessor value)
(bound
:initform nil
:initarg bound
:accessor bound)
(value-stack
:initform nil
:accessor value-stack)
(bound-stack
:initform nil
:accessor bound-stack)))
;; e.X
;; can be bound to anything
(defclass refal-e-var (refal-var)
())
;; t.X
;; can be bound to atom or to parenthesized subexpression
(defclass refal-t-var (refal-var)
())
;; s.X
;; can be bound to atom only
(defclass refal-s-var (refal-t-var)
())
(defmethod initialize-instance :after ((var refal-var) &key)
(with-accessors ((value value)
(bound bound)) var
(setf bound value)))
(defun push-scope (var)
(with-accessors ((value value)
(bound bound)
(value-stack value-stack)
(bound-stack bound-stack)) var
(push value value-stack)
(push bound bound-stack)
(unbind-var var)
var))
(defun pop-scope (var)
(with-accessors ((value value)
(bound bound)
(value-stack value-stack)
(bound-stack bound-stack)) var
(setf value (pop value-stack))
(setf bound (pop bound-stack))
var))
;; return symbol corresponding to type
;; can be used to construct fresh variable of the same type
(defgeneric var-type (var))
(defmethod var-type ((var refal-e-var))
#\e)
(defmethod var-type ((var refal-t-var))
#\t)
(defmethod var-type ((var refal-s-var))
#\s)
;; make unbound Refal variable of given type and name
(defun make-uniform-type (type)
(case type
(#\s #\s)
(#\S #\s)
(#\t #\t)
(#\T #\t)
(#\e #\e)
(#\E #\e)
(otherwise (error "Bad type"))))
(defun make-var (type name)
(make-instance (case (make-uniform-type type)
(#\s 'refal-s-var)
(#\t 'refal-t-var)
(#\e 'refal-e-var))
:name name))
;;; utilities for variables
(defmethod print-object ((var refal-var) stream)
(print-unreadable-object (var stream)
(if (bound var)
(format stream "~a.~a => ~a"
(var-type var)
(name var)
(value var))
(format stream "~a.~a"
(var-type var)
(name var)))))
(defun unbind-var (var)
(setf (bound var) nil)
(setf (value var) nil))
(defun bind-var (var value)
(setf (bound var) t)
(setf (value var) value))
;;; this class represents Refal data
;;; encapsulates list of atoms constituting scope
;;; and boundaries of scope yet unmatched
(defclass refal-scope ()
((start
:accessor start
:initarg :start
:initform 0)
(end
:accessor end
:initarg :end)
(data
:accessor data
:initarg :data
:initform nil)))
(defclass refal-pattern (refal-scope)
())
(defmethod initialize-instance :after ((scope refal-scope) &key)
(with-accessors ((end end)
(data data)) scope
(setf end (length data))))
(defgeneric scopep (obj))
(defmethod scopep ((obj t))
nil)
(defmethod scopep ((obj refal-scope))
t)
;; return list representing unmatched part of the scope
(defun active-scope (scope)
(with-accessors ((start start)
(end end)
(data data)) scope
(subseq data start end)))
;; promote scope boundaries after successful matching
(defun shift-scope (scope margin)
(make-instance 'refal-scope
:data (data scope)
:start (+ (start scope) margin)
:end (end scope)))
(defmethod print-object ((scope refal-scope) stream)
(print-unreadable-object (scope stream)
(format stream "~{~a ~}" (data scope))))
(defun empty (scope)
(zerop (length (active-scope scope))))
(defun data->scope (data)
(make-instance 'refal-scope :data data))
(defun data->pattern (data)
(make-instance 'refal-pattern :data data))
(defclass refal-module ()
((function-dict
:initform (make-hash-table :test #'equalp)
:accessor function-dict)
(module-name
:initform 'main
:initarg :module-name
:accessor module-name)))
(defparameter *main* (make-instance 'refal-module))
(defparameter *global* (make-instance 'refal-module
:module-name "$$global"))
(defun reset-module (module)
(with-accessors ((dict function-dict)) module
(setf dict (make-hash-table :test #'equalp))))
(defun refal-entry (module fname)
(with-accessors ((dict function-dict)
(name module-name)) module
(let ((func (gethash fname dict)))
(or func
(if (equalp name "$$global")
nil
(let ((func (refal-entry *global* fname)))
(or func
(error (format nil "no function ~a in module ~a"
fname module)))))))))
(defmethod (setf refal-entry) (code module fname)
(with-accessors ((dict function-dict)
(name module-name)) module
(if (gethash fname dict)
(warn (format nil "duplicate function ~a in module ~a"
fname module)))
(setf (gethash fname dict) code)))
(defmethod print-object ((module refal-module) stream)
(print-unreadable-object (module stream :identity t)
(format stream "~a ~a"
(module-name module)
(loop
for name
being each hash-key in (function-dict module)
collect name))))
(defclass refal-funcall ()
((module
:initarg :module
:initform *main*
:accessor module)
(function-name
:initarg :function-name
:initform (error "No function name specified")
:accessor function-name)
(function-argument
:initarg :function-argument
:initform (make-instance 'refal-pattern
:data nil)
:accessor function-argument)))
(defgeneric interpolate (object))
(defmethod interpolate ((var refal-var))
(if (bound var)
(value var)
(error (format nil "~a is unbound" var))))
(defmethod interpolate ((pattern refal-pattern))
(data->scope (mapcan (compose #'copy-list #'mklist #'interpolate)
(data pattern))))