Commit | Line | Data |
---|---|---|
da8f925e VJL |
1 | ;;; ebnf-abn.el --- parser for ABNF (Augmented BNF) |
2 | ||
73b0cd50 | 3 | ;; Copyright (C) 2001-2011 |
d7a0267c | 4 | ;; Free Software Foundation, Inc. |
da8f925e VJL |
5 | |
6 | ;; Author: Vinicius Jose Latorre <viniciusjl@ig.com.br> | |
7 | ;; Maintainer: Vinicius Jose Latorre <viniciusjl@ig.com.br> | |
da8f925e | 8 | ;; Keywords: wp, ebnf, PostScript |
3ced5caa | 9 | ;; Version: 1.2 |
bd78fa1d | 10 | ;; Package: ebnf2ps |
da8f925e VJL |
11 | |
12 | ;; This file is part of GNU Emacs. | |
13 | ||
b1fc2b50 | 14 | ;; GNU Emacs is free software: you can redistribute it and/or modify |
da8f925e | 15 | ;; it under the terms of the GNU General Public License as published by |
b1fc2b50 GM |
16 | ;; the Free Software Foundation, either version 3 of the License, or |
17 | ;; (at your option) any later version. | |
da8f925e VJL |
18 | |
19 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
20 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
21 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
22 | ;; GNU General Public License for more details. | |
23 | ||
24 | ;; You should have received a copy of the GNU General Public License | |
b1fc2b50 | 25 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. |
da8f925e VJL |
26 | |
27 | ;;; Commentary: | |
28 | ||
29 | ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
30 | ;; | |
31 | ;; | |
32 | ;; This is part of ebnf2ps package. | |
33 | ;; | |
34 | ;; This package defines a parser for ABNF (Augmented BNF). | |
35 | ;; | |
36 | ;; See ebnf2ps.el for documentation. | |
37 | ;; | |
38 | ;; | |
39 | ;; ABNF Syntax | |
40 | ;; ----------- | |
41 | ;; | |
42 | ;; See the URL: | |
7fd08a0a VJL |
43 | ;; `http://www.ietf.org/rfc/rfc2234.txt' |
44 | ;; or | |
da8f925e VJL |
45 | ;; `http://www.faqs.org/rfcs/rfc2234.html' |
46 | ;; or | |
47 | ;; `http://www.rnp.br/ietf/rfc/rfc2234.txt' | |
48 | ;; ("Augmented BNF for Syntax Specifications: ABNF"). | |
49 | ;; | |
50 | ;; | |
51 | ;; rulelist = 1*( rule / (*c-wsp c-nl) ) | |
52 | ;; | |
53 | ;; rule = rulename defined-as elements c-nl | |
54 | ;; ; continues if next line starts with white space | |
55 | ;; | |
56 | ;; rulename = ALPHA *(ALPHA / DIGIT / "-") | |
57 | ;; | |
58 | ;; defined-as = *c-wsp ("=" / "=/") *c-wsp | |
59 | ;; ; basic rules definition and incremental | |
60 | ;; ; alternatives | |
61 | ;; | |
62 | ;; elements = alternation *c-wsp | |
63 | ;; | |
64 | ;; c-wsp = WSP / (c-nl WSP) | |
65 | ;; | |
66 | ;; c-nl = comment / CRLF | |
67 | ;; ; comment or newline | |
68 | ;; | |
69 | ;; comment = ";" *(WSP / VCHAR) CRLF | |
70 | ;; | |
71 | ;; alternation = concatenation | |
72 | ;; *(*c-wsp "/" *c-wsp concatenation) | |
73 | ;; | |
74 | ;; concatenation = repetition *(1*c-wsp repetition) | |
75 | ;; | |
76 | ;; repetition = [repeat] element | |
77 | ;; | |
78 | ;; repeat = 1*DIGIT / (*DIGIT "*" *DIGIT) | |
79 | ;; | |
80 | ;; element = rulename / group / option / | |
81 | ;; char-val / num-val / prose-val | |
82 | ;; | |
83 | ;; group = "(" *c-wsp alternation *c-wsp ")" | |
84 | ;; | |
85 | ;; option = "[" *c-wsp alternation *c-wsp "]" | |
86 | ;; | |
87 | ;; char-val = DQUOTE *(%x20-21 / %x23-7E) DQUOTE | |
88 | ;; ; quoted string of SP and VCHAR without DQUOTE | |
89 | ;; | |
90 | ;; num-val = "%" (bin-val / dec-val / hex-val) | |
91 | ;; | |
92 | ;; bin-val = "b" 1*BIT | |
93 | ;; [ 1*("." 1*BIT) / ("-" 1*BIT) ] | |
94 | ;; ; series of concatenated bit values | |
95 | ;; ; or single ONEOF range | |
96 | ;; | |
97 | ;; dec-val = "d" 1*DIGIT | |
98 | ;; [ 1*("." 1*DIGIT) / ("-" 1*DIGIT) ] | |
99 | ;; | |
100 | ;; hex-val = "x" 1*HEXDIG | |
101 | ;; [ 1*("." 1*HEXDIG) / ("-" 1*HEXDIG) ] | |
102 | ;; | |
103 | ;; prose-val = "<" *(%x20-3D / %x3F-7E) ">" | |
104 | ;; ; bracketed string of SP and VCHAR without | |
105 | ;; ; angles | |
106 | ;; ; prose description, to be used as last resort | |
107 | ;; | |
108 | ;; ; Core rules -- the coding depends on the system, here is used 7-bit ASCII | |
109 | ;; | |
110 | ;; ALPHA = %x41-5A / %x61-7A | |
111 | ;; ; A-Z / a-z | |
112 | ;; | |
113 | ;; BIT = "0" / "1" | |
114 | ;; | |
115 | ;; CHAR = %x01-7F | |
116 | ;; ; any 7-bit US-ASCII character, excluding NUL | |
117 | ;; | |
118 | ;; CR = %x0D | |
119 | ;; ; carriage return | |
120 | ;; | |
121 | ;; CRLF = CR LF | |
122 | ;; ; Internet standard newline | |
123 | ;; | |
124 | ;; CTL = %x00-1F / %x7F | |
125 | ;; ; controls | |
126 | ;; | |
127 | ;; DIGIT = %x30-39 | |
128 | ;; ; 0-9 | |
129 | ;; | |
130 | ;; DQUOTE = %x22 | |
131 | ;; ; " (Double Quote) | |
132 | ;; | |
133 | ;; HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" | |
134 | ;; | |
135 | ;; HTAB = %x09 | |
136 | ;; ; horizontal tab | |
137 | ;; | |
138 | ;; LF = %x0A | |
139 | ;; ; linefeed | |
140 | ;; | |
141 | ;; LWSP = *(WSP / CRLF WSP) | |
142 | ;; ; linear white space (past newline) | |
143 | ;; | |
144 | ;; OCTET = %x00-FF | |
145 | ;; ; 8 bits of data | |
146 | ;; | |
147 | ;; SP = %x20 | |
148 | ;; ; space | |
149 | ;; | |
150 | ;; VCHAR = %x21-7E | |
151 | ;; ; visible (printing) characters | |
152 | ;; | |
153 | ;; WSP = SP / HTAB | |
154 | ;; ; white space | |
155 | ;; | |
156 | ;; | |
157 | ;; NOTES: | |
158 | ;; | |
159 | ;; 1. Rules name and terminal strings are case INSENSITIVE. | |
160 | ;; So, the following rule names are all equals: | |
161 | ;; Rule-name, rule-Name, rule-name, RULE-NAME | |
162 | ;; Also, the following strings are equals: | |
163 | ;; "abc", "ABC", "aBc", "Abc", "aBC", etc. | |
164 | ;; | |
165 | ;; 2. To have a case SENSITIVE string, use the character notation. | |
166 | ;; For example, to specify the lowercase string "abc", use: | |
167 | ;; %d97.98.99 | |
168 | ;; | |
169 | ;; 3. There are no implicit spaces between elements, for example, the | |
170 | ;; following rules: | |
171 | ;; | |
172 | ;; foo = %x61 ; a | |
173 | ;; | |
174 | ;; bar = %x62 ; b | |
175 | ;; | |
176 | ;; mumble = foo bar foo | |
177 | ;; | |
178 | ;; Are equivalent to the following rule: | |
179 | ;; | |
180 | ;; mumble = %x61.62.61 | |
181 | ;; | |
182 | ;; If spaces are needed, it should be explicit specified, like: | |
183 | ;; | |
184 | ;; spaces = 1*(%x20 / %x09) ; one or more spaces or tabs | |
185 | ;; | |
186 | ;; mumble = foo spaces bar spaces foo | |
187 | ;; | |
188 | ;; 4. Lines starting with space or tab are considered a continuation line. | |
189 | ;; For example, the rule: | |
190 | ;; | |
191 | ;; rule = foo | |
192 | ;; bar | |
193 | ;; | |
194 | ;; Is equivalent to: | |
195 | ;; | |
196 | ;; rule = foo bar | |
197 | ;; | |
198 | ;; | |
199 | ;; Differences Between ABNF And ebnf2ps ABNF | |
200 | ;; ----------------------------------------- | |
201 | ;; | |
202 | ;; Besides the characters that ABNF accepts, ebnf2ps ABNF accepts also the | |
203 | ;; underscore (_) for rule name and european 8-bit accentuated characters (from | |
204 | ;; \240 to \377) for rule name, string and comment. | |
205 | ;; | |
206 | ;; | |
207 | ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
208 | ||
209 | ;;; Code: | |
210 | ||
211 | ||
212 | (require 'ebnf-otz) | |
213 | ||
214 | ||
215 | (defvar ebnf-abn-lex nil | |
216 | "Value returned by `ebnf-abn-lex' function.") | |
217 | ||
218 | \f | |
219 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
220 | ;; Syntactic analyzer | |
221 | ||
222 | ||
223 | ;;; rulelist = 1*( rule / (*c-wsp c-nl) ) | |
224 | ||
225 | (defun ebnf-abn-parser (start) | |
226 | "ABNF parser." | |
227 | (let ((total (+ (- ebnf-limit start) 1)) | |
228 | (bias (1- start)) | |
229 | (origin (point)) | |
230 | rule-list token rule) | |
231 | (goto-char start) | |
232 | (setq token (ebnf-abn-lex)) | |
233 | (and (eq token 'end-of-input) | |
234 | (error "Invalid ABNF file format")) | |
9d59cbb0 VJL |
235 | (and (eq token 'end-of-rule) |
236 | (setq token (ebnf-abn-lex))) | |
da8f925e VJL |
237 | (while (not (eq token 'end-of-input)) |
238 | (ebnf-message-float | |
239 | "Parsing...%s%%" | |
240 | (/ (* (- (point) bias) 100.0) total)) | |
241 | (setq token (ebnf-abn-rule token) | |
242 | rule (cdr token) | |
243 | token (car token)) | |
244 | (or (ebnf-add-empty-rule-list rule) | |
245 | (setq rule-list (cons rule rule-list)))) | |
246 | (goto-char origin) | |
247 | rule-list)) | |
248 | ||
249 | ||
250 | ;;; rule = rulename defined-as elements c-nl | |
251 | ;;; ; continues if next line starts with white space | |
252 | ;;; | |
253 | ;;; rulename = ALPHA *(ALPHA / DIGIT / "-") | |
254 | ;;; | |
255 | ;;; defined-as = *c-wsp ("=" / "=/") *c-wsp | |
256 | ;;; ; basic rules definition and incremental | |
257 | ;;; ; alternatives | |
258 | ;;; | |
259 | ;;; elements = alternation *c-wsp | |
260 | ;;; | |
261 | ;;; c-wsp = WSP / (c-nl WSP) | |
262 | ;;; | |
263 | ;;; c-nl = comment / CRLF | |
264 | ;;; ; comment or newline | |
265 | ;;; | |
266 | ;;; comment = ";" *(WSP / VCHAR) CRLF | |
267 | ||
268 | ||
269 | (defun ebnf-abn-rule (token) | |
270 | (let ((name ebnf-abn-lex) | |
271 | (action ebnf-action) | |
272 | elements) | |
273 | (setq ebnf-action nil) | |
274 | (or (eq token 'non-terminal) | |
275 | (error "Invalid rule name")) | |
276 | (setq token (ebnf-abn-lex)) | |
277 | (or (memq token '(equal incremental-alternative)) | |
278 | (error "Invalid rule: missing `=' or `=/'")) | |
279 | (and (eq token 'incremental-alternative) | |
280 | (setq name (concat name " =/"))) | |
281 | (setq elements (ebnf-abn-alternation)) | |
282 | (or (memq (car elements) '(end-of-rule end-of-input)) | |
283 | (error "Invalid rule: there is no end of rule")) | |
284 | (setq elements (cdr elements)) | |
285 | (ebnf-eps-add-production name) | |
286 | (cons (ebnf-abn-lex) | |
287 | (ebnf-make-production name elements action)))) | |
288 | ||
289 | ||
290 | ;;; alternation = concatenation | |
291 | ;;; *(*c-wsp "/" *c-wsp concatenation) | |
292 | ||
293 | ||
294 | (defun ebnf-abn-alternation () | |
295 | (let (body concatenation) | |
296 | (while (eq (car (setq concatenation | |
297 | (ebnf-abn-concatenation (ebnf-abn-lex)))) | |
298 | 'alternative) | |
299 | (setq body (cons (cdr concatenation) body))) | |
300 | (ebnf-token-alternative body concatenation))) | |
301 | ||
302 | ||
303 | ;;; concatenation = repetition *(1*c-wsp repetition) | |
304 | ||
305 | ||
306 | (defun ebnf-abn-concatenation (token) | |
307 | (let ((term (ebnf-abn-repetition token)) | |
308 | seq) | |
309 | (or (setq token (car term) | |
310 | term (cdr term)) | |
311 | (error "Empty element")) | |
312 | (setq seq (cons term seq)) | |
313 | (while (setq term (ebnf-abn-repetition token) | |
314 | token (car term) | |
315 | term (cdr term)) | |
316 | (setq seq (cons term seq))) | |
317 | (cons token | |
6411a60a | 318 | (ebnf-token-sequence seq)))) |
da8f925e VJL |
319 | |
320 | ||
321 | ;;; repetition = [repeat] element | |
322 | ;;; | |
323 | ;;; repeat = 1*DIGIT / (*DIGIT "*" *DIGIT) | |
324 | ||
325 | ||
326 | (defun ebnf-abn-repetition (token) | |
327 | (let (lower upper) | |
328 | ;; INTEGER [ "*" [ INTEGER ] ] | |
329 | (when (eq token 'integer) | |
330 | (setq lower ebnf-abn-lex | |
331 | token (ebnf-abn-lex)) | |
332 | (or (eq token 'repeat) | |
333 | (setq upper lower))) | |
334 | ;; "*" [ INTEGER ] | |
335 | (when (eq token 'repeat) | |
336 | ;; only * ==> lower & upper are empty string | |
337 | (or lower | |
338 | (setq lower "" | |
339 | upper "")) | |
340 | (when (eq (setq token (ebnf-abn-lex)) 'integer) | |
341 | (setq upper ebnf-abn-lex | |
342 | token (ebnf-abn-lex)))) | |
343 | (let ((element (ebnf-abn-element token))) | |
344 | (cond | |
345 | ;; there is a repetition | |
346 | (lower | |
347 | (or element | |
348 | (error "Missing element repetition")) | |
349 | (setq token (ebnf-abn-lex)) | |
350 | (cond | |
351 | ;; one or more | |
352 | ((and (string= lower "1") (null upper)) | |
353 | (cons token (ebnf-make-one-or-more element))) | |
354 | ;; zero or more | |
355 | ((or (and (string= lower "0") (null upper)) | |
356 | (and (string= lower "") (string= upper ""))) | |
357 | (cons token (ebnf-make-zero-or-more element))) | |
358 | ;; real repetition | |
359 | (t | |
360 | (ebnf-token-repeat lower (cons token element) upper)))) | |
361 | ;; there is an element | |
362 | (element | |
363 | (cons (ebnf-abn-lex) element)) | |
364 | ;; something that caller has to deal | |
365 | (t | |
366 | (cons token nil)))))) | |
367 | ||
368 | ||
369 | ;;; element = rulename / group / option / | |
370 | ;;; char-val / num-val / prose-val | |
371 | ;;; | |
372 | ;;; group = "(" *c-wsp alternation *c-wsp ")" | |
373 | ;;; | |
374 | ;;; option = "[" *c-wsp alternation *c-wsp "]" | |
375 | ;;; | |
376 | ;;; char-val = DQUOTE *(%x20-21 / %x23-7E) DQUOTE | |
377 | ;;; ; quoted string of SP and VCHAR without DQUOTE | |
378 | ;;; | |
379 | ;;; num-val = "%" (bin-val / dec-val / hex-val) | |
380 | ;;; | |
381 | ;;; bin-val = "b" 1*BIT | |
382 | ;;; [ 1*("." 1*BIT) / ("-" 1*BIT) ] | |
383 | ;;; ; series of concatenated bit values | |
384 | ;;; ; or single ONEOF range | |
385 | ;;; | |
386 | ;;; dec-val = "d" 1*DIGIT | |
387 | ;;; [ 1*("." 1*DIGIT) / ("-" 1*DIGIT) ] | |
388 | ;;; | |
389 | ;;; hex-val = "x" 1*HEXDIG | |
390 | ;;; [ 1*("." 1*HEXDIG) / ("-" 1*HEXDIG) ] | |
391 | ;;; | |
392 | ;;; prose-val = "<" *(%x20-3D / %x3F-7E) ">" | |
393 | ;;; ; bracketed string of SP and VCHAR without | |
394 | ;;; ; angles | |
395 | ;;; ; prose description, to be used as last resort | |
396 | ||
397 | ||
398 | (defun ebnf-abn-element (token) | |
399 | (cond | |
400 | ;; terminal | |
401 | ((eq token 'terminal) | |
402 | (ebnf-make-terminal ebnf-abn-lex)) | |
403 | ;; non-terminal | |
404 | ((eq token 'non-terminal) | |
405 | (ebnf-make-non-terminal ebnf-abn-lex)) | |
406 | ;; group | |
407 | ((eq token 'begin-group) | |
408 | (let ((body (ebnf-abn-alternation))) | |
409 | (or (eq (car body) 'end-group) | |
410 | (error "Missing `)'")) | |
411 | (cdr body))) | |
412 | ;; optional | |
413 | ((eq token 'begin-optional) | |
414 | (let ((body (ebnf-abn-alternation))) | |
415 | (or (eq (car body) 'end-optional) | |
416 | (error "Missing `]'")) | |
417 | (ebnf-token-optional (cdr body)))) | |
418 | ;; no element | |
419 | (t | |
420 | nil) | |
421 | )) | |
422 | ||
423 | \f | |
424 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
425 | ;; Lexical analyzer | |
426 | ||
427 | ||
428 | (defconst ebnf-abn-token-table (make-vector 256 'error) | |
429 | "Vector used to map characters to a lexical token.") | |
430 | ||
431 | ||
432 | (defun ebnf-abn-initialize () | |
433 | "Initialize EBNF token table." | |
434 | ;; control character & control 8-bit character are set to `error' | |
435 | (let ((char ?\060)) | |
436 | ;; digits: 0-9 | |
437 | (while (< char ?\072) | |
438 | (aset ebnf-abn-token-table char 'integer) | |
439 | (setq char (1+ char))) | |
440 | ;; printable character: A-Z | |
441 | (setq char ?\101) | |
442 | (while (< char ?\133) | |
443 | (aset ebnf-abn-token-table char 'non-terminal) | |
444 | (setq char (1+ char))) | |
445 | ;; printable character: a-z | |
446 | (setq char ?\141) | |
447 | (while (< char ?\173) | |
448 | (aset ebnf-abn-token-table char 'non-terminal) | |
449 | (setq char (1+ char))) | |
450 | ;; European 8-bit accentuated characters: | |
451 | (setq char ?\240) | |
452 | (while (< char ?\400) | |
453 | (aset ebnf-abn-token-table char 'non-terminal) | |
454 | (setq char (1+ char))) | |
455 | ;; Override end of line characters: | |
456 | (aset ebnf-abn-token-table ?\n 'end-of-rule) ; [NL] linefeed | |
457 | (aset ebnf-abn-token-table ?\r 'end-of-rule) ; [CR] carriage return | |
458 | ;; Override space characters: | |
459 | (aset ebnf-abn-token-table ?\013 'space) ; [VT] vertical tab | |
460 | (aset ebnf-abn-token-table ?\t 'space) ; [HT] horizontal tab | |
461 | (aset ebnf-abn-token-table ?\ 'space) ; [SP] space | |
462 | ;; Override form feed character: | |
463 | (aset ebnf-abn-token-table ?\f 'form-feed) ; [FF] form feed | |
464 | ;; Override other lexical characters: | |
465 | (aset ebnf-abn-token-table ?< 'non-terminal) | |
466 | (aset ebnf-abn-token-table ?% 'terminal) | |
467 | (aset ebnf-abn-token-table ?\" 'terminal) | |
468 | (aset ebnf-abn-token-table ?\( 'begin-group) | |
469 | (aset ebnf-abn-token-table ?\) 'end-group) | |
470 | (aset ebnf-abn-token-table ?* 'repeat) | |
471 | (aset ebnf-abn-token-table ?= 'equal) | |
472 | (aset ebnf-abn-token-table ?\[ 'begin-optional) | |
473 | (aset ebnf-abn-token-table ?\] 'end-optional) | |
474 | (aset ebnf-abn-token-table ?/ 'alternative) | |
475 | ;; Override comment character: | |
476 | (aset ebnf-abn-token-table ?\; 'comment))) | |
477 | ||
478 | ||
479 | ;; replace the range "\240-\377" (see `ebnf-range-regexp'). | |
480 | (defconst ebnf-abn-non-terminal-chars | |
481 | (ebnf-range-regexp "-_0-9A-Za-z" ?\240 ?\377)) | |
482 | (defconst ebnf-abn-non-terminal-letter-chars | |
483 | (ebnf-range-regexp "A-Za-z" ?\240 ?\377)) | |
484 | ||
485 | ||
486 | (defun ebnf-abn-lex () | |
13a93e66 | 487 | "Lexical analyzer for ABNF. |
da8f925e VJL |
488 | |
489 | Return a lexical token. | |
490 | ||
491 | See documentation for variable `ebnf-abn-lex'." | |
492 | (if (>= (point) ebnf-limit) | |
493 | 'end-of-input | |
494 | (let (token) | |
495 | ;; skip spaces and comments | |
496 | (while (if (> (following-char) 255) | |
497 | (progn | |
498 | (setq token 'error) | |
499 | nil) | |
500 | (setq token (aref ebnf-abn-token-table (following-char))) | |
501 | (cond | |
502 | ((eq token 'space) | |
503 | (skip-chars-forward " \013\t" ebnf-limit) | |
504 | (< (point) ebnf-limit)) | |
505 | ((eq token 'comment) | |
506 | (ebnf-abn-skip-comment)) | |
507 | ((eq token 'form-feed) | |
508 | (forward-char) | |
509 | (setq ebnf-action 'form-feed)) | |
510 | ((eq token 'end-of-rule) | |
511 | (ebnf-abn-skip-end-of-rule)) | |
512 | (t nil) | |
513 | ))) | |
514 | (cond | |
515 | ;; end of input | |
516 | ((>= (point) ebnf-limit) | |
517 | 'end-of-input) | |
518 | ;; error | |
519 | ((eq token 'error) | |
eac9c0ef | 520 | (error "Invalid character")) |
da8f925e VJL |
521 | ;; end of rule |
522 | ((eq token 'end-of-rule) | |
523 | 'end-of-rule) | |
524 | ;; integer | |
525 | ((eq token 'integer) | |
526 | (setq ebnf-abn-lex (ebnf-buffer-substring "0-9")) | |
527 | 'integer) | |
528 | ;; terminal: "string" or %[bdx]NNN((.NNN)+|-NNN)? | |
529 | ((eq token 'terminal) | |
530 | (setq ebnf-abn-lex | |
531 | (if (= (following-char) ?\") | |
532 | (ebnf-abn-string) | |
533 | (ebnf-abn-character))) | |
534 | 'terminal) | |
535 | ;; non-terminal: NAME or <NAME> | |
536 | ((eq token 'non-terminal) | |
537 | (let ((prose-p (= (following-char) ?<))) | |
538 | (when prose-p | |
539 | (forward-char) | |
540 | (or (looking-at ebnf-abn-non-terminal-letter-chars) | |
541 | (error "Invalid prose value"))) | |
542 | (setq ebnf-abn-lex | |
543 | (ebnf-buffer-substring ebnf-abn-non-terminal-chars)) | |
544 | (when prose-p | |
545 | (or (= (following-char) ?>) | |
546 | (error "Invalid prose value")) | |
547 | (setq ebnf-abn-lex (concat "<" ebnf-abn-lex ">")))) | |
548 | 'non-terminal) | |
549 | ;; equal: =, =/ | |
550 | ((eq token 'equal) | |
551 | (forward-char) | |
552 | (if (/= (following-char) ?/) | |
553 | 'equal | |
554 | (forward-char) | |
555 | 'incremental-alternative)) | |
556 | ;; miscellaneous: (, ), [, ], /, * | |
557 | (t | |
558 | (forward-char) | |
559 | token) | |
560 | )))) | |
561 | ||
562 | ||
563 | (defun ebnf-abn-skip-end-of-rule () | |
564 | (let (eor-p) | |
565 | (while (progn | |
566 | ;; end of rule ==> 2 or more consecutive end of lines | |
567 | (setq eor-p (or (> (skip-chars-forward "\r\n" ebnf-limit) 1) | |
568 | eor-p)) | |
569 | ;; skip spaces | |
570 | (skip-chars-forward " \013\t" ebnf-limit) | |
571 | ;; skip comments | |
572 | (and (= (following-char) ?\;) | |
573 | (ebnf-abn-skip-comment)))) | |
574 | (not eor-p))) | |
575 | ||
576 | ||
577 | ;; replace the range "\177-\237" (see `ebnf-range-regexp'). | |
578 | (defconst ebnf-abn-comment-chars | |
579 | (ebnf-range-regexp "^\n\000-\010\016-\037" ?\177 ?\237)) | |
580 | ||
581 | ||
582 | (defun ebnf-abn-skip-comment () | |
583 | (forward-char) | |
584 | (cond | |
585 | ;; open EPS file | |
586 | ((and ebnf-eps-executing (= (following-char) ?\[)) | |
587 | (ebnf-eps-add-context (ebnf-abn-eps-filename))) | |
588 | ;; close EPS file | |
589 | ((and ebnf-eps-executing (= (following-char) ?\])) | |
590 | (ebnf-eps-remove-context (ebnf-abn-eps-filename))) | |
3ced5caa VJL |
591 | ;; EPS header |
592 | ((and ebnf-eps-executing (= (following-char) ?H)) | |
593 | (ebnf-eps-header-comment (ebnf-abn-eps-filename))) | |
594 | ;; EPS footer | |
595 | ((and ebnf-eps-executing (= (following-char) ?F)) | |
596 | (ebnf-eps-footer-comment (ebnf-abn-eps-filename))) | |
da8f925e VJL |
597 | ;; any other action in comment |
598 | (t | |
599 | (setq ebnf-action (aref ebnf-comment-table (following-char))) | |
600 | (skip-chars-forward ebnf-abn-comment-chars ebnf-limit)) | |
601 | ) | |
602 | ;; check for a valid end of comment | |
603 | (cond ((>= (point) ebnf-limit) | |
604 | nil) | |
605 | ((= (following-char) ?\n) | |
606 | t) | |
607 | (t | |
eac9c0ef | 608 | (error "Invalid character")) |
da8f925e VJL |
609 | )) |
610 | ||
611 | ||
612 | (defun ebnf-abn-eps-filename () | |
613 | (forward-char) | |
614 | (ebnf-buffer-substring ebnf-abn-comment-chars)) | |
615 | ||
616 | ||
617 | ;; replace the range "\240-\377" (see `ebnf-range-regexp'). | |
618 | (defconst ebnf-abn-string-chars | |
619 | (ebnf-range-regexp " -!#-~" ?\240 ?\377)) | |
620 | ||
621 | ||
622 | (defun ebnf-abn-string () | |
623 | (buffer-substring-no-properties | |
624 | (progn | |
625 | (forward-char) | |
626 | (point)) | |
627 | (progn | |
628 | (skip-chars-forward ebnf-abn-string-chars ebnf-limit) | |
629 | (or (= (following-char) ?\") | |
630 | (error "Missing `\"'")) | |
631 | (prog1 | |
632 | (point) | |
633 | (forward-char))))) | |
634 | ||
635 | ||
636 | (defun ebnf-abn-character () | |
637 | ;; %[bdx]NNN((-NNN)|(.NNN)+)? | |
638 | (buffer-substring-no-properties | |
639 | (point) | |
640 | (progn | |
641 | (forward-char) | |
642 | (let* ((char (following-char)) | |
643 | (chars (cond ((or (= char ?B) (= char ?b)) "01") | |
644 | ((or (= char ?D) (= char ?d)) "0-9") | |
645 | ((or (= char ?X) (= char ?x)) "0-9A-Fa-f") | |
646 | (t (error "Invalid terminal value"))))) | |
647 | (forward-char) | |
648 | (or (> (skip-chars-forward chars ebnf-limit) 0) | |
649 | (error "Invalid terminal value")) | |
650 | (if (= (following-char) ?-) | |
651 | (progn | |
652 | (forward-char) | |
653 | (or (> (skip-chars-forward chars ebnf-limit) 0) | |
654 | (error "Invalid terminal value range"))) | |
655 | (while (= (following-char) ?.) | |
656 | (forward-char) | |
657 | (or (> (skip-chars-forward chars ebnf-limit) 0) | |
658 | (error "Invalid terminal value"))))) | |
659 | (point)))) | |
660 | ||
661 | \f | |
662 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
663 | ||
664 | ||
665 | (provide 'ebnf-abn) | |
666 | ||
da8f925e | 667 | ;;; ebnf-abn.el ends here |