1 ;;; wisi-parse.el --- Wisi parser
3 ;; Copyright (C) 2013, 2014 Free Software Foundation, Inc.
5 ;; This file is part of GNU Emacs.
7 ;; GNU Emacs is free software: you can redistribute it and/or modify
8 ;; it under the terms of the GNU General Public License as published by
9 ;; the Free Software Foundation, either version 3 of the License, or
10 ;; (at your option) any later version.
12 ;; GNU Emacs is distributed in the hope that it will be useful,
13 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;; GNU General Public License for more details.
17 ;; You should have received a copy of the GNU General Public License
18 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23 ;; An extended LALR parser, that handles shift/reduce and
24 ;; reduce/reduce conflicts by spawning parallel parsers to follow each
30 (require 'semantic/wisent)
32 ;; WORKAROUND: for some reason, this condition doesn't work in batch mode!
33 ;; (when (and (= emacs-major-version 24)
34 ;; (= emacs-minor-version 2))
35 (require 'wisi-compat-24.2)
38 (defvar wisi-parse-max-parallel 15
39 "Maximum number of parallel parsers for acceptable performance.
40 If a file needs more than this, it's probably an indication that
41 the grammar is excessively redundant.")
43 (defvar wisi-parse-max-parallel-current (cons 0 0)
44 "Cons (count . point); Maximum number of parallel parsers used in most recent parse,
45 point at which that max was spawned.")
49 0 : normal - ignore parse errors, for indenting new code
50 1 : report parse errors (for running tests)
51 2 : show parse states, position point at parse errors, debug-on-error works in parser
52 3 : also show top 10 items of parser stack.")
54 (cl-defstruct (wisi-parser-state
56 label ;; integer identifying parser for debug
59 ;; 'shift - need new token
60 ;; 'reduce - need reduce
61 ;; 'accept - parsing completed
62 ;; 'error - failed, error not reported yet
65 ;; 'pending-shift, 'pending-reduce - newly created parser; see wisi-parse
68 ;; Each stack item takes two slots: (token-symbol token-text (token-start . token-end)), state
69 ;; token-text is nil for nonterminals.
70 ;; this is _not_ the same as the wisent-parse stack; that leaves out token-symbol.
75 ;; list of (action-symbol stack-fragment)
78 (defun wisi-error-msg (message &rest args)
79 (let ((line (line-number-at-pos))
80 (col (- (point) (line-beginning-position))))
83 (file-name-nondirectory (buffer-name)) ;; buffer-file-name is sometimes nil here!?
85 (apply 'format message args))))
87 (defvar wisi-parse-error nil)
88 (put 'wisi-parse-error
90 '(error wisi-parse-error))
91 (put 'wisi-parse-error
95 (defvar-local wisi-cache-max 0
96 "Maximimum position in buffer where wisi-cache text properties are valid.")
98 (defun wisi-token-text (token)
99 "Return buffer text from token range."
100 (let ((region (cdr token)))
102 (buffer-substring-no-properties (car region) (cdr region)))))
104 (defun wisi-parse (automaton lexer)
105 "Parse current buffer from bob using the automaton specified in AUTOMATON.
107 - AUTOMATON is the parse table generated by `wisi-compile-grammar'.
109 - LEXER is a function with no argument called by the parser to
110 obtain the next token from the current buffer after point, as a
111 list (symbol text start . end), where `symbol' is the terminal
112 symbol, `text' is the token string, `start . end' is the range
114 (let* ((actions (aref automaton 0))
115 (gotos (aref automaton 1))
116 (parser-states ;; vector of parallel parser states
118 (make-wisi-parser-state
121 :stack (make-vector wisent-parse-max-stack-size nil)
124 (active-parser-count 1)
125 active-parser-count-prev
131 (goto-char (point-min))
132 (aset (wisi-parser-state-stack (aref parser-states 0)) 0 0)
134 (setq token (funcall lexer))
135 (setq wisi-parse-max-parallel-current (cons 0 0))
137 (while (not (eq active 'accept))
138 (setq active-parser-count-prev active-parser-count)
139 (setq some-pending nil)
140 (dotimes (parser-index (length parser-states))
141 (when (eq active (wisi-parser-state-active (aref parser-states parser-index)))
142 (let* ((parser-state (aref parser-states parser-index))
143 (result (wisi-parse-1 token parser-state (> active-parser-count 1) actions gotos)))
145 ;; spawn a new parser
146 (when (= active-parser-count wisi-parse-max-parallel)
147 (signal 'wisi-parse-error
148 (let ((state (aref (wisi-parser-state-stack parser-state)
149 (wisi-parser-state-sp parser-state))))
150 (wisi-error-msg (concat "too many parallel parsers required in grammar state %d;"
151 " simplify grammar, or increase `wisi-parse-max-parallel'"
154 (let ((j (wisi-free-parser parser-states)))
157 ;; Add to parser-states; the new parser won't be executed
158 ;; again in this parser-index loop.
159 (setq parser-states (vconcat parser-states (vector nil)))
160 (setq j (1- (length parser-states))))
162 ;; The new parser won't be executed again in this
163 ;; parser-index loop; nothing to do.
166 ;; Don't let the new parser execute again in this
167 ;; parser-index loop.
168 (setq some-pending t)
169 (setf (wisi-parser-state-active result)
170 (cl-case (wisi-parser-state-active result)
171 (shift 'pending-shift)
172 (reduce 'pending-reduce)
175 (setq active-parser-count (1+ active-parser-count))
176 (when (> active-parser-count (car wisi-parse-max-parallel-current))
177 (setq wisi-parse-max-parallel-current (cons active-parser-count (point))))
178 (setf (wisi-parser-state-label result) j)
179 (aset parser-states j result))
180 (when (> wisi-debug 1)
181 (message "spawn parser (%d active)" active-parser-count)))
183 (when (eq 'error (wisi-parser-state-active parser-state))
184 (setq active-parser-count (1- active-parser-count))
185 (when (> wisi-debug 1)
186 (message "terminate parser (%d active)" active-parser-count))
187 (cl-case active-parser-count
190 ((= active-parser-count-prev 1)
191 ;; We were not in a parallel parse; report the error.
192 (let ((state (aref (wisi-parser-state-stack parser-state)
193 (wisi-parser-state-sp parser-state))))
194 (signal 'wisi-parse-error
195 (wisi-error-msg "syntax error in grammar state %d; unexpected %s, expecting one of %s"
197 (wisi-token-text token)
198 (mapcar 'car (aref actions state))))
201 ;; Report errors from all parsers that failed on this token.
203 (dotimes (_ (length parser-states))
204 (let* ((parser-state (aref parser-states parser-index))
205 (state (aref (wisi-parser-state-stack parser-state)
206 (wisi-parser-state-sp parser-state))))
207 (when (eq 'error (wisi-parser-state-active parser-state))
212 "syntax error in grammar state %d; unexpected %s, expecting one of %s"
214 (wisi-token-text token)
215 (mapcar 'car (aref actions state)))))
217 (signal 'wisi-parse-error msg)))
221 (setf (wisi-parser-state-active parser-state) nil); Don't save error for later.
222 (let ((parser-state (aref parser-states (wisi-active-parser parser-states))))
223 (wisi-execute-pending (wisi-parser-state-label parser-state)
224 (wisi-parser-state-pending parser-state))
225 (setf (wisi-parser-state-pending parser-state) nil)
228 ;; We were in a parallel parse, and this parser
229 ;; failed; mark it inactive, don't save error for
231 (setf (wisi-parser-state-active parser-state) nil)
236 ;; Change pending-* parsers to *.
237 (dotimes (parser-index (length parser-states))
239 ((eq (wisi-parser-state-active (aref parser-states parser-index)) 'pending-shift)
240 (setf (wisi-parser-state-active (aref parser-states parser-index)) 'shift))
241 ((eq (wisi-parser-state-active (aref parser-states parser-index)) 'pending-reduce)
242 (setf (wisi-parser-state-active (aref parser-states parser-index)) 'reduce))
245 (setq active (wisi-parsers-active parser-states active-parser-count))
246 (when (eq active 'shift)
247 (when (> active-parser-count 1)
248 (setq active-parser-count (wisi-parse-elim-identical parser-states active-parser-count)))
250 (setq token (funcall lexer)))
252 (when (> active-parser-count 1)
253 (error "ambiguous parse result"))))
255 (defun wisi-parsers-active-index (parser-states)
256 ;; only called when active-parser-count = 1
259 (while (and (not result)
260 (< i (length parser-states)))
261 (when (wisi-parser-state-active (aref parser-states i))
266 (defun wisi-parsers-active (parser-states active-count)
267 "Return the type of parser cycle to execute.
268 PARSER-STATES[*].active is the last action a parser took. If it
269 was 'shift, that parser used the input token, and should not be
270 executed again until another input token is available, after all
271 parsers have shifted the current token or terminated.
275 'accept : all PARSER-STATES have active set to nil or 'accept -
278 'shift : all PARSER-STATES have active set to nil, 'accept, or
279 'shift - get a new token, execute 'shift parsers.
281 'reduce : some PARSER-STATES have active set to 'reduce - no new
282 token, execute 'reduce parsers."
288 (while (and (not result)
289 (< i (length parser-states)))
290 (setq active (wisi-parser-state-active (aref parser-states i)))
292 ((eq active 'shift) (setq shift-count (1+ shift-count)))
293 ((eq active 'reduce) (setq result 'reduce))
294 ((eq active 'accept) (setq accept-count (1+ accept-count)))
300 ((= accept-count active-count)
302 ((= (+ shift-count accept-count) active-count)
305 ;; all parsers in error state; should not get here
306 (error "all parsers in error state; programmer error"))
309 (defun wisi-free-parser (parser-states)
310 "Return index to a non-active parser in PARSER-STATES, -1 if there is none."
313 (while (and (not result)
314 (< i (length parser-states)))
315 (when (not (wisi-parser-state-active (aref parser-states i)))
318 (if result result -1)))
320 (defun wisi-active-parser (parser-states)
321 "Return index to the first active parser in PARSER-STATES."
324 (while (and (not result)
325 (< i (length parser-states)))
326 (when (wisi-parser-state-active (aref parser-states i))
330 (error "no active parsers"))
333 (defun wisi-parse-elim-identical (parser-states active-parser-count)
334 "Check for parsers in PARSER-STATES that have reached identical states eliminate one.
335 Return new ACTIVE-PARSER-COUNT. Assumes all parsers have active
336 nil, 'shift, or 'accept."
337 ;; parser-states passed by reference; active-parser-count by copy
338 ;; see test/ada_mode-slices.adb for example
339 (dotimes (parser-i (1- (length parser-states)))
340 (when (wisi-parser-state-active (aref parser-states parser-i))
341 (dotimes (parser-j (- (length parser-states) parser-i 1))
342 (when (wisi-parser-state-active (aref parser-states (+ parser-i parser-j 1)))
343 (when (eq (wisi-parser-state-sp (aref parser-states parser-i))
344 (wisi-parser-state-sp (aref parser-states (+ parser-i parser-j 1))))
346 (dotimes (stack-i (wisi-parser-state-sp (aref parser-states parser-i)))
349 (and compare ;; bypass expensive 'arefs' after first stack item compare fail
350 (equal (aref (wisi-parser-state-stack (aref parser-states parser-i)) stack-i)
351 (aref (wisi-parser-state-stack (aref parser-states (+ parser-i parser-j 1))) stack-i)))))
353 ;; parser stacks are identical
354 (setq active-parser-count (1- active-parser-count))
355 (when (> wisi-debug 1)
356 (message "terminate identical parser %d (%d active)"
357 (+ parser-i parser-j 1) active-parser-count))
358 (setf (wisi-parser-state-active (aref parser-states (+ parser-i parser-j 1))) nil)
359 (when (= active-parser-count 1)
360 ;; the actions for the two parsers are not
361 ;; identical, but either is good enough for
362 ;; indentation and navigation, so we just do the
363 ;; actions for the one that is not terminating.
364 (let ((parser-state (aref parser-states parser-i)))
365 (wisi-execute-pending (wisi-parser-state-label parser-state)
366 (wisi-parser-state-pending parser-state))
367 (setf (wisi-parser-state-pending parser-state) nil)
373 (defun wisi-parse-max-pos (tokens)
374 "Return max position in tokens, or point if tokens nil."
375 (let ((result (if tokens 0 (point))))
379 (setq result (max (cddr token) result))))
384 (defun wisi-parse-exec-action (func tokens)
385 "Execute action if all tokens past wisi-cache-max."
386 ;; We don't execute actions if all tokens are before wisi-cache-max,
387 ;; because later actions can update existing caches, and if the
388 ;; parse fails that won't happen. It also saves time.
390 ;; Also skip if no tokens; nothing to do. This can happen when all
391 ;; tokens in a grammar statement are optional.
392 (if (< 0 (length tokens))
393 (if (>= (wisi-parse-max-pos tokens) wisi-cache-max)
395 (funcall func tokens)
397 (when (> wisi-debug 1)
398 (message "... action skipped; before wisi-cache-max %d" wisi-cache-max)))
400 (when (> wisi-debug 1)
401 (message "... action skipped; no tokens"))
404 (defun wisi-execute-pending (parser-label pending)
405 (when (> wisi-debug 1) (message "%d: pending actions:" parser-label))
407 (when (> wisi-debug 1) (message "%s" (car pending)))
409 (let ((func-args (pop pending)))
410 (wisi-parse-exec-action (car func-args) (cadr func-args)))
413 (defun wisi-parse-1 (token parser-state pendingp actions gotos)
414 "Perform one shift or reduce on PARSER-STATE.
415 If PENDINGP, push actions onto PARSER-STATE.pending; otherwise execute them.
416 See `wisi-parse' for full details.
417 Return nil or new parser (a wisi-parse-state struct)."
418 (let* ((state (aref (wisi-parser-state-stack parser-state)
419 (wisi-parser-state-sp parser-state)))
420 (parse-action (wisent-parse-action (car token) (aref actions state)))
423 (when (> wisi-debug 1)
427 ;; put top 10 stack items
428 (let* ((count (min 20 (wisi-parser-state-sp parser-state)))
429 (msg (make-vector (+ 1 count) nil)))
431 (aset msg (- count i)
432 (aref (wisi-parser-state-stack parser-state) (- (wisi-parser-state-sp parser-state) i)))
434 (message "%d: %s: %d: %s"
435 (wisi-parser-state-label parser-state)
436 (wisi-parser-state-active parser-state)
437 (wisi-parser-state-sp parser-state)
439 (message " %d: %s: %s" state token parse-action))
440 (message "%d: %d: %s: %s" (wisi-parser-state-label parser-state) state token parse-action)))
442 (when (and (listp parse-action)
443 (not (symbolp (car parse-action))))
444 ;; Conflict; spawn a new parser.
445 (setq new-parser-state
446 (make-wisi-parser-state
448 :stack (vconcat (wisi-parser-state-stack parser-state))
449 :sp (wisi-parser-state-sp parser-state)
450 :pending (wisi-parser-state-pending parser-state)))
452 (wisi-parse-2 (cadr parse-action) token new-parser-state t gotos)
454 (setq parse-action (car parse-action))
458 (wisi-parse-2 parse-action token parser-state pendingp gotos)
462 (defun wisi-parse-2 (action token parser-state pendingp gotos)
463 "Execute parser ACTION (must not be a conflict).
467 (setf (wisi-parser-state-active parser-state) 'accept))
470 (setf (wisi-parser-state-active parser-state) 'error))
473 ;; Shift token and new state (= action) onto stack
474 (let ((stack (wisi-parser-state-stack parser-state)); reference
475 (sp (wisi-parser-state-sp parser-state))); copy
477 (aset stack (1- sp) token)
478 (aset stack sp action)
479 (setf (wisi-parser-state-sp parser-state) sp))
480 (setf (wisi-parser-state-active parser-state) 'shift))
483 (wisi-parse-reduce action parser-state pendingp gotos)
484 (setf (wisi-parser-state-active parser-state) 'reduce))
487 (defun wisi-nonterm-bounds (stack i j)
488 "Return a pair (START . END), the buffer region for a nonterminal.
489 STACK is the parser stack. I and J are the indices in STACK of
490 the first and last tokens of the nonterminal."
491 (let ((start (cadr (aref stack i)))
492 (end (cddr (aref stack j))))
493 (while (and (or (not start) (not end))
497 ;; item i is an empty production
498 (setq start (cadr (aref stack (setq i (+ i 2))))))
501 ;; item j is an empty production
502 (setq end (cddr (aref stack (setq j (- j 2))))))
505 (and start end (cons start end))))
507 (defun wisi-parse-reduce (action parser-state pendingp gotos)
508 "Reduce PARSER-STATE.stack, and execute or pend ACTION."
509 (let* ((stack (wisi-parser-state-stack parser-state)); reference
510 (sp (wisi-parser-state-sp parser-state)); copy
511 (token-count (or (nth 2 action) 0))
512 (nonterm (nth 0 action))
513 (nonterm-region (when (> token-count 0)
514 (wisi-nonterm-bounds stack (- sp (* 2 (1- token-count)) 1) (1- sp))))
515 (post-reduce-state (aref stack (- sp (* 2 token-count))))
516 (new-state (cdr (assoc nonterm (aref gotos post-reduce-state))))
517 (tokens (make-vector token-count nil)))
519 (when (not new-state)
520 (error "no goto for %s %d" nonterm post-reduce-state))
522 (dotimes (i token-count)
523 (aset tokens (- token-count i 1) (aref stack (- sp (* 2 i) 1))))
525 (setq sp (+ 2 (- sp (* 2 token-count))))
526 (aset stack (1- sp) (cons nonterm nonterm-region))
527 (aset stack sp new-state)
528 (setf (wisi-parser-state-sp parser-state) sp)
531 (if (wisi-parser-state-pending parser-state)
532 (setf (wisi-parser-state-pending parser-state)
533 (append (wisi-parser-state-pending parser-state)
534 (list (list (nth 1 action) tokens))))
535 (setf (wisi-parser-state-pending parser-state)
536 (list (list (nth 1 action) tokens))))
539 (wisi-parse-exec-action (nth 1 action) tokens)
543 (provide 'wisi-parse)
544 ;;; wisi-parse.el ends here