1 ;;; ebnf-yac.el --- parser for Yacc/Bison
3 ;; Copyright (C) 1999-2015 Free Software Foundation, Inc.
5 ;; Author: Vinicius Jose Latorre <viniciusjl@ig.com.br>
6 ;; Maintainer: Vinicius Jose Latorre <viniciusjl@ig.com.br>
7 ;; Keywords: wp, ebnf, PostScript
11 ;; This file is part of GNU Emacs.
13 ;; GNU Emacs is free software: you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation, either version 3 of the License, or
16 ;; (at your option) any later version.
18 ;; GNU Emacs is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
28 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
31 ;; This is part of ebnf2ps package.
33 ;; This package defines a parser for Yacc/Bison.
35 ;; See ebnf2ps.el for documentation.
41 ;; YACC = { YACC-Definitions }* "%%" { YACC-Rule }* [ "%%" [ YACC-Code ] ].
43 ;; YACC-Definitions = ( "%token" | "%left" | "%right" | "%nonassoc" )
44 ;; [ "<" Name ">" ] Name-List
46 ;; | "any other Yacc definition"
49 ;; YACC-Code = "any C definition".
51 ;; YACC-Rule = Name ":" Alternative ";".
53 ;; Alternative = { Sequence || "|" }*.
55 ;; Sequence = { Factor }*.
58 ;; | "'" "character" "'"
60 ;; | "{" "C like commands" "}"
63 ;; Name-List = { Name || "," }*.
65 ;; Name = "[A-Za-z][A-Za-z0-9_.]*".
67 ;; Comment = "/*" "any character, but the sequence \"*/\"" "*/"
68 ;; | "//" "any character, but the newline \"\\n\"" "\\n".
71 ;; In other words, a valid Name begins with a letter (upper or lower case)
72 ;; followed by letters, decimal digits, underscore (_) or point (.). For
73 ;; example: this_is_a_valid.name, Another_EXAMPLE, mIxEd.CaSe.
79 ;; Thanks to Matthew K. Junker <junker@alum.mit.edu> for the suggestion to deal
80 ;; with %right, %left and %prec pragmas. His suggestion was extended to deal
81 ;; with %nonassoc pragma too.
84 ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
92 (defvar ebnf-yac-lex nil
93 "Value returned by `ebnf-yac-lex' function.")
96 (defvar ebnf-yac-token-list nil
97 "List of `%TOKEN' names.")
100 (defvar ebnf-yac-skip-char nil
101 "Non-nil means skip printable characters with no grammatical meaning.")
104 (defvar ebnf-yac-error nil
105 "Non-nil means \"error\" occurred.")
108 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
109 ;; Syntactic analyzer
112 ;;; YACC = { YACC-Definitions }* "%%" { YACC-Rule }* [ "%%" [ YACC-Code ] ].
114 ;;; YACC-Code = "any C definition".
116 (defun ebnf-yac-parser (start)
118 (let ((total (+ (- ebnf-limit start
) 1))
121 syntax-list token rule
)
123 (setq token
(ebnf-yac-lex))
124 (and (eq token
'end-of-input
)
125 (error "Invalid Yacc/Bison file format"))
126 (or (eq (ebnf-yac-definitions token
) 'yac-separator
)
127 (error "Missing `%%%%'"))
128 (setq token
(ebnf-yac-lex))
129 (while (not (memq token
'(end-of-input yac-separator
)))
132 (/ (* (- (point) bias
) 100.0) total
))
133 (setq token
(ebnf-yac-rule token
)
136 (or (ebnf-add-empty-rule-list rule
)
137 (setq syntax-list
(cons rule syntax-list
))))
142 ;;; YACC-Definitions = ( "%token" | "%left" | "%right" | "%nonassoc" )
143 ;;; [ "<" Name ">" ] Name-List
145 ;;; | "any other Yacc definition"
148 (defun ebnf-yac-definitions (token)
149 (let ((ebnf-yac-skip-char t
))
150 (while (not (memq token
'(yac-separator end-of-input
)))
153 ;; ( "%token" | "%left" | "%right" | "%nonassoc" )
154 ;; [ "<" Name ">" ] Name-List
155 ((eq token
'yac-token
)
156 (setq token
(ebnf-yac-lex))
157 (when (eq token
'open-angle
)
158 (or (eq (ebnf-yac-lex) 'non-terminal
)
159 (error "Missing type name"))
160 (or (eq (ebnf-yac-lex) 'close-angle
)
161 (error "Missing `>'"))
162 (setq token
(ebnf-yac-lex)))
163 (setq token
(ebnf-yac-name-list token
)
164 ebnf-yac-token-list
(nconc (cdr token
)
165 ebnf-yac-token-list
))
168 ((eq token
'yac-prec
)
169 (or (eq (ebnf-yac-lex) 'non-terminal
)
170 (error "Missing prec name"))
172 ;; "any other Yacc definition"
179 ;;; YACC-Rule = Name ":" Alternative ";".
181 (defun ebnf-yac-rule (token)
182 (let ((header ebnf-yac-lex
)
185 (setq ebnf-action nil
)
186 (or (eq token
'non-terminal
)
187 (error "Invalid rule name"))
188 (or (eq (ebnf-yac-lex) 'colon
)
189 (error "Invalid rule: missing `:'"))
190 (setq body
(ebnf-yac-alternative))
191 (or (eq (car body
) 'period
)
192 (error "Invalid rule: missing `;'"))
193 (setq body
(cdr body
))
194 (ebnf-eps-add-production header
)
196 (ebnf-make-production header body action
))))
199 ;;; Alternative = { Sequence || "|" }*.
201 (defun ebnf-yac-alternative ()
203 (while (eq (car (setq sequence
(ebnf-yac-sequence)))
205 (and (setq sequence
(cdr sequence
))
206 (setq body
(cons sequence body
))))
207 (ebnf-token-alternative body sequence
)))
210 ;;; Sequence = { Factor }*.
212 (defun ebnf-yac-sequence ()
213 (let (ebnf-yac-error token seq factor
)
214 (while (setq token
(ebnf-yac-lex)
215 factor
(ebnf-yac-factor token
))
216 (setq seq
(cons factor seq
)))
218 (if (and ebnf-yac-ignore-error-recovery ebnf-yac-error
)
219 ;; ignore error recovery
221 (ebnf-token-sequence seq
)))))
225 ;;; | "'" "character" "'"
227 ;;; | "{" "C like commands" "}"
230 (defun ebnf-yac-factor (token)
233 ((eq token
'terminal
)
234 (ebnf-make-terminal ebnf-yac-lex
))
236 ((eq token
'non-terminal
)
237 (ebnf-make-non-terminal ebnf-yac-lex
))
239 ((eq token
'yac-error
)
240 (ebnf-make-special ebnf-yac-lex
))
247 ;;; Name-List = { Name || "," }*.
249 (defun ebnf-yac-name-list (token)
251 (when (eq token
'non-terminal
)
253 (setq names
(cons ebnf-yac-lex names
)
254 token
(ebnf-yac-lex))
256 (or (eq (ebnf-yac-lex) 'non-terminal
)
257 (error "Missing token name"))))
261 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
265 ;;; Name = "[A-Za-z][A-Za-z0-9_.]*".
267 ;;; Comment = "/*" "any character, but the sequence \"*/\"" "*/"
268 ;;; | "//" "any character" "\\n".
270 (defconst ebnf-yac-token-table
271 ;; control character & 8-bit character are set to `error'
272 (let ((table (make-vector 256 'error
)))
273 ;; upper & lower case letters:
276 (aset table char
'non-terminal
))
277 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
278 ;; printable characters:
281 (aset table char
'character
))
282 "!#$&()*+-.0123456789=?@[\\]^_`~")
283 ;; Override space characters:
284 (aset table ?
\n 'space
) ; [NL] linefeed
285 (aset table ?
\r 'space
) ; [CR] carriage return
286 (aset table ?
\t 'space
) ; [HT] horizontal tab
287 (aset table ?\
'space
) ; [SP] space
288 ;; Override form feed character:
289 (aset table ?
\f 'form-feed
) ; [FF] form feed
290 ;; Override other lexical characters:
291 (aset table ?
< 'open-angle
)
292 (aset table ?
> 'close-angle
)
293 (aset table ?
, 'comma
)
294 (aset table ?%
'yac-pragma
)
295 (aset table ?
/ 'slash
)
296 (aset table ?\
{ 'yac-code
)
297 (aset table ?
\" 'string
)
298 (aset table ?
\' 'terminal
)
299 (aset table ?
: 'colon
)
300 (aset table ?|
'alternative
)
301 (aset table ?\
; 'period)
303 "Vector used to map characters to a lexical token.")
306 (defun ebnf-yac-initialize ()
307 "Initializations for Yacc/Bison parser."
308 (setq ebnf-yac-token-list nil
))
311 (defun ebnf-yac-lex ()
312 "Lexical analyzer for Yacc/Bison.
314 Return a lexical token.
316 See documentation for variable `ebnf-yac-lex'."
317 (if (>= (point) ebnf-limit
)
320 ;; skip spaces, code blocks and comments
321 (while (if (> (following-char) 255)
325 (setq token
(aref ebnf-yac-token-table
(following-char)))
327 ((or (eq token
'space
)
328 (and ebnf-yac-skip-char
329 (eq token
'character
)))
330 (ebnf-yac-skip-spaces))
331 ((eq token
'yac-code
)
332 (ebnf-yac-skip-code))
334 (ebnf-yac-handle-comment))
335 ((eq token
'form-feed
)
337 (setq ebnf-action
'form-feed
))
342 ((>= (point) ebnf-limit
)
346 (error "Invalid character"))
349 (setq ebnf-yac-lex
(ebnf-get-string))
352 ((eq token
'terminal
)
353 (setq ebnf-yac-lex
(ebnf-string " -&(-~" ?
\' "terminal"))
355 ;; non-terminal, terminal or "error"
356 ((eq token
'non-terminal
)
357 (setq ebnf-yac-lex
(ebnf-buffer-substring "0-9A-Za-z_."))
358 (cond ((member ebnf-yac-lex ebnf-yac-token-list
)
360 ((string= ebnf-yac-lex
"error")
361 (setq ebnf-yac-error t
)
366 ;; %% and Yacc pragmas (%TOKEN, %START, etc).
367 ((eq token
'yac-pragma
)
371 ((eq (following-char) ?%
)
374 ;; %TOKEN, %RIGHT, %LEFT, %PREC, %NONASSOC
375 ((cdr (assoc (upcase (ebnf-buffer-substring "0-9A-Za-z_"))
376 '(("TOKEN" . yac-token
)
377 ("RIGHT" . yac-token
)
379 ("NONASSOC" . yac-token
)
380 ("PREC" . yac-prec
)))))
381 ;; other Yacc pragmas
392 (defun ebnf-yac-skip-spaces ()
394 (if ebnf-yac-skip-char
395 "\n\r\t !#$&()*+-.0123456789=?@[\\\\]^_`~"
398 (< (point) ebnf-limit
))
401 ;; replace the range "\177-\377" (see `ebnf-range-regexp').
402 (defconst ebnf-yac-skip-chars
403 (ebnf-range-regexp "^{}/'\"\000-\010\013\016-\037" ?
\177 ?
\377))
406 (defun ebnf-yac-skip-code ()
410 (skip-chars-forward ebnf-yac-skip-chars ebnf-limit
)
412 ((= (following-char) ?
{)
414 (setq pair
(1+ pair
)))
415 ((= (following-char) ?
})
417 (setq pair
(1- pair
)))
418 ((= (following-char) ?
/)
419 (ebnf-yac-handle-comment))
420 ((= (following-char) ?
\")
422 ((= (following-char) ?
\')
423 (ebnf-string " -&(-~" ?
\' "character"))
425 (error "Invalid character"))
427 (ebnf-yac-skip-spaces))
430 (defun ebnf-yac-handle-comment ()
434 ((= (following-char) ?
*)
435 (ebnf-yac-skip-comment)
436 (ebnf-yac-skip-spaces))
438 ((= (following-char) ?
/)
440 (ebnf-yac-skip-spaces))
446 ;; replace the range "\177-\237" (see `ebnf-range-regexp').
447 (defconst ebnf-yac-comment-chars
448 (ebnf-range-regexp "^*\000-\010\013\016-\037" ?
\177 ?
\237))
451 (defun ebnf-yac-skip-comment ()
455 ((and ebnf-eps-executing
(= (following-char) ?\
[))
456 (ebnf-eps-add-context (ebnf-yac-eps-filename)))
458 ((and ebnf-eps-executing
(= (following-char) ?\
]))
459 (ebnf-eps-remove-context (ebnf-yac-eps-filename)))
461 ((and ebnf-eps-executing
(= (following-char) ?H
))
462 (ebnf-eps-header-comment (ebnf-yac-eps-filename)))
464 ((and ebnf-eps-executing
(= (following-char) ?F
))
465 (ebnf-eps-footer-comment (ebnf-yac-eps-filename)))
466 ;; any other action in comment
468 (setq ebnf-action
(aref ebnf-comment-table
(following-char))))
472 (skip-chars-forward ebnf-yac-comment-chars ebnf-limit
)
473 (cond ((>= (point) ebnf-limit
)
474 (error "Missing end of comment: `*/'"))
475 ((= (following-char) ?
*)
476 (skip-chars-forward "*" ebnf-limit
)
477 (when (= (following-char) ?
/)
482 (error "Invalid character"))
486 (defun ebnf-yac-eps-filename ()
488 (buffer-substring-no-properties
490 (let ((chars (concat ebnf-yac-comment-chars
"\n"))
493 (skip-chars-forward chars ebnf-limit
)
495 (cond ((>= (point) ebnf-limit
)
497 ((= (following-char) ?
*)
498 (skip-chars-forward "*" ebnf-limit
)
499 (if (/= (following-char) ?\
/)
509 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
515 ;;; ebnf-yac.el ends here