-
Notifications
You must be signed in to change notification settings - Fork 0
/
parser.lisp
95 lines (83 loc) · 2.25 KB
/
parser.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
(in-package :vanilla-lc)
(defun make-lexer (str)
(let ((chars (string-chars str)))
(labels ((self ()
(if (null chars)
(values nil nil)
(ematch (pop chars)
(#\-
;; remove comments
(loop until (char= (pop chars) #\Newline))
(self))
(#\(
(values 'lparen #\())
(#\)
(values 'rparen #\)))
((or #\\ #\λ)
(values 'lambda #\λ))
(#\.
(values 'period #\.))
(#\=
(values 'equals #\=))
(#\;
(values 'eol #\;))
((lower-case c)
(values 'var (string c)))
((upper-case c)
(push c chars)
(multiple-value-bind (word rest) (take-word chars)
(setq chars rest)
(values 'var word)))
((whitespace _) (self))))))
#'self)))
(eval-when (:load-toplevel :compile-toplevel :execute)
(defun parse-lambda (lambda var period terms)
(declare (ignore lambda period))
(mklambda var terms))
(defun parse-assignment (var equals terms)
(declare (ignore equals))
(list :assign var terms))
(defun parse-terms (term)
(list :term term))
(defun take-some (&rest idxs)
(lambda (&rest arr)
(loop for i in idxs collecting (nth i arr)))))
(defvar parser)
(define-parser parser
(:muffle-conflicts t)
(:start-symbol expressions)
(:terminals (lparen rparen lambda period equals eol var))
(:precedence ((:right comma)))
(expressions
(expression eol expressions (take-some 0 2))
(expression eol (compose #'car #'list))
expression)
(expression
(var equals terms #'parse-assignment)
(terms #'parse-terms))
(terms
(terms term #'mkapp)
term)
(term
(lparen terms rparen (compose #'cadr #'list))
(lambda var period terms #'parse-lambda)
var))
(defun decode-parsed (terms)
(let (last-term env)
(labels ((decode (terms)
(when terms
(ematch terms
((list :assign var t1)
(when (assoc var env)
(error "variable already bound ~a" var))
(push (cons var t1) env))
((list :term term)
(setq last-term term))
((list t1 t2)
(decode t1)
(decode t2))))))
(decode terms)
(values last-term (nreverse env)))))
(defun parse (str)
(decode-parsed
(parse-with-lexer (make-lexer str) parser)))