forked from Ramarren/cl-parser-combinators
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathcontexts.lisp
182 lines (145 loc) · 7.02 KB
/
contexts.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
(in-package :parser-combinators)
(defparameter *tag-stack* nil)
(defclass context-front ()
((context :accessor context-of :initarg :context :initform nil)
(tags :accessor tags-of :initarg :tags :initform nil)))
(defclass context-common ()
((length :accessor length-of :initarg :length :initform 0)
(front :accessor front-of :initarg :front :initform (make-instance 'context-front))
(cache :accessor cache-of :initarg :cache :initform nil)))
(defclass context ()
((common :accessor common-of :initarg :common)
(position :accessor position-of :initarg :position :initform 0)))
(defmethod cache-of ((context context))
(cache-of (common-of context)))
(defmethod length-of ((context context))
(length-of (common-of context)))
(defmethod front-of ((context context))
(front-of (common-of context)))
(defmethod (setf front-of) (new-value (context context))
(setf (front-of (common-of context)) new-value))
(defmethod position-of ((context-front context-front))
(position-of (context-of context-front)))
(defmacro copy-context (context class &rest additional-arguments)
`(make-instance ,class
,@(iter (with default = (gensym))
(for (initarg accessor) in
'((:common common-of)
(:position position-of)))
(appending (when (eql (getf additional-arguments initarg default)
default)
`(,initarg (,accessor ,context)))))
,@additional-arguments))
(defgeneric context-peek (context))
(defgeneric context-next (context))
(defgeneric context-equal (context1 context2)
(:method ((context1 context) (context2 context))
(or (eq context1 context2)
(and (eq (common-of context1)
(common-of context2))
(eql (position-of context1)
(position-of context2))))))
(defgeneric context-greater (context1 context2)
(:method ((context1 context) (context2 context))
(and (eq (common-of context1)
(common-of context2))
(> (position-of context1)
(position-of context2)))))
(defgeneric update-front-context (context)
(:method ((context context))
(let ((front (front-of context)))
(cond ((or (null (context-of front))
(context-greater context (context-of front)))
(setf (context-of front) context
(tags-of front) (list *tag-stack*)))
((context-equal context (context-of front))
(push *tag-stack* (tags-of front)))))))
(defmethod context-peek :after ((context context))
(update-front-context context))
(defmethod context-next :around ((context context))
(let ((cache (cache-of context)))
(etypecase cache
(null (call-next-method))
(vector (or (aref cache (position-of context))
(setf (aref cache (position-of context))
(call-next-method))))
(hash-table (or (gethash (position-of context) cache)
(setf (gethash (position-of context) cache)
(call-next-method)))))))
(defgeneric context-interval (context1 context2 &optional result-type)
(:method :before ((context1 context) (context2 context) &optional result-type)
(declare (ignore result-type))
(assert (eql (common-of context1)
(common-of context2)))
(assert (<= (position-of context1)
(position-of context2))))
(:method ((context1 context) (context2 context) &optional (result-type 'string))
(if (= (position-of context1) (position-of context2))
(coerce nil result-type)
(coerce (iter (for c initially context1 then (context-next c))
(until (eq c context2))
(collect (context-peek c)))
result-type))))
(defclass end-context (context)
())
(defgeneric end-context-p (context)
(:method ((context t))
nil)
(:method ((context end-context))
t))
(defmethod context-next ((context end-context))
(error "Can't go past the end"))
(defmethod context-peek ((context end-context))
(warn "Trying to peek past the end.")
nil)
(defclass list-context (context)
((storage :accessor storage-of :initarg :storage)))
(defmethod context-next ((context list-context))
(let ((new-position (1+ (position-of context))))
(if (= new-position (length-of context))
(copy-context context 'end-context :position new-position)
(copy-context context 'list-context :storage (cdr (storage-of context)) :position new-position))))
(defmethod context-peek ((context list-context))
(car (storage-of context)))
(defclass vector-context-common (context-common)
((storage :accessor storage-of :initarg :storage)))
(defclass vector-context (context)
())
(defmethod storage-of ((context vector-context))
(storage-of (common-of context)))
(defmethod context-next ((context vector-context))
(let ((new-position (1+ (position-of context))))
(if (= new-position (length-of context))
(copy-context context 'end-context :position new-position)
(copy-context context 'vector-context :position new-position))))
(defmethod context-peek ((context vector-context))
(aref (storage-of context) (position-of context)))
(defmethod context-interval ((context1 vector-context) (context2 vector-context) &optional (result-type 'string))
(let ((storage (storage-of context1)))
(coerce (subseq storage (position-of context1) (position-of context2)) result-type)))
(defmethod context-interval ((context1 vector-context) (context2 end-context) &optional (result-type 'string))
(let ((storage (storage-of context1)))
(coerce (subseq storage (position-of context1)) result-type)))
(defvar *default-context-cache* :vector)
(defun make-cache (cache-type length)
(ecase cache-type
((nil) nil)
(:vector (make-array length :initial-element nil))
(:hashtable (make-hash-table))))
(defgeneric make-context (sequence &optional cache-type))
(defmethod make-context ((list list) &optional (cache-type *default-context-cache*))
(if (null list)
(make-instance 'end-context :common (make-instance 'context-common))
(make-instance 'list-context
:storage list
:common (make-instance 'context-common
:length (length list)
:cache (make-cache cache-type (length list))))))
(defmethod make-context ((vector vector) &optional (cache-type *default-context-cache*))
(if (zerop (length vector))
(make-instance 'end-context :common (make-instance 'vector-context-common))
(make-instance 'vector-context
:common (make-instance 'vector-context-common
:storage vector
:length (length vector)
:cache (make-cache cache-type (length vector))))))