-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathcompiler.lisp
79 lines (74 loc) · 2.81 KB
/
compiler.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
(defpackage #:paras/compiler
(:use #:cl
#:paras/errors
#:paras/types)
(:shadowing-import-from #:paras/errors
#:end-of-file
#:undefined-function)
(:import-from #:paras/builtin)
(:import-from #:paras/user)
(:export #:compiled-form
#:compiled-form-bindings
#:compiled-form-body
#:compile-code
#:recompile-form))
(in-package #:paras/compiler)
(defstruct compiled-form
bindings
code
body)
(defun package-external-symbols (package)
(let ((symbols '()))
(do-external-symbols (s package symbols)
(push s symbols))))
(defun function-allowed-p (function-name)
(let ((package (symbol-package function-name))
(modules (list* "PARAS/SPECIAL" "PARAS/BUILTIN" paras/builtin:*modules*)))
(when (or (find (package-name package)
modules
:test #'string=)
(some (lambda (module-name)
(find module-name (package-nicknames package) :test 'equal))
modules))
(do-external-symbols (symbol package)
(when (eq symbol function-name)
(return-from function-allowed-p t))))))
(defun compile-code (code &optional (bindings '()))
(let ((*package* (find-package '#:paras-user)))
(check-type code paras-type)
(labels ((recur (code)
(typecase code
(cons
(let ((fn (first code)))
(unless (and (symbolp fn)
(handler-case (symbol-function fn)
(cl:undefined-function () nil))
(function-allowed-p fn))
;; The function is not allowed to be called.
(error 'undefined-function :name fn))
(if (macro-function fn)
(macroexpand code)
(macroexpand
(cons fn
(mapcar #'recur (rest code)))))))
(paras-variable-type
(handler-case (symbol-value code)
(cl:unbound-variable ()
(error 'undefined-variable :name code)))
code)
(paras-constant-type code)
(otherwise (error 'type-not-allowed :value code)))))
(make-compiled-form
:bindings bindings
:code code
:body
(progv
(mapcar #'car bindings)
(mapcar #'cdr bindings)
(recur code))))))
(defun recompile-form (form &optional (bindings '() bindings-specified-p))
(check-type form compiled-form)
(compile-code (compiled-form-code form)
(if bindings-specified-p
bindings
(compiled-form-bindings form))))