blob: c07120dba46170dcc03f0295c7102a62936d520c (
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
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
|
(in-package #:parser)
(defun run (p input)
(let ((r (funcall p input)))
(if (parsing-p r)
(parsing-tree r)
(input::generate-report (failure-place r) (failure-message r)))))
(defstruct parsing
tree
left)
(defstruct failure
place
message)
(defstruct (normal-failure (:include failure)))
(defstruct (critical-failure (:include failure)))
(defun new (tree)
(lambda (input)
(make-parsing :tree tree :left input)))
(defun bind (p f)
(lambda (input)
(let ((r (funcall p input)))
(if (parsing-p r)
(funcall (funcall f (parsing-tree r)) (parsing-left r))
r))))
(defun discarding-bind (p f)
(lambda (input)
(let ((r (funcall p input)))
(if (parsing-p r)
(funcall (funcall f) (parsing-left r))
r))))
(defmacro comp (bindings &body body)
(if (null bindings)
`(new (progn ,@body))
(let ((v (first (car bindings)))
(p (second (car bindings))))
(if (string= (symbol-name v) "_")
`(discarding-bind ,p (lambda () (comp ,(cdr bindings) ,@body)))
`(bind ,p (lambda (,v) (comp ,(cdr bindings) ,@body)))))))
(defun fail (&optional (message "Unknown error."))
(lambda (input)
(make-critical-failure :place input :message message)))
(defun one-of (first-parser second-parser &rest other-parsers)
(lambda (input)
(labels ((one-of-rec (body)
(if (cdr body)
(let ((r (funcall (car body) input)))
(if (normal-failure-p r)
(one-of-rec (cdr body))
r))
(funcall (car body) input))))
(one-of-rec (cons first-parser (cons second-parser other-parsers))))))
(defun all-of (first-parser second-parser &rest other-parsers)
(lambda (input)
(labels ((all-of-rec (body)
(if (cdr body)
(let ((r (funcall (car body) input)))
(if (parsing-p r)
(all-of-rec (cdr body))
r))
(funcall (car body) input))))
(all-of-rec (cons first-parser (cons second-parser other-parsers))))))
(defun negate (p)
(lambda (input)
(let ((r (funcall p input)))
(cond ((parsing-p r)
(make-normal-failure :place input :message "Negated parser result."))
((normal-failure-p r)
(make-parsing :tree nil :left input))
(t r)))))
(defun unit-if (&optional (predicate #'characterp))
(lambda (input)
(if (input::has-data? input)
(let ((c (input::peek input)))
(if (funcall predicate c)
(make-parsing :tree c :left (input::advance input))
(make-normal-failure :place input :message "Predicate not satisfied.")))
(make-normal-failure :place input :message "Reached end of input."))))
(defun unit (target)
(unit-if (lambda (x) (char= x target))))
(defun not-unit (target)
(unit-if (lambda (x) (char/= x target))))
(defun literal (target)
(lambda (input)
(if (input::has-data? input (length target))
(if (input::prefix? target input)
(make-parsing :tree target :left (input::advance input (length target)))
(make-normal-failure :place input :message "Predicate not satisfied."))
(make-normal-failure :place input :message "Not enough data."))))
(defun not-literal (target)
(lambda (input)
(if (input::has-data? input (length target))
(if (input::prefix? target input)
(make-normal-failure :place input :message "Predicate not satisfied.")
(make-parsing :tree (input::peek input) :left (input::advance input)))
(if (input::has-data? input)
(make-parsing :tree (input::peek input) :left (input::advance input))
(make-normal-failure :place input :message "Reached end of input.")))))
(defparameter nothing
(new nil))
(defun optional (p)
(one-of p nothing))
(defun many (p)
(comp ((x p)
(xs (optional (many p))))
(cons x xs)))
(defun separated-list (p separator &key (include-separator nil))
(comp ((v p)
(sep (optional separator))
(vn (if sep
(one-of (separated-list p separator)
(fail "Value expected."))
nothing)))
(if include-separator
(cons v (cons sep vn))
(cons v vn))))
(defparameter whitespace
(comp ((_ (optional (many (unit-if (lambda (x) (or (char= x #\Space) (char= x #\Newline) (char= x #\Tab))))))))
nil))
|