]> git.eshelyaron.com Git - emacs.git/commitdiff
*** empty log message ***
authorGerd Moellmann <gerd@gnu.org>
Thu, 27 Jan 2000 14:31:16 +0000 (14:31 +0000)
committerGerd Moellmann <gerd@gnu.org>
Thu, 27 Jan 2000 14:31:16 +0000 (14:31 +0000)
lisp/ChangeLog
lisp/progmodes/ebnf-bnf.el [new file with mode: 0644]
lisp/progmodes/ebnf-iso.el [new file with mode: 0644]
lisp/progmodes/ebnf-otz.el [new file with mode: 0644]
lisp/progmodes/ebnf-yac.el [new file with mode: 0644]
lisp/progmodes/ebnf2ps.el [new file with mode: 0644]

index 44e8ad862db9e7f448e439ee1fea48558699a098..7be61485f1beb82bfdb852c72fcbf0cb3276f93b 100644 (file)
@@ -1,3 +1,9 @@
+2000-01-27  Gerd Moellmann  <gerd@gnu.org>
+
+       * progmodes/ebnf2ps.el, progmodes/ebnf-bnf.el,
+       progmodes/ebnf-iso.el, progmodes/ebnf-otz.el,
+       progmodes/ebnf-yac.el: New files.
+
 2000-01-26  Dave Love  <fx@gnu.org>
 
        * emacs-lisp/checkdoc.el (checkdoc-interactive-loop): Don't lose
