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