1 ;;; semantic-bovine.el --- LL Parser/Analyzer core.
3 ;;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2006, 2007 Eric M. Ludlam
5 ;; X-CVS: $Id: semantic-bovine.el,v 1.1 2007-11-26 15:11:53 michaels Exp $
7 ;; This file is not part of GNU Emacs.
9 ;; Semantic is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; This software is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING. If not, write to the
21 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 ;; Boston, MA 02110-1301, USA.
26 ;; Semantix 1.x uses an LL parser named the "bovinator". This parser
27 ;; had several conveniences in it which made for parsing tags out of
28 ;; languages with list characters easy. This parser lives on as one
29 ;; of many available parsers for semantic the tool.
31 ;; This parser should be used when the language is simple, such as
32 ;; makefiles or other data-declaritive langauges.
36 (require 'bovine-debug)
41 (defvar semantic-bovinate-nonterminal-check-obarray nil
42 "Obarray of streams already parsed for nonterminal symbols.
43 Use this to detect infinite recursion during a parse.")
44 (make-variable-buffer-local 'semantic-bovinate-nonterminal-check-obarray)
48 ;; These are functions that can be called from within a bovine table.
49 ;; Most of these have code auto-generated from other construct in the
50 ;; bovine input grammar.
52 (defmacro semantic-lambda (&rest return-val)
53 "Create a lambda expression to return a list including RETURN-VAL.
54 The return list is a lambda expression to be used in a bovine table."
55 `(lambda (vals start end)
56 (append ,@return-val (list start end))))
58 ;;; Semantic Bovination
60 ;; Take a semantic token stream, and convert it using the bovinator.
61 ;; The bovinator takes a state table, and converts the token stream
62 ;; into a new semantic stream defined by the bovination table.
64 (defsubst semantic-bovinate-symbol-nonterminal-p (sym table)
65 "Return non-nil if SYM is in TABLE, indicating it is NONTERMINAL."
66 ;; sym is always a sym, so assq should be ok.
67 (if (assq sym table) t nil))
69 (defmacro semantic-bovinate-nonterminal-db-nt ()
70 "Return the current nonterminal symbol.
71 Part of the grammar source debugger. Depends on the existing
72 environment of `semantic-bovinate-stream'."
74 (car (aref (car nt-stack) 2))
77 (defun semantic-bovinate-nonterminal-check (stream nonterminal)
78 "Check if STREAM not already parsed for NONTERMINAL.
79 If so abort because an infinite recursive parse is suspected."
80 (or (vectorp semantic-bovinate-nonterminal-check-obarray)
81 (setq semantic-bovinate-nonterminal-check-obarray
82 (make-vector 13 nil)))
83 (let* ((nt (symbol-name nonterminal))
86 nt semantic-bovinate-nonterminal-check-obarray))))
88 ;; Always enter debugger to see the backtrace
89 (let ((debug-on-signal t)
91 (setq semantic-bovinate-nonterminal-check-obarray nil)
92 (error "Infinite recursive parse suspected on %s" nt))
93 (set (intern nt semantic-bovinate-nonterminal-check-obarray)
97 (defun semantic-bovinate-stream (stream &optional nonterminal)
98 "Bovinate STREAM, starting at the first NONTERMINAL rule.
99 Use `bovine-toplevel' if NONTERMINAL is not provided.
100 This is the core routine for converting a stream into a table.
101 Return the list (STREAM SEMANTIC-STREAM) where STREAM are those
102 elements of STREAM that have not been used. SEMANTIC-STREAM is the
103 list of semantic tokens found."
104 (if (not nonterminal)
105 (setq nonterminal 'bovine-toplevel))
107 ;; Try to detect infinite recursive parse when doing a full reparse.
108 (or semantic--buffer-cache
109 (semantic-bovinate-nonterminal-check stream nonterminal))
111 (let* ((table semantic--parse-table)
112 (matchlist (cdr (assq nonterminal table)))
113 (starting-stream stream)
114 (nt-loop t) ;non-terminal loop condition
115 nt-popup ;non-nil if return from nt recursion
116 nt-stack ;non-terminal recursion stack
117 s ;Temp Stream Tracker
118 lse ;Local Semantic Element
119 lte ;Local matchlist element
120 tev ;Matchlist entry values from buffer
121 val ;Value found in buffer.
122 cvl ;collected values list.
127 (condition-case debug-condition
129 (catch 'push-non-terminal
131 end (semantic-lex-token-end (car stream)))
132 (while (or nt-loop nt-popup)
135 (while (or nt-popup matchlist)
137 ;; End of a non-terminal recursion
139 ;; New matching process
140 (setq s stream ;init s from stream.
141 cvl nil ;re-init the collected value list.
142 lte (car matchlist) ;Get the local matchlist entry.
144 (if (or (byte-code-function-p (car lte))
146 ;; In this case, we have an EMPTY match! Make
148 (setq cvl (list nil))))
151 (not (byte-code-function-p (car lte)))
152 (not (listp (car lte))))
154 ;; GRAMMAR SOURCE DEBUGGING!
155 (if semantic-debug-enabled
156 (let* ((db-nt (semantic-bovinate-nonterminal-db-nt))
157 (db-ml (cdr (assq db-nt table)))
158 (db-mlen (length db-ml))
159 (db-midx (- db-mlen (length matchlist)))
160 (db-tlen (length (nth db-midx db-ml)))
161 (db-tidx (- db-tlen (length lte)))
162 (frame (semantic-bovine-debug-create-frame
163 db-nt db-midx db-tidx cvl (car s)))
164 (cmd (semantic-debug-break frame))
166 (cond ((eq 'fail cmd) (setq lte '(trash 0 . 0)))
167 ((eq 'quit cmd) (signal 'quit "Abort"))
168 ((eq 'abort cmd) (error "Abort"))
169 ;; support more commands here.
172 ;; END GRAMMAR SOURCE DEBUGGING!
175 ;; We have a nonterminal symbol. Recurse inline.
176 ((setq nt-loop (assq (car lte) table))
179 ;; push state into the nt-stack
180 nt-stack (cons (vector matchlist cvl lte stream end
183 ;; new non-terminal matchlist
184 matchlist (cdr nt-loop)
185 ;; new non-terminal stream
188 (throw 'push-non-terminal t)
193 (setq lse (car s) ;Get the local stream element
194 s (cdr s)) ;update stream.
196 (if (eq (car lte) (car lse)) ;syntactic match
197 (let ((valdot (cdr lse)))
198 (setq val (semantic-lex-token-text lse))
200 (if (stringp (car lte))
204 (if (string-match tev val)
207 '(comment semantic-list))
209 cvl)) ;append this value
210 (setq lte nil cvl nil))) ;clear the entry (exit)
213 '(comment semantic-list))
214 valdot val) cvl))) ;append unchecked value.
215 (setq end (cdr (cdr lse)))
217 (setq lte nil cvl nil)) ;No more matches, exit
219 (if (not cvl) ;lte=nil; there was no match.
220 (setq matchlist (cdr matchlist)) ;Move to next matchlist entry
221 (let ((start (semantic-lex-token-start (car stream))))
224 (funcall (car lte) ;call matchlist fn on values
225 (nreverse cvl) start end))
226 ((and (= (length cvl) 1)
228 (not (numberp (car (car cvl)))))
229 (append (car cvl) (list start end)))
231 ;;(append (nreverse cvl) (list start end))))
232 ;; MAYBE THE FOLLOWING NEEDS LESS CONS
233 ;; CELLS THAN THE ABOVE?
234 (nreverse (cons end (cons start cvl)))))
235 matchlist nil) ;;generate exit condition
241 (if (eq s starting-stream)
245 ;; pop previous state from the nt-stack
246 (let ((state (car nt-stack)))
249 ;; pop actual parser state
250 matchlist (aref state 0)
253 stream (aref state 3)
256 nt-stack (cdr nt-stack))
259 (let ((len (length out))
260 (strip (nreverse (cdr (cdr (reverse out))))))
261 (setq end (nth (1- len) out) ;reset end to the end of exp
262 cvl (cons strip cvl) ;prepend value of exp
263 lte (cdr lte)) ;update the local table entry
265 ;; No value means that we need to terminate this
267 (setq lte nil cvl nil)) ;No match, exit
270 ;; On error just move forward the stream of lexical tokens
271 (setq result (list (cdr starting-stream) nil))
272 (if semantic-debug-enabled
273 (let ((frame (semantic-create-bovine-debug-error-frame
275 (semantic-debug-break frame)
280 ;; Make it the default parser
282 (defalias 'semantic-parse-stream-default 'semantic-bovinate-stream)
284 (provide 'semantic-bovine)
286 ;;; semantic-bovine.el ends here