diff --git a/lisp/progmodes/ebnf-bnf.el b/lisp/progmodes/ebnf-bnf.el
new file mode 100644 (file)
index 0000000..c8e1d0d
--- /dev/null
@@ -0,0 +1,583 @@
+;;; ebnf-bnf --- Parser for EBNF
+
+;; Copyright (C) 1999 Vinicius Jose Latorre
+
+;; Author:     Vinicius Jose Latorre <vinicius@cpqd.com.br>
+;; Maintainer: Vinicius Jose Latorre <vinicius@cpqd.com.br>
+;; Keywords:   wp, ebnf, PostScript
+;; Time-stamp: <99/11/20 18:05:05 vinicius>
+;; Version:    1.4
+
+;; This file is *NOT* (yet?) part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;
+;; This is part of ebnf2ps package.
+;;
+;; This package defines a parser for EBNF.
+;;
+;; See ebnf2ps.el for documentation.
+;;
+;;
+;; EBNF Syntax
+;; -----------
+;;
+;; The current EBNF that ebnf2ps accepts has the following constructions:
+;;
+;;    ;                        comment (until end of line)
+;;    A                        non-terminal
+;;    "C"              terminal
+;;    ?C?              special
+;;    $A               default non-terminal
+;;    $"C"             default terminal
+;;    $?C?             default special
+;;    A = B.           production (A is the header and B the body)
+;;    C D              sequence (C occurs before D)
+;;    C | D            alternative (C or D occurs)
+;;    A - B            exception (A excluding B, B without any non-terminal)
+;;    n * A            repetition (A repeats n (integer) times)
+;;    (C)              group (expression C is grouped together)
+;;    [C]              optional (C may or not occurs)
+;;    C+               one or more occurrences of C
+;;    {C}+             one or more occurrences of C
+;;    {C}*             zero or more occurrences of C
+;;    {C}              zero or more occurrences of C
+;;    C / D            equivalent to: C {D C}*
+;;    {C || D}+                equivalent to: C {D C}*
+;;    {C || D}*                equivalent to: [C {D C}*]
+;;    {C || D}         equivalent to: [C {D C}*]
+;;
+;; The EBNF syntax written using the notation above is:
+;;
+;;    EBNF = {production}+.
+;;
+;;    production = non_terminal "=" body ".".   ;; production
+;;
+;;    body = {sequence || "|"}*.                ;; alternative
+;;
+;;    sequence = {exception}*.                  ;; sequence
+;;
+;;    exception = repeat [ "-" repeat].         ;; exception
+;;
+;;    repeat = [ integer "*" ] term.            ;; repetition
+;;
+;;    term = factor
+;;         | [factor] "+"                       ;; one-or-more
+;;         | [factor] "/" [factor]              ;; one-or-more
+;;         .
+;;
+;;    factor = [ "$" ] "\"" terminal "\""       ;; terminal
+;;           | [ "$" ] non_terminal             ;; non-terminal
+;;           | [ "$" ] "?" special "?"          ;; special
+;;           | "(" body ")"                     ;; group
+;;           | "[" body "]"                     ;; zero-or-one
+;;           | "{" body [ "||" body ] "}+"      ;; one-or-more
+;;           | "{" body [ "||" body ] "}*"      ;; zero-or-more
+;;           | "{" body [ "||" body ] "}"       ;; zero-or-more
+;;           .
+;;
+;;    non_terminal = "[A-Za-z\\240-\\377][!#%&'*-,0-:<>@-Z\\^-z~\\240-\\377]*".
+;;
+;;    terminal = "\\([^\"\\]\\|\\\\[ -~\\240-\\377]\\)+".
+;;
+;;    special = "[^?\\n\\000-\\010\\016-\\037\\177-\\237]*".
+;;
+;;    integer = "[0-9]+".
+;;
+;;    comment = ";" "[^\\n\\000-\\010\\016-\\037\\177-\\237]*" "\\n".
+;;
+;;
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;; code:
+
+
+(require 'ebnf-otz)
+
+
+(defvar ebnf-bnf-lex nil
+  "Value returned by `ebnf-bnf-lex' function.")
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Syntatic analyzer
+
+
+;;; EBNF = {production}+.
+
+(defun ebnf-bnf-parser (start)
+  "EBNF parser."
+  (let ((total (+ (- ebnf-limit start) 1))
+       (bias (1- start))
+       (origin (point))
+       prod-list token rule)
+    (goto-char start)
+    (setq token (ebnf-bnf-lex))
+    (and (eq token 'end-of-input)
+        (error "Invalid EBNF file format."))
+    (while (not (eq token 'end-of-input))
+      (ebnf-message-float
+       "Parsing...%s%%"
+       (/ (* (- (point) bias) 100.0) total))
+      (setq token (ebnf-production token)
+           rule  (cdr token)
+           token (car token))
+      (or (ebnf-add-empty-rule-list rule)
+         (setq prod-list (cons rule prod-list))))
+    (goto-char origin)
+    prod-list))
+
+
+;;; production = non-terminal "=" body ".".
+
+(defun ebnf-production (token)
+  (let ((header ebnf-bnf-lex)
+       (action ebnf-action)
+       body)
+    (setq ebnf-action nil)
+    (or (eq token 'non-terminal)
+       (error "Invalid header production."))
+    (or (eq (ebnf-bnf-lex) 'equal)
+       (error "Invalid production: missing `='."))
+    (setq body (ebnf-body))
+    (or (eq (car body) 'period)
+       (error "Invalid production: missing `.'."))
+    (setq body (cdr body))
+    (ebnf-eps-add-production header)
+    (cons (ebnf-bnf-lex)
+         (ebnf-make-production header body action))))
+
+
+;;; body = {sequence || "|"}*.
+
+(defun ebnf-body ()
+  (let (body sequence)
+    (while (eq (car (setq sequence (ebnf-sequence))) 'alternative)
+      (setq sequence (cdr sequence)
+           body     (cons sequence body)))
+    (ebnf-token-alternative body sequence)))
+
+
+;;; sequence = {exception}*.
+
+(defun ebnf-sequence ()
+  (let ((token (ebnf-bnf-lex))
+       seq term)
+    (while (setq term  (ebnf-exception token)
+                token (car term)
+                term  (cdr term))
+      (setq seq (cons term seq)))
+    (cons token
+         (cond
+          ;; null sequence
+          ((null seq)
+           (ebnf-make-empty))
+          ;; sequence with only one element
+          ((= (length seq) 1)
+           (car seq))
+          ;; a real sequence
+          (t
+           (ebnf-make-sequence (nreverse seq)))
+          ))))
+
+
+;;; exception = repeat [ "-" repeat].
+
+(defun ebnf-exception (token)
+  (let ((term (ebnf-repeat token)))
+    (if (not (eq (car term) 'except))
+       ;; repeat
+       term
+      ;; repeat - repeat
+      (let ((exception (ebnf-repeat (ebnf-bnf-lex))))
+       (ebnf-no-non-terminal (cdr exception))
+       (ebnf-token-except (cdr term) exception)))))
+
+
+(defun ebnf-no-non-terminal (node)
+  (and (vectorp node)
+       (let ((kind (ebnf-node-kind node)))
+        (cond
+         ((eq kind 'ebnf-generate-non-terminal)
+          (error "Exception sequence should not contain a non-terminal."))
+         ((eq kind 'ebnf-generate-repeat)
+          (ebnf-no-non-terminal (ebnf-node-separator node)))
+         ((memq kind '(ebnf-generate-optional ebnf-generate-except))
+          (ebnf-no-non-terminal (ebnf-node-list node)))
+         ((memq kind '(ebnf-generate-one-or-more ebnf-generate-zero-or-more))
+          (ebnf-no-non-terminal (ebnf-node-list node))
+          (ebnf-no-non-terminal (ebnf-node-separator node)))
+         ((memq kind '(ebnf-generate-alternative ebnf-generate-sequence))
+          (let ((seq (ebnf-node-list node)))
+            (while seq
+              (ebnf-no-non-terminal (car seq))
+              (setq seq (cdr seq)))))
+         ))))
+
+
+;;; repeat = [ integer "*" ] term.
+
+(defun ebnf-repeat (token)
+  (if (not (eq token 'integer))
+      (ebnf-term token)
+    (let ((times ebnf-bnf-lex))
+      (or (eq (ebnf-bnf-lex) 'repeat)
+         (error "Missing `*'."))
+      (ebnf-token-repeat times (ebnf-term (ebnf-bnf-lex))))))
+
+
+;;; term = factor
+;;;      | [factor] "+"                       ;; one-or-more
+;;;      | [factor] "/" [factor]              ;; one-or-more
+;;;      .
+
+(defun ebnf-term (token)
+  (let ((factor (ebnf-factor token)))
+    (and factor
+        (setq token (ebnf-bnf-lex)))
+    (cond
+     ;; [factor] +
+     ((eq token 'one-or-more)
+      (cons (ebnf-bnf-lex)
+           (and factor
+                (let ((kind (ebnf-node-kind factor)))
+                  (cond
+                   ;; { A }+ + ==> { A }+
+                   ;; { A }* + ==> { A }*
+                   ((memq kind '(ebnf-generate-zero-or-more
+                                 ebnf-generate-one-or-more))
+                    factor)
+                   ;; [ A ] + ==> { A }*
+                   ((eq kind 'ebnf-generate-optional)
+                    (ebnf-make-zero-or-more (list factor)))
+                   ;; A +
+                   (t
+                    (ebnf-make-one-or-more (list factor)))
+                   )))))
+     ;; [factor] / [factor]
+     ((eq token 'list)
+      (setq token (ebnf-bnf-lex))
+      (let ((sep (ebnf-factor token)))
+       (and sep
+            (setq factor (or factor (ebnf-make-empty))))
+       (cons (if sep
+                 (ebnf-bnf-lex)
+               token)
+             (and factor
+                  (ebnf-make-one-or-more factor sep)))))
+     ;; factor
+     (t
+      (cons token factor))
+     )))
+
+
+;;; factor = [ "$" ] "\"" terminal "\""         ;; terminal
+;;;        | [ "$" ] non_terminal               ;; non-terminal
+;;;        | [ "$" ] "?" special "?"            ;; special
+;;;        | "(" body ")"                       ;; group
+;;;        | "[" body "]"                       ;; zero-or-one
+;;;        | "{" body [ "||" body ] "}+"        ;; one-or-more
+;;;        | "{" body [ "||" body ] "}*"        ;; zero-or-more
+;;;        | "{" body [ "||" body ] "}"         ;; zero-or-more
+;;;        .
+
+(defun ebnf-factor (token)
+  (cond
+   ;; terminal
+   ((eq token 'terminal)
+    (ebnf-make-terminal ebnf-bnf-lex))
+   ;; non-terminal
+   ((eq token 'non-terminal)
+    (ebnf-make-non-terminal ebnf-bnf-lex))
+   ;; special
+   ((eq token 'special)
+    (ebnf-make-special ebnf-bnf-lex))
+   ;; group
+   ((eq token 'begin-group)
+    (let ((body (ebnf-body)))
+      (or (eq (car body) 'end-group)
+         (error "Missing `)'."))
+      (cdr body)))
+   ;; optional
+   ((eq token 'begin-optional)
+    (let ((body (ebnf-body)))
+      (or (eq (car body) 'end-optional)
+         (error "Missing `]'."))
+      (ebnf-token-optional (cdr body))))
+   ;; list
+   ((eq token 'begin-list)
+    (let* ((body      (ebnf-body))
+          (token     (car body))
+          (list-part (cdr body))
+          sep-part)
+      (and (eq token 'list-separator)
+          ;; { A || B }
+          (setq body     (ebnf-body)   ; get separator
+                token    (car body)
+                sep-part (cdr body)))
+      (cond
+       ;; { A }+
+       ((eq token 'end-one-or-more)
+       (ebnf-make-one-or-more list-part sep-part))
+       ;; { A }*
+       ((eq token 'end-zero-or-more)
+       (ebnf-make-zero-or-more list-part sep-part))
+       (t
+       (error "Missing `}+', `}*' or `}'."))
+       )))
+   ;; no term
+   (t
+    nil)
+   ))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Lexical analyzer
+
+
+(defconst ebnf-bnf-token-table (make-vector 256 'error)
+  "Vector used to map characters to a lexical token.")
+
+
+(defun ebnf-bnf-initialize ()
+  "Initialize EBNF token table."
+  ;; control character & control 8-bit character are set to `error'
+  (let ((char ?\040))
+    ;; printable character:
+    (while (< char ?\060)
+      (aset ebnf-bnf-token-table char 'non-terminal)
+      (setq char (1+ char)))
+    ;; digits:
+    (while (< char ?\072)
+      (aset ebnf-bnf-token-table char 'integer)
+      (setq char (1+ char)))
+    ;; printable character:
+    (while (< char ?\177)
+      (aset ebnf-bnf-token-table char 'non-terminal)
+      (setq char (1+ char)))
+    ;; European 8-bit accentuated characters:
+    (setq char ?\240)
+    (while (< char ?\400)
+      (aset ebnf-bnf-token-table char 'non-terminal)
+      (setq char (1+ char)))
+    ;; Override space characters:
+    (aset ebnf-bnf-token-table ?\013 'space) ; [VT] vertical tab
+    (aset ebnf-bnf-token-table ?\n   'space) ; [NL] linefeed
+    (aset ebnf-bnf-token-table ?\r   'space) ; [CR] carriage return
+    (aset ebnf-bnf-token-table ?\t   'space) ; [HT] horizontal tab
+    (aset ebnf-bnf-token-table ?\    'space) ; [SP] space
+    ;; Override form feed character:
+    (aset ebnf-bnf-token-table ?\f 'form-feed) ; [FF] form feed
+    ;; Override other lexical characters:
+    (aset ebnf-bnf-token-table ?\" 'terminal)
+    (aset ebnf-bnf-token-table ?\? 'special)
+    (aset ebnf-bnf-token-table ?\( 'begin-group)
+    (aset ebnf-bnf-token-table ?\) 'end-group)
+    (aset ebnf-bnf-token-table ?*  'repeat)
+    (aset ebnf-bnf-token-table ?-  'except)
+    (aset ebnf-bnf-token-table ?=  'equal)
+    (aset ebnf-bnf-token-table ?\[ 'begin-optional)
+    (aset ebnf-bnf-token-table ?\] 'end-optional)
+    (aset ebnf-bnf-token-table ?\{ 'begin-list)
+    (aset ebnf-bnf-token-table ?|  'alternative)
+    (aset ebnf-bnf-token-table ?\} 'end-list)
+    (aset ebnf-bnf-token-table ?/  'list)
+    (aset ebnf-bnf-token-table ?+  'one-or-more)
+    (aset ebnf-bnf-token-table ?$  'default)
+    ;; Override comment character:
+    (aset ebnf-bnf-token-table ebnf-lex-comment-char 'comment)
+    ;; Override end of production character:
+    (aset ebnf-bnf-token-table ebnf-lex-eop-char     'period)))
+
+
+(defun ebnf-bnf-lex ()
+  "Lexical analyser for EBNF.
+
+Return a lexical token.
+
+See documentation for variable `ebnf-bnf-lex'."
+  (if (>= (point) ebnf-limit)
+      'end-of-input
+    (let (token)
+      ;; skip spaces and comments
+      (while (if (> (following-char) 255)
+                (progn
+                  (setq token 'error)
+                  nil)
+              (setq token (aref ebnf-bnf-token-table (following-char)))
+              (cond
+               ((eq token 'space)
+                (skip-chars-forward " \013\n\r\t" ebnf-limit)
+                (< (point) ebnf-limit))
+               ((eq token 'comment)
+                (ebnf-bnf-skip-comment))
+               ((eq token 'form-feed)
+                (forward-char)
+                (setq ebnf-action 'form-feed))
+               (t nil)
+               )))
+      (setq ebnf-default-p nil)
+      (cond
+       ;; end of input
+       ((>= (point) ebnf-limit)
+       'end-of-input)
+       ;; error
+       ((eq token 'error)
+       (error "Illegal character."))
+       ;; default
+       ((eq token 'default)
+       (forward-char)
+       (if (memq (aref ebnf-bnf-token-table (following-char))
+                 '(terminal non-terminal special))
+           (prog1
+               (ebnf-bnf-lex)
+             (setq ebnf-default-p t))
+         (error "Illegal `default' element.")))
+       ;; integer
+       ((eq token 'integer)
+       (setq ebnf-bnf-lex (ebnf-buffer-substring "0-9"))
+       'integer)
+       ;; special: ?special?
+       ((eq token 'special)
+       (setq ebnf-bnf-lex (concat "?"
+                                  (ebnf-string " ->@-~" ?\? "special")
+                                  "?"))
+       'special)
+       ;; terminal: "string"
+       ((eq token 'terminal)
+       (setq ebnf-bnf-lex (ebnf-unescape-string (ebnf-get-string)))
+       'terminal)
+       ;; non-terminal or terminal
+       ((eq token 'non-terminal)
+       (setq ebnf-bnf-lex (ebnf-buffer-substring
+                           "!#%&'*-,0-:<>@-Z\\^-z~\240-\377"))
+       (let ((case-fold-search ebnf-case-fold-search)
+             match)
+         (if (and ebnf-terminal-regexp
+                  (setq match (string-match ebnf-terminal-regexp
+                                            ebnf-bnf-lex))
+                  (zerop match)
+                  (= (match-end 0) (length ebnf-bnf-lex)))
+             'terminal
+           'non-terminal)))
+       ;; end of list: }+, }*, }
+       ((eq token 'end-list)
+       (forward-char)
+       (cond
+        ((= (following-char) ?+)
+         (forward-char)
+         'end-one-or-more)
+        ((= (following-char) ?*)
+         (forward-char)
+         'end-zero-or-more)
+        (t
+         'end-zero-or-more)
+        ))
+       ;; alternative: |, ||
+       ((eq token 'alternative)
+       (forward-char)
+       (if (/= (following-char) ?|)
+           'alternative
+         (forward-char)
+         'list-separator))
+       ;; miscellaneous: {, (, ), [, ], ., =, /, +, -, *
+       (t
+       (forward-char)
+       token)
+       ))))
+
+
+(defconst ebnf-bnf-comment-chars "^\n\000-\010\016-\037\177-\237")
+
+
+(defun ebnf-bnf-skip-comment ()
+  (forward-char)
+  (cond
+   ;; open EPS file
+   ((and ebnf-eps-executing (= (following-char) ?\[))
+    (ebnf-eps-add-context (ebnf-bnf-eps-filename)))
+   ;; close EPS file
+   ((and ebnf-eps-executing (= (following-char) ?\]))
+    (ebnf-eps-remove-context (ebnf-bnf-eps-filename)))
+   ;; any other action in comment
+   (t
+    (setq ebnf-action (aref ebnf-comment-table (following-char)))
+    (skip-chars-forward ebnf-bnf-comment-chars ebnf-limit))
+   )
+  ;; check for a valid end of comment
+  (cond ((>= (point) ebnf-limit)
+        nil)
+       ((= (following-char) ?\n)
+        (forward-char)
+        t)
+       (t
+        (error "Illegal character."))
+       ))
+
+
+(defun ebnf-bnf-eps-filename ()
+  (forward-char)
+  (ebnf-buffer-substring ebnf-bnf-comment-chars))
+
+
+(defun ebnf-unescape-string (str)
+  (let* ((len (length str))
+        (size (1- len))
+        (istr 0)
+        (n-esc 0))
+    ;; count number of escapes
+    (while (< istr size)
+      (setq istr (+ istr
+                   (if (= (aref str istr) ?\\)
+                       (progn
+                         (setq n-esc (1+ n-esc))
+                         2)
+                     1))))
+    (if (zerop n-esc)
+       ;; no escapes
+       str
+      ;; at least one escape
+      (let ((new (make-string (- len n-esc) ?\ ))
+           (inew 0))
+       ;; eliminate all escapes
+       (setq istr 0)
+       (while (> n-esc 0)
+         (and (= (aref str istr) ?\\)
+              (setq istr  (1+ istr)
+                    n-esc (1- n-esc)))
+         (aset new inew (aref str istr))
+         (setq inew (1+ inew)
+               istr (1+ istr)))
+       ;; remaining string has no escape
+       (while (< istr len)
+         (aset new inew (aref str istr))
+         (setq inew (1+ inew)
+               istr (1+ istr)))
+       new))))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+(provide 'ebnf-bnf)
+
+
+;;; ebnf-bnf.el ends here
diff --git a/lisp/progmodes/ebnf-iso.el b/lisp/progmodes/ebnf-iso.el
new file mode 100644 (file)
index 0000000..2008685
--- /dev/null
@@ -0,0 +1,607 @@
+;;; ebnf-iso --- Parser for ISO EBNF
+
+;; Copyright (C) 1999 Vinicius Jose Latorre
+
+;; Author:     Vinicius Jose Latorre <vinicius@cpqd.com.br>
+;; Maintainer: Vinicius Jose Latorre <vinicius@cpqd.com.br>
+;; Keywords:   wp, ebnf, PostScript
+;; Time-stamp: <99/11/20 18:04:11 vinicius>
+;; Version:    1.4
+
+;; This file is *NOT* (yet?) part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;
+;; This is part of ebnf2ps package.
+;;
+;; This package defines a parser for ISO EBNF.
+;;
+;; See ebnf2ps.el for documentation.
+;;
+;;
+;; ISO EBNF Syntax
+;; ---------------
+;;
+;;     See the URL:
+;;     `http://www.cl.cam.ac.uk/~mgk25/iso-ebnf.html'
+;;     ("International Standard of the ISO EBNF Notation").
+;;
+;;
+;; ISO EBNF = syntax rule, {syntax rule};
+;;
+;; syntax rule = meta identifier, '=', definition list, ';';
+;;
+;; definition list = single definition, {'|', single definition};
+;;
+;; single definition = term, {',', term};
+;;
+;; term = factor, ['-', exception];
+;;
+;; exception = factor (* without <meta identifier> *);
+;;
+;; factor = [integer, '*'], primary;
+;;
+;; primary = optional sequence | repeated sequence | special sequence
+;;         | grouped sequence | meta identifier | terminal string
+;;         | empty;
+;;
+;; empty = ;
+;;
+;; optional sequence = '[', definition list, ']';
+;;
+;; repeated sequence = '{', definition list, '}';
+;;
+;; grouped sequence = '(', definition list, ')';
+;;
+;; terminal string = "'", character - "'", {character - "'"}, "'"
+;;                 | '"', character - '"', {character - '"'}, '"';
+;;
+;; special sequence = '?', {character - '?'}, '?';
+;;
+;; meta identifier = letter, { letter | decimal digit | ' ' };
+;;
+;; integer = decimal digit, {decimal digit};
+;;
+;; comment = '(*', {comment symbol}, '*)';
+;;
+;; comment symbol = comment (* <== NESTED COMMENT *)
+;;                | terminal string | special sequence | character;
+;;
+;; letter = ? A-Z a-z ?;
+;;
+;; decimal digit = ? 0-9 ?;
+;;
+;; character = letter | decimal digit
+;;           | ',' | '=' | '|' | '/' | '!' | '*' | '(' | ')' | '[' | ']' | '{'
+;;           | '}' | "'" | '"' | '?' | '-' | ';' | '.' | ' ' | ':' | '+' | '_'
+;;           | '%' | '@' | '&' | '#' | '$' | '<' | '>' | '\' | '^' | '`' | '~';
+;;
+;;
+;; There is also the following alternative representation:
+;;
+;; STANDARD   ALTERNATIVE
+;;    |    ==>   / or !
+;;    [    ==>   (/
+;;    ]    ==>   /)
+;;    {    ==>   (:
+;;    }    ==>   :)
+;;    ;    ==>   .
+;;
+;;
+;; Differences Between ISO EBNF And ebnf2ps ISO EBNF
+;; -------------------------------------------------
+;;
+;; ISO EBNF accepts the characters given by <character> production above,
+;; HORIZONTAL TAB (^I), VERTICAL TAB (^K), NEWLINE (^J or ^M) and FORM FEED
+;; (^L), any other characters are illegal.  But ebnf2ps accepts also the
+;; european 8-bit accentuated characters (from \240 to \377).
+;;
+;;
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;; code:
+
+
+(require 'ebnf-otz)
+
+
+(defvar ebnf-iso-lex nil
+  "Value returned by `ebnf-iso-lex' function.")
+
+
+(defconst ebnf-no-meta-identifier nil)
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Syntatic analyzer
+
+
+;;; ISO EBNF = syntax rule, {syntax rule};
+
+(defun ebnf-iso-parser (start)
+  "ISO EBNF parser."
+  (let ((total (+ (- ebnf-limit start) 1))
+       (bias (1- start))
+       (origin (point))
+       syntax-list token rule)
+    (goto-char start)
+    (setq token (ebnf-iso-lex))
+    (and (eq token 'end-of-input)
+        (error "Invalid ISO EBNF file format."))
+    (while (not (eq token 'end-of-input))
+      (ebnf-message-float
+       "Parsing...%s%%"
+       (/ (* (- (point) bias) 100.0) total))
+      (setq token (ebnf-iso-syntax-rule token)
+           rule  (cdr token)
+           token (car token))
+      (or (ebnf-add-empty-rule-list rule)
+         (setq syntax-list (cons rule syntax-list))))
+    (goto-char origin)
+    syntax-list))
+
+
+;;; syntax rule = meta identifier, '=', definition list, ';';
+
+(defun ebnf-iso-syntax-rule (token)
+  (let ((header ebnf-iso-lex)
+       (action ebnf-action)
+       body)
+    (setq ebnf-action nil)
+    (or (eq token 'non-terminal)
+       (error "Invalid meta identifier syntax rule."))
+    (or (eq (ebnf-iso-lex) 'equal)
+       (error "Invalid syntax rule: missing `='."))
+    (setq body (ebnf-iso-definition-list))
+    (or (eq (car body) 'period)
+       (error "Invalid syntax rule: missing `;' or `.'."))
+    (setq body (cdr body))
+    (ebnf-eps-add-production header)
+    (cons (ebnf-iso-lex)
+         (ebnf-make-production header body action))))
+
+
+;;; definition list = single definition, {'|', single definition};
+
+(defun ebnf-iso-definition-list ()
+  (let (body sequence)
+    (while (eq (car (setq sequence (ebnf-iso-single-definition)))
+              'alternative)
+      (setq sequence (cdr sequence)
+           body     (cons sequence body)))
+    (ebnf-token-alternative body sequence)))
+
+
+;;; single definition = term, {',', term};
+
+(defun ebnf-iso-single-definition ()
+  (let (token seq term)
+    (while (and (setq term  (ebnf-iso-term (ebnf-iso-lex))
+                     token (car term)
+                     term  (cdr term))
+               (eq token 'catenate))
+      (setq seq (cons term seq)))
+    (cons token
+         (cond
+          ;; null sequence
+          ((null seq)
+           term)
+          ;; sequence with only one element
+          ((and (null term) (= (length seq) 1))
+           (car seq))
+          ;; a real sequence
+          (t
+           (ebnf-make-sequence (nreverse (cons term seq))))
+          ))))
+
+
+;;; term = factor, ['-', exception];
+;;;
+;;; exception = factor (* without <meta identifier> *);
+
+(defun ebnf-iso-term (token)
+  (let ((factor (ebnf-iso-factor token)))
+    (if (not (eq (car factor) 'except))
+       ;; factor
+       factor
+      ;; factor - exception
+      (let ((ebnf-no-meta-identifier t))
+       (ebnf-token-except (cdr factor) (ebnf-iso-factor (ebnf-iso-lex)))))))
+
+
+;;; factor = [integer, '*'], primary;
+
+(defun ebnf-iso-factor (token)
+  (if (eq token 'integer)
+      (let ((times ebnf-iso-lex))
+       (or (eq (ebnf-iso-lex) 'repeat)
+           (error "Missing `*'."))
+       (ebnf-token-repeat times (ebnf-iso-primary (ebnf-iso-lex))))
+    (ebnf-iso-primary token)))
+
+
+;;; primary = optional sequence | repeated sequence | special sequence
+;;;         | grouped sequence | meta identifier | terminal string
+;;;         | empty;
+;;;
+;;; empty = ;
+;;;
+;;; optional sequence = '[', definition list, ']';
+;;;
+;;; repeated sequence = '{', definition list, '}';
+;;;
+;;; grouped sequence = '(', definition list, ')';
+;;;
+;;; terminal string = "'", character - "'", {character - "'"}, "'"
+;;;                 | '"', character - '"', {character - '"'}, '"';
+;;;
+;;; special sequence = '?', {character - '?'}, '?';
+;;;
+;;; meta identifier = letter, {letter | decimal digit};
+
+(defun ebnf-iso-primary (token)
+  (let ((primary
+        (cond
+         ;; terminal string
+         ((eq token 'terminal)
+          (ebnf-make-terminal ebnf-iso-lex))
+         ;; meta identifier
+         ((eq token 'non-terminal)
+          (ebnf-make-non-terminal ebnf-iso-lex))
+         ;; special sequence
+         ((eq token 'special)
+          (ebnf-make-special ebnf-iso-lex))
+         ;; grouped sequence
+         ((eq token 'begin-group)
+          (let ((body (ebnf-iso-definition-list)))
+            (or (eq (car body) 'end-group)
+                (error "Missing `)'."))
+            (cdr body)))
+         ;; optional sequence
+         ((eq token 'begin-optional)
+          (let ((body (ebnf-iso-definition-list)))
+            (or (eq (car body) 'end-optional)
+                (error "Missing `]' or `/)'."))
+            (ebnf-token-optional (cdr body))))
+         ;; repeated sequence
+         ((eq token 'begin-zero-or-more)
+          (let* ((body   (ebnf-iso-definition-list))
+                 (repeat (cdr body)))
+            (or (eq (car body) 'end-zero-or-more)
+                (error "Missing `}' or `:)'."))
+            (ebnf-make-zero-or-more repeat)))
+         ;; empty
+         (t
+          nil)
+         )))
+    (cons (if primary
+             (ebnf-iso-lex)
+           token)
+         primary)))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Lexical analyzer
+
+
+(defconst ebnf-iso-token-table
+  ;; control character & 8-bit character are set to `error'
+  (let ((table (make-vector 256 'error))
+       (char ?\040))
+    ;; printable character
+    (while (< char ?\060)
+      (aset table char 'character)
+      (setq char (1+ char)))
+    ;; digits:
+    (while (< char ?\072)
+      (aset table char 'integer)
+      (setq char (1+ char)))
+    (while (< char ?\101)
+      (aset table char 'character)
+      (setq char (1+ char)))
+    ;; upper case letters:
+    (while (< char ?\133)
+      (aset table char 'non-terminal)
+      (setq char (1+ char)))
+    (while (< char ?\141)
+      (aset table char 'character)
+      (setq char (1+ char)))
+    ;; lower case letters:
+    (while (< char ?\173)
+      (aset table char 'non-terminal)
+      (setq char (1+ char)))
+    (while (< char ?\177)
+      (aset table char 'character)
+      (setq char (1+ char)))
+    ;; European 8-bit accentuated characters:
+    (setq char ?\240)
+    (while (< char ?\400)
+      (aset table char 'non-terminal)
+      (setq char (1+ char)))
+    ;; Override space characters:
+    (aset table ?\013 'space)          ; [VT] vertical tab
+    (aset table ?\n   'space)          ; [NL] linefeed
+    (aset table ?\r   'space)          ; [CR] carriage return
+    (aset table ?\t   'space)          ; [HT] horizontal tab
+    (aset table ?\    'space)          ; [SP] space
+    ;; Override form feed character:
+    (aset table ?\f 'form-feed)                ; [FF] form feed
+    ;; Override other lexical characters:
+    (aset table ?\" 'double-terminal)
+    (aset table ?\' 'single-terminal)
+    (aset table ?\? 'special)
+    (aset table ?*  'repeat)
+    (aset table ?,  'catenate)
+    (aset table ?-  'except)
+    (aset table ?=  'equal)
+    (aset table ?\) 'end-group)
+    table)
+  "Vector used to map characters to a lexical token.")
+
+
+(defun ebnf-iso-initialize ()
+  "Initialize ISO EBNF token table."
+  (if ebnf-iso-alternative-p
+      ;; Override alternative lexical characters:
+      (progn
+       (aset ebnf-iso-token-table ?\( 'left-parenthesis)
+       (aset ebnf-iso-token-table ?\[ 'character)
+       (aset ebnf-iso-token-table ?\] 'character)
+       (aset ebnf-iso-token-table ?\{ 'character)
+       (aset ebnf-iso-token-table ?\} 'character)
+       (aset ebnf-iso-token-table ?|  'character)
+       (aset ebnf-iso-token-table ?\; 'character)
+       (aset ebnf-iso-token-table ?/  'slash)
+       (aset ebnf-iso-token-table ?!  'alternative)
+       (aset ebnf-iso-token-table ?:  'colon)
+       (aset ebnf-iso-token-table ?.  'period))
+    ;; Override standard lexical characters:
+    (aset ebnf-iso-token-table ?\( 'begin-parenthesis)
+    (aset ebnf-iso-token-table ?\[ 'begin-optional)
+    (aset ebnf-iso-token-table ?\] 'end-optional)
+    (aset ebnf-iso-token-table ?\{ 'begin-zero-or-more)
+    (aset ebnf-iso-token-table ?\} 'end-zero-or-more)
+    (aset ebnf-iso-token-table ?|  'alternative)
+    (aset ebnf-iso-token-table ?\; 'period)
+    (aset ebnf-iso-token-table ?/  'character)
+    (aset ebnf-iso-token-table ?!  'character)
+    (aset ebnf-iso-token-table ?:  'character)
+    (aset ebnf-iso-token-table ?.  'character)))
+
+
+(defun ebnf-iso-lex ()
+  "Lexical analyser for ISO EBNF.
+
+Return a lexical token.
+
+See documentation for variable `ebnf-iso-lex'."
+  (if (>= (point) ebnf-limit)
+      'end-of-input
+    (let (token)
+      ;; skip spaces and comments
+      (while (if (> (following-char) 255)
+                (progn
+                  (setq token 'error)
+                  nil)
+              (setq token (aref ebnf-iso-token-table (following-char)))
+              (cond
+               ((eq token 'space)
+                (skip-chars-forward " \013\n\r\t" ebnf-limit)
+                (< (point) ebnf-limit))
+               ((or (eq token 'begin-parenthesis)
+                    (eq token 'left-parenthesis))
+                (forward-char)
+                (if (/= (following-char) ?*)
+                    ;; no comment
+                    nil
+                  ;; comment
+                  (ebnf-iso-skip-comment)
+                  t))
+               ((eq token 'form-feed)
+                (forward-char)
+                (setq ebnf-action 'form-feed))
+               (t nil)
+               )))
+      (cond
+       ;; end of input
+       ((>= (point) ebnf-limit)
+       'end-of-input)
+       ;; error
+       ((eq token 'error)
+       (error "Illegal character."))
+       ;; integer
+       ((eq token 'integer)
+       (setq ebnf-iso-lex (ebnf-buffer-substring "0-9"))
+       'integer)
+       ;; special: ?special?
+       ((eq token 'special)
+       (setq ebnf-iso-lex (concat "?"
+                                  (ebnf-string " ->@-~" ?\? "special")
+                                  "?"))
+       'special)
+       ;; terminal: "string"
+       ((eq token 'double-terminal)
+       (setq ebnf-iso-lex (ebnf-string " !#-~" ?\" "terminal"))
+       'terminal)
+       ;; terminal: 'string'
+       ((eq token 'single-terminal)
+       (setq ebnf-iso-lex (ebnf-string " -&(-~" ?\' "terminal"))
+       'terminal)
+       ;; non-terminal
+       ((eq token 'non-terminal)
+       (setq ebnf-iso-lex (ebnf-iso-normalize
+                           (ebnf-trim-right
+                            (ebnf-buffer-substring " 0-9A-Za-z\240-\377"))))
+       (and ebnf-no-meta-identifier
+            (error "Exception sequence should not contain a meta identifier."))
+       'non-terminal)
+       ;; begin optional, begin list or begin group
+       ((eq token 'left-parenthesis)
+       (forward-char)
+       (cond ((= (following-char) ?/)
+              (forward-char)
+              'begin-optional)
+             ((= (following-char) ?:)
+              (forward-char)
+              'begin-zero-or-more)
+             (t
+              'begin-group)
+             ))
+       ;; end optional or alternative
+       ((eq token 'slash)
+       (forward-char)
+       (if (/= (following-char) ?\))
+           'alternative
+         (forward-char)
+         'end-optional))
+       ;; end list
+       ((eq token 'colon)
+       (forward-char)
+       (if (/= (following-char) ?\))
+           'character
+         (forward-char)
+         'end-zero-or-more))
+       ;; begin group
+       ((eq token 'begin-parenthesis)
+       'begin-group)
+       ;; miscellaneous
+       (t
+       (forward-char)
+       token)
+       ))))
+
+
+(defconst ebnf-iso-comment-chars "^*(\000-\010\016-\037\177-\237")
+
+
+(defun ebnf-iso-skip-comment ()
+  (forward-char)
+  (cond
+   ;; open EPS file
+   ((and ebnf-eps-executing (= (following-char) ?\[))
+    (ebnf-eps-add-context (ebnf-iso-eps-filename)))
+   ;; close EPS file
+   ((and ebnf-eps-executing (= (following-char) ?\]))
+    (ebnf-eps-remove-context (ebnf-iso-eps-filename)))
+   ;; any other action in comment
+   (t
+    (setq ebnf-action (aref ebnf-comment-table (following-char))))
+   )
+  (let ((pair 1))
+    (while (> pair 0)
+      (skip-chars-forward ebnf-iso-comment-chars ebnf-limit)
+      (cond ((>= (point) ebnf-limit)
+            (error "Missing end of comment: `*)'."))
+           ((= (following-char) ?*)
+            (skip-chars-forward "*" ebnf-limit)
+            (when (= (following-char) ?\))
+              ;; end of comment
+              (forward-char)
+              (setq pair (1- pair))))
+           ((= (following-char) ?\()
+            (skip-chars-forward "(" ebnf-limit)
+            (when (= (following-char) ?*)
+              ;; beginning of comment
+              (forward-char)
+              (setq pair (1+ pair))))
+           (t
+            (error "Illegal character."))
+           ))))
+
+
+(defun ebnf-iso-eps-filename ()
+  (forward-char)
+  (buffer-substring-no-properties
+   (point)
+   (let ((chars (concat ebnf-iso-comment-chars "\n"))
+        found)
+     (while (not found)
+       (skip-chars-forward chars ebnf-limit)
+       (setq found
+            (cond ((>= (point) ebnf-limit)
+                   (point))
+                  ((= (following-char) ?*)
+                   (skip-chars-forward "*" ebnf-limit)
+                   (if (/= (following-char) ?\))
+                       nil
+                     (backward-char)
+                     (point)))
+                  ((= (following-char) ?\()
+                   (forward-char)
+                   (if (/= (following-char) ?*)
+                       nil
+                     (backward-char)
+                     (point)))
+                  (t
+                   (point))
+                  )))
+     found)))
+
+
+(defun ebnf-iso-normalize (str)
+  (if (not ebnf-iso-normalize-p)
+      str
+    (let ((len (length str))
+         (stri 0)
+         (spaces 0))
+      ;; count exceeding spaces
+      (while (< stri len)
+       (if (/= (aref str stri) ?\ )
+           (setq stri (1+ stri))
+         (setq stri (1+ stri))
+         (while (and (< stri len) (= (aref str stri) ?\ ))
+           (setq stri   (1+ stri)
+                 spaces (1+ spaces)))))
+      (if (zerop spaces)
+         ;; no exceeding space
+         str
+       ;; at least one exceeding space
+       (let ((new (make-string (- len spaces) ?\ ))
+             (newi 0))
+         ;; eliminate exceeding spaces
+         (setq stri 0)
+         (while (> spaces 0)
+           (if (/= (aref str stri) ?\ )
+               (progn
+                 (aset new newi (aref str stri))
+                 (setq stri (1+ stri)
+                       newi (1+ newi)))
+             (aset new newi (aref str stri))
+             (setq stri (1+ stri)
+                   newi (1+ newi))
+             (while (and (> spaces 0) (= (aref str stri) ?\ ))
+               (setq stri   (1+ stri)
+                     spaces (1- spaces)))))
+         ;; remaining is normalized
+         (while (< stri len)
+           (aset new newi (aref str stri))
+           (setq stri (1+ stri)
+                 newi (1+ newi)))
+         new)))))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+(provide 'ebnf-iso)
+
+
+;;; ebnf-iso.el ends here
diff --git a/lisp/progmodes/ebnf-otz.el b/lisp/progmodes/ebnf-otz.el
new file mode 100644 (file)
index 0000000..5af9ef6
--- /dev/null
@@ -0,0 +1,661 @@
+;;; ebnf-otz --- Syntatic chart OpTimiZer
+
+;; Copyright (C) 1999 Vinicius Jose Latorre
+
+;; Author:     Vinicius Jose Latorre <vinicius@cpqd.com.br>
+;; Maintainer: Vinicius Jose Latorre <vinicius@cpqd.com.br>
+;; Keywords:   wp, ebnf, PostScript
+;; Time-stamp: <99/11/20 18:03:10 vinicius>
+;; Version:    1.0
+
+;; This file is *NOT* (yet?) part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;
+;; This is part of ebnf2ps package.
+;;
+;; This package defines an optimizer for ebnf2ps.
+;;
+;; See ebnf2ps.el for documentation.
+;;
+;;
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;; code:
+
+
+(require 'ebnf2ps)
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+(defvar ebnf-empty-rule-list nil
+  "List of empty rule name.")
+
+
+(defun ebnf-add-empty-rule-list (rule)
+  "Add empty RULE in `ebnf-empty-rule-list'."
+  (and ebnf-ignore-empty-rule
+       (eq (ebnf-node-kind (ebnf-node-production rule))
+          'ebnf-generate-empty)
+       (setq ebnf-empty-rule-list (cons (ebnf-node-name rule)
+                                       ebnf-empty-rule-list))))
+
+
+(defun ebnf-otz-initialize ()
+  "Initialize optimizer."
+  (setq ebnf-empty-rule-list nil))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Eliminate empty rules
+
+
+(defun ebnf-eliminate-empty-rules (syntax-list)
+  "Eliminate empty rules."
+  (while ebnf-empty-rule-list
+    (let ((ebnf-total (length syntax-list))
+         (ebnf-nprod 0)
+         (prod-list syntax-list)
+         new-list before)
+      (while prod-list
+       (ebnf-message-info "Eliminating empty rules")
+       (let ((rule (car prod-list)))
+         ;; if any non-terminal pertains to ebnf-empty-rule-list
+         ;; then eliminate non-terminal from rule
+         (if (ebnf-eliminate-empty rule)
+             (setq before prod-list)
+           ;; eliminate empty rule from syntax-list
+           (setq new-list (cons (ebnf-node-name rule) new-list))
+           (if before
+               (setcdr before (cdr prod-list))
+             (setq syntax-list (cdr syntax-list)))))
+       (setq prod-list (cdr prod-list)))
+      (setq ebnf-empty-rule-list new-list)))
+  syntax-list)
+
+
+;; [production   width-func entry height width name production action]
+;; [sequence     width-func entry height width list]
+;; [alternative  width-func entry height width list]
+;; [non-terminal width-func entry height width name default]
+;; [empty        width-func entry height width]
+;; [terminal     width-func entry height width name default]
+;; [special      width-func entry height width name default]
+
+(defun ebnf-eliminate-empty (rule)
+  (let ((kind (ebnf-node-kind rule)))
+    (cond
+     ;; non-terminal
+     ((eq kind 'ebnf-generate-non-terminal)
+      (if (member (ebnf-node-name rule) ebnf-empty-rule-list)
+         nil
+       rule))
+     ;; sequence
+     ((eq kind 'ebnf-generate-sequence)
+      (let ((seq    (ebnf-node-list rule))
+           (header (ebnf-node-list rule))
+           before elt)
+       (while seq
+         (setq elt (car seq))
+         (if (ebnf-eliminate-empty elt)
+             (setq before seq)
+           (if before
+               (setcdr before (cdr seq))
+             (setq header (cdr header))))
+         (setq seq (cdr seq)))
+       (when header
+         (ebnf-node-list rule header)
+         rule)))
+     ;; alternative
+     ((eq kind 'ebnf-generate-alternative)
+      (let ((seq    (ebnf-node-list rule))
+           (header (ebnf-node-list rule))
+           before elt)
+       (while seq
+         (setq elt (car seq))
+         (if (ebnf-eliminate-empty elt)
+             (setq before seq)
+           (if before
+               (setcdr before (cdr seq))
+             (setq header (cdr header))))
+         (setq seq (cdr seq)))
+       (when header
+         (if (= (length header) 1)
+             (car header)
+           (ebnf-node-list rule header)
+           rule))))
+     ;; production
+     ((eq kind 'ebnf-generate-production)
+      (let ((prod (ebnf-eliminate-empty (ebnf-node-production rule))))
+       (when prod
+         (ebnf-node-production rule prod)
+         rule)))
+     ;; terminal, special and empty
+     (t
+      rule)
+     )))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Optimizations
+
+
+;; *To be implemented*:
+;;    left recursion:
+;;    A = B | A C B | A C D.   ==>   A = B {C (B | D)}*.
+
+;;    right recursion:
+;;    A = B | C A.             ==>   A = {C}* B.
+;;    A = B | D | C A | E A.   ==>   A = { C | E }* ( B | D ).
+
+;;    optional:
+;;    A = B | C B.             ==>   A = [C] B.
+;;    A = B | B C.             ==>   A = B [C].
+;;    A = D | B D | B C D.     ==>   A = [B [C]] D.
+
+
+;; *Already implemented*:
+;;    left recursion:
+;;    A = B | A C.             ==>   A = B {C}*.
+;;    A = B | A B.             ==>   A = {B}+.
+;;    A =   | A B.             ==>   A = {B}*.
+;;    A = B | A C B.           ==>   A = {B || C}+.
+;;    A = B | D | A C | A E.   ==>   A = ( B | D ) { C | E }*.
+
+;;    optional:
+;;    A = B | .                ==>   A = [B].
+;;    A =   | B .              ==>   A = [B].
+
+;;    factoration:
+;;    A = B C | B D.           ==>   A = B (C | D).
+;;    A = C B | D B.           ==>   A = (C | D) B.
+;;    A = B C E | B D E.       ==>   A = B (C | D) E.
+
+;;    none:
+;;    A = B | C | .            ==>   A = B | C | .
+;;    A = B | C A D.           ==>   A = B | C A D.
+
+(defun ebnf-optimize (syntax-list)
+  "Syntatic chart optimizer."
+  (if (not ebnf-optimize)
+      syntax-list
+    (let ((ebnf-total (length syntax-list))
+         (ebnf-nprod 0)
+         new)
+      (while syntax-list
+       (setq new (cons (ebnf-optimize1 (car syntax-list)) new)
+             syntax-list (cdr syntax-list)))
+      (nreverse new))))
+
+
+;; left recursion:
+;; 1.  A = B | A C.             ==>   A = B {C}*.
+;; 2.  A = B | A B.             ==>   A = {B}+.
+;; 3.  A =   | A B.             ==>   A = {B}*.
+;; 4.  A = B | A C B.           ==>   A = {B || C}+.
+;; 5.  A = B | D | A C | A E.   ==>   A = ( B | D ) { C | E }*.
+
+;; optional:
+;; 6.  A = B | .                ==>   A = [B].
+;; 7.  A =   | B .              ==>   A = [B].
+
+;; factoration:
+;; 8.  A = B C | B D.           ==>   A = B (C | D).
+;; 9.  A = C B | D B.           ==>   A = (C | D) B.
+;; 10. A = B C E | B D E.       ==>   A = B (C | D) E.
+
+(defun ebnf-optimize1 (prod)
+  (ebnf-message-info "Optimizing syntatic chart")
+  (let ((production (ebnf-node-production prod)))
+    (and (eq (ebnf-node-kind production) 'ebnf-generate-alternative)
+        (let* ((hlist (ebnf-split-header-prefix
+                       (ebnf-node-list production)
+                       (ebnf-node-name prod)))
+               (nlist (car hlist))
+               (zlist (cdr hlist))
+               (elist (ebnf-split-header-suffix nlist zlist)))
+          (ebnf-node-production
+           prod
+           (cond
+            ;; cases 2., 4.
+            (elist
+             (and (eq elist t)
+                  (setq elist nil))
+             (setq elist (or (ebnf-prefix-suffix elist)
+                             elist))
+             (let* ((nl (ebnf-extract-empty nlist))
+                    (el (or (ebnf-prefix-suffix (cdr nl))
+                            (ebnf-create-alternative (cdr nl)))))
+               (if (car nl)
+                   (ebnf-make-zero-or-more el elist)
+                 (ebnf-make-one-or-more el elist))))
+            ;; cases 1., 3., 5.
+            (zlist
+             (let* ((xlist (cdr (ebnf-extract-empty zlist)))
+                    (znode (ebnf-make-zero-or-more
+                            (or (ebnf-prefix-suffix xlist)
+                                (ebnf-create-alternative xlist))))
+                    (nnode (ebnf-map-list-to-optional nlist)))
+               (and nnode
+                    (setq nlist (list nnode)))
+               (if (or (null nlist)
+                       (and (= (length nlist) 1)
+                            (eq (ebnf-node-kind (car nlist))
+                                'ebnf-generate-empty)))
+                   znode
+                 (ebnf-make-sequence
+                  (list (or (ebnf-prefix-suffix nlist)
+                            (ebnf-create-alternative nlist))
+                        znode)))))
+            ;; cases 6., 7.
+            ((ebnf-map-node-to-optional production)
+             )
+            ;; cases 8., 9., 10.
+            ((ebnf-prefix-suffix nlist)
+             )
+            ;; none
+            (t
+             production)
+            ))))
+    prod))
+
+
+(defun ebnf-split-header-prefix (node-list header)
+  (let* ((hlist (ebnf-split-header-prefix1 node-list header))
+        (nlist (car hlist))
+        zlist empty-p)
+    (while (setq hlist (cdr hlist))
+      (let ((elt (car hlist)))
+       (if (eq (ebnf-node-kind elt) 'ebnf-generate-sequence)
+           (setq zlist (cons
+                        (let ((seq (cdr (ebnf-node-list elt))))
+                          (if (= (length seq) 1)
+                              (car seq)
+                            (ebnf-node-list elt seq)
+                            elt))
+                        zlist))
+         (setq empty-p t))))
+    (and empty-p
+        (setq zlist (cons (ebnf-make-empty)
+                          zlist)))
+    (cons nlist (nreverse zlist))))
+
+
+(defun ebnf-split-header-prefix1 (node-list header)
+  (let (hlist nlist)
+    (while node-list
+      (if (ebnf-node-equal-header (car node-list) header)
+         (setq hlist (cons (car node-list) hlist))
+       (setq nlist (cons (car node-list) nlist)))
+      (setq node-list (cdr node-list)))
+    (cons (nreverse nlist) (nreverse hlist))))
+
+
+(defun ebnf-node-equal-header (node header)
+  (let ((kind (ebnf-node-kind node)))
+    (cond
+     ((eq kind 'ebnf-generate-sequence)
+      (ebnf-node-equal-header (car (ebnf-node-list node)) header))
+     ((eq kind 'ebnf-generate-non-terminal)
+      (string= (ebnf-node-name node) header))
+     (t
+      nil)
+     )))
+
+
+(defun ebnf-map-node-to-optional (node)
+  (and (eq (ebnf-node-kind node) 'ebnf-generate-alternative)
+       (ebnf-map-list-to-optional (ebnf-node-list node))))
+
+
+(defun ebnf-map-list-to-optional (nlist)
+  (and (= (length nlist) 2)
+       (let ((first  (nth 0 nlist))
+            (second (nth 1 nlist)))
+        (cond
+         ;; empty second
+         ((eq (ebnf-node-kind first) 'ebnf-generate-empty)
+          (ebnf-make-optional second))
+         ;; first empty
+         ((eq (ebnf-node-kind second) 'ebnf-generate-empty)
+          (ebnf-make-optional first))
+         ;; first second
+         (t
+          nil)
+         ))))
+
+
+(defun ebnf-extract-empty (elist)
+  (let ((now elist)
+       before empty-p)
+    (while now
+      (if (not (eq (ebnf-node-kind (car now)) 'ebnf-generate-empty))
+         (setq before now)
+       (setq empty-p t)
+       (if before
+           (setcdr before (cdr now))
+         (setq elist (cdr elist))))
+      (setq now (cdr now)))
+    (cons empty-p elist)))
+
+
+(defun ebnf-split-header-suffix (nlist zlist)
+  (let (new empty-p)
+    (and (cond
+         ((= (length nlist) 1)
+          (let ((ok t)
+                (elt (car nlist)))
+            (while (and ok zlist)
+              (setq ok    (ebnf-split-header-suffix1 elt (car zlist))
+                    zlist (cdr zlist))
+              (if (eq ok t)
+                  (setq empty-p t)
+                (setq new (cons ok new))))
+            ok))
+         ((= (length nlist) (length zlist))
+          (let ((ok t))
+            (while (and ok zlist)
+              (setq ok    (ebnf-split-header-suffix1 (car nlist) (car zlist))
+                    nlist (cdr nlist)
+                    zlist (cdr zlist))
+              (if (eq ok t)
+                  (setq empty-p t)
+                (setq new (cons ok new))))
+            ok))
+         (t
+          nil)
+         )
+        (let* ((lis (ebnf-unique-list new))
+               (len (length lis)))
+          (cond
+           ((zerop len)
+            t)
+           ((= len 1)
+            (setq lis (car lis))
+            (if empty-p
+                (ebnf-make-optional lis)
+              lis))
+           (t
+            (and empty-p
+                 (setq lis (cons (ebnf-make-empty) lis)))
+            (ebnf-create-alternative (nreverse lis)))
+           )))))
+
+
+(defun ebnf-split-header-suffix1 (ne ze)
+  (cond
+   ((eq (ebnf-node-kind ne) 'ebnf-generate-sequence)
+    (and (eq (ebnf-node-kind ze) 'ebnf-generate-sequence)
+        (let ((nl (ebnf-node-list ne))
+              (zl (ebnf-node-list ze))
+              len z)
+          (and (>= (length zl) (length nl))
+               (let ((ok t))
+                 (setq len (- (length zl) (length nl))
+                       z   (nthcdr len zl))
+                 (while (and ok z)
+                   (setq ok (ebnf-node-equal (car z) (car nl))
+                         z  (cdr z)
+                         nl (cdr nl)))
+                 ok)
+               (if (zerop len)
+                   t
+                 (setcdr (nthcdr (1- len) zl) nil)
+                 ze)))))
+   ((eq (ebnf-node-kind ze) 'ebnf-generate-sequence)
+    (let* ((zl  (ebnf-node-list ze))
+          (len (length zl)))
+      (and (ebnf-node-equal ne (car (nthcdr (1- len) zl)))
+          (cond
+           ((= len 1)
+            t)
+           ((= len 2)
+            (car zl))
+           (t
+            (setcdr (nthcdr (- len 2) zl) nil)
+            ze)
+           ))))
+   (t
+    (ebnf-node-equal ne ze))
+   ))
+
+
+(defun ebnf-prefix-suffix (lis)
+  (and lis (listp lis)
+       (let* ((prefix (ebnf-split-prefix lis))
+             (suffix (ebnf-split-suffix (cdr prefix)))
+             (middle (cdr suffix)))
+        (setq prefix (car prefix)
+              suffix (car suffix))
+        (and (or prefix suffix)
+             (ebnf-make-sequence
+              (nconc prefix
+                     (and middle
+                          (list (or (ebnf-map-list-to-optional middle)
+                                    (ebnf-create-alternative middle))))
+                     suffix))))))
+
+
+(defun ebnf-split-prefix (lis)
+  (let* ((len  (length lis))
+        (tail lis)
+        (head (if (eq (ebnf-node-kind (car lis)) 'ebnf-generate-sequence)
+                  (ebnf-node-list (car lis))
+                (list (car lis))))
+        (ipre (1+ len)))
+    ;; determine prefix length
+    (while (and (> ipre 0) (setq tail (cdr tail)))
+      (let ((cur head)
+           (this (if (eq (ebnf-node-kind (car tail)) 'ebnf-generate-sequence)
+                     (ebnf-node-list (car tail))
+                   (list (car tail))))
+           (i 0))
+       (while (and cur this
+                   (ebnf-node-equal (car cur) (car this)))
+         (setq cur  (cdr cur)
+               this (cdr this)
+               i    (1+ i)))
+       (setq ipre (min ipre i))))
+    (if (or (zerop ipre) (> ipre len))
+       ;; no prefix at all
+       (cons nil lis)
+      (let* ((tail   (nthcdr ipre head))
+            ;; get prefix
+            (prefix (progn
+                      (and tail
+                           (setcdr (nthcdr (1- ipre) head) nil))
+                      head))
+            empty-p before)
+       ;; adjust first element
+       (if (or (not (eq (ebnf-node-kind (car lis)) 'ebnf-generate-sequence))
+               (null tail))
+           (setq lis     (cdr lis)
+                 tail    lis
+                 empty-p t)
+         (if (= (length tail) 1)
+             (setcar lis (car tail))
+           (ebnf-node-list (car lis) tail))
+         (setq tail (cdr lis)))
+       ;; eliminate prefix from lis based on ipre
+       (while tail
+         (let ((elt (car tail))
+               rest)
+           (if (and (eq (ebnf-node-kind elt) 'ebnf-generate-sequence)
+                    (setq rest (nthcdr ipre (ebnf-node-list elt))))
+               (progn
+                 (if (= (length rest) 1)
+                     (setcar tail (car rest))
+                   (ebnf-node-list elt rest))
+                 (setq before tail))
+             (setq empty-p t)
+             (if before
+                 (setcdr before (cdr tail))
+               (setq lis (cdr lis))))
+           (setq tail (cdr tail))))
+       (cons prefix (ebnf-unique-list
+                     (if empty-p
+                         (nconc lis (list (ebnf-make-empty)))
+                       lis)))))))
+
+
+(defun ebnf-split-suffix (lis)
+  (let* ((len  (length lis))
+        (tail lis)
+        (head (nreverse
+               (if (eq (ebnf-node-kind (car lis)) 'ebnf-generate-sequence)
+                   (ebnf-node-list (car lis))
+                 (list (car lis)))))
+        (isuf (1+ len)))
+    ;; determine suffix length
+    (while (and (> isuf 0) (setq tail (cdr tail)))
+      (let* ((cur head)
+            (tlis (nreverse
+                   (if (eq (ebnf-node-kind (car tail)) 'ebnf-generate-sequence)
+                       (ebnf-node-list (car tail))
+                     (list (car tail)))))
+            (this tlis)
+            (i 0))
+       (while (and cur this
+                   (ebnf-node-equal (car cur) (car this)))
+         (setq cur  (cdr cur)
+               this (cdr this)
+               i    (1+ i)))
+       (nreverse tlis)
+       (setq isuf (min isuf i))))
+    (setq head (nreverse head))
+    (if (or (zerop isuf) (> isuf len))
+       ;; no suffix at all
+       (cons nil lis)
+      (let* ((n      (- (length head) isuf))
+            ;; get suffix
+            (suffix (nthcdr n head))
+            (tail   (and (> n 0)
+                         (progn
+                           (setcdr (nthcdr (1- n) head) nil)
+                           head)))
+            before empty-p)
+       ;; adjust first element
+       (if (or (not (eq (ebnf-node-kind (car lis)) 'ebnf-generate-sequence))
+               (null tail))
+           (setq lis     (cdr lis)
+                 tail    lis
+                 empty-p t)
+         (if (= (length tail) 1)
+             (setcar lis (car tail))
+           (ebnf-node-list (car lis) tail))
+         (setq tail (cdr lis)))
+       ;; eliminate suffix from lis based on isuf
+       (while tail
+         (let ((elt (car tail))
+               rest)
+           (if (and (eq (ebnf-node-kind elt) 'ebnf-generate-sequence)
+                    (setq rest (ebnf-node-list elt)
+                          n    (- (length rest) isuf))
+                    (> n 0))
+               (progn
+                 (if (= n 1)
+                     (setcar tail (car rest))
+                   (setcdr (nthcdr (1- n) rest) nil)
+                   (ebnf-node-list elt rest))
+                 (setq before tail))
+             (setq empty-p t)
+             (if before
+                 (setcdr before (cdr tail))
+               (setq lis (cdr lis))))
+           (setq tail (cdr tail))))
+       (cons suffix (ebnf-unique-list
+                     (if empty-p
+                         (nconc lis (list (ebnf-make-empty)))
+                       lis)))))))
+
+
+(defun ebnf-unique-list (nlist)
+  (let ((current nlist)
+       before)
+    (while current
+      (let ((tail (cdr current))
+           (head (car current))
+           remove-p)
+       (while tail
+         (if (not (ebnf-node-equal head (car tail)))
+             (setq tail (cdr tail))
+           (setq remove-p t
+                 tail     nil)
+           (if before
+               (setcdr before (cdr current))
+             (setq nlist (cdr nlist)))))
+       (or remove-p
+           (setq before current))
+       (setq current (cdr current))))
+    nlist))
+
+
+(defun ebnf-node-equal (A B)
+  (let ((kindA (ebnf-node-kind A))
+       (kindB (ebnf-node-kind B)))
+    (and (eq kindA kindB)
+        (cond
+         ;; empty
+         ((eq kindA 'ebnf-generate-empty)
+          t)
+         ;; non-terminal, terminal, special
+         ((memq kindA '(ebnf-generate-non-terminal
+                        ebnf-generate-terminal
+                        ebnf-generate-special))
+          (string= (ebnf-node-name A) (ebnf-node-name B)))
+         ;; alternative, sequence
+         ((memq kindA '(ebnf-generate-alternative ; any order
+                        ebnf-generate-sequence)) ; order is important
+          (let ((listA (ebnf-node-list A))
+                (listB (ebnf-node-list B)))
+            (and (= (length listA) (length listB))
+                 (let ((ok t))
+                   (while (and ok listA)
+                     (setq ok    (ebnf-node-equal (car listA) (car listB))
+                           listA (cdr listA)
+                           listB (cdr listB)))
+                   ok))))
+         ;; production
+         ((eq kindA 'ebnf-generate-production)
+          (and (string= (ebnf-node-name A) (ebnf-node-name B))
+               (ebnf-node-equal (ebnf-node-production A)
+                                (ebnf-node-production B))))
+         ;; otherwise
+         (t
+          nil)
+         ))))
+
+
+(defun ebnf-create-alternative (alt)
+  (if (> (length alt) 1)
+      (ebnf-make-alternative alt)
+    (car alt)))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+(provide 'ebnf-otz)
+
+
+;;; ebnf-otz.el ends here
diff --git a/lisp/progmodes/ebnf-yac.el b/lisp/progmodes/ebnf-yac.el
new file mode 100644 (file)
index 0000000..bbe135e
--- /dev/null
@@ -0,0 +1,487 @@
+;;; ebnf-yac --- Parser for Yacc/Bison
+
+;; Copyright (C) 1999 Vinicius Jose Latorre
+
+;; Author:     Vinicius Jose Latorre <vinicius@cpqd.com.br>
+;; Maintainer: Vinicius Jose Latorre <vinicius@cpqd.com.br>
+;; Keywords:   wp, ebnf, PostScript
+;; Time-stamp: <99/11/20 18:02:43 vinicius>
+;; Version:    1.0
+
+;; This file is *NOT* (yet?) part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;
+;; This is part of ebnf2ps package.
+;;
+;; This package defines a parser for Yacc/Bison.
+;;
+;; See ebnf2ps.el for documentation.
+;;
+;;
+;; Yacc/Bison Syntax
+;; -----------------
+;;
+;; YACC = { YACC-Definitions }* "%%" { YACC-Rule }* [ "%%" [ YACC-Code ] ].
+;;
+;; YACC-Definitions = "%token" [ "<" Name ">" ] Name-List
+;;                  | "any other Yacc definition"
+;;                  .
+;;
+;; YACC-Code = "any C definition".
+;;
+;; YACC-Rule = Name ":" Alternative ";".
+;;
+;; Alternative = { Sequence || "|" }*.
+;;
+;; Sequence = { Factor }*.
+;;
+;; Factor = Name
+;;        | "'" "character" "'"
+;;        | "error"
+;;        | "{" "C like commands" "}"
+;;        .
+;;
+;; Name-List = { Name || "," }*.
+;;
+;; Name = "[A-Za-z][A-Za-z0-9_.]*".
+;;
+;; Comment = "/*" "any character, but the sequence \"*/\"" "*/"
+;;         | "//" "any character" "\\n".
+;;
+;;
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;; code:
+
+
+(require 'ebnf-otz)
+
+
+(defvar ebnf-yac-lex nil
+  "Value returned by `ebnf-yac-lex' function.")
+
+
+(defvar ebnf-yac-token-list nil
+  "List of `%TOKEN' names.")
+
+
+(defvar ebnf-yac-skip-char nil
+  "Non-nil means skip printable characters with no grammatical meaning.")
+
+
+(defvar ebnf-yac-error nil
+  "Non-nil means \"error\" occured.")
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Syntatic analyzer
+
+
+;;; YACC = { YACC-Definitions }* "%%" { YACC-Rule }* [ "%%" [ YACC-Code ] ].
+;;;
+;;; YACC-Code = "any C definition".
+
+(defun ebnf-yac-parser (start)
+  "yacc/Bison parser."
+  (let ((total (+ (- ebnf-limit start) 1))
+       (bias (1- start))
+       (origin (point))
+       syntax-list token rule)
+    (goto-char start)
+    (setq token (ebnf-yac-lex))
+    (and (eq token 'end-of-input)
+        (error "Invalid Yacc/Bison file format."))
+    (or (eq (ebnf-yac-definitions token) 'yac-separator)
+       (error "Missing `%%%%'."))
+    (setq token (ebnf-yac-lex))
+    (while (not (memq token '(end-of-input yac-separator)))
+      (ebnf-message-float
+       "Parsing...%s%%"
+       (/ (* (- (point) bias) 100.0) total))
+      (setq token (ebnf-yac-rule token)
+           rule  (cdr token)
+           token (car token))
+      (or (ebnf-add-empty-rule-list rule)
+         (setq syntax-list (cons rule syntax-list))))
+    (goto-char origin)
+    syntax-list))
+
+
+;;; YACC-Definitions = "%token" [ "<" Name ">" ] Name-List
+;;;                  | "any other Yacc definition"
+;;;                  .
+
+(defun ebnf-yac-definitions (token)
+  (let ((ebnf-yac-skip-char t))
+    (while (not (memq token '(yac-separator end-of-input)))
+      (setq token
+           (cond
+            ;; "%token" [ "<" Name ">" ] Name-List
+            ((eq token 'yac-token)
+             (setq token (ebnf-yac-lex))
+             (when (eq token 'open-angle)
+               (or (eq (ebnf-yac-lex) 'non-terminal)
+                   (error "Missing type name."))
+               (or (eq (ebnf-yac-lex) 'close-angle)
+                   (error "Missing `>'."))
+               (setq token (ebnf-yac-lex)))
+             (setq token               (ebnf-yac-name-list token)
+                   ebnf-yac-token-list (nconc (cdr token)
+                                              ebnf-yac-token-list))
+             (car token))
+            ;; "any other Yacc definition"
+            (t
+             (ebnf-yac-lex))
+            )))
+    token))
+
+
+;;; YACC-Rule = Name ":" Alternative ";".
+
+(defun ebnf-yac-rule (token)
+  (let ((header ebnf-yac-lex)
+       (action ebnf-action)
+       body)
+    (setq ebnf-action nil)
+    (or (eq token 'non-terminal)
+       (error "Invalid rule name."))
+    (or (eq (ebnf-yac-lex) 'colon)
+       (error "Invalid rule: missing `:'."))
+    (setq body (ebnf-yac-alternative))
+    (or (eq (car body) 'period)
+       (error "Invalid rule: missing `;'."))
+    (setq body (cdr body))
+    (ebnf-eps-add-production header)
+    (cons (ebnf-yac-lex)
+         (ebnf-make-production header body action))))
+
+
+;;; Alternative = { Sequence || "|" }*.
+
+(defun ebnf-yac-alternative ()
+  (let (body sequence)
+    (while (eq (car (setq sequence (ebnf-yac-sequence)))
+              'alternative)
+      (and (setq sequence (cdr sequence))
+          (setq body     (cons sequence body))))
+    (ebnf-token-alternative body sequence)))
+
+
+;;; Sequence = { Factor }*.
+
+(defun ebnf-yac-sequence ()
+  (let (ebnf-yac-error token seq factor)
+    (while (setq token  (ebnf-yac-lex)
+                factor (ebnf-yac-factor token))
+      (setq seq (cons factor seq)))
+    (cons token
+         (cond
+          ;; ignore error recovery
+          ((and ebnf-yac-ignore-error-recovery ebnf-yac-error)
+           nil)
+          ;; null sequence
+          ((null seq)
+           (ebnf-make-empty))
+          ;; sequence with only one element
+          ((= (length seq) 1)
+           (car seq))
+          ;; a real sequence
+          (t
+           (ebnf-make-sequence (nreverse seq)))
+          ))))
+
+
+;;; Factor = Name
+;;;        | "'" "character" "'"
+;;;        | "error"
+;;;        | "{" "C like commands" "}"
+;;;        .
+
+(defun ebnf-yac-factor (token)
+  (cond
+   ;; 'character'
+   ((eq token 'terminal)
+    (ebnf-make-terminal ebnf-yac-lex))
+   ;; Name
+   ((eq token 'non-terminal)
+    (ebnf-make-non-terminal ebnf-yac-lex))
+   ;; "error"
+   ((eq token 'yac-error)
+    (ebnf-make-special ebnf-yac-lex))
+   ;; not a factor
+   (t
+    nil)
+   ))
+
+
+;;; Name-List = { Name || "," }*.
+
+(defun ebnf-yac-name-list (token)
+  (let (names)
+    (when (eq token 'non-terminal)
+      (while (progn
+              (setq names (cons ebnf-yac-lex names)
+                    token (ebnf-yac-lex))
+              (eq token 'comma))
+       (or (eq (ebnf-yac-lex) 'non-terminal)
+           (error "Missing token name."))))
+    (cons token names)))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Lexical analyzer
+
+
+;;; Name = "[A-Za-z][A-Za-z0-9_.]*".
+;;;
+;;; Comment = "/*" "any character, but the sequence \"*/\"" "*/"
+;;;         | "//" "any character" "\\n".
+
+(defconst ebnf-yac-token-table
+  ;; control character & 8-bit character are set to `error'
+  (let ((table (make-vector 256 'error)))
+    ;; upper & lower case letters:
+    (mapcar
+     #'(lambda (char)
+        (aset table char 'non-terminal))
+     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
+    ;; printable characters:
+    (mapcar
+     #'(lambda (char)
+        (aset table char 'character))
+     "!#$&()*+-.0123456789=?@[\\]^_`~")
+    ;; Override space characters:
+    (aset table ?\n 'space)            ; [NL] linefeed
+    (aset table ?\r 'space)            ; [CR] carriage return
+    (aset table ?\t 'space)            ; [HT] horizontal tab
+    (aset table ?\  'space)            ; [SP] space
+    ;; Override form feed character:
+    (aset table ?\f 'form-feed)                ; [FF] form feed
+    ;; Override other lexical characters:
+    (aset table ?<  'open-angle)
+    (aset table ?>  'close-angle)
+    (aset table ?,  'comma)
+    (aset table ?%  'yac-pragma)
+    (aset table ?/  'slash)
+    (aset table ?\{ 'yac-code)
+    (aset table ?\" 'string)
+    (aset table ?\' 'terminal)
+    (aset table ?:  'colon)
+    (aset table ?|  'alternative)
+    (aset table ?\; 'period)
+    table)
+  "Vector used to map characters to a lexical token.")
+
+
+(defun ebnf-yac-initialize ()
+  "Initializations for Yacc/Bison parser."
+  (setq ebnf-yac-token-list nil))
+
+
+(defun ebnf-yac-lex ()
+  "Lexical analyser for Yacc/Bison.
+
+Return a lexical token.
+
+See documentation for variable `ebnf-yac-lex'."
+  (if (>= (point) ebnf-limit)
+      'end-of-input
+    (let (token)
+      ;; skip spaces, code blocks and comments
+      (while (if (> (following-char) 255)
+                (progn
+                  (setq token 'error)
+                  nil)
+              (setq token (aref ebnf-yac-token-table (following-char)))
+              (cond
+               ((or (eq token 'space)
+                    (and ebnf-yac-skip-char
+                         (eq token 'character)))
+                (ebnf-yac-skip-spaces))
+               ((eq token 'yac-code)
+                (ebnf-yac-skip-code))
+               ((eq token 'slash)
+                (ebnf-yac-handle-comment))
+               ((eq token 'form-feed)
+                (forward-char)
+                (setq ebnf-action 'form-feed))
+               (t nil)
+               )))
+      (cond
+       ;; end of input
+       ((>= (point) ebnf-limit)
+       'end-of-input)
+       ;; error
+       ((eq token 'error)
+       (error "Illegal character."))
+       ;; "string"
+       ((eq token 'string)
+       (setq ebnf-yac-lex (ebnf-get-string))
+       'string)
+       ;; terminal: 'char'
+       ((eq token 'terminal)
+       (setq ebnf-yac-lex (ebnf-string " -&(-~" ?\' "terminal"))
+       'terminal)
+       ;; non-terminal, terminal or "error"
+       ((eq token 'non-terminal)
+       (setq ebnf-yac-lex (ebnf-buffer-substring "0-9A-Za-z_."))
+       (cond ((member ebnf-yac-lex ebnf-yac-token-list)
+              'terminal)
+             ((string= ebnf-yac-lex "error")
+              (setq ebnf-yac-error t)
+              'yac-error)
+             (t
+              'non-terminal)
+             ))
+       ;; %% and Yacc pragmas (%TOKEN, %START, etc).
+       ((eq token 'yac-pragma)
+       (forward-char)
+       (cond
+        ;; Yacc separator
+        ((eq (following-char) ?%)
+         (forward-char)
+         'yac-separator)
+        ;; %TOKEN
+        ((string= (upcase (ebnf-buffer-substring "0-9A-Za-z_")) "TOKEN")
+         'yac-token)
+        ;; other Yacc pragmas
+        (t
+         'yac-pragma)
+        ))
+       ;; miscellaneous
+       (t
+       (forward-char)
+       token)
+       ))))
+
+
+(defun ebnf-yac-skip-spaces ()
+  (skip-chars-forward
+   (if ebnf-yac-skip-char
+       "\n\r\t !#$&()*+-.0123456789=?@[\\\\]^_`~"
+     "\n\r\t ")
+   ebnf-limit)
+  (< (point) ebnf-limit))
+
+
+(defun ebnf-yac-skip-code ()
+  (forward-char)
+  (let ((pair 1))
+    (while (> pair 0)
+      (skip-chars-forward "^{}/'\"\000-\010\013\016-\037\177-\377" ebnf-limit)
+      (cond
+       ((= (following-char) ?{)
+       (forward-char)
+       (setq pair (1+ pair)))
+       ((= (following-char) ?})
+       (forward-char)
+       (setq pair (1- pair)))
+       ((= (following-char) ?/)
+       (ebnf-yac-handle-comment))
+       ((= (following-char) ?\")
+       (ebnf-get-string))
+       ((= (following-char) ?\')
+       (ebnf-string " -&(-~" ?\' "character"))
+       (t
+       (error "Illegal character."))
+       )))
+  (ebnf-yac-skip-spaces))
+
+
+(defun ebnf-yac-handle-comment ()
+  (forward-char)
+  (cond
+   ;; begin comment
+   ((= (following-char) ?*)
+    (ebnf-yac-skip-comment)
+    (ebnf-yac-skip-spaces))
+   ;; line comment
+   ((= (following-char) ?/)
+    (end-of-line)
+    (ebnf-yac-skip-spaces))
+   ;; no comment
+   (t nil)
+   ))
+
+
+(defconst ebnf-yac-comment-chars "^*\000-\010\013\016-\037\177-\237")
+
+
+(defun ebnf-yac-skip-comment ()
+  (forward-char)
+  (cond
+   ;; open EPS file
+   ((and ebnf-eps-executing (= (following-char) ?\[))
+    (ebnf-eps-add-context (ebnf-yac-eps-filename)))
+   ;; close EPS file
+   ((and ebnf-eps-executing (= (following-char) ?\]))
+    (ebnf-eps-remove-context (ebnf-yac-eps-filename)))
+   ;; any other action in comment
+   (t
+    (setq ebnf-action (aref ebnf-comment-table (following-char))))
+   )
+  (let ((not-end t))
+    (while not-end
+      (skip-chars-forward ebnf-yac-comment-chars ebnf-limit)
+      (cond ((>= (point) ebnf-limit)
+            (error "Missing end of comment: `*/'."))
+           ((= (following-char) ?*)
+            (skip-chars-forward "*" ebnf-limit)
+            (when (= (following-char) ?/)
+              ;; end of comment
+              (forward-char)
+              (setq not-end nil)))
+           (t
+            (error "Illegal character."))
+           ))))
+
+
+(defun ebnf-yac-eps-filename ()
+  (forward-char)
+  (buffer-substring-no-properties
+   (point)
+   (let ((chars (concat ebnf-yac-comment-chars "\n"))
+        found)
+     (while (not found)
+       (skip-chars-forward chars ebnf-limit)
+       (setq found
+            (cond ((>= (point) ebnf-limit)
+                   (point))
+                  ((= (following-char) ?*)
+                   (skip-chars-forward "*" ebnf-limit)
+                   (if (/= (following-char) ?\/)
+                       nil
+                     (backward-char)
+                     (point)))
+                  (t
+                   (point))
+                  )))
+     found)))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+(provide 'ebnf-yac)
+
+
+;;; ebnf-yac.el ends here
diff --git a/lisp/progmodes/ebnf2ps.el b/lisp/progmodes/ebnf2ps.el
new file mode 100644 (file)
index 0000000..477dbd9
--- /dev/null
@@ -0,0 +1,5339 @@
+;;; ebnf2ps --- Translate an EBNF to a syntatic chart on PostScript
+
+;; Copyright (C) 1999 Vinicius Jose Latorre
+
+;; Author:     Vinicius Jose Latorre <vinicius@cpqd.com.br>
+;; Maintainer: Vinicius Jose Latorre <vinicius@cpqd.com.br>
+;; Keywords:   wp, ebnf, PostScript
+;; Time-stamp: <99/12/11 21:41:24 vinicius>
+;; Version:    3.1
+
+(defconst ebnf-version "3.1"
+  "ebnf2ps.el, v 3.1 <99/12/11 vinicius>
+
+Vinicius's last change version.  When reporting bugs, please also
+report the version of Emacs, if any, that ebnf2ps was running with.
+
+Please send all bug fixes and enhancements to
+       Vinicius Jose Latorre <vinicius@cpqd.com.br>.
+")
+
+;; This file is *NOT* (yet?) part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; Introduction
+;; ------------
+;;
+;; This package translates an EBNF to a syntatic chart on PostScript.
+;;
+;; To use ebnf2ps, insert in your ~/.emacs:
+;;
+;;        (require 'ebnf2ps)
+;;
+;; ebnf2ps uses ps-print package (version 3.05.1 or later), so see ps-print to
+;; know how to set options like landscape printing, page headings, margins, etc.
+;;
+;; NOTE: ps-print zebra stripes and line number options doesn't have effect on
+;;       ebnf2ps, they behave as it's turned off.
+;;
+;; For good performance, be sure to byte-compile ebnf2ps.el, e.g.
+;;
+;;    M-x byte-compile-file <give the path to ebnf2ps.el when prompted>
+;;
+;; This will generate ebnf2ps.elc, which will be loaded instead of ebnf2ps.el.
+;;
+;; ebnf2ps was tested with GNU Emacs 20.4.1.
+;;
+;;
+;; Using ebnf2ps
+;; -------------
+;;
+;; ebnf2ps provides six commands for generating PostScript syntatic chart images
+;; of Emacs buffers:
+;;
+;;        ebnf-print-buffer
+;;        ebnf-print-region
+;;        ebnf-spool-buffer
+;;        ebnf-spool-region
+;;        ebnf-eps-buffer
+;;        ebnf-eps-region
+;;
+;; These commands all perform essentially the same function: they generate
+;; PostScript syntatic chart images suitable for printing on a PostScript
+;; printer or displaying with GhostScript.  These commands are collectively
+;; referred to as "ebnf- commands".
+;;
+;; The word "print", "spool" and "eps" in the command name determines when the
+;; PostScript image is sent to the printer (or file):
+;;
+;;        print  - The PostScript image is immediately sent to the printer;
+;;
+;;        spool  - The PostScript image is saved temporarily in an Emacs buffer.
+;;                 Many images may be spooled locally before printing them.  To
+;;                 send the spooled images to the printer, use the command
+;;                 `ebnf-despool'.
+;;
+;;        eps    - The PostScript image is immediately sent to a EPS file.
+;;
+;; The spooling mechanism is the same as used by ps-print and was designed for
+;; printing lots of small files to save paper that would otherwise be wasted on
+;; banner pages, and to make it easier to find your output at the printer (it's
+;; easier to pick up one 50-page printout than to find 50 single-page
+;; printouts).  As ebnf2ps and ps-print use the same Emacs buffer to spool
+;; images, you can intermix the spooling of ebnf2ps and ps-print images.
+;;
+;; ebnf2ps use the same hook of ps-print in the `kill-emacs-hook' so that you
+;; won't accidentally quit from Emacs while you have unprinted PostScript
+;; waiting in the spool buffer.  If you do attempt to exit with spooled
+;; PostScript, you'll be asked if you want to print it, and if you decline,
+;; you'll be asked to confirm the exit; this is modeled on the confirmation that
+;; Emacs uses for modified buffers.
+;;
+;; The word "buffer" or "region" in the command name determines how much of the
+;; buffer is printed:
+;;
+;;        buffer  - Print the entire buffer.
+;;
+;;        region  - Print just the current region.
+;;
+;; Two ebnf- command examples:
+;;
+;;        ebnf-print-buffer  - translate and print the entire buffer, and send
+;;                             it immediately to the printer.
+;;
+;;        ebnf-spool-region  - translate and print just the current region, and
+;;                             spool the image in Emacs to send to the printer
+;;                             later.
+;;
+;; Note that `ebnf-eps-buffer' and `ebnf-eps-region' never spool the EPS image,
+;; so they don't use the ps-print spooling mechanism.  See section "Actions in
+;; Comments" for an explanation about EPS file generation.
+;;
+;;
+;; Invoking Ebnf2ps
+;; ----------------
+;;
+;; To translate and print your buffer, type
+;;
+;;        M-x ebnf-print-buffer
+;;
+;; or substitute one of the other four ebnf- commands.  The command will
+;; generate the PostScript image and print or spool it as specified.  By giving
+;; the command a prefix argument
+;;
+;;        C-u M-x ebnf-print-buffer
+;;
+;; it will save the PostScript image to a file instead of sending it to the
+;; printer; you will be prompted for the name of the file to save the image to.
+;; The prefix argument is ignored by the commands that spool their images, but
+;; you may save the spooled images to a file by giving a prefix argument to
+;; `ebnf-despool':
+;;
+;;        C-u M-x ebnf-despool
+;;
+;; When invoked this way, `ebnf-despool' will prompt you for the name of the
+;; file to save to.
+;;
+;; The prefix argument is also ignored by `ebnf-eps-buffer' and
+;; `ebnf-eps-region'.
+;;
+;; Any of the `ebnf-' commands can be bound to keys.  Here are some examples:
+;;
+;;   (global-set-key 'f22 'ebnf-print-buffer) ;f22 is prsc
+;;   (global-set-key '(shift f22) 'ebnf-print-region)
+;;   (global-set-key '(control f22) 'ebnf-despool)
+;;
+;;
+;; EBNF Syntax
+;; -----------
+;;
+;; The current EBNF that ebnf2ps accepts has the following constructions:
+;;
+;;    ;                        comment (until end of line)
+;;    A                        non-terminal
+;;    "C"              terminal
+;;    ?C?              special
+;;    $A               default non-terminal (see text below)
+;;    $"C"             default terminal (see text below)
+;;    $?C?             default special (see text below)
+;;    A = B.           production (A is the header and B the body)
+;;    C D              sequence (C occurs before D)
+;;    C | D            alternative (C or D occurs)
+;;    A - B            exception (A excluding B, B without any non-terminal)
+;;    n * A            repetition (A repeats n (integer) times)
+;;    (C)              group (expression C is grouped together)
+;;    [C]              optional (C may or not occurs)
+;;    C+               one or more occurrences of C
+;;    {C}+             one or more occurrences of C
+;;    {C}*             zero or more occurrences of C
+;;    {C}              zero or more occurrences of C
+;;    C / D            equivalent to: C {D C}*
+;;    {C || D}+                equivalent to: C {D C}*
+;;    {C || D}*                equivalent to: [C {D C}*]
+;;    {C || D}         equivalent to: [C {D C}*]
+;;
+;; The EBNF syntax written using the notation above is:
+;;
+;;    EBNF = {production}+.
+;;
+;;    production = non_terminal "=" body ".".   ;; production
+;;
+;;    body = {sequence || "|"}*.                ;; alternative
+;;
+;;    sequence = {exception}*.                  ;; sequence
+;;
+;;    exception = repeat [ "-" repeat].         ;; exception
+;;
+;;    repeat = [ integer "*" ] term.            ;; repetition
+;;
+;;    term = factor
+;;         | [factor] "+"                       ;; one-or-more
+;;         | [factor] "/" [factor]              ;; one-or-more
+;;         .
+;;
+;;    factor = [ "$" ] "\"" terminal "\""       ;; terminal
+;;           | [ "$" ] non_terminal             ;; non-terminal
+;;           | [ "$" ] "?" special "?"          ;; special
+;;           | "(" body ")"                     ;; group
+;;           | "[" body "]"                     ;; zero-or-one
+;;           | "{" body [ "||" body ] "}+"      ;; one-or-more
+;;           | "{" body [ "||" body ] "}*"      ;; zero-or-more
+;;           | "{" body [ "||" body ] "}"       ;; zero-or-more
+;;           .
+;;
+;;    non_terminal = "[A-Za-z\\240-\\377][!#%&'*-,0-:<>@-Z\\^-z~\\240-\\377]*".
+;;
+;;    terminal = "\\([^\"\\]\\|\\\\[ -~\\240-\\377]\\)+".
+;;
+;;    special = "[^?\\n\\000-\\010\\016-\\037\\177-\\237]*".
+;;
+;;    integer = "[0-9]+".
+;;
+;;    comment = ";" "[^\\n\\000-\\010\\016-\\037\\177-\\237]*" "\\n".
+;;
+;; Try to use the above EBNF to test ebnf2ps.
+;;
+;; The `default' terminal, non-terminal and special is a way to indicate a
+;; default path in a production.  For example, the production:
+;;
+;;    X = [ $A ( B | $C ) | D ].
+;;
+;; Indicates that the default meaning for "X" is "A C" if "X" is empty.
+;;
+;; The terminal name is controlled by `ebnf-terminal-regexp' and
+;; `ebnf-case-fold-search', so it's possible to match other kind of terminal
+;; name besides that enclosed by `"'.
+;;
+;; Let's see an example:
+;;
+;;    (setq ebnf-terminal-regexp "[A-Z][_A-Z]*") ; upper case name
+;;    (setq ebnf-case-fold-search nil) ; exact matching
+;;
+;; If you have the production:
+;;
+;;    Logical = "(" Expression ( OR | AND | "XOR" ) Expression ")".
+;;
+;; The names are classified as:
+;;
+;;    Logical  Expression              non-terminal
+;;    "("  OR  AND  "XOR"  ")"         terminal
+;;
+;; The line comment is controlled by `ebnf-lex-comment-char'.  The default value
+;; is ?\; (character `;').
+;;
+;; The end of production is controlled by `ebnf-lex-eop-char'.  The default
+;; value is ?. (character `.').
+;;
+;; The variable `ebnf-syntax' specifies which syntax to recognize:
+;;
+;;    `ebnf'           ebnf2ps recognizes the syntax described above.
+;;                     The following variables *ONLY* have effect with this
+;;                     setting:
+;;                     `ebnf-terminal-regexp', `ebnf-case-fold-search',
+;;                     `ebnf-lex-comment-char' and `ebnf-lex-eop-char'.
+;;
+;;    `iso-ebnf'       ebnf2ps recognizes the syntax described in the URL:
+;;                     `http://www.cl.cam.ac.uk/~mgk25/iso-ebnf.html'
+;;                     ("International Standard of the ISO EBNF Notation").
+;;                     The following variables *ONLY* have effect with this
+;;                     setting:
+;;                     `ebnf-iso-alternative-p' and `ebnf-iso-normalize-p'.
+;;
+;;    `yacc'           ebnf2ps recognizes the Yacc/Bison syntax.
+;;                     The following variable *ONLY* has effect with this
+;;                     setting:
+;;                     `ebnf-yac-ignore-error-recovery'.
+;;
+;; Any other value is treated as `ebnf'.
+;;
+;; The default value is `ebnf'.
+;;
+;;
+;; Optimizations
+;; -------------
+;;
+;; The following EBNF optimizations are done:
+;;
+;;    [ { A }* ]          ==> { A }*
+;;    [ { A }+ ]          ==> { A }*
+;;    [ A ] +             ==> { A }*
+;;    { A }* +            ==> { A }*
+;;    { A }+ +            ==> { A }+
+;;    { A }-              ==> { A }+
+;;    [ A ]-              ==> A
+;;    ( A | EMPTY )-      ==> A
+;;    ( A | B | EMPTY )-  ==> A | B
+;;    [ A | B ]           ==> A | B | EMPTY
+;;    n * EMPTY           ==> EMPTY
+;;    EMPTY +             ==> EMPTY
+;;    EMPTY / EMPTY       ==> EMPTY
+;;    EMPTY - A           ==> EMPTY
+;;
+;; The following optimizations are done when `ebnf-optimize' is non-nil:
+;;
+;; left recursion:
+;;    1.  A = B | A C.             ==>   A = B {C}*.
+;;    2.  A = B | A B.             ==>   A = {B}+.
+;;    3.  A =   | A B.             ==>   A = {B}*.
+;;    4.  A = B | A C B.           ==>   A = {B || C}+.
+;;    5.  A = B | D | A C | A E.   ==>   A = ( B | D ) { C | E }*.
+;;
+;; optional:
+;;    6.  A = B | .                ==>   A = [B].
+;;    7.  A =   | B .              ==>   A = [B].
+;;
+;; factoration:
+;;    8.  A = B C | B D.           ==>   A = B (C | D).
+;;    9.  A = C B | D B.           ==>   A = (C | D) B.
+;;    10. A = B C E | B D E.       ==>   A = B (C | D) E.
+;;
+;; The above optimizations are specially useful when `ebnf-syntax' is `yacc'.
+;;
+;;
+;; Form Feed
+;; ---------
+;;
+;; You may use form feed (^L \014) to force a production to start on a new page,
+;; for example:
+;;
+;;    a) A = B | C.
+;;      ^L
+;;      X = Y | Z.
+;;
+;;    b) A = B ^L | C.
+;;      X = Y | Z.
+;;
+;;    c) A = B ^L^L^L | C.^L
+;;      ^L
+;;      X = Y | Z.
+;;
+;; In all examples above, only the production X will start on a new page.
+;;
+;;
+;; Actions in Comments
+;; -------------------
+;;
+;; ebnf2ps accepts the following actions in comments:
+;;
+;;    ;>       the next production starts in the same line as the current one.
+;;             It is useful when `ebnf-horizontal-orientation' is nil.
+;;
+;;    ;<       the next production starts in the next line.
+;;             It is useful when `ebnf-horizontal-orientation' is non-nil.
+;;
+;;    ;[EPS    open a new EPS file.  The EPS file name has the form:
+;;                     <PREFIX><NAME>.eps
+;;             where <PREFIX> is given by variable `ebnf-eps-prefix' and <NAME>
+;;             is the string given by ;[ action comment, this string is mapped
+;;             to form a valid file name (see documentation for
+;;             `ebnf-eps-buffer' or `ebnf-eps-region').
+;;             It has effect only during `ebnf-eps-buffer' or
+;;             `ebnf-eps-region' execution.
+;;             It's an error to try to open an already opened EPS file.
+;;
+;;    ;]EPS    close an opened EPS file.
+;;             It has effect only during `ebnf-eps-buffer' or
+;;             `ebnf-eps-region' execution.
+;;             It's an error to try to close a not opened EPS file.
+;;
+;; So if you have:
+;;
+;;    (setq ebnf-horizontal-orientation nil)
+;;
+;;    A = t.
+;;    C = x.
+;;    ;> C and B are drawn in the same line
+;;    B = y.
+;;    W = v.
+;;
+;; The graphical result is:
+;;
+;;    +---+
+;;    | A |
+;;    +---+
+;;
+;;    +---------+   +-----+
+;;    |         |   |     |
+;;    |    C    |   |     |
+;;    |         |   |  B  |
+;;    +---------+   |     |
+;;                  |     |
+;;                  +-----+
+;;
+;;    +-----------+
+;;    |     W     |
+;;    +-----------+
+;;
+;; Note that if ascending production sort is used, the productions A and B will
+;; be drawn in the same line instead of C and B.
+;;
+;; If consecutive actions occur, only the last one takes effect, so if you have:
+;;
+;;    A = X.
+;;    ;<
+;;    ^L
+;;    ;>
+;;    B = Y.
+;;
+;; Only the ;> will take effect, that is, A and B will be drawn in the same
+;; line.
+;;
+;; In ISO EBNF the above actions are specified as (*>*), (*<*), (*[EPS*) and
+;; (*]EPS*).  The first example above should be written:
+;;
+;;    A = t;
+;;    C = x;
+;;    (*> C and B are drawn in the same line *)
+;;    B = y;
+;;    W = v;
+;;
+;; For an example of EPS action when executing `ebnf-eps-buffer' or
+;; `ebnf-eps-region':
+;;
+;;    Z = B0.
+;;    ;[CC
+;;    ;[AA
+;;    A = B1.
+;;    ;[BB
+;;    C = B2.
+;;    ;]AA
+;;    B = B3.
+;;    ;]BB
+;;    ;]CC
+;;    D = B4.
+;;    E = B5.
+;;    ;[CC
+;;    F = B6.
+;;    ;]CC
+;;    G = B7.
+;;
+;; The following table summarizes the results:
+;;
+;; EPS FILE NAME    NO SORT    ASCENDING SORT    DESCENDING SORT
+;; ebnf--AA.eps     A C        A C               C A
+;; ebnf--BB.eps     C B        B C               C B
+;; ebnf--CC.eps     A C B F    A B C F           F C B A
+;; ebnf--D.eps      D          D                 D
+;; ebnf--E.eps      E          E                 E
+;; ebnf--G.eps      G          G                 G
+;; ebnf--Z.eps      Z          Z                 Z
+;;
+;; As you can see if EPS actions is not used, each single production is
+;; generated per EPS file.  To avoid overriding EPS files, use names in ;[ that
+;; it's not an existing production name.
+;;
+;; In the following case:
+;;
+;;    A = B0.
+;;    ;[AA
+;;    A = B1.
+;;    ;[BB
+;;    A = B2.
+;;
+;; The production A is generated in both files ebnf--AA.eps and ebnf--BB.eps.
+;;
+;;
+;; Utilities
+;; ---------
+;;
+;; Some tools are provided to help you.
+;;
+;; `ebnf-setup' returns the current setup.
+;;
+;; `ebnf-syntax-buffer' does a syntatic analysis of your EBNF in the current
+;; buffer.
+;;
+;; `ebnf-syntax-region' does a syntatic analysis of your EBNF in the current
+;; region.
+;;
+;; `ebnf-customize' activates a customization buffer for ebnf2ps options.
+;;
+;; `ebnf-syntax-buffer', `ebnf-syntax-region' and `ebnf-customize' can be bound
+;; to keys in the same way as `ebnf-' commands.
+;;
+;;
+;; Hooks
+;; -----
+;;
+;; ebn2ps has the following hook variables:
+;;
+;; `ebnf-hook'
+;;    It is evaluated once before any ebnf2ps process.
+;;
+;; `ebnf-production-hook'
+;;    It is evaluated on each beginning of production.
+;;
+;; `ebnf-page-hook'
+;;    It is evaluated on each beginning of page.
+;;
+;;
+;; Options
+;; -------
+;;
+;; Below it's shown a brief description of ebnf2ps options, please, see the
+;; options declaration in the code for a long documentation.
+;;
+;; `ebnf-horizontal-orientation'       Non-nil means productions are drawn
+;;                                     horizontally.
+;;
+;; `ebnf-horizontal-max-height'                Non-nil means to use maximum production
+;;                                     height in horizontal orientation.
+;;
+;; `ebnf-production-horizontal-space'  Specify horizontal space in points
+;;                                     between productions.
+;;
+;; `ebnf-production-vertical-space'    Specify vertical space in points between
+;;                                     productions.
+;;
+;; `ebnf-justify-sequence'             Specify justification of terms in a
+;;                                     sequence inside alternatives.
+;;
+;; `ebnf-terminal-regexp'              Specify how it's a terminal name.
+;;
+;; `ebnf-case-fold-search'             Non-nil means ignore case on matching.
+;;
+;; `ebnf-terminal-font'                        Specify terminal font.
+;;
+;; `ebnf-terminal-shape'               Specify terminal box shape.
+;;
+;; `ebnf-terminal-shadow'              Non-nil means terminal box will have a
+;;                                     shadow.
+;;
+;; `ebnf-terminal-border-width'                Specify border width for terminal box.
+;;
+;; `ebnf-terminal-border-color'                Specify border color for terminal box.
+;;
+;; `ebnf-sort-production'              Specify how productions are sorted.
+;;
+;; `ebnf-production-font'              Specify production font.
+;;
+;; `ebnf-non-terminal-font'            Specify non-terminal font.
+;;
+;; `ebnf-non-terminal-shape'           Specify non-terminal box shape.
+;;
+;; `ebnf-non-terminal-shadow'          Non-nil means non-terminal box will have
+;;                                     a shadow.
+;;
+;; `ebnf-non-terminal-border-width'    Specify border width for non-terminal
+;;                                     box.
+;;
+;; `ebnf-non-terminal-border-color'    Specify border color for non-terminal
+;;                                     box.
+;;
+;; `ebnf-special-font'                 Specify special font.
+;;
+;; `ebnf-special-shape'                        Specify special box shape.
+;;
+;; `ebnf-special-shadow'               Non-nil means special box will have a
+;;                                     shadow.
+;;
+;; `ebnf-special-border-width'         Specify border width for special box.
+;;
+;; `ebnf-special-border-color'         Specify border color for special box.
+;;
+;; `ebnf-except-font'                  Specify except font.
+;;
+;; `ebnf-except-shape'                 Specify except box shape.
+;;
+;; `ebnf-except-shadow'                        Non-nil means except box will have a
+;;                                     shadow.
+;;
+;; `ebnf-except-border-width'          Specify border width for except box.
+;;
+;; `ebnf-except-border-color'          Specify border color for except box.
+;;
+;; `ebnf-repeat-font'                  Specify repeat font.
+;;
+;; `ebnf-repeat-shape'                 Specify repeat box shape.
+;;
+;; `ebnf-repeat-shadow'                        Non-nil means repeat box will have a
+;;                                     shadow.
+;;
+;; `ebnf-repeat-border-width'          Specify border width for repeat box.
+;;
+;; `ebnf-repeat-border-color'          Specify border color for repeat box.
+;;
+;; `ebnf-entry-percentage'             Specify entry height on alternatives.
+;;
+;; `ebnf-arrow-shape'                  Specify the arrow shape.
+;;
+;; `ebnf-chart-shape'                  Specify chart flow shape.
+;;
+;; `ebnf-color-p'                      Non-nil means use color.
+;;
+;; `ebnf-line-width'                   Specify flow line width.
+;;
+;; `ebnf-line-color'                   Specify flow line color.
+;;
+;; `ebnf-user-arrow'                   Specify a user arrow shape (a PostScript
+;;                                     code).
+;;
+;; `ebnf-debug-ps'                     Non-nil means to generate PostScript
+;;                                     debug procedures.
+;;
+;; `ebnf-lex-comment-char'             Specify the line comment character.
+;;
+;; `ebnf-lex-eop-char'                 Specify the end of production character.
+;;
+;; `ebnf-syntax'                       Specify syntax to be recognized.
+;;
+;; `ebnf-iso-alternative-p'            Non-nil means use alternative ISO EBNF.
+;;
+;; `ebnf-iso-normalize-p'              Non-nil means normalize ISO EBNF syntax
+;;                                     names.
+;;
+;; `ebnf-default-width'                        Specify additional border width over
+;;                                     default terminal, non-terminal or
+;;                                     special.
+;;
+;; `ebnf-eps-prefix'                   Specify EPS prefix file name.
+;;
+;; `ebnf-use-float-format'             Non-nil means use `%f' float format.
+;;
+;; `ebnf-yac-ignore-error-recovery'    Non-nil means ignore error recovery.
+;;
+;; `ebnf-ignore-empty-rule'            Non-nil means ignore empty rules.
+;;
+;; `ebnf-optimize'                     Non-nil means optimize syntatic chart of
+;;                                     rules.
+;;
+;; To set the above options you may:
+;;
+;; a) insert the code in your ~/.emacs, like:
+;;
+;;      (setq ebnf-terminal-shape 'bevel)
+;;
+;;    This way always keep your default settings when you enter a new Emacs
+;;    session.
+;;
+;; b) or use `set-variable' in your Emacs session, like:
+;;
+;;      M-x set-variable RET ebnf-terminal-shape RET bevel RET
+;;
+;;    This way keep your settings only during the current Emacs session.
+;;
+;; c) or use customization, for example:
+;;      click on menu-bar *Help* option,
+;;      then click on *Customize*,
+;;      then click on *Browse Customization Groups*,
+;;      expand *PostScript* group,
+;;      expand *Ebnf2ps* group
+;;      and then customize ebnf2ps options.
+;;    Through this way, you may choose if the settings are kept or not when
+;;    you leave out the current Emacs session.
+;;
+;; d) or see the option value:
+;;
+;;      C-h v ebnf-terminal-shape RET
+;;
+;;    and click the *customize* hypertext button.
+;;    Through this way, you may choose if the settings are kept or not when
+;;    you leave out the current Emacs session.
+;;
+;; e) or invoke:
+;;
+;;      M-x ebnf-customize RET
+;;
+;;    and then customize ebnf2ps options.
+;;    Through this way, you may choose if the settings are kept or not when
+;;    you leave out the current Emacs session.
+;;
+;;
+;; Styles
+;; ------
+;;
+;; Sometimes you need to change the EBNF style you are using, for example,
+;; change the shapes and colors.  These changes may force you to set some
+;; variables and after use, set back the variables to the old values.
+;;
+;; To help to handle this situation, ebnf2ps has the following commands to
+;; handle styles:
+;;
+;; `ebnf-insert-style' Insert a new style NAME with inheritance INHERITS and
+;;                     values VALUES.
+;;
+;; `ebnf-merge-style'  Merge values of style NAME with style VALUES.
+;;
+;; `ebnf-apply-style'  Set STYLE to current style.
+;;
+;; `ebnf-reset-style'  Reset current style.
+;;
+;; `ebnf-push-style'   Push the current style and set STYLE to current style.
+;;
+;; `ebnf-pop-style'    Pop a style and set it to current style.
+;;
+;; These commands helps to put together a lot of variable settings in a group
+;; and name this group.  So when you wish to apply these settings it's only
+;; needed to give the name.
+;;
+;; There is also a notion of simple inheritance of style; so if you declare that
+;; a style A inherits from a style B, all settings of B is applied first and
+;; then the settings of A is applied.  This is useful when you wish to modify
+;; some aspects of an existing style, but at same time wish to keep it
+;; unmodified.
+;;
+;; See documentation for `ebnf-style-database'.
+;;
+;;
+;; Layout
+;; ------
+;;
+;; Below it is the layout of minimum area to draw each element, and it's used
+;; the following terms:
+;;
+;;    font height      is given by:
+;;                     (terminal font height + non-terminal font height) / 2
+;;
+;;    entry            is the vertical position used to know where it should be
+;;                     drawn the flow line in the current element.
+;;
+;;
+;;    * SPECIAL, TERMINAL and NON-TERMINAL
+;;
+;;           +==============+...................................
+;;           |              |      } font height / 2  } entry  }
+;;           |   XXXXXXXX...|.......                  }        }
+;;       ====+   XXXXXXXX   +====  } text height ......        } height
+;;       :   |   XXXXXXXX...|...:...                           }
+;;       :   |   :      :   |   :  } font height / 2           }
+;;       :   +==============+...:...............................
+;;       :   :   :      :   :   :
+;;       :   :   :      :   :   :......................
+;;       :   :   :      :   :      } font height      }
+;;       :   :   :      :   :.......                  }
+;;       :   :   :      :          } font height / 2  }
+;;       :   :   :      :...........                  }
+;;       :   :   :                 } text width       } width
+;;       :   :   :..................                  }
+;;       :   :                     } font height / 2  }
+;;       :   :......................                  }
+;;       :                         } font height      }
+;;       :.............................................
+;;
+;;
+;;    * OPTIONAL
+;;
+;;              +==========+.....................................
+;;              |          |         }        }                 }
+;;              |          |         } entry  }                 }
+;;              |          |         }        }                 }
+;;       ===+===+          +===+===...        } element height  } height
+;;       :   \  |          |  /   :           }                 }
+;;       :    + |          | +    :           }                 }
+;;       :    | +==========+.|.................                 }
+;;       :    | :          : |    :           } font height     }
+;;       :    +==============+...................................
+;;       :      :          :      :
+;;       :      :          :      :......................
+;;       :      :          :         } font height * 2  }
+;;       :      :          :..........                  }
+;;       :      :                    } element width    } width
+;;       :      :.....................                  }
+;;       :                           } font height * 2  }
+;;       :...............................................
+;;
+;;
+;;    * ALTERNATIVE
+;;
+;;               +===+...................................
+;;            +==+ A +==+       } A height     }        }
+;;            |  +===+..|........              } entry  }
+;;            +         +       } font height  }        }
+;;           /   +===+...\.......              }        }
+;;       ===+====+ B +====+===  } B height .....        } height
+;;       :   \   +===+.../.......                       }
+;;       :    +         +    :  } font height           }
+;;       :    |  +===+..|........                       }
+;;       :    +==+ C +==+    :  } C height              }
+;;       :     : +===+...................................
+;;       :     :       :     :
+;;       :     :       :     :......................
+;;       :     :       :        } font height * 2  }
+;;       :     :       :.........                  }
+;;       :     :                } max width        } width
+;;       :     :.................                  }
+;;       :                      } font height * 2  }
+;;       :..........................................
+;;
+;;       NOTES:
+;;          1. An empty alternative has zero of height.
+;;
+;;          2. The variable `ebnf-entry-percentage' is used to determine the
+;;             entry point.
+;;
+;;
+;;    * ZERO OR MORE
+;;
+;;               +===========+...............................
+;;             +=+ separator +=+        } separator height  }
+;;            /  +===========+..\........                   }
+;;           +                   +      }                   }
+;;           |                   |      } font height       }
+;;           +                   +      }                   }
+;;            \  +===========+../........                   } height = entry
+;;             +=+ element   +=+        } element height    }
+;;            /: +===========+..\........                   }
+;;           + :               : +      }                   }
+;;           + :               : +      } font height       }
+;;          /  :               :  \     }                   }
+;;       ==+=======================+==.......................
+;;       :     :               :     :
+;;       :     :               :     :.......................
+;;       :     :               :        } font height * 2   }
+;;       :     :               :.........                   }
+;;       :     :                        } max width         } width
+;;       :     :.........................                   }
+;;       :                              } font height * 2   }
+;;       :...................................................
+;;
+;;
+;;    * ONE OR MORE
+;;
+;;            +===========+......................................
+;;          +=+ separator +=+      } separator height  }        }
+;;         /  +===========+..\......                   }        }
+;;        +                   +    }                   } entry  }
+;;        |                   |    } font height       }        } height
+;;        +                   +    }                   }        }
+;;         \  +===========+../......                   }        }
+;;       ===+=+ element   +=+===   } element height ....        }
+;;       :  : +===========+......................................
+;;       :  :               :  :
+;;       :  :               :  :........................
+;;       :  :               :      } font height * 2   }
+;;       :  :               :.......                   }
+;;       :  :                      } max width         } width
+;;       :  :.......................                   }
+;;       :                         } font height * 2   }
+;;       :..............................................
+;;
+;;
+;;    * PRODUCTION
+;;
+;;       XXXXXX:......................................
+;;       XXXXXX:           } production font height  }
+;;       XXXXXX:............                         }
+;;                         } font height             }
+;;           +======+.......                         } height = entry
+;;           |      |      }                         }
+;;       ====+      +====  } element height          }
+;;       :   |      |   :  }                         }
+;;       :   +======+.................................
+;;       :   :      :   :
+;;       :   :      :   :......................
+;;       :   :      :      } font height * 2  }
+;;       :   :      :.......                  }
+;;       :   :             } element width    } width
+;;       :   :..............                  }
+;;       :                 } font height * 2  }
+;;       :.....................................
+;;
+;;
+;;    * REPEAT
+;;
+;;           +================+...................................
+;;           |                |      } font height / 2  } entry  }
+;;           |        +===+...|.......                  }        }
+;;       ====+  N *   | X |   +====  } X height .........        } height
+;;       :   |  :  :  +===+...|...:...                           }
+;;       :   |  :  :  :   :   |   :  } font height / 2           }
+;;       :   +================+...:...............................
+;;       :   :  :  :  :   :   :   :
+;;       :   :  :  :  :   :   :   :......................
+;;       :   :  :  :  :   :   :      } font height      }
+;;       :   :  :  :  :   :   :.......                  }
+;;       :   :  :  :  :   :          } font height / 2  }
+;;       :   :  :  :  :   :...........                  }
+;;       :   :  :  :  :              } X width          }
+;;       :   :  :  :  :...............                  }
+;;       :   :  :  :                 } font height / 2  } width
+;;       :   :  :  :..................                  }
+;;       :   :  :                    } text width       }
+;;       :   :  :.....................                  }
+;;       :   :                       } font height / 2  }
+;;       :   :........................                  }
+;;       :                           } font height      }
+;;       :...............................................
+;;
+;;
+;;    * EXCEPT
+;;
+;;           +==================+...................................
+;;           |                  |      } font height / 2  } entry  }
+;;           |  +===+   +===+...|.......                  }        }
+;;       ====+  | X | - | y |   +====  } max height .......        } height
+;;       :   |  +===+   +===+...|...:...                           }
+;;       :   |  :   :   :   :   |   :  } font height / 2           }
+;;       :   +==================+...:...............................
+;;       :   :  :   :   :   :   :   :
+;;       :   :  :   :   :   :   :   :......................
+;;       :   :  :   :   :   :   :      } font height      }
+;;       :   :  :   :   :   :   :.......                  }
+;;       :   :  :   :   :   :          } font height / 2  }
+;;       :   :  :   :   :   :...........                  }
+;;       :   :  :   :   :              } Y width          }
+;;       :   :  :   :   :...............                  }
+;;       :   :  :   :                  } font height      } width
+;;       :   :  :   :...................                  }
+;;       :   :  :                      } X width          }
+;;       :   :  :.......................                  }
+;;       :   :                         } font height / 2  }
+;;       :   :..........................                  }
+;;       :                             } font height      }
+;;       :.................................................
+;;
+;;       NOTE: If Y element is empty, it's draw nothing at Y place.
+;;
+;;
+;; Internal Structures
+;; -------------------
+;;
+;; ebnf2ps has two passes.  The first pass does a lexical and syntatic analysis
+;; of current buffer and generates an intermediate representation.  The second
+;; pass uses the intermediate representation to generate the PostScript syntatic
+;; chart.
+;;
+;; The intermediate representation is a list of vectors, the vector element
+;; represents a syntatic chart element.  Below is a vector representation for
+;; each syntatic chart element.
+;;
+;; [production   WIDTH-FUN DIM-FUN ENTRY HEIGHT WIDTH NAME    PRODUCTION ACTION]
+;; [alternative  WIDTH-FUN DIM-FUN ENTRY HEIGHT WIDTH LIST]
+;; [sequence     WIDTH-FUN DIM-FUN ENTRY HEIGHT WIDTH LIST]
+;; [terminal     WIDTH-FUN DIM-FUN ENTRY HEIGHT WIDTH NAME    DEFAULT]
+;; [non-terminal WIDTH-FUN DIM-FUN ENTRY HEIGHT WIDTH NAME    DEFAULT]
+;; [special      WIDTH-FUN DIM-FUN ENTRY HEIGHT WIDTH NAME    DEFAULT]
+;; [empty        WIDTH-FUN DIM-FUN ENTRY HEIGHT WIDTH]
+;; [optional     WIDTH-FUN DIM-FUN ENTRY HEIGHT WIDTH ELEMENT]
+;; [one-or-more  WIDTH-FUN DIM-FUN ENTRY HEIGHT WIDTH ELEMENT SEPARATOR]
+;; [zero-or-more WIDTH-FUN DIM-FUN ENTRY HEIGHT WIDTH ELEMENT SEPARATOR]
+;; [repeat       WIDTH-FUN DIM-FUN ENTRY HEIGHT WIDTH TIMES   ELEMENT]
+;; [except       WIDTH-FUN DIM-FUN ENTRY HEIGHT WIDTH ELEMENT ELEMENT]
+;;
+;; The first vector position is a function symbol used to generate PostScript
+;; for this element.
+;; WIDTH-FUN is a function symbol called to adjust the element width.
+;; DIM-FUN is a function symbol called to set the element dimensions.
+;; ENTRY is the element entry point.
+;; HEIGHT and WIDTH are the element height and width, respectively.
+;; NAME is a string that it's the element name.
+;; DEFAULT is a boolean that indicates if it's a `default' element.
+;; PRODUCTION and ELEMENT are vectors that represents sub-elements of current
+;; one.
+;; LIST is a list of vector that represents the list part for alternatives and
+;; sequences.
+;; SEPARATOR is a vector that represents the sub-element used to separate the
+;; list elements.
+;; TIMES is a string representing the number of times that ELEMENT is repeated
+;; on a repeat construction.
+;; ACTION indicates some action that should be done before production is
+;; generated.  The current actions are:
+;;
+;;    nil              no action.
+;;
+;;    form-feed                current production starts on a new page.
+;;
+;;    newline          current production starts on next line, this is useful
+;;                     when `ebnf-horizontal-orientation' is non-nil.
+;;
+;;    keep-line                current production continues on the current line, this
+;;                     is useful when `ebnf-horizontal-orientation' is nil.
+;;
+;;
+;; Things To Change
+;; ----------------
+;;
+;; . Handle situations when syntatic chart is out of paper.
+;; . Use other alphabet than ascii.
+;; . Optimizations...
+;;
+;;
+;; Acknowledgements
+;; ----------------
+;;
+;; Thanks to all who emailed comments.
+;;
+;;
+;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;; code:
+
+
+(require 'ps-print)
+
+(and (string< ps-print-version "3.05.1")
+     (error "`ebnf2ps' requires `ps-print' package version 3.05.1 or later"))
+
+
+;; temporary fix for ps-print
+(or (fboundp 'set-buffer-multibyte)
+    (defun set-buffer-multibyte (arg)
+      (setq enable-multibyte-characters arg)))
+
+(or (fboundp 'string-as-unibyte)
+    (defun string-as-unibyte (arg) arg))
+
+(or (fboundp 'string-as-multibyte)
+    (defun string-as-multibyte (arg) arg))
+
+(or (fboundp 'charset-after)
+    (defun charset-after (&optional arg)
+      (char-charset (char-after arg))))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; User Variables:
+
+
+;;; Interface to the command system
+
+(defgroup postscript nil
+  "PostScript Group"
+  :tag "PostScript"
+  :group 'emacs)
+
+
+(defgroup ebnf2ps nil
+  "Translate an EBNF to a syntatic chart on PostScript"
+  :prefix "ebnf-"
+  :group 'wp
+  :group 'postscript)
+
+
+(defgroup ebnf-special nil
+  "Special customization"
+  :prefix "ebnf-"
+  :tag "Special"
+  :group 'ebnf2ps)
+
+
+(defgroup ebnf-except nil
+  "Except customization"
+  :prefix "ebnf-"
+  :tag "Except"
+  :group 'ebnf2ps)
+
+
+(defgroup ebnf-repeat nil
+  "Repeat customization"
+  :prefix "ebnf-"
+  :tag "Repeat"
+  :group 'ebnf2ps)
+
+
+(defgroup ebnf-terminal nil
+  "Terminal customization"
+  :prefix "ebnf-"
+  :tag "Terminal"
+  :group 'ebnf2ps)
+
+
+(defgroup ebnf-non-terminal nil
+  "Non-Terminal customization"
+  :prefix "ebnf-"
+  :tag "Non-Terminal"
+  :group 'ebnf2ps)
+
+
+(defgroup ebnf-production nil
+  "Production customization"
+  :prefix "ebnf-"
+  :tag "Production"
+  :group 'ebnf2ps)
+
+
+(defgroup ebnf-shape nil
+  "Shapes customization"
+  :prefix "ebnf-"
+  :tag "Shape"
+  :group 'ebnf2ps)
+
+
+(defgroup ebnf-displacement nil
+  "Displacement customization"
+  :prefix "ebnf-"
+  :tag "Displacement"
+  :group 'ebnf2ps)
+
+
+(defgroup ebnf-syntatic nil
+  "Syntatic customization"
+  :prefix "ebnf-"
+  :tag "Syntatic"
+  :group 'ebnf2ps)
+
+
+(defgroup ebnf-optimization nil
+  "Optimization customization"
+  :prefix "ebnf-"
+  :tag "Optimization"
+  :group 'ebnf2ps)
+
+
+(defcustom ebnf-horizontal-orientation nil
+  "*Non-nil means productions are drawn horizontally."
+  :type 'boolean
+  :group 'ebnf-displacement)
+
+
+(defcustom ebnf-horizontal-max-height nil
+  "*Non-nil means to use maximum production height in horizontal orientation.
+
+It is only used when `ebnf-horizontal-orientation' is non-nil."
+  :type 'boolean
+  :group 'ebnf-displacement)
+
+
+(defcustom ebnf-production-horizontal-space 0.0 ; use ebnf2ps default value
+  "*Specify horizontal space in points between productions.
+
+Value less or equal to zero forces ebnf2ps to set a proper default value."
+  :type 'number
+  :group 'ebnf-displacement)
+
+
+(defcustom ebnf-production-vertical-space 0.0 ; use ebnf2ps default value
+  "*Specify vertical space in points between productions.
+
+Value less or equal to zero forces ebnf2ps to set a proper default value."
+  :type 'number
+  :group 'ebnf-displacement)
+
+
+(defcustom ebnf-justify-sequence 'center
+  "*Specify justification of terms in a sequence inside alternatives.
+
+Valid values are:
+
+   `left'              left justification
+   `right'             right justification
+   any other value     centralize"
+  :type '(radio :tag "Sequence Justification"
+               (const left) (const right) (other :tag "center" center))
+  :group 'ebnf-displacement)
+
+
+(defcustom ebnf-special-font '(7 Courier "Black" "Gray95" bold italic)
+  "*Specify special font.
+
+See documentation for `ebnf-production-font'."
+  :type '(list :tag "Special Font"
+              (number :tag "Font Size")
+              (symbol :tag "Font Name")
+              (choice :tag "Foreground Color"
+                      (string :tag "Name")
+                      (other :tag "Default" nil))
+              (choice :tag "Background Color"
+                      (string :tag "Name")
+                      (other :tag "Default" nil))
+              (repeat :tag "Font Attributes" :inline t
+                      (choice (const bold)      (const italic)
+                              (const underline) (const strikeout)
+                              (const overline)  (const shadow)
+                              (const box)       (const outline))))
+  :group 'ebnf-special)
+
+
+(defcustom ebnf-special-shape 'bevel
+  "*Specify special box shape.
+
+See documentation for `ebnf-non-terminal-shape'."
+  :type '(radio :tag "Special Shape"
+               (const miter) (const round) (const bevel))
+  :group 'ebnf-special)
+
+
+(defcustom ebnf-special-shadow nil
+  "*Non-nil means special box will have a shadow."
+  :type 'boolean
+  :group 'ebnf-special)
+
+
+(defcustom ebnf-special-border-width 0.5
+  "*Specify border width for special box."
+  :type 'number
+  :group 'ebnf-special)
+
+
+(defcustom ebnf-special-border-color "Black"
+  "*Specify border color for special box."
+  :type 'string
+  :group 'ebnf-special)
+
+
+(defcustom ebnf-except-font '(7 Courier "Black" "Gray90" bold italic)
+  "*Specify except font.
+
+See documentation for `ebnf-production-font'."
+  :type '(list :tag "Except Font"
+              (number :tag "Font Size")
+              (symbol :tag "Font Name")
+              (choice :tag "Foreground Color"
+                      (string :tag "Name")
+                      (other :tag "Default" nil))
+              (choice :tag "Background Color"
+                      (string :tag "Name")
+                      (other :tag "Default" nil))
+              (repeat :tag "Font Attributes" :inline t
+                      (choice (const bold)      (const italic)
+                              (const underline) (const strikeout)
+                              (const overline)  (const shadow)
+                              (const box)       (const outline))))
+  :group 'ebnf-except)
+
+
+(defcustom ebnf-except-shape 'bevel
+  "*Specify except box shape.
+
+See documentation for `ebnf-non-terminal-shape'."
+  :type '(radio :tag "Except Shape"
+               (const miter) (const round) (const bevel))
+  :group 'ebnf-except)
+
+
+(defcustom ebnf-except-shadow nil
+  "*Non-nil means except box will have a shadow."
+  :type 'boolean
+  :group 'ebnf-except)
+
+
+(defcustom ebnf-except-border-width 0.25
+  "*Specify border width for except box."
+  :type 'number
+  :group 'ebnf-except)
+
+
+(defcustom ebnf-except-border-color "Black"
+  "*Specify border color for except box."
+  :type 'string
+  :group 'ebnf-except)
+
+
+(defcustom ebnf-repeat-font '(7 Courier "Black" "Gray85" bold italic)
+  "*Specify repeat font.
+
+See documentation for `ebnf-production-font'."
+  :type '(list :tag "Repeat Font"
+              (number :tag "Font Size")
+              (symbol :tag "Font Name")
+              (choice :tag "Foreground Color"
+                      (string :tag "Name")
+                      (other :tag "Default" nil))
+              (choice :tag "Background Color"
+                      (string :tag "Name")
+                      (other :tag "Default" nil))
+              (repeat :tag "Font Attributes" :inline t
+                      (choice (const bold)      (const italic)
+                              (const underline) (const strikeout)
+                              (const overline)  (const shadow)
+                              (const box)       (const outline))))
+  :group 'ebnf-repeat)
+
+
+(defcustom ebnf-repeat-shape 'bevel
+  "*Specify repeat box shape.
+
+See documentation for `ebnf-non-terminal-shape'."
+  :type '(radio :tag "Repeat Shape"
+               (const miter) (const round) (const bevel))
+  :group 'ebnf-repeat)
+
+
+(defcustom ebnf-repeat-shadow nil
+  "*Non-nil means repeat box will have a shadow."
+  :type 'boolean
+  :group 'ebnf-repeat)
+
+
+(defcustom ebnf-repeat-border-width 0.0
+  "*Specify border width for repeat box."
+  :type 'number
+  :group 'ebnf-repeat)
+
+
+(defcustom ebnf-repeat-border-color "Black"
+  "*Specify border color for repeat box."
+  :type 'string
+  :group 'ebnf-repeat)
+
+
+(defcustom ebnf-terminal-font '(7 Courier "Black" "White")
+  "*Specify terminal font.
+
+See documentation for `ebnf-production-font'."
+  :type '(list :tag "Terminal Font"
+              (number :tag "Font Size")
+              (symbol :tag "Font Name")
+              (choice :tag "Foreground Color"
+                      (string :tag "Name")
+                      (other :tag "Default" nil))
+              (choice :tag "Background Color"
+                      (string :tag "Name")
+                      (other :tag "Default" nil))
+              (repeat :tag "Font Attributes" :inline t
+                      (choice (const bold)      (const italic)
+                              (const underline) (const strikeout)
+                              (const overline)  (const shadow)
+                              (const box)       (const outline))))
+  :group 'ebnf-terminal)
+
+
+(defcustom ebnf-terminal-shape 'miter
+  "*Specify terminal box shape.
+
+See documentation for `ebnf-non-terminal-shape'."
+  :type '(radio :tag "Terminal Shape"
+               (const miter) (const round) (const bevel))
+  :group 'ebnf-terminal)
+
+
+(defcustom ebnf-terminal-shadow nil
+  "*Non-nil means terminal box will have a shadow."
+  :type 'boolean
+  :group 'ebnf-terminal)
+
+
+(defcustom ebnf-terminal-border-width 1.0
+  "*Specify border width for terminal box."
+  :type 'number
+  :group 'ebnf-terminal)
+
+
+(defcustom ebnf-terminal-border-color "Black"
+  "*Specify border color for terminal box."
+  :type 'string
+  :group 'ebnf-terminal)
+
+
+(defcustom ebnf-sort-production nil
+  "*Specify how productions are sorted.
+
+Valid values are:
+
+   nil                 don't sort productions.
+   `ascending'         ascending sort.
+   any other value     descending sort."
+  :type '(radio :tag "Production Sort"
+               (const :tag "Ascending" ascending)
+               (const :tag "Descending" descending)
+               (other :tag "No Sort" nil))
+  :group 'ebnf-production)
+
+
+(defcustom ebnf-production-font '(10 Helvetica "Black" "White" bold)
+  "*Specify production header font.
+
+It is a list with the following form:
+
+   (SIZE NAME FOREGROUND BACKGROUND ATTRIBUTE...)
+
+Where:
+SIZE is the font size.
+NAME is the font name symbol.
+ATTRIBUTE is one of the following symbols:
+   bold      - use bold font.
+   italic    - use italic font.
+   underline - put a line under text.
+   strikeout - like underline, but the line is in middle of text.
+   overline  - like underline, but the line is over the text.
+   shadow    - text will have a shadow.
+   box       - text will be surrounded by a box.
+   outline   - print characters as hollow outlines.
+FOREGROUND is a foreground string color name; if it's nil, the default color is
+\"Black\".
+BACKGROUND is a background string color name; if it's nil, the default color is
+\"White\".
+
+See `ps-font-info-database' for valid font name."
+  :type '(list :tag "Production Font"
+              (number :tag "Font Size")
+              (symbol :tag "Font Name")
+              (choice :tag "Foreground Color"
+                      (string :tag "Name")
+                      (other :tag "Default" nil))
+              (choice :tag "Background Color"
+                      (string :tag "Name")
+                      (other :tag "Default" nil))
+              (repeat :tag "Font Attributes" :inline t
+                      (choice (const bold)      (const italic)
+                              (const underline) (const strikeout)
+                              (const overline)  (const shadow)
+                              (const box)       (const outline))))
+  :group 'ebnf-production)
+
+
+(defcustom ebnf-non-terminal-font '(7 Helvetica "Black" "White")
+  "*Specify non-terminal font.
+
+See documentation for `ebnf-production-font'."
+  :type '(list :tag "Non-Terminal Font"
+              (number :tag "Font Size")
+              (symbol :tag "Font Name")
+              (choice :tag "Foreground Color"
+                      (string :tag "Name")
+                      (other :tag "Default" nil))
+              (choice :tag "Background Color"
+                      (string :tag "Name")
+                      (other :tag "Default" nil))
+              (repeat :tag "Font Attributes" :inline t
+                      (choice (const bold)      (const italic)
+                              (const underline) (const strikeout)
+                              (const overline)  (const shadow)
+                              (const box)       (const outline))))
+  :group 'ebnf-non-terminal)
+
+
+(defcustom ebnf-non-terminal-shape 'round
+  "*Specify non-terminal box shape.
+
+Valid values are:
+
+   `miter'     +-------+
+               |       |
+               +-------+
+
+   `round'      -------
+               (       )
+                -------
+
+   `bevel'     /-------\\
+               |       |
+               \\-------/
+
+Any other value is treated as `miter'."
+  :type '(radio :tag "Non-Terminal Shape"
+               (const miter) (const round) (const bevel))
+  :group 'ebnf-non-terminal)
+
+
+(defcustom ebnf-non-terminal-shadow nil
+  "*Non-nil means non-terminal box will have a shadow."
+  :type 'boolean
+  :group 'ebnf-non-terminal)
+
+
+(defcustom ebnf-non-terminal-border-width 1.0
+  "*Specify border width for non-terminal box."
+  :type 'number
+  :group 'ebnf-non-terminal)
+
+
+(defcustom ebnf-non-terminal-border-color "Black"
+  "*Specify border color for non-terminal box."
+  :type 'string
+  :group 'ebnf-non-terminal)
+
+
+(defcustom ebnf-arrow-shape 'hollow
+  "*Specify the arrow shape.
+
+Valid values are:
+
+   `none'      ======
+
+   `semi-up'      *               `transparent'  *
+                   *                             |*
+               =====*                            | *
+                                               ==+==*
+                                                 | *
+                                                 |*
+                                                 *
+
+   `semi-down' =====*             `hollow'       *
+                   *                             |*
+                  *                              | *
+                                               ==+  *
+                                                 | *
+                                                 |*
+                                                 *
+
+   `simple'       *               `full'         *
+                   *                             |*
+               =====*                            |X*
+                   *                           ==+XX*
+                  *                              |X*
+                                                 |*
+                                                 *
+
+   `user'      See also documentation for variable `ebnf-user-arrow'.
+
+Any other value is treated as `none'."
+  :type '(radio :tag "Arrow Shape"
+               (const none)        (const semi-up)
+               (const semi-down)   (const simple)
+               (const transparent) (const hollow)
+               (const full)        (const user))
+  :group 'ebnf-shape)
+
+
+(defcustom ebnf-chart-shape 'round
+  "*Specify chart flow shape.
+
+See documentation for `ebnf-non-terminal-shape'."
+  :type '(radio :tag "Chart Flow Shape"
+               (const miter) (const round) (const bevel))
+  :group 'ebnf-shape)
+
+
+(defcustom ebnf-user-arrow nil
+  "*Specify a user arrow shape (a PostScript code).
+
+PostScript code should draw a right arrow.
+
+The anatomy of a right arrow is:
+
+   ...... Initial position
+   :
+   :     *.................
+   :     | *       }      }
+   :     |   *     } hT4  }
+   v     |     *   }      }
+   ======+======*...      } hT2
+   :     |     *:  }      }
+   :     |   *  :  } hT4  }
+   :     | *    :  }      }
+   :     *.................
+   :     :      :
+   :     :      :..........
+   :     :         } hT2  }
+   :     :..........      } hT
+   :               } hT2  }
+   :.......................
+
+Where `hT', `hT2' and `hT4' are predefined PostScript variable names that can be
+used to generate your own arrow.  As these variables are used along PostScript
+execution, *DON'T* modify the values of them.  Instead, copy the values, if you
+need to modify them.
+
+The relation between these variables is: hT = 2 * hT2 = 4 * hT4.
+
+The variable `ebnf-user-arrow' is only used when `ebnf-arrow-shape' is set to
+symbol `user'.
+
+See function `ebnf-user-arrow' for valid values and how values are processed."
+  :type '(radio :tag "User Arrow Shape"
+               (const nil)
+               string
+               symbol
+               (repeat :tag "List"
+                       (radio string
+                              symbol
+                              sexp)))
+  :group 'ebnf-shape)
+
+
+(defcustom ebnf-syntax 'ebnf
+  "*Specify syntax to be recognized.
+
+Valid values are:
+
+   `ebnf'      ebnf2ps recognizes the syntax described above.
+               The following variables *ONLY* have effect with this
+               setting:
+               `ebnf-terminal-regexp', `ebnf-case-fold-search',
+               `ebnf-lex-comment-char' and `ebnf-lex-eop-char'.
+
+   `iso-ebnf'  ebnf2ps recognizes the syntax described in the URL:
+               `http://www.cl.cam.ac.uk/~mgk25/iso-ebnf.html'
+               (\"International Standard of the ISO EBNF Notation\").
+               The following variables *ONLY* have effect with this
+               setting:
+               `ebnf-iso-alternative-p' and `ebnf-iso-normalize-p'.
+
+   `yacc'      ebnf2ps recognizes the Yacc/Bison syntax.
+               The following variable *ONLY* has effect with this
+               setting:
+               `ebnf-yac-ignore-error-recovery'.
+
+Any other value is treated as `ebnf'."
+  :type '(radio :tag "Syntax"
+               (const ebnf) (const iso-ebnf) (const yacc))
+  :group 'ebnf-syntatic)
+
+
+(defcustom ebnf-lex-comment-char ?\;
+  "*Specify the line comment character.
+
+It's used only when `ebnf-syntax' is `ebnf'."
+  :type 'character
+  :group 'ebnf-syntatic)
+
+
+(defcustom ebnf-lex-eop-char ?.
+  "*Specify the end of production character.
+
+It's used only when `ebnf-syntax' is `ebnf'."
+  :type 'character
+  :group 'ebnf-syntatic)
+
+
+(defcustom ebnf-terminal-regexp nil
+  "*Specify how it's a terminal name.
+
+If it's nil, the terminal name must be enclosed by `\"'.
+If it's a string, it should be a regexp that it'll be used to determine a
+terminal name; terminal name may also be enclosed by `\"'.
+
+It's used only when `ebnf-syntax' is `ebnf'."
+  :type '(radio :tag "Terminal Name"
+               (const nil) regexp)
+  :group 'ebnf-syntatic)
+
+
+(defcustom ebnf-case-fold-search nil
+  "*Non-nil means ignore case on matching.
+
+It's only used when `ebnf-terminal-regexp' is non-nil and when `ebnf-syntax' is
+`ebnf'."
+  :type 'boolean
+  :group 'ebnf-syntatic)
+
+
+(defcustom ebnf-iso-alternative-p nil
+  "*Non-nil means use alternative ISO EBNF.
+
+It's only used when `ebnf-syntax' is `iso-ebnf'.
+
+This variable affects the following symbol set:
+
+   STANDARD   ALTERNATIVE
+      |    ==>   / or !
+      [    ==>   (/
+      ]    ==>   /)
+      {    ==>   (:
+      }    ==>   :)
+      ;    ==>   ."
+  :type 'boolean
+  :group 'ebnf-syntatic)
+
+
+(defcustom ebnf-iso-normalize-p nil
+  "*Non-nil means normalize ISO EBNF syntax names.
+
+Normalize a name means that several contiguous spaces inside name become a
+single space, so \"A  B   C\" is normalized to  \"A B C\".
+
+It's only used when `ebnf-syntax' is `iso-ebnf'."
+  :type 'boolean
+  :group 'ebnf-syntatic)
+
+
+(defcustom ebnf-eps-prefix "ebnf--"
+  "*Specify EPS prefix file name.
+
+See `ebnf-eps-buffer' and `ebnf-eps-region' commands."
+  :type 'string
+  :group 'ebnf2ps)
+
+
+(defcustom ebnf-entry-percentage 0.5   ; middle
+  "*Specify entry height on alternatives.
+
+It must be a float between 0.0 (top) and 1.0 (bottom)."
+  :type 'number
+  :group 'ebnf2ps)
+
+
+(defcustom ebnf-default-width 0.6
+  "*Specify additional border width over default terminal, non-terminal or
+special."
+  :type 'number
+  :group 'ebnf2ps)
+
+
+;; Printing color requires x-color-values.
+(defcustom ebnf-color-p (or (fboundp 'x-color-values) ; Emacs
+                           (fboundp 'color-instance-rgb-components)) ; XEmacs
+  "*Non-nil means use color."
+  :type 'boolean
+  :group 'ebnf2ps)
+
+
+(defcustom ebnf-line-width 1.0
+  "*Specify flow line width."
+  :type 'number
+  :group 'ebnf2ps)
+
+
+(defcustom ebnf-line-color "Black"
+  "*Specify flow line color."
+  :type 'string
+  :group 'ebnf2ps)
+
+
+(defcustom ebnf-debug-ps nil
+  "*Non-nil means to generate PostScript debug procedures.
+
+It is intended to help PostScript programmers in debugging."
+  :type 'boolean
+  :group 'ebnf2ps)
+
+
+(defcustom ebnf-use-float-format t
+  "*Non-nil means use `%f' float format.
+
+The advantage of using float format is that ebnf2ps generates a little short
+PostScript file.
+
+If it occurs the error message:
+
+   Invalid format operation %f
+
+when executing ebnf2ps, set `ebnf-use-float-format' to nil."
+  :type 'boolean
+  :group 'ebnf2ps)
+
+
+(defcustom ebnf-yac-ignore-error-recovery nil
+  "*Non-nil means ignore error recovery.
+
+It's only used when `ebnf-syntax' is `yacc'."
+  :type 'boolean
+  :group 'ebnf-syntatic)
+
+
+(defcustom ebnf-ignore-empty-rule nil
+  "*Non-nil means ignore empty rules.
+
+It's interesting to set this variable if your Yacc/Bison grammar has a lot of
+middle action rule."
+  :type 'boolean
+  :group 'ebnf-optimization)
+
+
+(defcustom ebnf-optimize nil
+  "*Non-nil means optimize syntatic chart of rules.
+
+The following optimizations are done:
+
+   left recursion:
+   1.  A = B | A C.             ==>   A = B {C}*.
+   2.  A = B | A B.             ==>   A = {B}+.
+   3.  A =   | A B.             ==>   A = {B}*.
+   4.  A = B | A C B.           ==>   A = {B || C}+.
+   5.  A = B | D | A C | A E.   ==>   A = ( B | D ) { C | E }*.
+
+   optional:
+   6.  A = B | .                ==>   A = [B].
+   7.  A =   | B .              ==>   A = [B].
+
+   factoration:
+   8.  A = B C | B D.           ==>   A = B (C | D).
+   9.  A = C B | D B.           ==>   A = (C | D) B.
+   10. A = B C E | B D E.       ==>   A = B (C | D) E.
+
+The above optimizations are specially useful when `ebnf-syntax' is `yacc'."
+  :type 'boolean
+  :group 'ebnf-optimization)
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Customization
+
+
+;;;###autoload
+(defun ebnf-customize ()
+  "Customization for ebnf group."
+  (interactive)
+  (customize-group 'ebnf2ps))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; User commands
+
+
+;;;###autoload
+(defun ebnf-print-buffer (&optional filename)
+  "Generate and print a PostScript syntatic chart image of the buffer.
+
+When called with a numeric prefix argument (C-u), prompts the user for
+the name of a file to save the PostScript image in, instead of sending
+it to the printer.
+
+More specifically, the FILENAME argument is treated as follows: if it
+is nil, send the image to the printer.  If FILENAME is a string, save
+the PostScript image in a file with that name.  If FILENAME is a
+number, prompt the user for the name of the file to save in."
+  (interactive (list (ps-print-preprint current-prefix-arg)))
+  (ebnf-print-region (point-min) (point-max) filename))
+
+
+;;;###autoload
+(defun ebnf-print-region (from to &optional filename)
+  "Generate and print a PostScript syntatic chart image of the region.
+Like `ebnf-print-buffer', but prints just the current region."
+  (interactive (list (point) (mark) (ps-print-preprint current-prefix-arg)))
+  (run-hooks 'ebnf-hook)
+  (or (ebnf-spool-region from to)
+      (ps-do-despool filename)))
+
+
+;;;###autoload
+(defun ebnf-spool-buffer ()
+  "Generate and spool a PostScript syntatic chart image of the buffer.
+Like `ebnf-print-buffer' except that the PostScript image is saved in a
+local buffer to be sent to the printer later.
+
+Use the command `ebnf-despool' to send the spooled images to the printer."
+  (interactive)
+  (ebnf-spool-region (point-min) (point-max)))
+
+
+;;;###autoload
+(defun ebnf-spool-region (from to)
+  "Generate a PostScript syntatic chart image of the region and spool locally.
+Like `ebnf-spool-buffer', but spools just the current region.
+
+Use the command `ebnf-despool' to send the spooled images to the printer."
+  (interactive "r")
+  (ebnf-generate-region from to 'ebnf-generate))
+
+
+;;;###autoload
+(defun ebnf-eps-buffer ()
+  "Generate a PostScript syntatic chart image of the buffer in a EPS file.
+
+Indeed, for each production is generated a EPS file.
+The EPS file name has the following form:
+
+   <PREFIX><PRODUCTION>.eps
+
+<PREFIX>     is given by variable `ebnf-eps-prefix'.
+            The default value is \"ebnf--\".
+
+<PRODUCTION> is the production name.
+            The production name is mapped to form a valid file name.
+            For example, the production name \"A/B + C\" is mapped to
+            \"A_B_+_C\" and the EPS file name used is \"ebnf--A_B_+_C.eps\".
+
+WARNING: It's *NOT* asked any confirmation to override an existing file."
+  (interactive)
+  (ebnf-eps-region (point-min) (point-max)))
+
+
+;;;###autoload
+(defun ebnf-eps-region (from to)
+  "Generate a PostScript syntatic chart image of the region in a EPS file.
+
+Indeed, for each production is generated a EPS file.
+The EPS file name has the following form:
+
+   <PREFIX><PRODUCTION>.eps
+
+<PREFIX>     is given by variable `ebnf-eps-prefix'.
+            The default value is \"ebnf--\".
+
+<PRODUCTION> is the production name.
+            The production name is mapped to form a valid file name.
+            For example, the production name \"A/B + C\" is mapped to
+            \"A_B_+_C\" and the EPS file name used is \"ebnf--A_B_+_C.eps\".
+
+WARNING: It's *NOT* asked any confirmation to override an existing file."
+  (interactive "r")
+  (let ((ebnf-eps-executing t))
+    (ebnf-generate-region from to 'ebnf-generate-eps)))
+
+
+;;;###autoload
+(defalias 'ebnf-despool 'ps-despool)
+
+
+;;;###autoload
+(defun ebnf-syntax-buffer ()
+  "Does a syntatic analysis of the current buffer."
+  (interactive)
+  (ebnf-syntax-region (point-min) (point-max)))
+
+
+;;;###autoload
+(defun ebnf-syntax-region (from to)
+  "Does a syntatic analysis of a region."
+  (interactive "r")
+  (ebnf-generate-region from to nil))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Utilities
+
+
+;;;###autoload
+(defun ebnf-setup ()
+  "Return the current ebnf2ps setup."
+  (format
+   "
+\(setq ebnf-special-font                %s
+      ebnf-special-shape               %s
+      ebnf-special-shadow              %S
+      ebnf-special-border-width        %S
+      ebnf-special-border-color        %S
+      ebnf-except-font                 %s
+      ebnf-except-shape                %s
+      ebnf-except-shadow               %S
+      ebnf-except-border-width         %S
+      ebnf-except-border-color         %S
+      ebnf-repeat-font                 %s
+      ebnf-repeat-shape                %s
+      ebnf-repeat-shadow               %S
+      ebnf-repeat-border-width         %S
+      ebnf-repeat-border-color         %S
+      ebnf-terminal-regexp             %S
+      ebnf-case-fold-search            %S
+      ebnf-terminal-font               %s
+      ebnf-terminal-shape              %s
+      ebnf-terminal-shadow             %S
+      ebnf-terminal-border-width       %S
+      ebnf-terminal-border-color       %S
+      ebnf-non-terminal-font           %s
+      ebnf-non-terminal-shape          %s
+      ebnf-non-terminal-shadow         %S
+      ebnf-non-terminal-border-width   %S
+      ebnf-non-terminal-border-color   %S
+      ebnf-sort-production             %s
+      ebnf-production-font             %s
+      ebnf-arrow-shape                 %s
+      ebnf-chart-shape                 %s
+      ebnf-user-arrow                  %s
+      ebnf-horizontal-orientation      %S
+      ebnf-horizontal-max-height       %S
+      ebnf-production-horizontal-space %S
+      ebnf-production-vertical-space   %S
+      ebnf-justify-sequence            %s
+      ebnf-lex-comment-char            ?\\%03o
+      ebnf-lex-eop-char                ?\\%03o
+      ebnf-syntax                      %s
+      ebnf-iso-alternative-p           %S
+      ebnf-iso-normalize-p             %S
+      ebnf-eps-prefix                  %S
+      ebnf-entry-percentage            %S
+      ebnf-color-p                     %S
+      ebnf-line-width                  %S
+      ebnf-line-color                  %S
+      ebnf-debug-ps                    %S
+      ebnf-use-float-format            %S
+      ebnf-yac-ignore-error-recovery   %S
+      ebnf-ignore-empty-rule           %S
+      ebnf-optimize                    %S)
+"
+   (ps-print-quote ebnf-special-font)
+   (ps-print-quote ebnf-special-shape)
+   ebnf-special-shadow
+   ebnf-special-border-width
+   ebnf-special-border-color
+   (ps-print-quote ebnf-except-font)
+   (ps-print-quote ebnf-except-shape)
+   ebnf-except-shadow
+   ebnf-except-border-width
+   ebnf-except-border-color
+   (ps-print-quote ebnf-repeat-font)
+   (ps-print-quote ebnf-repeat-shape)
+   ebnf-repeat-shadow
+   ebnf-repeat-border-width
+   ebnf-repeat-border-color
+   ebnf-terminal-regexp
+   ebnf-case-fold-search
+   (ps-print-quote ebnf-terminal-font)
+   (ps-print-quote ebnf-terminal-shape)
+   ebnf-terminal-shadow
+   ebnf-terminal-border-width
+   ebnf-terminal-border-color
+   (ps-print-quote ebnf-non-terminal-font)
+   (ps-print-quote ebnf-non-terminal-shape)
+   ebnf-non-terminal-shadow
+   ebnf-non-terminal-border-width
+   ebnf-non-terminal-border-color
+   (ps-print-quote ebnf-sort-production)
+   (ps-print-quote ebnf-production-font)
+   (ps-print-quote ebnf-arrow-shape)
+   (ps-print-quote ebnf-chart-shape)
+   (ps-print-quote ebnf-user-arrow)
+   ebnf-horizontal-orientation
+   ebnf-horizontal-max-height
+   ebnf-production-horizontal-space
+   ebnf-production-vertical-space
+   (ps-print-quote ebnf-justify-sequence)
+   ebnf-lex-comment-char
+   ebnf-lex-eop-char
+   (ps-print-quote ebnf-syntax)
+   ebnf-iso-alternative-p
+   ebnf-iso-normalize-p
+   ebnf-eps-prefix
+   ebnf-entry-percentage
+   ebnf-color-p
+   ebnf-line-width
+   ebnf-line-color
+   ebnf-debug-ps
+   ebnf-use-float-format
+   ebnf-yac-ignore-error-recovery
+   ebnf-ignore-empty-rule
+   ebnf-optimize))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Style variables
+
+
+(defvar ebnf-stack-style nil
+  "Used in functions `ebnf-reset-style', `ebnf-push-style' and
+`ebnf-pop-style'.")
+
+
+(defvar ebnf-current-style 'default
+  "Used in functions `ebnf-apply-style' and `ebnf-push-style'.")
+
+
+(defconst ebnf-style-custom-list
+  '(ebnf-special-font
+    ebnf-special-shape
+    ebnf-special-shadow
+    ebnf-special-border-width
+    ebnf-special-border-color
+    ebnf-except-font
+    ebnf-except-shape
+    ebnf-except-shadow
+    ebnf-except-border-width
+    ebnf-except-border-color
+    ebnf-repeat-font
+    ebnf-repeat-shape
+    ebnf-repeat-shadow
+    ebnf-repeat-border-width
+    ebnf-repeat-border-color
+    ebnf-terminal-regexp
+    ebnf-case-fold-search
+    ebnf-terminal-font
+    ebnf-terminal-shape
+    ebnf-terminal-shadow
+    ebnf-terminal-border-width
+    ebnf-terminal-border-color
+    ebnf-non-terminal-font
+    ebnf-non-terminal-shape
+    ebnf-non-terminal-shadow
+    ebnf-non-terminal-border-width
+    ebnf-non-terminal-border-color
+    ebnf-sort-production
+    ebnf-production-font
+    ebnf-arrow-shape
+    ebnf-chart-shape
+    ebnf-user-arrow
+    ebnf-horizontal-orientation
+    ebnf-horizontal-max-height
+    ebnf-production-horizontal-space
+    ebnf-production-vertical-space
+    ebnf-justify-sequence
+    ebnf-lex-comment-char
+    ebnf-lex-eop-char
+    ebnf-syntax
+    ebnf-iso-alternative-p
+    ebnf-iso-normalize-p
+    ebnf-eps-prefix
+    ebnf-entry-percentage
+    ebnf-color-p
+    ebnf-line-width
+    ebnf-line-color
+    ebnf-debug-ps
+    ebnf-use-float-format
+    ebnf-yac-ignore-error-recovery
+    ebnf-ignore-empty-rule
+    ebnf-optimize)
+  "List of valid symbol custom variable.")
+
+
+(defvar ebnf-style-database
+  '(;; EBNF default
+    (default
+      nil
+      (ebnf-special-font          . '(7 Courier "Black" "Gray95" bold italic))
+      (ebnf-special-shape               . 'bevel)
+      (ebnf-special-shadow              . nil)
+      (ebnf-special-border-width        . 0.5)
+      (ebnf-special-border-color        . "Black")
+      (ebnf-except-font           . '(7 Courier "Black" "Gray90" bold italic))
+      (ebnf-except-shape                . 'bevel)
+      (ebnf-except-shadow               . nil)
+      (ebnf-except-border-width         . 0.25)
+      (ebnf-except-border-color         . "Black")
+      (ebnf-repeat-font           . '(7 Courier "Black" "Gray85" bold italic))
+      (ebnf-repeat-shape                . 'bevel)
+      (ebnf-repeat-shadow               . nil)
+      (ebnf-repeat-border-width         . 0.0)
+      (ebnf-repeat-border-color         . "Black")
+      (ebnf-terminal-regexp             . nil)
+      (ebnf-case-fold-search            . nil)
+      (ebnf-terminal-font               . '(7  Courier   "Black" "White"))
+      (ebnf-terminal-shape              . 'miter)
+      (ebnf-terminal-shadow             . nil)
+      (ebnf-terminal-border-width       . 1.0)
+      (ebnf-terminal-border-color       . "Black")
+      (ebnf-non-terminal-font           . '(7  Helvetica "Black" "White"))
+      (ebnf-non-terminal-shape          . 'round)
+      (ebnf-non-terminal-shadow         . nil)
+      (ebnf-non-terminal-border-width   . 1.0)
+      (ebnf-non-terminal-border-color   . "Black")
+      (ebnf-sort-production             . nil)
+      (ebnf-production-font             . '(10 Helvetica "Black" "White" bold))
+      (ebnf-arrow-shape                 . 'hollow)
+      (ebnf-chart-shape                 . 'round)
+      (ebnf-user-arrow                  . nil)
+      (ebnf-horizontal-orientation      . nil)
+      (ebnf-horizontal-max-height       . nil)
+      (ebnf-production-horizontal-space . 0.0)
+      (ebnf-production-vertical-space   . 0.0)
+      (ebnf-justify-sequence            . 'center)
+      (ebnf-lex-comment-char            . ?\;)
+      (ebnf-lex-eop-char                . ?.)
+      (ebnf-syntax                      . 'ebnf)
+      (ebnf-iso-alternative-p           . nil)
+      (ebnf-iso-normalize-p             . nil)
+      (ebnf-eps-prefix                  . "ebnf--")
+      (ebnf-entry-percentage            . 0.5)
+      (ebnf-color-p   . (or (fboundp 'x-color-values) ; Emacs
+                           (fboundp 'color-instance-rgb-components))) ; XEmacs
+      (ebnf-line-width                  . 1.0)
+      (ebnf-line-color                  . "Black")
+      (ebnf-debug-ps                    . nil)
+      (ebnf-use-float-format            . t)
+      (ebnf-yac-ignore-error-recovery   . nil)
+      (ebnf-ignore-empty-rule           . nil)
+      (ebnf-optimize                    . nil))
+    ;; Happy EBNF default
+    (happy
+     default
+     (ebnf-justify-sequence            . 'left)
+     (ebnf-lex-comment-char            . ?\#)
+     (ebnf-lex-eop-char                . ?\;))
+    ;; ISO EBNF default
+    (iso-ebnf
+     default
+     (ebnf-syntax                      . 'iso-ebnf))
+    ;; Yacc/Bison default
+    (yacc
+     default
+     (ebnf-syntax                      . 'yacc))
+    )
+  "Style database.
+
+Each element has the following form:
+
+   (CUSTOM INHERITS (VAR . VALUE)...)
+
+CUSTOM is a symbol name style.
+INHERITS is a symbol name style from which the current style inherits the
+context.  If INHERITS is nil, means that there is no inheritance.
+VAR is a valid ebnf2ps symbol custom variable.  See `ebnf-style-custom-list' for
+valid symbol variable.
+VALUE is a sexp which it'll be evaluated to set the value to VAR.  So, don't
+forget to quote symbols and constant lists.  See `default' style for an
+example.
+
+Don't handle this variable directly.  Use functions `ebnf-insert-style' and
+`ebnf-merge-style'.")
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Style commands
+
+
+;;;###autoload
+(defun ebnf-insert-style (name inherits &rest values)
+  "Insert a new style NAME with inheritance INHERITS and values VALUES."
+  (interactive)
+  (and (assoc name ebnf-style-database)
+       (error "Style name already exists: %s" name))
+  (or (assoc inherits ebnf-style-database)
+      (error "Style inheritance name does'nt exist: %s" inherits))
+  (setq ebnf-style-database
+       (cons (cons name (cons inherits (ebnf-check-style-values values)))
+             ebnf-style-database)))
+
+
+;;;###autoload
+(defun ebnf-merge-style (name &rest values)
+  "Merge values of style NAME with style VALUES."
+  (interactive)
+  (let ((style (or (assoc name ebnf-style-database)
+                  (error "Style name does'nt exist: %s" name)))
+       (merge (ebnf-check-style-values values))
+       val elt new check)
+    ;; modify value of existing variables
+    (setq val (nthcdr 2 style))
+    (while merge
+      (setq check (car merge)
+           merge (cdr merge)
+           elt   (assoc (car check) val))
+      (if elt
+         (setcdr elt (cdr check))
+       (setq new (cons check new))))
+    ;; insert new variables
+    (nconc style (nreverse new))))
+
+
+;;;###autoload
+(defun ebnf-apply-style (style)
+  "Set STYLE to current style.
+
+It returns the old style symbol."
+  (interactive)
+  (prog1
+      ebnf-current-style
+    (and (ebnf-apply-style1 style)
+        (setq ebnf-current-style style))))
+
+
+;;;###autoload
+(defun ebnf-reset-style (&optional style)
+  "Reset current style.
+
+It returns the old style symbol."
+  (interactive)
+  (setq ebnf-stack-style nil)
+  (ebnf-apply-style (or style 'default)))
+
+
+;;;###autoload
+(defun ebnf-push-style (&optional style)
+  "Push the current style and set STYLE to current style.
+
+It returns the old style symbol."
+  (interactive)
+  (prog1
+      ebnf-current-style
+    (setq ebnf-stack-style (cons ebnf-current-style ebnf-stack-style))
+    (and style
+        (ebnf-apply-style style))))
+
+
+;;;###autoload
+(defun ebnf-pop-style ()
+  "Pop a style and set it to current style.
+
+It returns the old style symbol."
+  (interactive)
+  (prog1
+      (ebnf-apply-style (car ebnf-stack-style))
+    (setq ebnf-stack-style (cdr ebnf-stack-style))))
+
+
+(defun ebnf-apply-style1 (style)
+  (let ((value (cdr (assoc style ebnf-style-database))))
+    (prog1
+       value
+      (and (car value) (ebnf-apply-style1 (car value)))
+      (while (setq value (cdr value))
+       (set (caar value) (eval (cdar value)))))))
+
+
+(defun ebnf-check-style-values (values)
+  (let (style)
+    (while values
+      (and (memq (car values) ebnf-style-custom-list)
+          (setq style (cons (car values) style)))
+      (setq values (cdr values)))
+    (nreverse style)))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Internal variables
+
+
+(make-local-hook 'ebnf-hook)
+(make-local-hook 'ebnf-production-hook)
+(make-local-hook 'ebnf-page-hook)
+
+
+(defvar ebnf-eps-buffer-name " *EPS*")
+(defvar ebnf-parser-func     nil)
+(defvar ebnf-eps-executing   nil)
+(defvar ebnf-eps-upper-x     0.0)
+(make-variable-buffer-local 'ebnf-eps-upper-x)
+(defvar ebnf-eps-upper-y     0.0)
+(make-variable-buffer-local 'ebnf-eps-upper-y)
+(defvar ebnf-eps-prod-width  0.0)
+(make-variable-buffer-local 'ebnf-eps-prod-width)
+(defvar ebnf-eps-max-height 0.0)
+(make-variable-buffer-local 'ebnf-eps-max-height)
+(defvar ebnf-eps-max-width  0.0)
+(make-variable-buffer-local 'ebnf-eps-max-width)
+
+
+(defvar ebnf-eps-context nil
+  "List of EPS file name during parsing.
+
+See section \"Actions in Comments\" in ebnf2ps documentation.")
+
+
+(defvar ebnf-eps-production-list nil
+  "Alist associating production name with EPS file name list.
+
+Each element has the following form:
+
+   (PRODUCTION EPS-FILENAME...)
+
+PRODUCTION is the production name.
+EPS-FILENAME is the EPS file name.
+
+It's generated during parsing and used during EPS generation.
+
+See `ebnf-eps-context' and section \"Actions in Comments\" in ebnf2ps
+documentation.")
+
+
+(defconst ebnf-arrow-shape-alist
+  '((none        . 0)
+    (semi-up     . 1)
+    (semi-down   . 2)
+    (simple      . 3)
+    (transparent . 4)
+    (hollow      . 5)
+    (full        . 6)
+    (user        . 7))
+  "Alist associating values for `ebnf-arrow-shape'.
+
+See documentation for `ebnf-arrow-shape'.")
+
+
+(defconst ebnf-terminal-shape-alist
+  '((miter . 0)
+    (round . 1)
+    (bevel . 2))
+  "Alist associating values from `ebnf-terminal-shape' to a bit vector.
+
+See documentation for `ebnf-terminal-shape', `ebnf-non-terminal-shape' and
+`ebnf-chart-shape'.")
+
+
+(defvar ebnf-limit            nil)
+(defvar ebnf-action           nil)
+(defvar ebnf-action-list      nil)
+
+
+(defvar ebnf-default-p        nil)
+
+
+(defvar ebnf-font-height-P    0)
+(defvar ebnf-font-height-T    0)
+(defvar ebnf-font-height-NT   0)
+(defvar ebnf-font-height-S    0)
+(defvar ebnf-font-height-E    0)
+(defvar ebnf-font-height-R    0)
+(defvar ebnf-font-width-P     0)
+(defvar ebnf-font-width-T     0)
+(defvar ebnf-font-width-NT    0)
+(defvar ebnf-font-width-S     0)
+(defvar ebnf-font-width-E     0)
+(defvar ebnf-font-width-R     0)
+(defvar ebnf-space-T          0)
+(defvar ebnf-space-NT         0)
+(defvar ebnf-space-S          0)
+(defvar ebnf-space-E          0)
+(defvar ebnf-space-R          0)
+
+
+(defvar ebnf-basic-width      0)
+(defvar ebnf-basic-height     0)
+(defvar ebnf-vertical-space   0)
+(defvar ebnf-horizontal-space 0)
+
+
+(defvar ebnf-settings         nil)
+(defvar ebnf-fonts-required   nil)
+
+
+(defconst ebnf-debug
+  "
+% === begin EBNF procedures to help debugging
+
+% Mark visually current point:  string debug
+/debug
+{/-s- exch def
+ currentpoint
+ gsave -s- show grestore
+ gsave
+ 20 20 rlineto
+ 0 -40 rlineto
+ -40 40 rlineto
+ 0 -40 rlineto
+ 20 20 rlineto
+ stroke
+ grestore
+ moveto
+}def
+
+% Show number value:  number string debug-number
+/debug-number
+{gsave
+ 20 0 rmoveto show ([) show 60 string cvs show (]) show
+ grestore
+}def
+
+% === end   EBNF procedures to help debugging
+
+"
+  "This is intended to help debugging PostScript programming.")
+
+
+(defconst ebnf-prologue
+  "
+% === begin EBNF engine
+
+% --- Basic Definitions
+
+/fS F
+/SpaceS FontHeight 0.5 mul def
+/HeightS FontHeight FontHeight add def
+
+/fE F
+/SpaceE FontHeight 0.5 mul def
+/HeightE FontHeight FontHeight add def
+
+/fR F
+/SpaceR FontHeight 0.5 mul def
+/HeightR FontHeight FontHeight add def
+
+/fT F
+/SpaceT FontHeight 0.5 mul def
+/HeightT FontHeight FontHeight add def
+
+/fNT F
+/SpaceNT FontHeight 0.5 mul def
+/HeightNT FontHeight FontHeight add def
+
+/T HeightT HeightNT add 0.5 mul def
+/hT T 0.5 mul def
+/hT2 hT 0.5 mul def
+/hT4 hT 0.25 mul def
+
+/Er 0.1 def    % Error factor
+
+
+/c{currentpoint}bind def
+/xyi{/xi c /yi exch def def}bind def
+/xyo{/xo c /yo exch def def}bind def
+/xyp{/xp c /yp exch def def}bind def
+/xyt{/xt c /yt exch def def}bind def
+
+% vertical movement: x y height vm
+/vm{add moveto}bind def
+
+% horizontal movement: x y width hm
+/hm{3 -1 roll exch add exch moveto}bind def
+
+% set color: [R G B] SetRGB
+/SetRGB{aload pop setrgbcolor}bind def
+
+% filling gray area: gray-scale FillGray
+/FillGray{gsave setgray fill grestore}bind def
+
+% filling color area: [R G B] FillRGB
+/FillRGB{gsave SetRGB fill grestore}bind def
+
+/Stroke{LineWidth setlinewidth LineColor SetRGB stroke}bind def
+/StrokeShape{borderwidth setlinewidth bordercolor SetRGB stroke}bind def
+/Gstroke{gsave Stroke grestore}bind def
+
+% Empty Line: width EL
+/EL{0 rlineto Gstroke}bind def
+
+% --- Arrows
+
+/Down{hT2 neg hT4 neg rlineto}bind def
+
+/Arrow
+{hT2 neg hT4 rmoveto
+ hT2 hT4 neg rlineto
+ Down
+}bind def
+
+/ArrowPath{c newpath moveto Arrow closepath}bind def
+
+%>Right Arrow: RA
+%    \\
+% *---+
+%    /
+/RA-vector
+[{}                                    % 0 - none
+ {hT2 neg hT4 rlineto}                 % 1 - semi-up
+ {Down}                                        % 2 - semi-down
+ {Arrow}                               % 3 - simple
+ {Gstroke ArrowPath}                   % 4 - transparent
+ {Gstroke ArrowPath 1 FillGray}                % 5 - hollow
+ {Gstroke ArrowPath LineColor FillRGB} % 6 - full
+ {Gstroke gsave UserArrow grestore}    % 7 - user
+]def
+
+/RA
+{hT 0 rlineto
+ c
+ RA-vector ArrowShape get exec
+ Gstroke
+ moveto
+}def
+
+% rotation DrawArrow
+/DrawArrow
+{gsave
+ 0 0 translate
+ rotate
+ RA
+ c
+ grestore
+ rmoveto
+}def
+
+%>Left Arrow: LA
+%  /
+% +---*
+%  \\
+/LA{180 DrawArrow}def
+
+%>Up Arrow: UA
+%  +
+% /|\\
+%  |
+%  *
+/UA{90 DrawArrow}def
+
+%>Down Arrow: DA
+%  *
+%  |
+% \\|/
+%  +
+/DA{270 DrawArrow}def
+
+% --- Corners
+
+%>corner Right Descendent: height arrow corner_RD
+%   _             | arrow
+%  /   height > 0 |  0 - none
+% |               |  1 - right
+% *    ---------- |  2 - left
+% |               |  3 - vertical
+%  \\   height < 0 |
+%   -             |
+/cRD0-vector
+[% 0 - none
+ {0 h rlineto
+  hT 0 rlineto}
+ % 1 - right
+ {0 h rlineto
+  RA}
+ % 2 - left
+ {hT 0 rmoveto xyi
+  LA
+  0 h neg rlineto
+  xi yi moveto}
+ % 3 - vertical
+ {hT h rmoveto xyi
+  hT neg 0 rlineto
+  h 0 gt{DA}{UA}ifelse
+  xi yi moveto}
+]def
+
+/cRD-vector
+[{cRD0-vector arrow get exec}  % 0 - miter
+ {0 0 0 h hT h rcurveto}       % 1 - rounded
+ {hT h rlineto}                        % 2 - bevel
+]def
+
+/corner_RD
+{/arrow exch def /h exch def
+ cRD-vector ChartShape get exec
+ Gstroke
+}def
+
+%>corner Right Ascendent: height arrow corner_RA
+%                 | arrow
+%    | height > 0 |  0 - none
+%   /             |  1 - right
+% *-   ---------- |  2 - left
+%   \\             |  3 - vertical
+%    | height < 0 |
+%                 |
+/cRA0-vector
+[% 0 - none
+ {hT 0 rlineto
+  0 h rlineto}
+ % 1 - right
+ {RA
+  0 h rlineto}
+ % 2 - left
+ {hT h rmoveto xyi
+  0 h neg rlineto
+  LA
+  xi yi moveto}
+ % 3 - vertical
+ {hT h rmoveto xyi
+  h 0 gt{DA}{UA}ifelse
+  hT neg 0 rlineto
+  xi yi moveto}
+]def
+
+/cRA-vector
+[{cRA0-vector arrow get exec}  % 0 - miter
+ {0 0 hT 0 hT h rcurveto}      % 1 - rounded
+ {hT h rlineto}                        % 2 - bevel
+]def
+
+/corner_RA
+{/arrow exch def /h exch def
+ cRA-vector ChartShape get exec
+ Gstroke
+}def
+
+%>corner Left Descendent: height arrow corner_LD
+%  _              | arrow
+%   \\  height > 0 |  0 - none
+%    |            |  1 - right
+%    * ---------- |  2 - left
+%    |            |  3 - vertical
+%   /  height < 0 |
+%  -              |
+/cLD0-vector
+[% 0 - none
+ {0 h rlineto
+  hT neg 0 rlineto}
+ % 1 - right
+ {hT neg h rmoveto xyi
+  RA
+  0 h neg rlineto
+  xi yi moveto}
+ % 2 - left
+ {0 h rlineto
+  LA}
+ % 3 - vertical
+ {hT neg h rmoveto xyi
+  hT 0 rlineto
+  h 0 gt{DA}{UA}ifelse
+  xi yi moveto}
+]def
+
+/cLD-vector
+[{cLD0-vector arrow get exec}  % 0 - miter
+ {0 0 0 h hT neg h rcurveto}   % 1 - rounded
+ {hT neg h rlineto}            % 2 - bevel
+]def
+
+/corner_LD
+{/arrow exch def /h exch def
+ cLD-vector ChartShape get exec
+ Gstroke
+}def
+
+%>corner Left Ascendent: height arrow corner_LA
+%                 | arrow
+% |    height > 0 |  0 - none
+%  \\              |  1 - right
+%   -* ---------- |  2 - left
+%  /              |  3 - vertical
+% |    height < 0 |
+%                 |
+/cLA0-vector
+[% 0 - none
+ {hT neg 0 rlineto
+  0 h rlineto}
+ % 1 - right
+ {hT neg h rmoveto xyi
+  0 h neg rlineto
+  RA
+  xi yi moveto}
+ % 2 - left
+ {LA
+  0 h rlineto}
+ % 3 - vertical
+ {hT neg h rmoveto xyi
+  h 0 gt{DA}{UA}ifelse
+  hT 0 rlineto
+  xi yi moveto}
+]def
+
+/cLA-vector
+[{cLA0-vector arrow get exec}          % 0 - miter
+ {0 0 hT neg 0 hT neg h rcurveto}      % 1 - rounded
+ {hT neg h rlineto}                    % 2 - bevel
+]def
+
+/corner_LA
+{/arrow exch def /h exch def
+ cLA-vector ChartShape get exec
+ Gstroke
+}def
+
+% --- Flow Stuff
+
+% height prepare_height |- line_height corner_height corner_height
+/prepare_height
+{dup 0 gt
+ {T sub hT}
+ {T add hT neg}ifelse
+ dup
+}def
+
+%>Left Alternative: height LAlt
+%      _
+%     /
+%    |   height > 0
+%    |
+%   /
+% *-     ----------
+%   \\
+%    |
+%    |   height < 0
+%     \\
+%      -
+/LAlt
+{dup 0 eq
+ {T exch rlineto}
+ {dup abs T lt
+  {0.5 mul dup
+   1 corner_RA
+   0 corner_RD}
+  {prepare_height
+   1 corner_RA
+   exch 0 exch rlineto
+   0 corner_RD
+  }ifelse
+ }ifelse
+}def
+
+%>Left Loop: height LLoop
+%   _
+%  /
+% |      height > 0
+% |
+%  \\
+%   -*   ----------
+%  /
+% |
+% |      height < 0
+%  \\
+%   -
+/LLoop
+{prepare_height
+ 3 corner_LA
+ exch 0 exch rlineto
+ 0 corner_RD
+}def
+
+%>Right Alternative: height RAlt
+% _
+%  \\
+%   |    height > 0
+%   |
+%    \\
+%     -* ----------
+%    /
+%   |
+%   |    height < 0
+%  /
+% -
+/RAlt
+{dup 0 eq
+ {T neg exch rlineto}
+ {dup abs T lt
+  {0.5 mul dup
+   1 corner_LA
+   0 corner_LD}
+  {prepare_height
+   1 corner_LA
+   exch 0 exch rlineto
+   0 corner_LD
+  }ifelse
+ }ifelse
+}def
+
+%>Right Loop: height RLoop
+%  _
+%   \\
+%    |   height > 0
+%    |
+%   /
+% *-     ----------
+%   \\
+%    |
+%    |   height < 0
+%   /
+%  -
+/RLoop
+{prepare_height
+ 1 corner_RA
+ exch 0 exch rlineto
+ 0 corner_LD
+}def
+
+% --- Terminal, Non-terminal and Special Basics
+
+% string width prepare-width |- string
+/prepare-width
+{/width exch def
+ dup stringwidth pop space add space add width exch sub 0.5 mul
+ /w exch def
+}def
+
+% string width begin-right
+/begin-right
+{xyo
+ prepare-width
+ w hT sub EL
+ RA
+}def
+
+% end-right
+/end-right
+{xo width add Er add yo moveto
+ w Er add neg EL
+ xo yo moveto
+}def
+
+% string width begin-left
+/begin-left
+{xyo
+ prepare-width
+ w EL
+}def
+
+% end-left
+/end-left
+{xo width add Er add yo moveto
+ hT w sub Er add EL
+ LA
+ xo yo moveto
+}def
+
+/ShapePath-vector
+[% 0 - miter
+ {xx yy moveto
+  xx YY lineto
+  XX YY lineto
+  XX yy lineto}
+ % 1 - rounded
+ {/half YY yy sub 0.5 mul abs def
+  xx half add YY moveto
+  0 0 half neg 0 half neg half neg rcurveto
+  0 0 0 half neg half half neg rcurveto
+  XX xx sub abs half sub half sub 0 rlineto
+  0 0 half 0 half half rcurveto
+  0 0 0 half half neg half rcurveto}
+ % 2 - bevel
+ {/quarter YY yy sub 0.25 mul abs def
+  xx quarter add YY moveto
+  quarter neg quarter neg rlineto
+  0 quarter quarter add neg rlineto
+  quarter quarter neg rlineto
+  XX xx sub abs quarter sub quarter sub 0 rlineto
+  quarter quarter rlineto
+  0 quarter quarter add rlineto
+  quarter neg quarter rlineto}
+]def
+
+/doShapePath
+{newpath
+ ShapePath-vector shape get exec
+ closepath
+}def
+
+/doShapeShadow
+{gsave
+ Xshadow Xshadow add Xshadow add
+ Yshadow Yshadow add Yshadow add translate
+ doShapePath
+ 0.9 FillGray
+ grestore
+}def
+
+/doShape
+{gsave
+ doShapePath
+ shapecolor FillRGB
+ StrokeShape
+ grestore
+}def
+
+% string SBound |- string
+/SBound
+{/xx c dup /yy exch def
+ FontHeight add /YY exch def def
+ dup stringwidth pop xx add /XX exch def
+ Effect 8 and 0 ne
+ {/yy yy YShadow add def
+  /XX XX XShadow add def
+ }if
+}def
+
+% string SBox
+/SBox
+{gsave
+ c space sub moveto
+ SBound
+ /XX XX space add space add def
+ /YY YY space add def
+ /yy yy space sub def
+ shadow{doShapeShadow}if
+ doShape
+ space Descent abs rmoveto
+ foreground SetRGB S
+ grestore
+}def
+
+% --- Terminal
+
+% TeRminal: string TR
+/TR
+{/Effect EffectT def
+ /shape ShapeT def
+ /shapecolor BackgroundT def
+ /borderwidth BorderWidthT def
+ /bordercolor BorderColorT def
+ /foreground ForegroundT def
+ /shadow ShadowT def
+ SBox
+}def
+
+%>Right Terminal: string width RT |- x y
+/RT
+{xyt
+ /fT F
+ /space SpaceT def
+ begin-right
+ TR
+ end-right
+ xt yt
+}def
+
+%>Left Terminal: string width LT |- x y
+/LT
+{xyt
+ /fT F
+ /space SpaceT def
+ begin-left
+ TR
+ end-left
+ xt yt
+}def
+
+%>Right Terminal Default: string width RTD |- x y
+/RTD
+{/-save- BorderWidthT def
+ /BorderWidthT BorderWidthT DefaultWidth add def
+ RT
+ /BorderWidthT -save- def
+}def
+
+%>Left Terminal Default: string width LTD |- x y
+/LTD
+{/-save- BorderWidthT def
+ /BorderWidthT BorderWidthT DefaultWidth add def
+ LT
+ /BorderWidthT -save- def
+}def
+
+% --- Non-Terminal
+
+% Non-Terminal: string NT
+/NT
+{/Effect EffectNT def
+ /shape ShapeNT def
+ /shapecolor BackgroundNT def
+ /borderwidth BorderWidthNT def
+ /bordercolor BorderColorNT def
+ /foreground ForegroundNT def
+ /shadow ShadowNT def
+ SBox
+}def
+
+%>Right Non-Terminal: string width RNT |- x y
+/RNT
+{xyt
+ /fNT F
+ /space SpaceNT def
+ begin-right
+ NT
+ end-right
+ xt yt
+}def
+
+%>Left Non-Terminal: string width LNT |- x y
+/LNT
+{xyt
+ /fNT F
+ /space SpaceNT def
+ begin-left
+ NT
+ end-left
+ xt yt
+}def
+
+%>Right Non-Terminal Default: string width RNTD |- x y
+/RNTD
+{/-save- BorderWidthNT def
+ /BorderWidthNT BorderWidthNT DefaultWidth add def
+ RNT
+ /BorderWidthNT -save- def
+}def
+
+%>Left Non-Terminal Default: string width LNTD |- x y
+/LNTD
+{/-save- BorderWidthNT def
+ /BorderWidthNT BorderWidthNT DefaultWidth add def
+ LNT
+ /BorderWidthNT -save- def
+}def
+
+% --- Special
+
+% SPecial: string SP
+/SP
+{/Effect EffectS def
+ /shape ShapeS def
+ /shapecolor BackgroundS def
+ /borderwidth BorderWidthS def
+ /bordercolor BorderColorS def
+ /foreground ForegroundS def
+ /shadow ShadowS def
+ SBox
+}def
+
+%>Right SPecial: string width RSP |- x y
+/RSP
+{xyt
+ /fS F
+ /space SpaceS def
+ begin-right
+ SP
+ end-right
+ xt yt
+}def
+
+%>Left SPecial: string width LSP |- x y
+/LSP
+{xyt
+ /fS F
+ /space SpaceS def
+ begin-left
+ SP
+ end-left
+ xt yt
+}def
+
+%>Right SPecial Default: string width RSPD |- x y
+/RSPD
+{/-save- BorderWidthS def
+ /BorderWidthS BorderWidthS DefaultWidth add def
+ RSP
+ /BorderWidthS -save- def
+}def
+
+%>Left SPecial Default: string width LSPD |- x y
+/LSPD
+{/-save- BorderWidthS def
+ /BorderWidthS BorderWidthS DefaultWidth add def
+ LSP
+ /BorderWidthS -save- def
+}def
+
+% --- Repeat and Except basics
+
+/begin-direction
+{/w width rwidth sub 0.5 mul def
+ width 0 rmoveto}def
+
+/end-direction
+{gsave
+ /xx c entry add /YY exch def def
+ /yy YY height sub def
+ /XX xx rwidth add def
+ shadow{doShapeShadow}if
+ doShape
+ grestore
+}def
+
+/right-direction
+{begin-direction
+ w neg EL
+ xt yt moveto
+ w hT sub EL RA
+ end-direction
+}def
+
+/left-direction
+{begin-direction
+ hT w sub EL LA
+ xt yt moveto
+ w EL
+ end-direction
+}def
+
+% --- Repeat
+
+% entry height width rwidth begin-repeat
+/begin-repeat
+{/rwidth exch def
+ /width exch def
+ /height exch def
+ /entry exch def
+ /fR F
+ /space SpaceR def
+ /Effect EffectR def
+ /shape ShapeR def
+ /shapecolor BackgroundR def
+ /borderwidth BorderWidthR def
+ /bordercolor BorderColorR def
+ /foreground ForegroundR def
+ /shadow ShadowR def
+ xyt
+}def
+
+% string end-repeat |- x y
+/end-repeat
+{gsave
+ space Descent rmoveto
+ foreground SetRGB S
+ c Descent sub
+ grestore
+ exch space add exch moveto
+ xt yt
+}def
+
+%>Right RePeat: string entry height width rwidth RRP |- x y
+/RRP{begin-repeat right-direction end-repeat}def
+
+%>Left RePeat: string entry height width rwidth LRP |- x y
+/LRP{begin-repeat left-direction end-repeat}def
+
+% --- Except
+
+% entry height width rwidth begin-except
+/begin-except
+{/rwidth exch def
+ /width exch def
+ /height exch def
+ /entry exch def
+ /fE F
+ /space SpaceE def
+ /Effect EffectE def
+ /shape ShapeE def
+ /shapecolor BackgroundE def
+ /borderwidth BorderWidthE def
+ /bordercolor BorderColorE def
+ /foreground ForegroundE def
+ /shadow ShadowE def
+ xyt
+}def
+
+% x-width end-except |- x y
+/end-except
+{gsave
+ space space add add Descent rmoveto
+ (-) foreground SetRGB S
+ grestore
+ space 0 rmoveto
+ xt yt
+}def
+
+%>Right EXcept: x-width entry height width rwidth REX |- x y
+/REX{begin-except right-direction end-except}def
+
+%>Left EXcept: x-width entry height width rwidth LEX |- x y
+/LEX{begin-except left-direction end-except}def
+
+% --- Sequence
+
+%>Beginning Of Sequence: BOS |- x y
+/BOS{currentpoint}bind def
+
+%>End Of Sequence: x y x1 y1 EOS |- x y
+/EOS{pop pop}bind def
+
+% --- Production
+
+%>Beginning Of Production: string width height BOP |- y x
+/BOP
+{xyp
+ neg yp add /yw exch def
+ xp add T sub /xw exch def
+ /Effect EffectP def
+ /fP F ForegroundP SetRGB BackgroundP aload pop true BG S
+ /Effect 0 def
+ ( :) S false BG
+ xw yw moveto
+ hT EL RA
+ xp yw moveto
+ T EL
+ yp xp
+}def
+
+%>End Of Production: y x delta EOP
+/EOPH{add exch moveto}bind def                 % horizontal
+/EOPV{exch pop sub 0 exch moveto}bind def      % vertical
+
+% --- Empty Alternative
+
+%>Empty Alternative: width EA |- x y
+/EA
+{gsave
+ Er add 0 rlineto
+ Stroke
+ grestore
+ c
+}def
+
+% --- Alternative
+
+%>AlTernative: h1 h2 ... hn n width AT |- x y
+/AT
+{xyo xo add /xw exch def
+ xw yo moveto
+ Er EL
+ {xw yo moveto
+  dup RAlt
+  xo yo moveto
+  LAlt}repeat
+ xo yo
+}def
+
+% --- Optional
+
+%>OPtional: height width OP |- x y
+/OP
+{xyo
+ T sub /ow exch def
+ ow Er sub 0 rmoveto
+ T Er add EL
+ neg dup RAlt
+ ow T sub neg EL
+ xo yo moveto
+ LAlt
+ xo yo moveto
+ T EL
+ xo yo
+}def
+
+% --- List Flow
+
+%>One or More: height width OM |- x y
+/OM
+{xyo
+ /ow exch def
+ ow Er add 0 rmoveto
+ T Er add neg EL
+ dup RLoop
+ xo T add yo moveto
+ LLoop
+ xo yo moveto
+ T EL
+ xo yo
+}def
+
+%>Zero or More: h2 h1 width ZM |- x y
+/ZM
+{xyo
+ Er add EL
+ Er neg 0 rmoveto
+ dup RAlt
+ exch dup RLoop
+ xo yo moveto
+ exch dup LAlt
+ exch LLoop
+ yo add xo T add exch moveto
+ xo yo
+}def
+
+% === end EBNF engine
+
+"
+  "EBNF PostScript prologue")
+
+
+(defconst ebnf-eps-prologue
+  "
+/#ebnf2ps#dict 230 dict def
+#ebnf2ps#dict begin
+
+% Initiliaze variables to avoid name-conflicting with document variables.
+% This is the case when using `bind' operator.
+/-fillp-               0 def           /h              0 def
+/-ox-                  0 def           /half           0 def
+/-oy-                  0 def           /height         0 def
+/-save-                        0 def           /ow             0 def
+/Ascent                        0 def           /quarter        0 def
+/Descent               0 def           /rXX            0 def
+/Effect                        0 def           /rYY            0 def
+/FontHeight            0 def           /rwidth         0 def
+/LineThickness         0 def           /rxx            0 def
+/OverlinePosition      0 def           /ryy            0 def
+/SpaceBackground       0 def           /shadow         0 def
+/StrikeoutPosition     0 def           /shape          0 def
+/UnderlinePosition     0 def           /shapecolor     0 def
+/XBox                  0 def           /space          0 def
+/XX                    0 def           /st             1 string def
+/Xshadow               0 def           /w              0 def
+/YBox                  0 def           /width          0 def
+/YY                    0 def           /xi             0 def
+/Yshadow               0 def           /xo             0 def
+/arrow                 0 def           /xp             0 def
+/bg                    false def       /xt             0 def
+/bgcolor               0 def           /xw             0 def
+/bordercolor           0 def           /xx             0 def
+/borderwidth           0 def           /yi             0 def
+/dd                    0 def           /yo             0 def
+/entry                 0 def           /yp             0 def
+/foreground            0 def           /yt             0 def
+                                       /yy             0 def
+
+
+% ISOLatin1Encoding stolen from ps_init.ps in GhostScript 2.6.1.4:
+/ISOLatin1Encoding where
+{pop}
+{% -- The ISO Latin-1 encoding vector isn't known, so define it.
+ % -- The first half is the same as the standard encoding,
+ % -- except for minus instead of hyphen at code 055.
+ /ISOLatin1Encoding
+  StandardEncoding 0  45 getinterval aload pop
+  /minus
+  StandardEncoding 46 82 getinterval aload pop
+ %*** NOTE: the following are missing in the Adobe documentation,
+ %*** but appear in the displayed table:
+ %*** macron at 0225, dieresis at 0230, cedilla at 0233, space at 0240.
+ % 0200 (128)
+  /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+  /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+  /dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent
+  /dieresis /.notdef /ring /cedilla /.notdef /hungarumlaut /ogonek /caron
+ % 0240 (160)
+  /space /exclamdown /cent /sterling
+       /currency /yen /brokenbar /section
+  /dieresis /copyright /ordfeminine /guillemotleft
+       /logicalnot /hyphen /registered /macron
+  /degree /plusminus /twosuperior /threesuperior
+       /acute /mu /paragraph /periodcentered
+  /cedilla /onesuperior /ordmasculine /guillemotright
+       /onequarter /onehalf /threequarters /questiondown
+ % 0300 (192)
+  /Agrave /Aacute /Acircumflex /Atilde
+       /Adieresis /Aring /AE /Ccedilla
+  /Egrave /Eacute /Ecircumflex /Edieresis
+       /Igrave /Iacute /Icircumflex /Idieresis
+  /Eth /Ntilde /Ograve /Oacute
+       /Ocircumflex /Otilde /Odieresis /multiply
+  /Oslash /Ugrave /Uacute /Ucircumflex
+       /Udieresis /Yacute /Thorn /germandbls
+ % 0340 (224)
+  /agrave /aacute /acircumflex /atilde
+       /adieresis /aring /ae /ccedilla
+  /egrave /eacute /ecircumflex /edieresis
+       /igrave /iacute /icircumflex /idieresis
+  /eth /ntilde /ograve /oacute
+       /ocircumflex /otilde /odieresis /divide
+  /oslash /ugrave /uacute /ucircumflex
+       /udieresis /yacute /thorn /ydieresis
+ 256 packedarray def
+}ifelse
+
+/reencodeFontISO       %def
+{dup
+ length 12 add dict    % Make a new font (a new dict the same size
+                       % as the old one) with room for our new symbols.
+
+ begin                 % Make the new font the current dictionary.
+  {1 index /FID ne
+   {def}{pop pop}ifelse
+  }forall              % Copy each of the symbols from the old dictionary
+                       % to the new one except for the font ID.
+
+  currentdict /FontType get 0 ne
+  {/Encoding ISOLatin1Encoding def}if  % Override the encoding with
+                                       % the ISOLatin1 encoding.
+
+  % Use the font's bounding box to determine the ascent, descent,
+  % and overall height; don't forget that these values have to be
+  % transformed using the font's matrix.
+
+  %          ^    (x2 y2)
+  %          |       |
+  %          |       v
+  %          |  +----+ - -
+  %          |  |    |   ^
+  %          |  |    |   | Ascent (usually > 0)
+  %          |  |    |   |
+  % (0 0) -> +--+----+-------->
+  %             |    |   |
+  %             |    |   v Descent (usually < 0)
+  % (x1 y1) --> +----+ - -
+
+  currentdict /FontType get 0 ne
+  {/FontBBox load aload pop            % -- x1 y1 x2 y2
+   FontMatrix transform /Ascent  exch def pop
+   FontMatrix transform /Descent exch def pop}
+  {/PrimaryFont FDepVector 0 get def
+   PrimaryFont /FontBBox get aload pop
+   PrimaryFont /FontMatrix get transform /Ascent exch def pop
+   PrimaryFont /FontMatrix get transform /Descent exch def pop
+  }ifelse
+
+  /FontHeight Ascent Descent sub def   % use `sub' because descent < 0
+
+  % Define these in case they're not in the FontInfo
+  % (also, here they're easier to get to).
+  /UnderlinePosition  Descent 0.70 mul def
+  /OverlinePosition   Descent UnderlinePosition sub Ascent add def
+  /StrikeoutPosition  Ascent 0.30 mul def
+  /LineThickness      FontHeight 0.05 mul def
+  /Xshadow            FontHeight  0.08 mul def
+  /Yshadow            FontHeight -0.09 mul def
+  /SpaceBackground    Descent neg UnderlinePosition add def
+  /XBox               Descent neg def
+  /YBox               LineThickness 0.7 mul def
+
+  currentdict  % Leave the new font on the stack
+ end           % Stop using the font as the current dictionary
+ definefont    % Put the font into the font dictionary
+ pop           % Discard the returned font
+}bind def
+
+% Font definition
+/DefFont{findfont exch scalefont reencodeFontISO}def
+
+% Font selection
+/F
+{findfont
+ dup /Ascent            get /Ascent            exch def
+ dup /Descent           get /Descent           exch def
+ dup /FontHeight        get /FontHeight        exch def
+ dup /UnderlinePosition get /UnderlinePosition exch def
+ dup /OverlinePosition  get /OverlinePosition  exch def
+ dup /StrikeoutPosition get /StrikeoutPosition exch def
+ dup /LineThickness     get /LineThickness     exch def
+ dup /Xshadow           get /Xshadow           exch def
+ dup /Yshadow           get /Yshadow           exch def
+ dup /SpaceBackground   get /SpaceBackground   exch def
+ dup /XBox              get /XBox              exch def
+ dup /YBox              get /YBox              exch def
+ setfont
+}def
+
+/BG
+{dup /bg exch def
+ {mark 4 1 roll ]}
+ {[ 1.0 1.0 1.0 ]}
+ ifelse
+ /bgcolor exch def
+}def
+
+% stack:  --
+/FillBgColor{bgcolor aload pop setrgbcolor fill}bind def
+
+% stack:  fill-or-not lower-x lower-y upper-x upper-y  |-  --
+/doRect
+{/rYY exch def
+ /rXX exch def
+ /ryy exch def
+ /rxx exch def
+ gsave
+ newpath
+ rXX rYY moveto
+ rxx rYY lineto
+ rxx ryy lineto
+ rXX ryy lineto
+ closepath
+ % top of stack: fill-or-not
+ {FillBgColor}
+ {LineThickness setlinewidth stroke}
+ ifelse
+ grestore
+}bind def
+
+% stack:  string fill-or-not  |-  --
+/doOutline
+{/-fillp- exch def
+ /-ox- currentpoint /-oy- exch def def
+ gsave
+ LineThickness setlinewidth
+ {st 0 3 -1 roll put
+  st dup true charpath
+  -fillp- {gsave FillBgColor grestore}if
+  stroke stringwidth
+  -oy- add /-oy- exch def
+  -ox- add /-ox- exch def
+  -ox- -oy- moveto
+ }forall
+ grestore
+ -ox- -oy- moveto
+}bind def
+
+% stack:  fill-or-not delta  |-  --
+/doBox
+{/dd exch def
+ xx XBox sub dd sub yy YBox sub dd sub
+ XX XBox add dd add YY YBox add dd add
+ doRect
+}bind def
+
+% stack:  string  |-  --
+/doShadow
+{gsave
+ Xshadow Yshadow rmoveto
+ false doOutline
+ grestore
+}bind def
+
+% stack:  position  |-  --
+/Hline
+{currentpoint exch pop add dup
+ gsave
+ newpath
+ xx exch moveto
+ XX exch lineto
+ closepath
+ LineThickness setlinewidth stroke
+ grestore
+}bind def
+
+% stack:  string  |-  --
+% effect: 1  - underline  2   - strikeout  4  - overline
+%         8  - shadow     16  - box        32 - outline
+/S
+{/xx currentpoint dup Descent add /yy exch def
+ Ascent add /YY exch def def
+ dup stringwidth pop xx add /XX exch def
+ Effect 8 and 0 ne
+ {/yy yy Yshadow add def
+  /XX XX Xshadow add def
+ }if
+ bg
+ {true
+  Effect 16 and 0 ne
+  {SpaceBackground doBox}
+  {xx yy XX YY doRect}
+  ifelse
+ }if                                           % background
+ Effect 16 and 0 ne{false 0 doBox}if           % box
+ Effect 8  and 0 ne{dup doShadow}if            % shadow
+ Effect 32 and 0 ne
+ {true doOutline}                              % outline
+ {show}                                                % normal text
+ ifelse
+ Effect 1  and 0 ne{UnderlinePosition Hline}if % underline
+ Effect 2  and 0 ne{StrikeoutPosition Hline}if % strikeout
+ Effect 4  and 0 ne{OverlinePosition  Hline}if % overline
+}bind def
+
+"
+  "EBNF EPS prologue")
+
+
+(defconst ebnf-eps-begin
+  "
+end
+
+% x y #ebnf2ps#begin
+/#ebnf2ps#begin
+{#ebnf2ps#dict begin /#ebnf2ps#save save def
+ moveto false BG 0.0 0.0 0.0 setrgbcolor}def
+
+/#ebnf2ps#end{showpage #ebnf2ps#save restore end}def
+
+%%EndPrologue
+"
+  "EBNF EPS begin")
+
+
+(defconst ebnf-eps-end
+  "#ebnf2ps#end
+%%EOF
+"
+  "EBNF EPS end")
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Formatting
+
+
+(defvar ebnf-format-float "%1.3f")
+
+
+(defun ebnf-format-float (&rest floats)
+  (mapconcat
+   #'(lambda (float)
+       (format ebnf-format-float float))
+   floats
+   " "))
+
+
+(defun ebnf-format-color (format-str color default)
+  (let* ((the-color (or color default))
+        (rgb (mapcar 'ps-color-value (ps-color-values the-color))))
+    (format format-str
+           (concat "["
+                   (ebnf-format-float (nth 0 rgb) (nth 1 rgb) (nth 2 rgb))
+                   "]")
+           the-color)))
+
+
+(defvar ebnf-message-float "%3.2f")
+
+
+(defsubst ebnf-message-float (format-str value)
+  (message format-str
+          (format ebnf-message-float value)))
+
+
+(defsubst ebnf-message-info (messag)
+  (message "%s...%3d%%"
+          messag
+          (round (/ (* (setq ebnf-nprod (1+ ebnf-nprod)) 100.0) ebnf-total))))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Macros
+
+
+(defmacro ebnf-node-kind (vec &optional value)
+  (if value
+      `(aset ,vec 0 ,value)
+    `(aref ,vec 0)))
+
+
+(defmacro ebnf-node-width-func (node width)
+  `(funcall (aref ,node 1) ,node ,width))
+
+
+(defmacro ebnf-node-dimension-func (node &optional value)
+  (if value
+      `(aset ,node 2 ,value)
+    `(funcall (aref ,node 2) ,node)))
+
+
+(defmacro ebnf-node-entry (vec &optional value)
+  (if value
+      `(aset ,vec 3 ,value)
+    `(aref ,vec 3)))
+
+
+(defmacro ebnf-node-height (vec &optional value)
+  (if value
+      `(aset ,vec 4 ,value)
+    `(aref ,vec 4)))
+
+
+(defmacro ebnf-node-width (vec &optional value)
+  (if value
+      `(aset ,vec 5 ,value)
+    `(aref ,vec 5)))
+
+
+(defmacro ebnf-node-name (vec)
+  `(aref ,vec 6))
+
+
+(defmacro ebnf-node-list (vec &optional value)
+  (if value
+      `(aset ,vec 6 ,value)
+    `(aref ,vec 6)))
+
+
+(defmacro ebnf-node-default (vec)
+  `(aref ,vec 7))
+
+
+(defmacro ebnf-node-production (vec &optional value)
+  (if value
+      `(aset ,vec 7 ,value)
+    `(aref ,vec 7)))
+
+
+(defmacro ebnf-node-separator (vec &optional value)
+  (if value
+      `(aset ,vec 7 ,value)
+    `(aref ,vec 7)))
+
+
+(defmacro ebnf-node-action (vec &optional value)
+  (if value
+      `(aset ,vec 8 ,value)
+    `(aref ,vec 8)))
+
+
+(defmacro ebnf-node-generation (node)
+  `(funcall (ebnf-node-kind ,node) ,node))
+
+
+(defmacro ebnf-max-width (prod)
+  `(max (ebnf-node-width ,prod)
+       (+ (* (length (ebnf-node-name ,prod))
+             ebnf-font-width-P)
+          ebnf-production-horizontal-space)))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; PostScript generation
+
+
+(defun ebnf-generate-eps (ebnf-tree)
+  (let* ((ps-color-p           (and ebnf-color-p (ps-color-device)))
+        (ps-print-color-scale (if ps-color-p
+                                  (float (car (ps-color-values "white")))
+                                1.0))
+        (ebnf-total           (length ebnf-tree))
+        (ebnf-nprod           0)
+        (old-ps-output        (symbol-function 'ps-output))
+        (old-ps-output-string (symbol-function 'ps-output-string))
+        (eps-buffer           (get-buffer-create ebnf-eps-buffer-name))
+        ebnf-debug-ps error-msg horizontal
+        prod prod-name prod-width prod-height prod-list file-list)
+    ;; redefines `ps-output' and `ps-output-string'
+    (defalias 'ps-output        'ebnf-eps-output)
+    (defalias 'ps-output-string 'ps-output-string-prim)
+    ;; generate EPS file
+    (save-excursion
+      (condition-case data
+         (progn
+           (while ebnf-tree
+             (setq prod        (car ebnf-tree)
+                   prod-name   (ebnf-node-name prod)
+                   prod-width  (ebnf-max-width prod)
+                   prod-height (ebnf-node-height prod)
+                   horizontal  (memq (ebnf-node-action prod) ebnf-action-list))
+             ;; generate production in EPS buffer
+             (save-excursion
+               (set-buffer eps-buffer)
+               (setq ebnf-eps-upper-x    0.0
+                     ebnf-eps-upper-y    0.0
+                     ebnf-eps-max-width  prod-width
+                     ebnf-eps-max-height prod-height)
+               (ebnf-generate-production prod))
+             (if (setq prod-list (cdr (assoc prod-name
+                                             ebnf-eps-production-list)))
+                 ;; insert EPS buffer in all buffer associated with production
+                 (ebnf-eps-production-list prod-list 'file-list horizontal
+                                           prod-width prod-height eps-buffer)
+               ;; write EPS file for production
+               (ebnf-eps-finish-and-write eps-buffer
+                                          (ebnf-eps-filename prod-name)))
+             ;; prepare for next loop
+             (save-excursion
+               (set-buffer eps-buffer)
+               (erase-buffer))
+             (setq ebnf-tree (cdr ebnf-tree)))
+           ;; write and kill temporary buffers
+           (ebnf-eps-write-kill-temp file-list t)
+           (setq file-list nil))
+       ;; handler
+       ((quit error)
+        (setq error-msg (error-message-string data)))))
+    ;; restore `ps-output' and `ps-output-string'
+    (defalias 'ps-output        old-ps-output)
+    (defalias 'ps-output-string old-ps-output-string)
+    ;; kill temporary buffers
+    (kill-buffer eps-buffer)
+    (ebnf-eps-write-kill-temp file-list nil)
+    (and error-msg (error error-msg))
+    (message " ")))
+
+
+;; write and kill temporary buffers
+(defun ebnf-eps-write-kill-temp (file-list write-p)
+  (while file-list
+    (let ((buffer (get-buffer (concat " *" (car file-list) "*"))))
+      (when buffer
+       (and write-p
+            (ebnf-eps-finish-and-write buffer (car file-list)))
+       (kill-buffer buffer)))
+    (setq file-list (cdr file-list))))
+
+
+;; insert EPS buffer in all buffer associated with production
+(defun ebnf-eps-production-list (prod-list file-list-sym horizontal
+                                          prod-width prod-height eps-buffer)
+  (while prod-list
+    (add-to-list file-list-sym (car prod-list))
+    (save-excursion
+      (set-buffer (get-buffer-create (concat " *" (car prod-list) "*")))
+      (goto-char (point-max))
+      (cond
+       ;; first production
+       ((zerop (buffer-size))
+       (setq ebnf-eps-upper-x    0.0
+             ebnf-eps-upper-y    0.0
+             ebnf-eps-max-width  prod-width
+             ebnf-eps-max-height prod-height))
+       ;; horizontal
+       (horizontal
+       (ebnf-eop-horizontal ebnf-eps-prod-width)
+       (setq ebnf-eps-max-width  (+ ebnf-eps-max-width
+                                    ebnf-production-horizontal-space
+                                    prod-width)
+             ebnf-eps-max-height (max ebnf-eps-max-height prod-height)))
+       ;; vertical
+       (t
+       (ebnf-eop-vertical ebnf-eps-max-height)
+       (setq ebnf-eps-upper-x    (max ebnf-eps-upper-x ebnf-eps-max-width)
+             ebnf-eps-upper-y    (if (zerop ebnf-eps-upper-y)
+                                     ebnf-eps-max-height
+                                   (+ ebnf-eps-upper-y
+                                      ebnf-production-vertical-space
+                                      ebnf-eps-max-height))
+             ebnf-eps-max-width  prod-width
+             ebnf-eps-max-height prod-height))
+       )
+      (setq ebnf-eps-prod-width prod-width)
+      (insert-buffer eps-buffer))
+    (setq prod-list (cdr prod-list))))
+
+
+(defun ebnf-generate (ebnf-tree)
+  (let* ((ps-color-p           (and ebnf-color-p (ps-color-device)))
+        (ps-print-color-scale (if ps-color-p
+                                  (float (car (ps-color-values "white")))
+                                1.0))
+        ps-zebra-stripes ps-line-number ps-razzle-dazzle
+        ps-print-hook
+        ps-print-begin-sheet-hook
+        ps-print-begin-page-hook
+        ps-print-begin-column-hook)
+    (ps-generate (current-buffer) (point-min) (point-max)
+                'ebnf-generate-postscript)))
+
+
+(defvar ebnf-tree      nil)
+(defvar ebnf-direction "R")
+(defvar ebnf-total     0)
+(defvar ebnf-nprod     0)
+
+
+(defun ebnf-generate-postscript (from to)
+  (ebnf-begin-file)
+  (if ebnf-horizontal-max-height
+      (ebnf-generate-with-max-height)
+    (ebnf-generate-without-max-height))
+  (message " "))
+
+
+(defun ebnf-generate-with-max-height ()
+  (let ((ebnf-total (length ebnf-tree))
+       (ebnf-nprod 0)
+       next-line max-height prod the-width)
+    (while ebnf-tree
+      ;; find next line point
+      (setq next-line  ebnf-tree
+           prod       (car ebnf-tree)
+           max-height (ebnf-node-height prod))
+      (ebnf-begin-line prod (ebnf-max-width prod))
+      (while (and (setq next-line (cdr next-line))
+                 (setq prod      (car next-line))
+                 (memq (ebnf-node-action prod) ebnf-action-list)
+                 (setq the-width (ebnf-max-width prod))
+                 (<= the-width ps-width-remaining))
+       (setq max-height         (max max-height (ebnf-node-height prod))
+             ps-width-remaining (- ps-width-remaining
+                                   (+ the-width
+                                      ebnf-production-horizontal-space))))
+      ;; generate current line
+      (ebnf-newline max-height)
+      (setq prod (car ebnf-tree))
+      (ebnf-generate-production prod)
+      (while (not (eq (setq ebnf-tree (cdr ebnf-tree)) next-line))
+       (ebnf-eop-horizontal (ebnf-max-width prod))
+       (setq prod (car ebnf-tree))
+       (ebnf-generate-production prod))
+      (ebnf-eop-vertical max-height))))
+
+
+(defun ebnf-generate-without-max-height ()
+  (let ((ebnf-total (length ebnf-tree))
+       (ebnf-nprod 0)
+       max-height prod bef-width cur-width)
+    (while ebnf-tree
+      ;; generate current line
+      (setq prod       (car ebnf-tree)
+           max-height (ebnf-node-height prod)
+           bef-width  (ebnf-max-width prod))
+      (ebnf-begin-line prod bef-width)
+      (ebnf-generate-production prod)
+      (while (and (setq ebnf-tree (cdr ebnf-tree))
+                 (setq prod      (car ebnf-tree))
+                 (memq (ebnf-node-action prod) ebnf-action-list)
+                 (setq cur-width (ebnf-max-width prod))
+                 (<= cur-width ps-width-remaining)
+                 (<= (ebnf-node-height prod) ps-height-remaining))
+       (ebnf-eop-horizontal bef-width)
+       (ebnf-generate-production prod)
+       (setq bef-width          cur-width
+             max-height         (max max-height (ebnf-node-height prod))
+             ps-width-remaining (- ps-width-remaining
+                                   (+ cur-width
+                                      ebnf-production-horizontal-space))))
+      (ebnf-eop-vertical max-height)
+      ;; prepare next line
+      (ebnf-newline max-height))))
+
+
+(defun ebnf-begin-line (prod width)
+  (and (or (eq (ebnf-node-action prod) 'form-feed)
+          (> (ebnf-node-height prod) ps-height-remaining))
+       (ebnf-new-page))
+  (setq ps-width-remaining (- ps-width-remaining
+                             (+ width
+                                ebnf-production-horizontal-space))))
+
+
+(defun ebnf-newline (height)
+  (and (> height ps-height-remaining)
+       (ebnf-new-page))
+  (setq ps-width-remaining  ps-print-width
+       ps-height-remaining (- ps-height-remaining
+                              (+ height
+                                 ebnf-production-vertical-space))))
+
+
+;; [production width-fun dim-fun entry height width name production action]
+(defun ebnf-generate-production (production)
+  (ebnf-message-info "Generating")
+  (run-hooks 'ebnf-production-hook)
+  (ps-output-string (ebnf-node-name production))
+  (ps-output " "
+            (ebnf-format-float
+             (ebnf-node-width production)
+             (+ ebnf-basic-height
+                (ebnf-node-entry (ebnf-node-production production))))
+            " BOP\n")
+  (ebnf-node-generation (ebnf-node-production production))
+  (ps-output "EOS\n"))
+
+
+;; [alternative width-fun dim-fun entry height width list]
+(defun ebnf-generate-alternative (alternative)
+  (let ((alt   (ebnf-node-list alternative))
+       (entry (ebnf-node-entry alternative))
+       (nlist 0)
+       alt-height alt-entry)
+    (while alt
+      (ps-output (ebnf-format-float (- entry (ebnf-node-entry (car alt))))
+                " ")
+      (setq entry (- entry (ebnf-node-height (car alt)) ebnf-vertical-space)
+           nlist (1+ nlist)
+           alt   (cdr alt)))
+    (ps-output (format "%d " nlist)
+              (ebnf-format-float (ebnf-node-width alternative))
+              " AT\n")
+    (setq alt (ebnf-node-list alternative))
+    (when alt
+      (ebnf-node-generation (car alt))
+      (setq alt-height (- (ebnf-node-height (car alt))
+                         (ebnf-node-entry (car alt)))))
+    (while (setq alt (cdr alt))
+      (setq alt-entry (ebnf-node-entry (car alt)))
+      (ebnf-vertical-movement
+       (- (+ alt-height ebnf-vertical-space alt-entry)))
+      (ebnf-node-generation (car alt))
+      (setq alt-height (- (ebnf-node-height (car alt)) alt-entry))))
+  (ps-output "EOS\n"))
+
+
+;; [sequence width-fun dim-fun entry height width list]
+(defun ebnf-generate-sequence (sequence)
+  (ps-output "BOS\n")
+  (let ((seq (ebnf-node-list sequence))
+       seq-width)
+    (when seq
+      (ebnf-node-generation (car seq))
+      (setq seq-width (ebnf-node-width (car seq))))
+    (while (setq seq (cdr seq))
+      (ebnf-horizontal-movement seq-width)
+      (ebnf-node-generation (car seq))
+      (setq seq-width (ebnf-node-width (car seq)))))
+  (ps-output "EOS\n"))
+
+
+;; [terminal width-fun dim-fun entry height width name]
+(defun ebnf-generate-terminal (terminal)
+  (ebnf-gen-terminal terminal "T"))
+
+
+;; [non-terminal width-fun dim-fun entry height width name]
+(defun ebnf-generate-non-terminal (non-terminal)
+  (ebnf-gen-terminal non-terminal "NT"))
+
+
+;; [empty width-fun dim-fun entry height width]
+(defun ebnf-generate-empty (empty)
+  (ebnf-empty-alternative (ebnf-node-width empty)))
+
+
+;; [optional width-fun dim-fun entry height width element]
+(defun ebnf-generate-optional (optional)
+  (let ((the-optional (ebnf-node-list optional)))
+    (ps-output (ebnf-format-float
+               (+ (- (ebnf-node-height the-optional)
+                     (ebnf-node-entry optional))
+                  ebnf-vertical-space)
+               (ebnf-node-width optional))
+              " OP\n")
+    (ebnf-node-generation the-optional)
+    (ps-output "EOS\n")))
+
+
+;; [one-or-more width-fun dim-fun entry height width element separator]
+(defun ebnf-generate-one-or-more (one-or-more)
+  (let* ((width (ebnf-node-width one-or-more))
+        (sep   (ebnf-node-separator one-or-more))
+        (entry (- (ebnf-node-entry one-or-more)
+                  (if sep
+                      (ebnf-node-entry sep)
+                    0))))
+    (ps-output (ebnf-format-float entry width)
+              " OM\n")
+    (ebnf-node-generation (ebnf-node-list one-or-more))
+    (ebnf-vertical-movement entry)
+    (if sep
+       (let ((ebnf-direction "L"))
+         (ebnf-node-generation sep))
+      (ebnf-empty-alternative (- width ebnf-horizontal-space))))
+  (ps-output "EOS\n"))
+
+
+;; [zero-or-more width-fun dim-fun entry height width element separator]
+(defun ebnf-generate-zero-or-more (zero-or-more)
+  (let* ((width      (ebnf-node-width zero-or-more))
+        (node-list  (ebnf-node-list zero-or-more))
+        (list-entry (ebnf-node-entry node-list))
+        (node-sep   (ebnf-node-separator zero-or-more))
+        (entry      (+ list-entry
+                       ebnf-vertical-space
+                       (if node-sep
+                           (- (ebnf-node-height node-sep)
+                              (ebnf-node-entry node-sep))
+                         0))))
+    (ps-output (ebnf-format-float entry
+                                 (+ (- (ebnf-node-height node-list)
+                                       list-entry)
+                                    ebnf-vertical-space)
+                                 width)
+              " ZM\n")
+    (ebnf-node-generation (ebnf-node-list zero-or-more))
+    (ebnf-vertical-movement entry)
+    (if (ebnf-node-separator zero-or-more)
+       (let ((ebnf-direction "L"))
+         (ebnf-node-generation (ebnf-node-separator zero-or-more)))
+      (ebnf-empty-alternative (- width ebnf-horizontal-space))))
+  (ps-output "EOS\n"))
+
+
+;; [special width-fun dim-fun entry height width name]
+(defun ebnf-generate-special (special)
+  (ebnf-gen-terminal special "SP"))
+
+
+;; [repeat width-fun dim-fun entry height width times element]
+(defun ebnf-generate-repeat (repeat)
+  (let ((times   (ebnf-node-name repeat))
+       (element (ebnf-node-separator repeat)))
+    (ps-output-string times)
+    (ps-output " "
+              (ebnf-format-float
+               (ebnf-node-entry repeat)
+               (ebnf-node-height repeat)
+               (ebnf-node-width repeat)
+               (if element
+                   (+ (ebnf-node-width element)
+                      ebnf-space-R ebnf-space-R ebnf-space-R
+                      (* (length times) ebnf-font-width-R))
+                 0.0))
+              " " ebnf-direction "RP\n")
+    (and element
+        (ebnf-node-generation element)))
+  (ps-output "EOS\n"))
+
+
+;; [except width-fun dim-fun entry height width element element]
+(defun ebnf-generate-except (except)
+  (let* ((element   (ebnf-node-list except))
+        (exception (ebnf-node-separator except))
+        (width     (ebnf-node-width element)))
+    (ps-output (ebnf-format-float
+               width
+               (ebnf-node-entry except)
+               (ebnf-node-height except)
+               (ebnf-node-width except)
+               (+ width
+                  ebnf-space-E ebnf-space-E ebnf-space-E
+                  ebnf-font-width-E
+                  (if exception
+                      (+ (ebnf-node-width exception) ebnf-space-E)
+                    0.0)))
+              " " ebnf-direction "EX\n")
+    (ebnf-node-generation (ebnf-node-list except))
+    (when exception
+      (ebnf-horizontal-movement (+ width ebnf-space-E
+                                  ebnf-font-width-E ebnf-space-E))
+      (ebnf-node-generation exception)))
+  (ps-output "EOS\n"))
+
+
+(defun ebnf-gen-terminal (node code)
+  (ps-output-string (ebnf-node-name node))
+  (ps-output " " (ebnf-format-float (ebnf-node-width node))
+            " " ebnf-direction code
+            (if (ebnf-node-default node)
+                "D\n"
+              "\n")))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Internal functions
+
+
+(defvar ebnf-map-name
+  (let ((map (make-vector 256 ?\_)))
+    (mapcar #'(lambda (char)
+               (aset map char char))
+           (concat "#$%&+-.0123456789=?@~"
+                   "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+                   "abcdefghijklmnopqrstuvwxyz"))
+    map))
+
+
+(defun ebnf-eps-filename (str)
+  (let* ((len  (length str))
+        (stri 0)
+        (new  (make-string len ?\ )))
+    (while (< stri len)
+      (aset new stri (aref ebnf-map-name (aref str stri)))
+      (setq stri (1+ stri)))
+    (concat ebnf-eps-prefix new ".eps")))
+
+
+(defun ebnf-eps-output (&rest args)
+  (while args
+    (insert (car args))
+    (setq args (cdr args))))
+
+
+(defun ebnf-generate-region (from to gen-func)
+  (run-hooks 'ebnf-hook)
+  (let ((ebnf-limit (max from to))
+       the-point)
+    (save-excursion
+      (save-restriction
+       (save-match-data
+         (condition-case data
+             (let ((tree (ebnf-parse-and-sort (min from to))))
+               (when gen-func
+                 (funcall gen-func
+                          (ebnf-dimensions
+                           (ebnf-optimize
+                            (ebnf-eliminate-empty-rules tree))))))
+           ;; handler
+           ((quit error)
+            (ding)
+            (setq the-point (max (1- (point)) (point-min)))
+            (message (error-message-string data)))))))
+    (cond
+     (the-point
+      (goto-char the-point))
+     (gen-func
+      nil)
+     (t
+      (message "EBNF syntatic analysis: NO ERRORS.")))))
+
+
+(defun ebnf-parse-and-sort (start)
+  (ebnf-begin-job)
+  (let ((tree (funcall ebnf-parser-func start)))
+    (if ebnf-sort-production
+       (progn
+         (message "Sorting...")
+         (sort tree
+               (if (eq ebnf-sort-production 'ascending)
+                   'ebnf-sorter-ascending
+                 'ebnf-sorter-descending)))
+      (nreverse tree))))
+
+
+(defun ebnf-sorter-ascending (first second)
+  (string< (ebnf-node-name first)
+          (ebnf-node-name second)))
+
+
+(defun ebnf-sorter-descending (first second)
+  (string< (ebnf-node-name second)
+          (ebnf-node-name first)))
+
+
+(defun ebnf-empty-alternative (width)
+  (ps-output (ebnf-format-float width) " EA\n"))
+
+
+(defun ebnf-vertical-movement (height)
+  (ps-output (ebnf-format-float height) " vm\n"))
+
+
+(defun ebnf-horizontal-movement (width)
+  (ps-output (ebnf-format-float width) " hm\n"))
+
+
+(defun ebnf-entry (height)
+  (* height ebnf-entry-percentage))
+
+
+(defun ebnf-eop-vertical (height)
+  (ps-output (ebnf-format-float (+ height ebnf-production-vertical-space))
+            " EOPV\n\n"))
+
+
+(defun ebnf-eop-horizontal (width)
+  (ps-output (ebnf-format-float (+ width ebnf-production-horizontal-space))
+            " EOPH\n\n"))
+
+
+(defun ebnf-new-page ()
+  (when (< ps-height-remaining ps-print-height)
+    (run-hooks 'ebnf-page-hook)
+    (ps-next-page)
+    (ps-output "\n")))
+
+
+(defsubst ebnf-font-size (font) (nth 0 font))
+(defsubst ebnf-font-name (font) (nth 1 font))
+(defsubst ebnf-font-foreground (font) (nth 2 font))
+(defsubst ebnf-font-background (font) (nth 3 font))
+(defsubst ebnf-font-list (font) (nthcdr 4 font))
+(defsubst ebnf-font-attributes (font)
+  (lsh (ps-extension-bit (cdr font)) -2))
+
+
+(defconst ebnf-font-name-select
+  (vector 'normal 'bold 'italic 'bold-italic))
+
+
+(defun ebnf-font-name-select (font)
+  (let* ((font-list  (ebnf-font-list font))
+        (font-index (+ (if (memq 'bold   font-list) 1 0)
+                       (if (memq 'italic font-list) 2 0)))
+        (name       (ebnf-font-name font))
+        (database   (cdr (assoc name ps-font-info-database)))
+        (info-list  (or (cdr (assoc 'fonts database))
+                        (error "Invalid font: %s" name))))
+    (or (cdr (assoc (aref ebnf-font-name-select font-index)
+                   info-list))
+       (error "Invalid attributes for font %s" name))))
+
+
+(defun ebnf-font-select (font select)
+  (let* ((name     (ebnf-font-name font))
+        (database (cdr (assoc name ps-font-info-database)))
+        (size     (cdr (assoc 'size database)))
+        (base     (cdr (assoc select database))))
+    (if (and size base)
+       (/ (* (ebnf-font-size font) base)
+          size)
+      (error "Invalid font: %s" name))))
+
+
+(defsubst ebnf-font-width (font)
+  (ebnf-font-select font 'avg-char-width))
+(defsubst ebnf-font-height (font)
+  (ebnf-font-select font 'line-height))
+
+
+(defun ebnf-begin-job ()
+  (ps-printing-region nil)
+  (if ebnf-use-float-format
+      (setq ebnf-format-float  "%1.3f"
+           ebnf-message-float "%3.2f")
+    (setq ebnf-format-float  "%s"
+         ebnf-message-float "%s"))
+  (ebnf-otz-initialize)
+  ;; to avoid compilation gripes when calling autoloaded functions
+  (funcall (cond ((eq ebnf-syntax 'iso-ebnf)
+                 (setq ebnf-parser-func 'ebnf-iso-parser)
+                 'ebnf-iso-initialize)
+                ((eq ebnf-syntax 'yacc)
+                 (setq ebnf-parser-func 'ebnf-yac-parser)
+                 'ebnf-yac-initialize)
+                (t
+                 (setq ebnf-parser-func 'ebnf-bnf-parser)
+                 'ebnf-bnf-initialize)))
+  (and ebnf-terminal-regexp            ; ensures that it's a string or nil
+       (not (stringp ebnf-terminal-regexp))
+       (setq ebnf-terminal-regexp nil))
+  (or (and ebnf-eps-prefix             ; ensures that it's a string
+          (stringp ebnf-eps-prefix))
+      (setq ebnf-eps-prefix "ebnf--"))
+  (setq ebnf-entry-percentage          ; ensures value between 0.0 and 1.0
+       (min (max ebnf-entry-percentage 0.0) 1.0)
+       ebnf-action-list         (if ebnf-horizontal-orientation
+                                    '(nil keep-line)
+                                  '(keep-line))
+       ebnf-settings            nil
+       ebnf-fonts-required      nil
+       ebnf-action              nil
+       ebnf-default-p           nil
+       ebnf-eps-context         nil
+       ebnf-eps-production-list nil
+       ebnf-eps-upper-x         0.0
+       ebnf-eps-upper-y         0.0
+       ebnf-font-height-P       (ebnf-font-height ebnf-production-font)
+       ebnf-font-height-T       (ebnf-font-height ebnf-terminal-font)
+       ebnf-font-height-NT      (ebnf-font-height ebnf-non-terminal-font)
+       ebnf-font-height-S       (ebnf-font-height ebnf-special-font)
+       ebnf-font-height-E       (ebnf-font-height ebnf-except-font)
+       ebnf-font-height-R       (ebnf-font-height ebnf-repeat-font)
+       ebnf-font-width-P        (ebnf-font-width ebnf-production-font)
+       ebnf-font-width-T        (ebnf-font-width ebnf-terminal-font)
+       ebnf-font-width-NT       (ebnf-font-width ebnf-non-terminal-font)
+       ebnf-font-width-S        (ebnf-font-width ebnf-special-font)
+       ebnf-font-width-E        (ebnf-font-width ebnf-except-font)
+       ebnf-font-width-R        (ebnf-font-width ebnf-repeat-font)
+       ebnf-space-T             (* ebnf-font-height-T 0.5)
+       ebnf-space-NT            (* ebnf-font-height-NT 0.5)
+       ebnf-space-S             (* ebnf-font-height-S 0.5)
+       ebnf-space-E             (* ebnf-font-height-E 0.5)
+       ebnf-space-R             (* ebnf-font-height-R 0.5))
+  (let ((basic (+ ebnf-font-height-T ebnf-font-height-NT)))
+    (setq ebnf-basic-width      (* basic 0.5)
+         ebnf-horizontal-space (+ basic basic)
+         ebnf-basic-height     ebnf-basic-width
+         ebnf-vertical-space   ebnf-basic-width)
+    ;; ensures value is greater than zero
+    (or (and (numberp ebnf-production-horizontal-space)
+            (> ebnf-production-horizontal-space 0.0))
+       (setq ebnf-production-horizontal-space basic))
+    ;; ensures value is greater than zero
+    (or (and (numberp ebnf-production-vertical-space)
+            (> ebnf-production-vertical-space 0.0))
+       (setq ebnf-production-vertical-space basic))))
+
+
+(defsubst ebnf-shape-value (sym alist)
+  (or (cdr (assq sym alist)) 0))
+
+
+(defsubst ebnf-boolean (value)
+  (if value "true" "false"))
+
+
+(defun ebnf-begin-file ()
+  (ps-flush-output)
+  (save-excursion
+    (set-buffer ps-spool-buffer)
+    (goto-char (point-min))
+    (and (search-forward "%%Creator: " nil t)
+        (not (search-forward "& ebnf2ps v"
+                             (save-excursion (end-of-line) (point))
+                             t))
+        (progn
+          ;; adjust creator comment
+          (end-of-line)
+          (backward-char)
+          (insert " & ebnf2ps v" ebnf-version)
+          ;; insert ebnf settings & engine
+          (goto-char (point-max))
+          (search-backward "\n%%EndPrologue\n")
+          (ebnf-insert-ebnf-prologue)
+          (ps-output "\n")))))
+
+
+(defun ebnf-eps-finish-and-write (buffer filename)
+  (save-excursion
+    (set-buffer buffer)
+    (setq ebnf-eps-upper-x (max ebnf-eps-upper-x ebnf-eps-max-width)
+         ebnf-eps-upper-y (if (zerop ebnf-eps-upper-y)
+                              ebnf-eps-max-height
+                            (+ ebnf-eps-upper-y
+                               ebnf-production-vertical-space
+                               ebnf-eps-max-height)))
+    ;; prologue
+    (goto-char (point-min))
+    (insert
+     "%!PS-Adobe-3.0 EPSF-3.0"
+     "\n%%BoundingBox: 0 0 "
+     (format "%d %d" (1+ ebnf-eps-upper-x) (1+ ebnf-eps-upper-y))
+     "\n%%Title: " filename
+     "\n%%CreationDate: " (time-stamp-hh:mm:ss) " " (time-stamp-mon-dd-yyyy)
+     "\n%%Creator: " (user-full-name) " (using ebnf2ps v" ebnf-version ")"
+     "\n%%DocumentNeededResources: font "
+     (or ebnf-fonts-required
+        (setq ebnf-fonts-required
+              (let ((fonts (ps-remove-duplicates
+                            (mapcar 'ebnf-font-name-select
+                                    (list ebnf-production-font
+                                          ebnf-terminal-font
+                                          ebnf-non-terminal-font
+                                          ebnf-special-font
+                                          ebnf-except-font
+                                          ebnf-repeat-font)))))
+                (concat (car fonts)
+                        (and (cdr fonts) "\n%%+ font ")
+                        (mapconcat 'identity (cdr fonts) "\n%%+ font ")))))
+     "\n%%Pages: 0\n%%EndComments\n\n%%BeginPrologue\n"
+     ebnf-eps-prologue)
+    (ebnf-insert-ebnf-prologue)
+    (insert ebnf-eps-begin
+           "\n0 " (ebnf-format-float
+                   (- ebnf-eps-upper-y (* ebnf-font-height-P 0.7)))
+           " #ebnf2ps#begin\n")
+    ;; epilogue
+    (goto-char (point-max))
+    (insert ebnf-eps-end)
+    ;; write file
+    (message "Saving...")
+    (setq filename (expand-file-name filename))
+    (let ((coding-system-for-write 'raw-text-unix))
+      (write-region (point-min) (point-max) filename))
+    (message "Wrote %s" filename)))
+
+
+(defun ebnf-insert-ebnf-prologue ()
+  (insert
+   (or ebnf-settings
+       (setq ebnf-settings
+            (concat
+             "\n\n% === begin EBNF settings\n\n"
+             ;; production
+             (format "/fP            %s /%s DefFont\n"
+                     (ebnf-format-float (ebnf-font-size ebnf-production-font))
+                     (ebnf-font-name-select ebnf-production-font))
+             (ebnf-format-color "/ForegroundP   %s def %% %s\n"
+                                (ebnf-font-foreground ebnf-production-font)
+                                "Black")
+             (ebnf-format-color "/BackgroundP   %s def %% %s\n"
+                                (ebnf-font-background ebnf-production-font)
+                                "White")
+             (format "/EffectP       %d def\n"
+                     (ebnf-font-attributes ebnf-production-font))
+             ;; terminal
+             (format "/fT            %s /%s DefFont\n"
+                     (ebnf-format-float (ebnf-font-size ebnf-terminal-font))
+                     (ebnf-font-name-select ebnf-terminal-font))
+             (ebnf-format-color "/ForegroundT   %s def %% %s\n"
+                                (ebnf-font-foreground ebnf-terminal-font)
+                                "Black")
+             (ebnf-format-color "/BackgroundT   %s def %% %s\n"
+                                (ebnf-font-background ebnf-terminal-font)
+                                "White")
+             (format "/EffectT       %d def\n"
+                     (ebnf-font-attributes ebnf-terminal-font))
+             (format "/BorderWidthT  %s def\n"
+                     (ebnf-format-float ebnf-terminal-border-width))
+             (ebnf-format-color "/BorderColorT  %s def %% %s\n"
+                                ebnf-terminal-border-color
+                                "Black")
+             (format "/ShapeT        %d def\n"
+                     (ebnf-shape-value ebnf-terminal-shape
+                                       ebnf-terminal-shape-alist))
+             (format "/ShadowT       %s def\n"
+                     (ebnf-boolean ebnf-terminal-shadow))
+             ;; non-terminal
+             (format "/fNT           %s /%s DefFont\n"
+                     (ebnf-format-float
+                      (ebnf-font-size ebnf-non-terminal-font))
+                     (ebnf-font-name-select ebnf-non-terminal-font))
+             (ebnf-format-color "/ForegroundNT  %s def %% %s\n"
+                                (ebnf-font-foreground ebnf-non-terminal-font)
+                                "Black")
+             (ebnf-format-color "/BackgroundNT  %s def %% %s\n"
+                                (ebnf-font-background ebnf-non-terminal-font)
+                                "White")
+             (format "/EffectNT      %d def\n"
+                     (ebnf-font-attributes ebnf-non-terminal-font))
+             (format "/BorderWidthNT %s def\n"
+                     (ebnf-format-float ebnf-non-terminal-border-width))
+             (ebnf-format-color "/BorderColorNT %s def %% %s\n"
+                                ebnf-non-terminal-border-color
+                                "Black")
+             (format "/ShapeNT       %d def\n"
+                     (ebnf-shape-value ebnf-non-terminal-shape
+                                       ebnf-terminal-shape-alist))
+             (format "/ShadowNT      %s def\n"
+                     (ebnf-boolean ebnf-non-terminal-shadow))
+             ;; special
+             (format "/fS            %s /%s DefFont\n"
+                     (ebnf-format-float (ebnf-font-size ebnf-special-font))
+                     (ebnf-font-name-select ebnf-special-font))
+             (ebnf-format-color "/ForegroundS   %s def %% %s\n"
+                                (ebnf-font-foreground ebnf-special-font)
+                                "Black")
+             (ebnf-format-color "/BackgroundS   %s def %% %s\n"
+                                (ebnf-font-background ebnf-special-font)
+                                "Gray95")
+             (format "/EffectS       %d def\n"
+                     (ebnf-font-attributes ebnf-special-font))
+             (format "/BorderWidthS  %s def\n"
+                     (ebnf-format-float ebnf-special-border-width))
+             (ebnf-format-color "/BorderColorS  %s def %% %s\n"
+                                ebnf-special-border-color
+                                "Black")
+             (format "/ShapeS        %d def\n"
+                     (ebnf-shape-value ebnf-special-shape
+                                       ebnf-terminal-shape-alist))
+             (format "/ShadowS       %s def\n"
+                     (ebnf-boolean ebnf-special-shadow))
+             ;; except
+             (format "/fE            %s /%s DefFont\n"
+                     (ebnf-format-float (ebnf-font-size ebnf-except-font))
+                     (ebnf-font-name-select ebnf-except-font))
+             (ebnf-format-color "/ForegroundE   %s def %% %s\n"
+                                (ebnf-font-foreground ebnf-except-font)
+                                "Black")
+             (ebnf-format-color "/BackgroundE   %s def %% %s\n"
+                                (ebnf-font-background ebnf-except-font)
+                                "Gray90")
+             (format "/EffectE       %d def\n"
+                     (ebnf-font-attributes ebnf-except-font))
+             (format "/BorderWidthE  %s def\n"
+                     (ebnf-format-float ebnf-except-border-width))
+             (ebnf-format-color "/BorderColorE  %s def %% %s\n"
+                                ebnf-except-border-color
+                                "Black")
+             (format "/ShapeE        %d def\n"
+                     (ebnf-shape-value ebnf-except-shape
+                                       ebnf-terminal-shape-alist))
+             (format "/ShadowE       %s def\n"
+                     (ebnf-boolean ebnf-except-shadow))
+             ;; repeat
+             (format "/fR            %s /%s DefFont\n"
+                     (ebnf-format-float (ebnf-font-size ebnf-repeat-font))
+                     (ebnf-font-name-select ebnf-repeat-font))
+             (ebnf-format-color "/ForegroundR   %s def %% %s\n"
+                                (ebnf-font-foreground ebnf-repeat-font)
+                                "Black")
+             (ebnf-format-color "/BackgroundR   %s def %% %s\n"
+                                (ebnf-font-background ebnf-repeat-font)
+                                "Gray85")
+             (format "/EffectR       %d def\n"
+                     (ebnf-font-attributes ebnf-repeat-font))
+             (format "/BorderWidthR  %s def\n"
+                     (ebnf-format-float ebnf-repeat-border-width))
+             (ebnf-format-color "/BorderColorR  %s def %% %s\n"
+                                ebnf-repeat-border-color
+                                "Black")
+             (format "/ShapeR        %d def\n"
+                     (ebnf-shape-value ebnf-repeat-shape
+                                       ebnf-terminal-shape-alist))
+             (format "/ShadowR       %s def\n"
+                     (ebnf-boolean ebnf-repeat-shadow))
+             ;; miscellaneous
+             (format "/DefaultWidth  %s def\n"
+                     (ebnf-format-float ebnf-default-width))
+             (format "/LineWidth     %s def\n"
+                     (ebnf-format-float ebnf-line-width))
+             (ebnf-format-color "/LineColor     %s def %% %s\n"
+                                ebnf-line-color
+                                "Black")
+             (format "/ArrowShape    %d def\n"
+                     (ebnf-shape-value ebnf-arrow-shape
+                                       ebnf-arrow-shape-alist))
+             (format "/ChartShape    %d def\n"
+                     (ebnf-shape-value ebnf-chart-shape
+                                       ebnf-terminal-shape-alist))
+             (format "/UserArrow{%s}def\n"
+                     (ebnf-user-arrow ebnf-user-arrow))
+             "\n% === end   EBNF settings\n\n"
+             (and ebnf-debug-ps ebnf-debug))))
+   ebnf-prologue))
+
+
+(defun ebnf-user-arrow (user-arrow)
+  "Return a user arrow shape from USER-ARROW (a PostScript code).
+
+This function is only called when `ebnf-arrow-shape' is set to symbol `user'.
+
+If is a string, should be a PostScript procedure body.
+If is a variable symbol, should contain a string.
+If is a function symbol, it is called and the result is applied recursively.
+If is a cons and car is a function symbol, it is called as:
+   (funcall (car cons) (cdr cons))
+and the result is applied recursively.
+If is a cons and car is not a function symbol, it is applied recursively on
+car and cdr, and the results are concatened as:
+   (concat RESULT-FROM-CAR \" \" RESULT-FROM-CDR)
+If is a list and car is a function symbol, it is called as:
+   (apply (car list) (cdr list))
+and the result is applied recursively.
+If is a list and car is not a function symbol, it is applied recursively on
+each element and the resulting list is concatened as:
+   (mapconcat 'identity RESULTING-LIST \" \")
+Otherwise, it is treated as an empty string."
+  (cond
+   ((null user-arrow)
+    "")
+   ((stringp user-arrow)
+    user-arrow)
+   ((and (symbolp user-arrow) (fboundp user-arrow))
+    (ebnf-user-arrow (funcall user-arrow)))
+   ((and (symbolp user-arrow) (boundp user-arrow))
+    (ebnf-user-arrow (symbol-value user-arrow)))
+   ((consp user-arrow)
+    (if (and (symbolp (car user-arrow)) (fboundp (car user-arrow)))
+       (ebnf-user-arrow (funcall (car user-arrow) (cdr user-arrow)))
+      (concat (ebnf-user-arrow (car user-arrow))
+             " "
+             (ebnf-user-arrow (cdr user-arrow)))))
+   ((listp user-arrow)
+    (if (and (symbolp (car user-arrow))
+            (fboundp (car user-arrow)))
+       (ebnf-user-arrow (apply (car user-arrow) (cdr user-arrow)))
+      (mapconcat 'ebnf-user-arrow user-arrow " ")))
+   (t
+    "")
+   ))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Adjusting dimensions
+
+
+(defun ebnf-dimensions (tree)
+  (let ((ebnf-total (length tree))
+       (ebnf-nprod 0))
+    (mapcar 'ebnf-production-dimension tree))
+  tree)
+
+
+;; [empty width-fun dim-fun entry height width]
+;;(defun ebnf-empty-dimension (empty)
+;;  )
+
+
+;; [production width-fun dim-fun entry height width name production action]
+(defun ebnf-production-dimension (production)
+  (ebnf-message-info "Calculating dimensions")
+  (ebnf-node-dimension-func (ebnf-node-production production))
+  (let* ((prod   (ebnf-node-production production))
+        (height (+ ebnf-font-height-P
+                   ebnf-basic-height
+                   (ebnf-node-height prod))))
+    (ebnf-node-entry  production height)
+    (ebnf-node-height production height)
+    (ebnf-node-width  production (+ (ebnf-node-width prod)
+                                   ebnf-horizontal-space))))
+
+
+;; [terminal width-fun dim-fun entry height width name]
+(defun ebnf-terminal-dimension (terminal)
+  (ebnf-terminal-dimension1 terminal
+                           ebnf-font-height-T
+                           ebnf-font-width-T
+                           ebnf-space-T))
+
+
+;; [non-terminal width-fun dim-fun entry height width name]
+(defun ebnf-non-terminal-dimension (non-terminal)
+  (ebnf-terminal-dimension1 non-terminal
+                           ebnf-font-height-NT
+                           ebnf-font-width-NT
+                           ebnf-space-NT))
+
+
+;; [special width-fun dim-fun entry height width name]
+(defun ebnf-special-dimension (special)
+  (ebnf-terminal-dimension1 special
+                           ebnf-font-height-S
+                           ebnf-font-width-S
+                           ebnf-space-S))
+
+
+(defun ebnf-terminal-dimension1 (node font-height font-width space)
+  (let ((height (+ space font-height space))
+       (len    (length (ebnf-node-name node))))
+    (ebnf-node-entry  node (* height 0.5))
+    (ebnf-node-height node height)
+    (ebnf-node-width  node (+ ebnf-basic-width space
+                             (* len font-width)
+                             space ebnf-basic-width))))
+
+
+(defconst ebnf-null-vector (vector t t t 0.0 0.0 0.0))
+
+
+;; [repeat width-fun dim-fun entry height width times element]
+(defun ebnf-repeat-dimension (repeat)
+  (let ((times   (ebnf-node-name repeat))
+       (element (ebnf-node-separator repeat)))
+    (if element
+       (ebnf-node-dimension-func element)
+      (setq element ebnf-null-vector))
+    (ebnf-node-entry  repeat (+ (ebnf-node-entry element)
+                               ebnf-space-R))
+    (ebnf-node-height repeat (+ (max (ebnf-node-height element)
+                                    ebnf-font-height-S)
+                               ebnf-space-R ebnf-space-R))
+    (ebnf-node-width  repeat (+ (ebnf-node-width element)
+                               ebnf-space-R ebnf-space-R ebnf-space-R
+                               ebnf-horizontal-space
+                               (* (length times) ebnf-font-width-R)))))
+
+
+;; [except width-fun dim-fun entry height width element element]
+(defun ebnf-except-dimension (except)
+  (let ((factor  (ebnf-node-list except))
+       (element (ebnf-node-separator except)))
+    (ebnf-node-dimension-func factor)
+    (if element
+       (ebnf-node-dimension-func element)
+      (setq element ebnf-null-vector))
+    (ebnf-node-entry  except (+ (max (ebnf-node-entry factor)
+                                    (ebnf-node-entry element))
+                               ebnf-space-E))
+    (ebnf-node-height except (+ (max (ebnf-node-height factor)
+                                    (ebnf-node-height element))
+                               ebnf-space-E ebnf-space-E))
+    (ebnf-node-width  except (+ (ebnf-node-width factor)
+                               (ebnf-node-width element)
+                               ebnf-space-E ebnf-space-E
+                               ebnf-space-E ebnf-space-E
+                               ebnf-font-width-E
+                               ebnf-horizontal-space))))
+
+
+;; [alternative width-fun dim-fun entry height width list]
+(defun ebnf-alternative-dimension (alternative)
+  (let ((body (ebnf-node-list alternative))
+       (lis  (ebnf-node-list alternative)))
+    (while lis
+      (ebnf-node-dimension-func (car lis))
+      (setq lis (cdr lis)))
+    (let ((height 0.0)
+         (width  0.0)
+         (alt    body)
+         (tail   (car (last body)))
+         (entry  (ebnf-node-entry (car body)))
+         node)
+      (while alt
+       (setq node   (car alt)
+             alt    (cdr alt)
+             height (+ (ebnf-node-height node) height)
+             width  (max (ebnf-node-width node) width)))
+      (ebnf-adjust-width body width)
+      (setq height (+ height (* (1- (length body)) ebnf-vertical-space)))
+      (ebnf-node-entry  alternative (+ entry
+                                      (ebnf-entry
+                                       (- height entry
+                                          (- (ebnf-node-height tail)
+                                             (ebnf-node-entry tail))))))
+      (ebnf-node-height alternative height)
+      (ebnf-node-width  alternative (+ width ebnf-horizontal-space))
+      (ebnf-node-list   alternative body))))
+
+
+;; [optional width-fun dim-fun entry height width element]
+(defun ebnf-optional-dimension (optional)
+  (let ((body (ebnf-node-list optional)))
+    (ebnf-node-dimension-func body)
+    (ebnf-node-entry  optional (ebnf-node-entry body))
+    (ebnf-node-height optional (+ (ebnf-node-height body)
+                                 ebnf-vertical-space))
+    (ebnf-node-width  optional (+ (ebnf-node-width body)
+                                 ebnf-horizontal-space))))
+
+
+;; [one-or-more width-fun dim-fun entry height width element separator]
+(defun ebnf-one-or-more-dimension (or-more)
+  (let ((list-part (ebnf-node-list or-more))
+       (sep-part  (ebnf-node-separator or-more)))
+    (ebnf-node-dimension-func list-part)
+    (and sep-part
+        (ebnf-node-dimension-func sep-part))
+    (let ((height (+ (if sep-part
+                        (ebnf-node-height sep-part)
+                      0.0)
+                    ebnf-vertical-space
+                    (ebnf-node-height list-part)))
+         (width  (max (if sep-part
+                          (ebnf-node-width sep-part)
+                        0.0)
+                      (ebnf-node-width list-part))))
+      (when sep-part
+       (ebnf-adjust-width list-part width)
+       (ebnf-adjust-width sep-part width))
+      (ebnf-node-entry  or-more (+ (- height (ebnf-node-height list-part))
+                                  (ebnf-node-entry list-part)))
+      (ebnf-node-height or-more height)
+      (ebnf-node-width  or-more (+ width ebnf-horizontal-space)))))
+
+
+;; [zero-or-more width-fun dim-fun entry height width element separator]
+(defun ebnf-zero-or-more-dimension (or-more)
+  (let ((list-part (ebnf-node-list or-more))
+       (sep-part  (ebnf-node-separator or-more)))
+    (ebnf-node-dimension-func list-part)
+    (and sep-part
+        (ebnf-node-dimension-func sep-part))
+    (let ((height (+ (if sep-part
+                        (ebnf-node-height sep-part)
+                      0.0)
+                    ebnf-vertical-space
+                    (ebnf-node-height list-part)
+                    ebnf-vertical-space))
+         (width  (max (if sep-part
+                          (ebnf-node-width sep-part)
+                        0.0)
+                      (ebnf-node-width list-part))))
+      (when sep-part
+       (ebnf-adjust-width list-part width)
+       (ebnf-adjust-width sep-part width))
+      (ebnf-node-entry  or-more height)
+      (ebnf-node-height or-more height)
+      (ebnf-node-width  or-more (+ width ebnf-horizontal-space)))))
+
+
+;; [sequence width-fun dim-fun entry height width list]
+(defun ebnf-sequence-dimension (sequence)
+  (let ((above 0.0)
+       (below 0.0)
+       (width 0.0)
+       (lis   (ebnf-node-list sequence))
+       entry node)
+    (while lis
+      (setq node (car lis)
+           lis  (cdr lis))
+      (ebnf-node-dimension-func node)
+      (setq entry (ebnf-node-entry node)
+           above (max above entry)
+           below (max below (- (ebnf-node-height node) entry))
+           width (+ width (ebnf-node-width node))))
+    (ebnf-node-entry  sequence above)
+    (ebnf-node-height sequence (+ above below))
+    (ebnf-node-width  sequence width)))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Adjusting width
+
+
+(defun ebnf-adjust-width (node width)
+  (cond
+   ((listp node)
+    (prog1
+       node
+      (while node
+       (setcar node (ebnf-adjust-width (car node) width))
+       (setq node (cdr node)))))
+   ((vectorp node)
+    (cond
+     ;; nothing to be done
+     ((= width (ebnf-node-width node))
+      node)
+     ;; left justify term
+     ((eq ebnf-justify-sequence 'left)
+      (ebnf-adjust-empty node width nil))
+     ;; right justify terms
+     ((eq ebnf-justify-sequence 'right)
+      (ebnf-adjust-empty node width t))
+     ;; centralize terms
+     (t
+      (ebnf-node-width-func node width)
+      (ebnf-node-width node width)
+      node)
+     ))
+   (t
+    node)
+   ))
+
+
+(defun ebnf-adjust-empty (node width last-p)
+  (if (eq (ebnf-node-kind node) 'ebnf-generate-empty)
+      (progn
+       (ebnf-node-width node width)
+       node)
+    (let ((empty (ebnf-make-empty (- width (ebnf-node-width node)))))
+      (ebnf-make-dup-sequence node
+                             (if last-p
+                                 (list empty node)
+                               (list node empty))))))
+
+
+;; [terminal     width-fun dim-fun entry height width name]
+;; [non-terminal width-fun dim-fun entry height width name]
+;; [empty        width-fun dim-fun entry height width]
+;; [special      width-fun dim-fun entry height width name]
+;; [repeat       width-fun dim-fun entry height width times element]
+;; [except       width-fun dim-fun entry height width element element]
+;;(defun ebnf-terminal-width (terminal width)
+;;  )
+
+
+;; [alternative width-fun dim-fun entry height width list]
+;; [optional    width-fun dim-fun entry height width element]
+(defun ebnf-alternative-width (alternative width)
+  (ebnf-adjust-width (ebnf-node-list alternative)
+                    (- width ebnf-horizontal-space)))
+
+
+;; [one-or-more  width-fun dim-fun entry height width element separator]
+;; [zero-or-more width-fun dim-fun entry height width element separator]
+(defun ebnf-list-width (or-more width)
+  (setq width (- width ebnf-horizontal-space))
+  (ebnf-node-list or-more
+                 (ebnf-justify-list or-more
+                                    (ebnf-node-list or-more)
+                                    width))
+  (ebnf-node-separator or-more
+                      (ebnf-justify-list or-more
+                                         (ebnf-node-separator or-more)
+                                         width)))
+
+
+;; [sequence width-fun dim-fun entry height width list]
+(defun ebnf-sequence-width (sequence width)
+  (ebnf-node-list sequence
+                 (ebnf-justify-list sequence (ebnf-node-list sequence) width)))
+
+
+(defun ebnf-justify-list (node seq width)
+  (let ((seq-width (ebnf-node-width node)))
+    (if (= width seq-width)
+       seq
+      (cond
+       ;; left justify terms
+       ((eq ebnf-justify-sequence 'left)
+       (ebnf-justify node seq seq-width width t))
+       ;; right justify terms
+       ((eq ebnf-justify-sequence 'right)
+       (ebnf-justify node seq seq-width width nil))
+       ;; centralize terms
+       (t
+       (let ((the-width (/ (- width seq-width) (length seq)))
+             (lis seq))
+         (while lis
+           (ebnf-adjust-width (car lis)
+                              (+ (ebnf-node-width (car lis))
+                                 the-width))
+           (setq lis (cdr lis)))
+         seq))
+       ))))
+
+
+(defun ebnf-justify (node seq seq-width width last-p)
+  (let ((term (car (if last-p (last seq) seq))))
+    (cond
+     ;; adjust empty term
+     ((eq (ebnf-node-kind term) 'ebnf-generate-empty)
+      (ebnf-node-width term (+ (- width seq-width)
+                              (ebnf-node-width term)))
+      seq)
+     ;; insert empty at end ==> left justify
+     (last-p
+      (nconc seq
+            (list (ebnf-make-empty (- width seq-width)))))
+     ;; insert empty at beginning ==> right justify
+     (t
+      (cons (ebnf-make-empty (- width seq-width))
+           seq))
+     )))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Functions used by parsers
+
+
+(defun ebnf-eps-add-context (name)
+  (let ((filename (ebnf-eps-filename name)))
+    (if (member filename ebnf-eps-context)
+       (error "Try to open an already opened EPS file: %s" filename)
+      (setq ebnf-eps-context (cons filename ebnf-eps-context)))))
+
+
+(defun ebnf-eps-remove-context (name)
+  (let ((filename (ebnf-eps-filename name)))
+    (if (member filename ebnf-eps-context)
+       (setq ebnf-eps-context (delete filename ebnf-eps-context))
+      (error "Try to close a not opened EPS file: %s" filename))))
+
+
+(defun ebnf-eps-add-production (header)
+  (and ebnf-eps-executing
+       ebnf-eps-context
+       (let ((prod (assoc header ebnf-eps-production-list)))
+        (if prod
+            (setcdr prod (append ebnf-eps-context (cdr prod)))
+          (setq ebnf-eps-production-list
+                (cons (cons header (ebnf-dup-list ebnf-eps-context))
+                      ebnf-eps-production-list))))))
+
+
+(defun ebnf-dup-list (old)
+  (let (new)
+    (while old
+      (setq new (cons (car old) new)
+           old (cdr old)))
+    (nreverse new)))
+
+
+(defun ebnf-buffer-substring (chars)
+  (buffer-substring-no-properties
+   (point)
+   (progn
+     (skip-chars-forward chars ebnf-limit)
+     (point))))
+
+
+(defun ebnf-string (chars eos-char kind)
+  (forward-char)
+  (buffer-substring-no-properties
+   (point)
+   (progn
+     (skip-chars-forward (concat chars "\240-\377") ebnf-limit)
+     (if (or (eobp) (/= (following-char) eos-char))
+        (error "Illegal %s: missing `%c'." kind eos-char)
+       (forward-char)
+       (1- (point))))))
+
+
+(defun ebnf-get-string ()
+  (forward-char)
+  (buffer-substring-no-properties (point) (ebnf-end-of-string)))
+
+
+(defun ebnf-end-of-string ()
+  (let ((n 1))
+    (while (> (logand n 1) 0)
+      (skip-chars-forward "^\"" ebnf-limit)
+      (setq n (- (skip-chars-backward "\\\\")))
+      (goto-char (+ (point) n 1))))
+  (if (= (preceding-char) ?\")
+      (1- (point))
+    (error "Missing `\"'.")))
+
+
+(defun ebnf-trim-right (str)
+  (let* ((len   (1- (length str)))
+        (index len))
+    (while (and (> index 0) (= (aref str index) ?\ ))
+      (setq index (1- index)))
+    (if (= index len)
+       str
+      (substring str 0 (1+ index)))))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Vector creation
+
+
+(defun ebnf-make-empty (&optional width)
+  (vector 'ebnf-generate-empty
+         'ignore
+         'ignore
+         0.0
+         0.0
+         (or width ebnf-horizontal-space)))
+
+
+(defun ebnf-make-terminal (name)
+  (ebnf-make-terminal1 name
+                      'ebnf-generate-terminal
+                      'ebnf-terminal-dimension))
+
+
+(defun ebnf-make-non-terminal (name)
+  (ebnf-make-terminal1 name
+                      'ebnf-generate-non-terminal
+                      'ebnf-non-terminal-dimension))
+
+
+(defun ebnf-make-special (name)
+  (ebnf-make-terminal1 name
+                      'ebnf-generate-special
+                      'ebnf-special-dimension))
+
+
+(defun ebnf-make-terminal1 (name gen-func dim-func)
+  (vector gen-func
+         'ignore
+         dim-func
+         0.0
+         0.0
+         0.0
+         (let ((len (length name)))
+           (cond ((> len 2) name)
+                 ((= len 2) (concat " " name))
+                 ((= len 1) (concat " " name " "))
+                 (t         "   ")))
+         ebnf-default-p))
+
+
+(defun ebnf-make-one-or-more (list-part &optional sep-part)
+  (ebnf-make-or-more1 'ebnf-generate-one-or-more
+                     'ebnf-one-or-more-dimension
+                     list-part
+                     sep-part))
+
+
+(defun ebnf-make-zero-or-more (list-part &optional sep-part)
+  (ebnf-make-or-more1 'ebnf-generate-zero-or-more
+                     'ebnf-zero-or-more-dimension
+                     list-part
+                     sep-part))
+
+
+(defun ebnf-make-or-more1 (gen-func dim-func list-part sep-part)
+  (vector gen-func
+         'ebnf-list-width
+         dim-func
+         0.0
+         0.0
+         0.0
+         (if (listp list-part)
+             (ebnf-make-sequence list-part)
+           list-part)
+         (if (and sep-part (listp sep-part))
+             (ebnf-make-sequence sep-part)
+           sep-part)))
+
+
+(defun ebnf-make-production (name prod action)
+  (vector 'ebnf-generate-production
+         'ignore
+         'ebnf-production-dimension
+         0.0
+         0.0
+         0.0
+         name
+         prod
+         action))
+
+
+(defun ebnf-make-alternative (body)
+  (vector 'ebnf-generate-alternative
+         'ebnf-alternative-width
+         'ebnf-alternative-dimension
+         0.0
+         0.0
+         0.0
+         body))
+
+
+(defun ebnf-make-optional (body)
+  (vector 'ebnf-generate-optional
+         'ebnf-alternative-width
+         'ebnf-optional-dimension
+         0.0
+         0.0
+         0.0
+         body))
+
+
+(defun ebnf-make-except (factor exception)
+  (vector 'ebnf-generate-except
+         'ignore
+         'ebnf-except-dimension
+         0.0
+         0.0
+         0.0
+         factor
+         exception))
+
+
+(defun ebnf-make-repeat (times primary)
+  (vector 'ebnf-generate-repeat
+         'ignore
+         'ebnf-repeat-dimension
+         0.0
+         0.0
+         0.0
+         (concat times " *")
+         primary))
+
+
+(defun ebnf-make-sequence (seq)
+  (vector 'ebnf-generate-sequence
+         'ebnf-sequence-width
+         'ebnf-sequence-dimension
+         0.0
+         0.0
+         0.0
+         seq))
+
+
+(defun ebnf-make-dup-sequence (node seq)
+  (vector 'ebnf-generate-sequence
+         'ebnf-sequence-width
+         'ebnf-sequence-dimension
+          (ebnf-node-entry node)
+          (ebnf-node-height node)
+          (ebnf-node-width node)
+          seq))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Optimizers used by parsers
+
+
+(defun ebnf-token-except (element exception)
+  (cons (prog1
+           (car exception)
+         (setq exception (cdr exception)))
+       (and element                    ; EMPTY - A ==> EMPTY
+            (let ((kind (ebnf-node-kind element)))
+              (cond
+               ;; [ A ]- ==> A
+               ((and (null exception)
+                     (eq kind 'ebnf-generate-optional))
+                (ebnf-node-list element))
+               ;; { A }- ==> { A }+
+               ((and (null exception)
+                     (eq kind 'ebnf-generate-zero-or-more))
+                (ebnf-node-kind element 'ebnf-generate-one-or-more)
+                (ebnf-node-dimension-func element 'ebnf-one-or-more-dimension)
+                element)
+               ;; ( A | EMPTY )-      ==> A
+               ;; ( A | B | EMPTY )-  ==> A | B
+               ((and (null exception)
+                     (eq kind 'ebnf-generate-alternative)
+                     (eq (ebnf-node-kind (car (last (ebnf-node-list element))))
+                         'ebnf-generate-empty))
+                (let ((elt (ebnf-node-list element))
+                      bef)
+                  (while (cdr elt)
+                    (setq bef elt
+                          elt (cdr elt)))
+                  (if (null bef)
+                      ;; this should not happen!!?!
+                      (setq element (ebnf-make-empty
+                                     (ebnf-node-width element)))
+                    (setcdr bef nil)
+                    (setq elt (ebnf-node-list element))
+                    (and (= (length elt) 1)
+                         (setq element (car elt))))
+                  element))
+               ;; A - B
+               (t
+                (ebnf-make-except element exception))
+               )))))
+
+
+(defun ebnf-token-repeat (times repeat)
+  (if (null (cdr repeat))
+      ;; n * EMPTY ==> EMPTY
+      repeat
+    ;; n * term
+    (cons (car repeat)
+         (ebnf-make-repeat times (cdr repeat)))))
+
+
+(defun ebnf-token-optional (body)
+  (let ((kind (ebnf-node-kind body)))
+    (cond
+     ;; [ EMPTY ] ==> EMPTY
+     ((eq kind 'ebnf-generate-empty)
+      nil)
+     ;; [ { A }* ] ==> { A }*
+     ((eq kind 'ebnf-generate-zero-or-more)
+      body)
+     ;; [ { A }+ ] ==> { A }*
+     ((eq kind 'ebnf-generate-one-or-more)
+      (ebnf-node-kind body 'ebnf-generate-zero-or-more)
+      body)
+     ;; [ A | B ] ==> A | B | EMPTY
+     ((eq kind 'ebnf-generate-alternative)
+      (ebnf-node-list body (nconc (ebnf-node-list body)
+                                 (list (ebnf-make-empty))))
+      body)
+     ;; [ A ]
+     (t
+      (ebnf-make-optional body))
+     )))
+
+
+(defun ebnf-token-alternative (body sequence)
+  (if (null body)
+      (if (cdr sequence)
+         sequence
+       (cons (car sequence)
+             (ebnf-make-empty)))
+    (cons (car sequence)
+         (let ((seq (cdr sequence)))
+           (if (and (= (length body) 1) (null seq))
+               (car body)
+             (ebnf-make-alternative (nreverse (if seq
+                                                  (cons seq body)
+                                                body))))))))
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Variables used by parsers
+
+
+(defconst ebnf-comment-table
+  (let ((table (make-vector 256 nil)))
+    ;; Override special comment character:
+    (aset table ?< 'newline)
+    (aset table ?> 'keep-line)
+    table)
+  "Vector used to map characters to a special comment token.")
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; To make this file smaller, some commands go in a separate file.
+;; But autoload them here to make the separation invisible.
+
+(autoload 'ebnf-bnf-parser            "ebnf-bnf"
+  "EBNF parser.")
+
+(autoload 'ebnf-bnf-initialize        "ebnf-bnf"
+  "Initialize EBNF token table.")
+
+(autoload 'ebnf-iso-parser            "ebnf-iso"
+  "ISO EBNF parser.")
+
+(autoload 'ebnf-iso-initialize        "ebnf-iso"
+  "Initialize ISO EBNF token table.")
+
+(autoload 'ebnf-yac-parser            "ebnf-yac"
+  "Yacc/Bison parser.")
+
+(autoload 'ebnf-yac-initialize        "ebnf-yac"
+  "Initializations for Yacc/Bison parser.")
+
+(autoload 'ebnf-eliminate-empty-rules "ebnf-otz"
+  "Eliminate empty rules.")
+
+(autoload 'ebnf-optimize              "ebnf-otz"
+  "Syntatic chart optimizer.")
+
+(autoload 'ebnf-otz-initialize        "ebnf-otz"
+  "Initialize optimizer.")
+
+\f
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+
+(provide 'ebnf2ps)
+
+
+;;; ebnf2ps.el ends here