From 755c7ccc7009b40a5d29de9e74cd480fcb61e436 Mon Sep 17 00:00:00 2001
From: Kubat <mael.martin31@gmail.com>
Date: Thu, 25 Nov 2021 13:01:24 +0100
Subject: [PATCH] Add antlr syntax

---
 README.org             |    1 +
 config.el              |    1 +
 packages/antlr-mode.el | 2663 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 2665 insertions(+)
 create mode 100644 packages/antlr-mode.el

diff --git a/README.org b/README.org
index b5cc3c4..6839a24 100644
--- a/README.org
+++ b/README.org
@@ -62,6 +62,7 @@ I prefer Fira Code, but ligatures are not handled correctly for now...
 ** Custom package imports
 #+begin_src emacs-lisp
 (load! "packages/porth-mode.el")
+(load! "packages/antlr-mode.el")
 #+end_src
 
 ** Plain text file configuration
diff --git a/config.el b/config.el
index 64b4ecb..7847da9 100644
--- a/config.el
+++ b/config.el
@@ -28,6 +28,7 @@
  '(org-level-5 ((t (:inherit outline-5 :height 1.0)))))
 
 (load! "packages/porth-mode.el")
+(load! "packages/antlr-mode.el")
 
 (set-file-template! "\\.tex$"      :trigger "__" :mode 'latex-mode)
 (set-file-template! "\\.org$"      :trigger "__" :mode 'org-mode)
diff --git a/packages/antlr-mode.el b/packages/antlr-mode.el
new file mode 100644
index 0000000..26ad8ca
--- /dev/null
+++ b/packages/antlr-mode.el
@@ -0,0 +1,2663 @@
+;;; antlr-mode.el --- major mode for ANTLR grammar files
+
+;; Copyright (C) 1999-2014 Free Software Foundation, Inc.
+
+;; Author: Christoph Wedler <Christoph.Wedler@sap.com>
+;; Keywords: languages, ANTLR, code generator
+;; Version: 2.2c
+;; X-URL: http://antlr-mode.sourceforge.net/
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs 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 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs 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.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; The Emacs package ANTLR-Mode provides: syntax highlighting for ANTLR grammar
+;; files, automatic indentation, menus containing rule/token definitions and
+;; supported options and various other things like running ANTLR from within
+;; Emacs.
+
+;; For details, check <http://antlr-mode.sourceforge.net/> or, if you prefer
+;; the manual style, follow all commands mentioned in the documentation of
+;; `antlr-mode'.  ANTLR is a LL(k)-based recognition tool which generates
+;; lexers, parsers and tree transformers in Java, C++ or Sather and can be
+;; found at <http://www.antlr.org/>.
+
+;; Bug fixes, bug reports, improvements, and suggestions for the newest version
+;; are strongly appreciated.
+
+;; To-do/Wish-list:
+;;
+;;  * Next Version [C-c C-w].  Produce HTML document with syntax highlighted
+;;    and hyper-links (using htmlize).
+;;  * Next Version [C-c C-u].  Insert/update special comments: each rule lists
+;;    all rules which use the current rule.  With font-lock update.
+;;  * Next Version.  Make hiding much more customizable.
+;;  * Planned [C-c C-j].  Jump to generated coding.
+;;  * Planned.  Further support for imenu, i.e., include entries for method
+;;    definitions at beginning of grammar class.
+;;  * Planned [C-c C-p].  Pack/unpack rule/subrule & options (one/multi-line).
+;;
+;;  * Probably.  Show rules/dependencies for ANT like for Makefile (does ANT
+;;    support vocabularies and grammar inheritance?), I have to look at
+;;    jde-ant.el: http://jakarta.apache.org/ant/manual/OptionalTasks/antlr.html
+;;  * Probably.  Make `indent-region' faster, especially in actions.  ELP
+;;    profiling in a class init action shows half the time is spent in
+;;    `antlr-next-rule', the other half in `c-guess-basic-syntax'.
+;;  * Unlikely.  Sather as generated language with syntax highlighting etc/.
+;;    Questions/problems: is sather-mode.el the standard mode for sather, is it
+;;    still supported, what is its relationship to eiffel3.el?  Requirement:
+;;    this mode must not depend on a Sather mode.
+;;  * Unlikely.  Faster syntax highlighting: sectionize the buffer into Antlr
+;;    and action code and run special highlighting functions on these regions.
+;;    Problems: code size, this mode would depend on font-lock internals.
+
+;;; Installation:
+
+;; This file requires Emacs-20.3, XEmacs-20.4 or higher and package cc-mode.
+
+;; If antlr-mode is not part of your distribution, put this file into your
+;; load-path and the following into your init file:
+;;   (autoload 'antlr-mode "antlr-mode" nil t)
+;;   (setq auto-mode-alist (cons '("\\.g\\'" . antlr-mode) auto-mode-alist))
+;;   (add-hook 'speedbar-load-hook  ; would be too late in antlr-mode.el
+;;             (lambda () (speedbar-add-supported-extension ".g")))
+
+;; I strongly recommend to use font-lock with a support mode like fast-lock,
+;; lazy-lock or better jit-lock (Emacs-21.1+) / lazy-shot (XEmacs).
+
+;; To customize, use menu item "Antlr" -> "Customize Antlr".
+
+;;; Code:
+
+(eval-when-compile
+  (require 'cl))
+
+(require 'easymenu)
+(require 'cc-mode)
+
+;; Just to get the rid of the byte compiler warning.  The code for
+;; this function and its friends are too complex for their own good.
+(declare-function cond-emacs-xemacs-macfn "antlr-mode" (args &optional msg))
+
+;; General Emacs/XEmacs-compatibility compile-time macros
+(eval-when-compile
+  (defmacro cond-emacs-xemacs (&rest args)
+    (cond-emacs-xemacs-macfn
+     args "`cond-emacs-xemacs' must return exactly one element"))
+  (defun cond-emacs-xemacs-macfn (args &optional msg)
+    (if (atom args) args
+      (and (eq (car args) :@) (null msg) ; (:@ ...spliced...)
+           (setq args (cdr args)
+                 msg "(:@ ....) must return exactly one element"))
+      (let ((ignore (if (featurep 'xemacs) :EMACS :XEMACS))
+            (mode :BOTH) code)
+        (while (consp args)
+          (if (memq (car args) '(:EMACS :XEMACS :BOTH)) (setq mode (pop args)))
+          (if (atom args)
+              (or args (error "Used selector %s without elements" mode))
+            (or (eq ignore mode)
+                (push (cond-emacs-xemacs-macfn (car args)) code))
+            (pop args)))
+        (cond (msg (if (or args (cdr code)) (error msg) (car code)))
+              ((or (null args) (eq ignore mode)) (nreverse code))
+              (t (nconc (nreverse code) args))))))
+  ;; Emacs/XEmacs-compatibility `defun': remove interactive "_" for Emacs, use
+  ;; existing functions when they are `fboundp', provide shortcuts if they are
+  ;; known to be defined in a specific Emacs branch (for short .elc)
+  (defmacro defunx (name arglist &rest definition)
+    (let ((xemacsp (featurep 'xemacs)) reuses)
+      (while (memq (car definition)
+                   '(:try :emacs-and-try :xemacs-and-try))
+        (if (eq (pop definition) (if xemacsp :xemacs-and-try :emacs-and-try))
+            (setq reuses (car definition)
+                  definition nil)
+          (push (pop definition) reuses)))
+      (if (and reuses (symbolp reuses))
+          `(defalias ',name ',reuses)
+        (let* ((docstring (if (stringp (car definition)) (pop definition)))
+               (spec (and (not xemacsp)
+                          (eq (car-safe (car definition)) 'interactive)
+                          (null (cddar definition))
+                          (cadar definition))))
+          (if (and (stringp spec)
+                   (not (string-equal spec ""))
+                   (eq (aref spec 0) ?_))
+              (setq definition
+                    (cons (if (string-equal spec "_")
+                              '(interactive)
+                            `(interactive ,(substring spec 1)))
+                          (cdr definition))))
+          (if (null reuses)
+              `(defun ,name ,arglist ,docstring
+                 ,@(cond-emacs-xemacs-macfn definition))
+            ;; no dynamic docstring in this case
+            `(eval-and-compile          ; no warnings in Emacs
+               (defalias ',name
+                 (cond ,@(mapcar (lambda (func) `((fboundp ',func) ',func))
+                                 (nreverse reuses))
+                       (t ,(if definition
+                               `(lambda ,arglist ,docstring
+                                  ,@(cond-emacs-xemacs-macfn definition))
+                             'ignore))))))))))
+  (defmacro ignore-errors-x (&rest body)
+    (let ((specials '((scan-sexps . 4) (scan-lists . 5)))
+          spec nils)
+      (if (and (featurep 'xemacs)
+               (null (cdr body)) (consp (car body))
+               (setq spec (assq (caar body) specials))
+               (>= (setq nils (- (cdr spec) (length (car body)))) 0))
+          `(,@(car body) ,@(make-list nils nil) t)
+        `(ignore-errors ,@body)))))
+
+;; More compile-time-macros
+(eval-when-compile
+  (defmacro save-buffer-state-x (&rest body) ; similar to EMACS/lazy-lock.el
+    (let ((modified (with-no-warnings (gensym "save-buffer-state-x-modified-"))))
+      `(let ((,modified (buffer-modified-p)))
+         (unwind-protect
+             (let ((buffer-undo-list t) (inhibit-read-only t)
+                   ,@(unless (featurep 'xemacs)
+                       '((inhibit-point-motion-hooks t) deactivate-mark))
+                   before-change-functions after-change-functions
+                   buffer-file-name buffer-file-truename)
+               ,@body)
+           (and (not ,modified) (buffer-modified-p)
+                (set-buffer-modified-p nil)))))))
+(put 'save-buffer-state-x 'lisp-indent-function 0)
+
+(defvar outline-level)
+(defvar imenu-use-markers)
+(defvar imenu-create-index-function)
+
+;; We cannot use `c-forward-syntactic-ws' directly since it is a macro since
+;; cc-mode-5.30 => antlr-mode compiled with older cc-mode would fail (macro
+;; call) when used with newer cc-mode.  Also, antlr-mode compiled with newer
+;; cc-mode would fail (undefined `c-forward-sws') when used with older cc-mode.
+;; Additional to the `defalias' below, we must set `antlr-c-forward-sws' to
+;; `c-forward-syntactic-ws' when `c-forward-sws' is not defined after requiring
+;; cc-mode.
+(defalias 'antlr-c-forward-sws 'c-forward-sws)
+
+
+;;;;##########################################################################
+;;;;  Variables
+;;;;##########################################################################
+
+
+(defgroup antlr nil
+  "Major mode for ANTLR grammar files."
+  :group 'languages
+  :link '(emacs-commentary-link "antlr-mode.el")
+  :link '(url-link "http://antlr-mode.sourceforge.net/")
+  :prefix "antlr-")
+
+(defconst antlr-version "2.2c"
+  "ANTLR major mode version number.
+Check <http://antlr-mode.sourceforge.net/> for the newest.")
+
+
+;;;===========================================================================
+;;;  Controlling ANTLR's code generator (language option)
+;;;===========================================================================
+
+(defvar antlr-language nil
+  "Major mode corresponding to ANTLR's \"language\" option.
+Set via `antlr-language-alist'.  The only useful place to change this
+buffer-local variable yourself is in `antlr-mode-hook' or in the \"local
+variable list\" near the end of the file, see
+`enable-local-variables'.")
+
+(defcustom antlr-language-alist
+  '((java-mode "Java" nil "\"Java\"" "Java")
+    (c++-mode "C++" "\"Cpp\"" "Cpp"))
+  "List of ANTLR's supported languages.
+Each element in this list looks like
+  \(MAJOR-MODE MODELINE-STRING OPTION-VALUE...)
+
+MAJOR-MODE, the major mode of the code in the grammar's actions, is the
+value of `antlr-language' if the first group in the string matched by
+REGEXP in `antlr-language-limit-n-regexp' is one of the OPTION-VALUEs.
+An OPTION-VALUE of nil denotes the fallback element.  MODELINE-STRING is
+also displayed in the mode line next to \"Antlr\"."
+  :group 'antlr
+  :type '(repeat (group :value (java-mode "")
+                        (function :tag "Major mode")
+                        (string :tag "Mode line string")
+                        (repeat :tag "ANTLR language option" :inline t
+                                (choice (const :tag "Default" nil)
+                                        string )))))
+
+(defcustom antlr-language-limit-n-regexp
+  '(8192 . "language[ \t]*=[ \t]*\\(\"?[A-Z][A-Za-z_]*\"?\\)")
+  "Used to set a reasonable value for `antlr-language'.
+Looks like \(LIMIT \. REGEXP).  Search for REGEXP from the beginning of
+the buffer to LIMIT and use the first group in the matched string to set
+the language according to `antlr-language-alist'."
+  :group 'antlr
+  :type '(cons (choice :tag "Limit" (const :tag "No" nil) (integer :value 0))
+               regexp))
+
+
+;;;===========================================================================
+;;;  Hide/Unhide, Indent/Tabs
+;;;===========================================================================
+
+(defcustom antlr-action-visibility 3
+  "Visibility of actions when command `antlr-hide-actions' is used.
+If nil, the actions with their surrounding braces are hidden.  If a
+number, do not hide the braces, only hide the contents if its length is
+greater than this number."
+  :group 'antlr
+  :type '(choice (const :tag "Completely hidden" nil)
+                 (integer :tag "Hidden if longer than" :value 3)))
+
+(defcustom antlr-indent-comment 'tab
+  "Non-nil, if the indentation should touch lines in block comments.
+If nil, no continuation line of a block comment is changed.  If t, they
+are changed according to `c-indentation-line'.  When not nil and not t,
+they are only changed by \\[antlr-indent-command]."
+  :group 'antlr
+  :type '(radio (const :tag "No" nil)
+                (const :tag "Always" t)
+                (sexp :tag "With TAB" :format "%t" :value tab)))
+
+(defcustom antlr-tab-offset-alist
+  '((antlr-mode nil 4 nil)
+    (java-mode "antlr" 4 nil))
+  "Alist to determine whether to use ANTLR's convention for TABs.
+Each element looks like \(MAJOR-MODE REGEXP TAB-WIDTH INDENT-TABS-MODE).
+The first element whose MAJOR-MODE is nil or equal to `major-mode' and
+whose REGEXP is nil or matches variable `buffer-file-name' is used to
+set `tab-width' and `indent-tabs-mode'.  This is useful to support both
+ANTLR's and Java's indentation styles.  Used by `antlr-set-tabs'."
+  :group 'antlr
+  :type '(repeat (group :value (antlr-mode nil 8 nil)
+                        (choice (const :tag "All" nil)
+                                (function :tag "Major mode"))
+                        (choice (const :tag "All" nil) regexp)
+                        (integer :tag "Tab width")
+                        (boolean :tag "Indent-tabs-mode"))))
+
+(defcustom antlr-indent-style "java"
+  "If non-nil, cc-mode indentation style used for `antlr-mode'.
+See `c-set-style' and for details, where the most interesting part in
+`c-style-alist' is the value of `c-basic-offset'."
+  :group 'antlr
+  :type '(choice (const nil) regexp))
+
+(defcustom antlr-indent-item-regexp
+  "[]}):;|&]" ; & is local ANTLR extension (SGML's and-connector)
+  "Regexp matching lines which should be indented by one TAB less.
+See `antlr-indent-line' and command \\[antlr-indent-command]."
+  :group 'antlr
+  :type 'regexp)
+
+(defcustom antlr-indent-at-bol-alist
+  ;; eval-when-compile not usable with defcustom...
+  '((java-mode . "\\(package\\|import\\)\\>")
+    (c++-mode . "#\\(assert\\|cpu\\|define\\|endif\\|el\\(if\\|se\\)\\|i\\(dent\\|f\\(def\\|ndef\\)?\\|mport\\|nclude\\(_next\\)?\\)\\|line\\|machine\\|pragma\\|system\\|un\\(assert\\|def\\)\\|warning\\)\\>"))
+  "Alist of regexps matching lines are indented at column 0.
+Each element in this list looks like (MODE . REGEXP) where MODE is a
+function and REGEXP is a regular expression.
+
+If `antlr-language' equals to a MODE, the line starting at the first
+non-whitespace is matched by the corresponding REGEXP, and the line is
+part of a header action, indent the line at column 0 instead according
+to the normal rules of `antlr-indent-line'."
+  :group 'antlr
+  :type '(repeat (cons (function :tag "Major mode") regexp)))
+
+;; adopt indentation to cc-engine
+(defvar antlr-disabling-cc-syntactic-symbols
+  '(statement-block-intro
+    defun-block-intro topmost-intro statement-case-intro member-init-intro
+    arglist-intro brace-list-intro knr-argdecl-intro inher-intro
+    objc-method-intro
+    block-close defun-close class-close brace-list-close arglist-close
+    inline-close extern-lang-close namespace-close))
+
+
+;;;===========================================================================
+;;;  Options: customization
+;;;===========================================================================
+
+(defcustom antlr-options-use-submenus t
+  "Non-nil, if the major mode menu should include option submenus.
+If nil, the menu just includes a command to insert options.  Otherwise,
+it includes four submenus to insert file/grammar/rule/subrule options."
+  :group 'antlr
+  :type 'boolean)
+
+(defcustom antlr-tool-version 20701
+  "The version number of the Antlr tool.
+The value is an integer of the form XYYZZ which stands for vX.YY.ZZ.
+This variable is used to warn about non-supported options and to supply
+version correct option values when using \\[antlr-insert-option].
+
+Don't use a number smaller than 20600 since the stored history of
+Antlr's options starts with v2.06.00, see `antlr-options-alists'.  You
+can make this variable buffer-local."
+  :group 'antlr
+  :type 'integer)
+
+(defcustom antlr-options-auto-colon t
+  "Non-nil, if `:' is inserted with a rule or subrule options section.
+A `:' is only inserted if this value is non-nil, if a rule or subrule
+option is inserted with \\[antlr-insert-option], if there was no rule or
+subrule options section before, and if a `:' is not already present
+after the section, ignoring whitespace, comments and the init action."
+  :group 'antlr
+  :type 'boolean)
+
+(defcustom antlr-options-style nil
+  "List of symbols which determine the style of option values.
+If a style symbol is present, the corresponding option value is put into
+quotes, i.e., represented as a string, otherwise it is represented as an
+identifier.
+
+The only style symbol used in the default value of `antlr-options-alist'
+is `language-as-string'.  See also `antlr-read-value'."
+  :group 'antlr
+  :type '(repeat (symbol :tag "Style symbol")))
+
+(defcustom antlr-options-push-mark t
+  "Non-nil, if inserting an option should set & push mark.
+If nil, never set mark when inserting an option with command
+\\[antlr-insert-option].  If t, always set mark via `push-mark'.  If a
+number, only set mark if point was outside the options area before and
+the number of lines between point and the insert position is greater
+than this value.  Otherwise, only set mark if point was outside the
+options area before."
+  :group 'antlr
+  :type '(radio (const :tag "No" nil)
+                (const :tag "Always" t)
+                (integer :tag "Lines between" :value 10)
+                (sexp :tag "If outside options" :format "%t" :value outside)))
+
+(defcustom antlr-options-assign-string " = "
+  "String containing `=' to use between option name and value.
+This string is only used if the option to insert did not exist before
+or if there was no `=' after it.  In other words, the spacing around an
+existing `=' won't be changed when changing an option value."
+  :group 'antlr
+  :type 'string)
+
+
+;;;===========================================================================
+;;;  Options: definitions
+;;;===========================================================================
+
+(defvar antlr-options-headings '("file" "grammar" "rule" "subrule")
+  "Headings for the four different option kinds.
+The standard value is (\"file\" \"grammar\" \"rule\" \"subrule\").  See
+`antlr-options-alists'")
+
+(defvar antlr-options-alists
+  '(;; file options ----------------------------------------------------------
+    (("language" antlr-language-option-extra
+      (20600 antlr-read-value
+             "Generated language: " language-as-string
+             (("Java") ("Cpp") ("HTML") ("Diagnostic")))
+      (20700 antlr-read-value
+             "Generated language: " language-as-string
+             (("Java") ("Cpp") ("HTML") ("Diagnostic") ("Sather"))))
+     ("mangleLiteralPrefix" nil
+      (20600 antlr-read-value
+             "Prefix for literals (default LITERAL_): " t))
+     ("namespace" antlr-c++-mode-extra
+      (20700 antlr-read-value
+             "Wrap generated C++ code in namespace: " t))
+     ("namespaceStd" antlr-c++-mode-extra
+      (20701 antlr-read-value
+             "Replace ANTLR_USE_NAMESPACE(std) by: " t))
+     ("namespaceAntlr" antlr-c++-mode-extra
+      (20701 antlr-read-value
+             "Replace ANTLR_USE_NAMESPACE(antlr) by: " t))
+     ("genHashLines" antlr-c++-mode-extra
+      (20701 antlr-read-boolean
+             "Include #line in generated C++ code? "))
+     )
+    ;; grammar options --------------------------------------------------------
+    (("k" nil
+      (20600 antlr-read-value
+             "Lookahead depth: "))
+     ("importVocab" nil
+      (20600 antlr-read-value
+             "Import vocabulary: "))
+     ("exportVocab" nil
+      (20600 antlr-read-value
+             "Export vocabulary: "))
+     ("testLiterals" nil                ; lexer only
+      (20600 antlr-read-boolean
+             "Test each token against literals table? "))
+     ("defaultErrorHandler" nil                 ; not for lexer
+      (20600 antlr-read-boolean
+             "Generate default exception handler for each rule? "))
+     ("codeGenMakeSwitchThreshold" nil
+      (20600 antlr-read-value
+             "Min number of alternatives for 'switch': "))
+     ("codeGenBitsetTestThreshold" nil
+      (20600 antlr-read-value
+             "Min size of lookahead set for bitset test: "))
+     ("analyzerDebug" nil
+      (20600 antlr-read-boolean
+             "Display debugging info during grammar analysis? "))
+     ("codeGenDebug" nil
+      (20600 antlr-read-boolean
+             "Display debugging info during code generation? "))
+     ("buildAST" nil                    ; not for lexer
+      (20600 antlr-read-boolean
+             "Use automatic AST construction/transformation? "))
+     ("ASTLabelType" nil                ; not for lexer
+      (20600 antlr-read-value
+             "Class of user-defined AST node: " t))
+     ("charVocabulary" nil              ; lexer only
+      (20600 nil
+             "Insert character vocabulary"))
+     ("interactive" nil
+      (20600 antlr-read-boolean
+             "Generate interactive lexer/parser? "))
+     ("caseSensitive" nil               ; lexer only
+      (20600 antlr-read-boolean
+             "Case significant when matching characters? "))
+     ("caseSensitiveLiterals" nil       ; lexer only
+      (20600 antlr-read-boolean
+             "Case significant when testing literals table? "))
+     ("classHeaderSuffix" nil
+      (20600 nil
+             "Additional string for grammar class definition"))
+     ("filter" nil                      ; lexer only
+      (20600 antlr-read-boolean
+             "Skip rule (the name, true or false): "
+             antlr-grammar-tokens))
+     ("namespace" antlr-c++-mode-extra
+      (20700 antlr-read-value
+             "Wrap generated C++ code for grammar in namespace: " t))
+     ("namespaceStd" antlr-c++-mode-extra
+      (20701 antlr-read-value
+             "Replace ANTLR_USE_NAMESPACE(std) by: " t))
+     ("namespaceAntlr" antlr-c++-mode-extra
+      (20701 antlr-read-value
+             "Replace ANTLR_USE_NAMESPACE(antlr) by: " t))
+     ("genHashLines" antlr-c++-mode-extra
+      (20701 antlr-read-boolean
+             "Include #line in generated C++ code? "))
+;;;     ("autoTokenDef" nil             ; parser only
+;;;      (80000 antlr-read-boolean              ; default: true
+;;;          "Automatically define referenced token? "))
+;;;     ("keywordsMeltTo" nil           ; parser only
+;;;      (80000 antlr-read-value
+;;;          "Change non-matching keywords to token type: "))
+     )
+    ;; rule options ----------------------------------------------------------
+    (("testLiterals" nil                ; lexer only
+      (20600 antlr-read-boolean
+             "Test this token against literals table? "))
+     ("defaultErrorHandler" nil                 ; not for lexer
+      (20600 antlr-read-boolean
+             "Generate default exception handler for this rule? "))
+     ("ignore" nil                      ; lexer only
+      (20600 antlr-read-value
+             "In this rule, ignore tokens of type: " nil
+             antlr-grammar-tokens))
+     ("paraphrase" nil                  ; lexer only
+      (20600 antlr-read-value
+             "In messages, replace name of this token by: " t))
+     )
+    ;; subrule options -------------------------------------------------------
+    (("warnWhenFollowAmbig" nil
+      (20600 antlr-read-boolean
+             "Display warnings for ambiguities with FOLLOW? "))
+     ("generateAmbigWarnings" nil
+      (20600 antlr-read-boolean
+             "Display warnings for ambiguities? "))
+     ("greedy" nil
+      (20700 antlr-read-boolean
+             "Make this optional/loop subrule greedy? "))
+     ))
+  "Definitions for Antlr's options of all four different kinds.
+
+The value looks like \(FILE GRAMMAR RULE SUBRULE) where each FILE,
+GRAMMAR, RULE, and SUBRULE is a list of option definitions of the
+corresponding kind, i.e., looks like \(OPTION-DEF...).
+
+Each OPTION-DEF looks like \(OPTION-NAME EXTRA-FN VALUE-SPEC...) which
+defines a file/grammar/rule/subrule option with name OPTION-NAME.  The
+OPTION-NAMEs are used for the creation of the \"Insert XXX Option\"
+submenus, see `antlr-options-use-submenus', and to allow to insert the
+option name with completion when using \\[antlr-insert-option].
+
+If EXTRA-FN is a function, it is called at different phases of the
+insertion with arguments \(PHASE OPTION-NAME).  PHASE can have the
+values `before-input' or `after-insertion', additional phases might be
+defined in future versions of this mode.  The phase `before-input'
+occurs before the user is asked to insert a value.  The phase
+`after-insertion' occurs after the option value has been inserted.
+EXTRA-FN might be called with additional arguments in future versions of
+this mode.
+
+Each specification VALUE-SPEC looks like \(VERSION READ-FN ARG...).  The
+last VALUE-SPEC in an OPTION-DEF whose VERSION is smaller or equal to
+`antlr-tool-version' specifies how the user is asked for the value of
+the option.
+
+If READ-FN is nil, the only ARG is a string which is printed at the echo
+area to guide the user what to insert at point.  Otherwise, READ-FN is
+called with arguments \(INIT-VALUE ARG...) to get the new value of the
+option.  INIT-VALUE is the old value of the option or nil.
+
+The standard value contains the following functions as READ-FN:
+`antlr-read-value' with ARGs = \(PROMPT AS-STRING TABLE) which reads a
+general value, or `antlr-read-boolean' with ARGs = \(PROMPT TABLE) which
+reads a boolean value or a member of TABLE.  PROMPT is the prompt when
+asking for a new value.  If non-nil, TABLE is a table for completion or
+a function evaluating to such a table.  The return value is quoted if
+AS-STRING is non-nil and is either t or a symbol which is a member of
+`antlr-options-style'.")
+
+
+;;;===========================================================================
+;;;  Run tool, create Makefile dependencies
+;;;===========================================================================
+
+(defcustom antlr-tool-command "java antlr.Tool"
+  "Command used in \\[antlr-run-tool] to run the Antlr tool.
+This variable should include all options passed to Antlr except the
+option \"-glib\" which is automatically suggested if necessary."
+  :group 'antlr
+  :type 'string)
+
+(defcustom antlr-ask-about-save t
+  "If not nil, \\[antlr-run-tool] asks which buffers to save.
+Otherwise, it saves all modified buffers before running without asking."
+  :group 'antlr
+  :type 'boolean)
+
+(defcustom antlr-makefile-specification
+  '("\n" ("GENS" "GENS%d" " \\\n\t") "$(ANTLR)")
+  "Variable to specify the appearance of the generated makefile rules.
+This variable influences the output of \\[antlr-show-makefile-rules].
+It looks like \(RULE-SEP GEN-VAR-SPEC COMMAND).
+
+RULE-SEP is the string to separate different makefile rules.  COMMAND is
+a string with the command which runs the Antlr tool, it should include
+all options except the option \"-glib\" which is automatically added
+if necessary.
+
+If GEN-VAR-SPEC is nil, each target directly consists of a list of
+files.  If GEN-VAR-SPEC looks like \(GEN-VAR GEN-VAR-FORMAT GEN-SEP), a
+Makefile variable is created for each rule target.
+
+Then, GEN-VAR is a string with the name of the variable which contains
+the file names of all makefile rules.  GEN-VAR-FORMAT is a format string
+producing the variable of each target with substitution COUNT/%d where
+COUNT starts with 1.  GEN-SEP is used to separate long variable values."
+  :group 'antlr
+  :type '(list (string :tag "Rule separator")
+               (choice
+                (const :tag "Direct targets" nil)
+                (list :tag "Variables for targets"
+                      (string :tag "Variable for all targets")
+                      (string :tag "Format for each target variable")
+                      (string :tag "Variable separator")))
+               (string :tag "ANTLR command")))
+
+(defvar antlr-file-formats-alist
+  '((java-mode ("%sTokenTypes.java") ("%s.java"))
+    (c++-mode ("%sTokenTypes.hpp") ("%s.cpp" "%s.hpp")))
+  "Language dependent formats which specify generated files.
+Each element in this list looks looks like
+  \(MAJOR-MODE (VOCAB-FILE-FORMAT...) (CLASS-FILE-FORMAT...)).
+
+The element whose MAJOR-MODE is equal to `antlr-language' is used to
+specify the generated files which are language dependent.  See variable
+`antlr-special-file-formats' for language independent files.
+
+VOCAB-FILE-FORMAT is a format string, it specifies with substitution
+VOCAB/%s the generated file for each export vocabulary VOCAB.
+CLASS-FILE-FORMAT is a format string, it specifies with substitution
+CLASS/%s the generated file for each grammar class CLASS.")
+
+(defvar antlr-special-file-formats '("%sTokenTypes.txt" "expanded%s.g")
+  "Language independent formats which specify generated files.
+The value looks like \(VOCAB-FILE-FORMAT EXPANDED-GRAMMAR-FORMAT).
+
+VOCAB-FILE-FORMAT is a format string, it specifies with substitution
+VOCAB/%s the generated or input file for each export or import
+vocabulary VOCAB, respectively.  EXPANDED-GRAMMAR-FORMAT is a format
+string, it specifies with substitution GRAMMAR/%s the constructed
+grammar file if the file GRAMMAR.g contains a grammar class which
+extends a class other than \"Lexer\", \"Parser\" or \"TreeParser\".
+
+See variable `antlr-file-formats-alist' for language dependent
+formats.")
+
+(defvar antlr-unknown-file-formats '("?%s?.g" "?%s?")
+  "Formats which specify the names of unknown files.
+The value looks like \(SUPER-GRAMMAR-FILE-FORMAT SUPER-EVOCAB-FORMAT).
+
+SUPER-GRAMMAR-FORMAT is a format string, it specifies with substitution
+SUPER/%s the name of a grammar file for Antlr's option \"-glib\" if no
+grammar file in the current directory defines the class SUPER or if it
+is defined more than once.  SUPER-EVOCAB-FORMAT is a format string, it
+specifies with substitution SUPER/%s the name for the export vocabulary
+of above mentioned class SUPER.")
+
+(defvar antlr-help-unknown-file-text
+  "## The following rules contain filenames of the form
+##  \"?SUPERCLASS?.g\" (and \"?SUPERCLASS?TokenTypes.txt\")
+## where SUPERCLASS is not found to be defined in any grammar file of
+## the current directory or is defined more than once.  Please replace
+## these filenames by the grammar files (and their exportVocab).\n\n"
+  "String indicating the existence of unknown files in the Makefile.
+See \\[antlr-show-makefile-rules] and `antlr-unknown-file-formats'.")
+
+(defvar antlr-help-rules-intro
+  "The following Makefile rules define the dependencies for all (non-
+expanded) grammars in directory \"%s\".\n
+They are stored in the kill-ring, i.e., you can insert them with C-y
+into your Makefile.  You can also invoke M-x antlr-show-makefile-rules
+from within a Makefile to insert them directly.\n\n\n"
+  "Introduction to use with \\[antlr-show-makefile-rules].
+It is a format string and used with substitution DIRECTORY/%s where
+DIRECTORY is the name of the current directory.")
+
+
+;;;===========================================================================
+;;;  Menu
+;;;===========================================================================
+
+(defcustom antlr-imenu-name t ; (featurep 'xemacs) ; TODO: Emacs-21 bug?
+  "Non-nil, if a \"Index\" menu should be added to the menubar.
+If it is a string, it is used instead \"Index\".  Requires package
+imenu."
+  :group 'antlr
+  :type '(choice (const :tag "No menu" nil)
+                 (const :tag "Index menu" t)
+                 (string :tag "Other menu name")))
+
+(defvar antlr-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map "\t" 'antlr-indent-command)
+    (define-key map "\e\C-a" 'antlr-beginning-of-rule)
+    (define-key map "\e\C-e" 'antlr-end-of-rule)
+    (define-key map "\C-c\C-a" 'antlr-beginning-of-body)
+    (define-key map "\C-c\C-e" 'antlr-end-of-body)
+    (define-key map "\C-c\C-f" 'c-forward-into-nomenclature)
+    (define-key map "\C-c\C-b" 'c-backward-into-nomenclature)
+    (define-key map "\C-c\C-c" 'comment-region)
+    (define-key map "\C-c\C-v" 'antlr-hide-actions)
+    (define-key map "\C-c\C-r" 'antlr-run-tool)
+    (define-key map "\C-c\C-o" 'antlr-insert-option)
+    ;; I'm too lazy to define my own:
+    (define-key map "\ea" 'c-beginning-of-statement)
+    (define-key map "\ee" 'c-end-of-statement)
+    ;; electric keys:
+    (define-key map ":" 'antlr-electric-character)
+    (define-key map ";" 'antlr-electric-character)
+    (define-key map "|" 'antlr-electric-character)
+    (define-key map "&" 'antlr-electric-character)
+    (define-key map "(" 'antlr-electric-character)
+    (define-key map ")" 'antlr-electric-character)
+    (define-key map "{" 'antlr-electric-character)
+    (define-key map "}" 'antlr-electric-character)
+    map)
+  "Keymap used in `antlr-mode' buffers.")
+
+(easy-menu-define antlr-mode-menu antlr-mode-map
+  "Major mode menu."
+  `("Antlr"
+    ,@(if (cond-emacs-xemacs
+           :EMACS (and antlr-options-use-submenus
+                       (>= emacs-major-version 21))
+           :XEMACS antlr-options-use-submenus)
+          `(("Insert File Option"
+             :filter ,(lambda (x) (antlr-options-menu-filter 1 x)))
+            ("Insert Grammar Option"
+             :filter ,(lambda (x) (antlr-options-menu-filter 2 x)))
+            ("Insert Rule Option"
+             :filter ,(lambda (x) (antlr-options-menu-filter 3 x)))
+            ("Insert Subrule Option"
+             :filter ,(lambda (x) (antlr-options-menu-filter 4 x)))
+            "---")
+        '(["Insert Option" antlr-insert-option
+           :active (not buffer-read-only)]))
+    ("Forward/Backward"
+     ["Backward Rule" antlr-beginning-of-rule t]
+     ["Forward Rule" antlr-end-of-rule t]
+     ["Start of Rule Body" antlr-beginning-of-body
+      :active (antlr-inside-rule-p)]
+     ["End of Rule Body" antlr-end-of-body
+      :active (antlr-inside-rule-p)]
+     "---"
+     ["Backward Statement" c-beginning-of-statement t]
+     ["Forward Statement" c-end-of-statement t]
+     ["Backward Into Nomencl." c-backward-into-nomenclature t]
+     ["Forward Into Nomencl." c-forward-into-nomenclature t])
+    ["Indent Region" indent-region
+     :active (and (not buffer-read-only) (c-region-is-active-p))]
+    ["Comment Out Region" comment-region
+     :active (and (not buffer-read-only) (c-region-is-active-p))]
+    ["Uncomment Region"
+     (comment-region (region-beginning) (region-end) '(4))
+     :active (and (not buffer-read-only) (c-region-is-active-p))]
+    "---"
+    ["Hide Actions (incl. Args)" antlr-hide-actions t]
+    ["Hide Actions (excl. Args)" (antlr-hide-actions 2) t]
+    ["Unhide All Actions" (antlr-hide-actions 0) t]
+    "---"
+    ["Run Tool on Grammar" antlr-run-tool t]
+    ["Show Makefile Rules" antlr-show-makefile-rules t]
+    "---"
+    ["Customize Antlr" (customize-group 'antlr) t]))
+
+
+;;;===========================================================================
+;;;  font-lock
+;;;===========================================================================
+
+(defcustom antlr-font-lock-maximum-decoration 'inherit
+  "The maximum decoration level for fontifying actions.
+Value `none' means, do not fontify actions, just normal grammar code
+according to `antlr-font-lock-additional-keywords'.  Value `inherit'
+means, use value of `font-lock-maximum-decoration'.  Any other value is
+interpreted as in `font-lock-maximum-decoration' with no level-0
+fontification, see `antlr-font-lock-keywords-alist'.
+
+While calculating the decoration level for actions, `major-mode' is
+bound to `antlr-language'.  For example, with value
+  \((java-mode \. 2) (c++-mode \. 0))
+Java actions are fontified with level 2 and C++ actions are not
+fontified at all."
+  :group 'antlr
+  :type '(choice (const :tag "None" none)
+                 (const :tag "Inherit" inherit)
+                 (const :tag "Default" nil)
+                 (const :tag "Maximum" t)
+                 (integer :tag "Level" 1)
+                 (repeat :menu-tag "Mode specific" :tag "Mode specific"
+                         :value ((t . t))
+                         (cons :tag "Instance"
+                               (radio :tag "Mode"
+                                      (const :tag "All" t)
+                                      (symbol :tag "Name"))
+                               (radio :tag "Decoration"
+                                      (const :tag "Default" nil)
+                                      (const :tag "Maximum" t)
+                                      (integer :tag "Level" 1))))))
+
+(defconst antlr-no-action-keywords nil
+  ;; Using nil directly won't work (would use highest level, see
+  ;; `font-lock-choose-keywords'), but a non-symbol, i.e., (list), at `car'
+  ;; would break Emacs-21.0:
+  "Empty font-lock keywords for actions.
+Do not change the value of this constant.")
+
+(defvar antlr-font-lock-keywords-alist
+  '((java-mode
+     antlr-no-action-keywords
+     java-font-lock-keywords-1 java-font-lock-keywords-2
+     java-font-lock-keywords-3)
+    (c++-mode
+     antlr-no-action-keywords
+     c++-font-lock-keywords-1 c++-font-lock-keywords-2
+     c++-font-lock-keywords-3))
+  "List of font-lock keywords for actions in the grammar.
+Each element in this list looks like
+  \(MAJOR-MODE KEYWORD...)
+
+If `antlr-language' is equal to MAJOR-MODE, the KEYWORDs are the
+font-lock keywords according to `font-lock-defaults' used for the code
+in the grammar's actions and semantic predicates, see
+`antlr-font-lock-maximum-decoration'.")
+
+(defvar antlr-default-face 'antlr-default)
+(defface antlr-default '((t nil))
+  "Face to prevent strings from language dependent highlighting.
+Do not change."
+  :group 'antlr)
+;; backward-compatibility alias
+(put 'antlr-font-lock-default-face 'face-alias 'antlr-default)
+(put 'antlr-font-lock-default-face 'obsolete-face "22.1")
+
+(defvar antlr-keyword-face 'antlr-keyword)
+(defface antlr-keyword
+  (cond-emacs-xemacs
+   '((((class color) (background light))
+      (:foreground "black" :EMACS :weight bold :XEMACS :bold t))
+     (t :inherit font-lock-keyword-face)))
+  "ANTLR keywords."
+  :group 'antlr)
+;; backward-compatibility alias
+(put 'antlr-font-lock-keyword-face 'face-alias 'antlr-keyword)
+(put 'antlr-font-lock-keyword-face 'obsolete-face "22.1")
+
+(defvar antlr-syntax-face 'antlr-keyword)
+(defface antlr-syntax
+  (cond-emacs-xemacs
+   '((((class color) (background light))
+      (:foreground "black" :EMACS :weight bold :XEMACS :bold t))
+     (t :inherit font-lock-constant-face)))
+  "ANTLR syntax symbols like :, |, (, ), ...."
+  :group 'antlr)
+;; backward-compatibility alias
+(put 'antlr-font-lock-syntax-face 'face-alias 'antlr-syntax)
+(put 'antlr-font-lock-syntax-face 'obsolete-face "22.1")
+
+(defvar antlr-ruledef-face 'antlr-ruledef)
+(defface antlr-ruledef
+  (cond-emacs-xemacs
+   '((((class color) (background light))
+      (:foreground "blue" :EMACS :weight bold :XEMACS :bold t))
+     (t :inherit font-lock-function-name-face)))
+  "ANTLR rule references (definition)."
+  :group 'antlr)
+;; backward-compatibility alias
+(put 'antlr-font-lock-ruledef-face 'face-alias 'antlr-ruledef)
+(put 'antlr-font-lock-ruledef-face 'obsolete-face "22.1")
+
+(defvar antlr-tokendef-face 'antlr-tokendef)
+(defface antlr-tokendef
+  (cond-emacs-xemacs
+   '((((class color) (background light))
+      (:foreground "blue" :EMACS :weight bold :XEMACS :bold t))
+     (t :inherit font-lock-function-name-face)))
+  "ANTLR token references (definition)."
+  :group 'antlr)
+;; backward-compatibility alias
+(put 'antlr-font-lock-tokendef-face 'face-alias 'antlr-tokendef)
+(put 'antlr-font-lock-tokendef-face 'obsolete-face "22.1")
+
+(defvar antlr-ruleref-face 'antlr-ruleref)
+(defface antlr-ruleref
+  '((((class color) (background light)) (:foreground "blue4"))
+    (t :inherit font-lock-type-face))
+  "ANTLR rule references (usage)."
+  :group 'antlr)
+;; backward-compatibility alias
+(put 'antlr-font-lock-ruleref-face 'face-alias 'antlr-ruleref)
+(put 'antlr-font-lock-ruleref-face 'obsolete-face "22.1")
+
+(defvar antlr-tokenref-face 'antlr-tokenref)
+(defface antlr-tokenref
+  '((((class color) (background light)) (:foreground "orange4"))
+    (t :inherit font-lock-type-face))
+  "ANTLR token references (usage)."
+  :group 'antlr)
+;; backward-compatibility alias
+(put 'antlr-font-lock-tokenref-face 'face-alias 'antlr-tokenref)
+(put 'antlr-font-lock-tokenref-face 'obsolete-face "22.1")
+
+(defvar antlr-literal-face 'antlr-literal)
+(defface antlr-literal
+  (cond-emacs-xemacs
+   '((((class color) (background light))
+      (:foreground "brown4" :EMACS :weight bold :XEMACS :bold t))
+     (t :inherit font-lock-string-face)))
+  "ANTLR special literal tokens.
+It is used to highlight strings matched by the first regexp group of
+`antlr-font-lock-literal-regexp'."
+  :group 'antlr)
+;; backward-compatibility alias
+(put 'antlr-font-lock-literal-face 'face-alias 'antlr-literal)
+(put 'antlr-font-lock-literal-face 'obsolete-face "22.1")
+
+(defcustom antlr-font-lock-literal-regexp "\"\\(\\sw\\(\\sw\\|-\\)*\\)\""
+  "Regexp matching literals with special syntax highlighting, or nil.
+If nil, there is no special syntax highlighting for some literals.
+Otherwise, it should be a regular expression which must contain a regexp
+group.  The string matched by the first group is highlighted with
+`antlr-font-lock-literal-face'."
+  :group 'antlr
+  :type '(choice (const :tag "None" nil) regexp))
+
+(defvar antlr-class-header-regexp
+  "\\(class\\)[ \t]+\\([A-Za-z\300-\326\330-\337]\\sw*\\)[ \t]+\\(extends\\)[ \t]+\\([A-Za-z\300-\326\330-\337]\\sw*\\)[ \t]*;"
+  "Regexp matching class headers.")
+
+(defvar antlr-font-lock-additional-keywords
+  (cond-emacs-xemacs
+   `((antlr-invalidate-context-cache)
+     ("\\$setType[ \t]*(\\([A-Za-z\300-\326\330-\337]\\sw*\\))"
+      (1 antlr-tokendef-face))
+     ("\\$\\sw+" (0 antlr-keyword-face))
+     ;; the tokens are already fontified as string/docstrings:
+     (,(lambda (limit)
+         (if antlr-font-lock-literal-regexp
+             (antlr-re-search-forward antlr-font-lock-literal-regexp limit)))
+      (1 antlr-literal-face t)
+      :XEMACS (0 nil))                  ; XEmacs bug workaround
+     (,(lambda (limit)
+         (antlr-re-search-forward antlr-class-header-regexp limit))
+      (1 antlr-keyword-face)
+      (2 antlr-ruledef-face)
+      (3 antlr-keyword-face)
+      (4 (if (member (match-string 4) '("Lexer" "Parser" "TreeParser"))
+             antlr-keyword-face
+           font-lock-type-face)))
+     (,(lambda (limit)
+         (antlr-re-search-forward
+          "\\<\\(header\\|options\\|tokens\\|exception\\|catch\\|returns\\)\\>"
+          limit))
+     (1 antlr-keyword-face))
+     (,(lambda (limit)
+         (antlr-re-search-forward
+          "^\\(private\\|public\\|protected\\)\\>[ \t]*\\(\\(\\sw+[ \t]*\\(:\\)?\\)\\)?"
+          limit))
+     (1 font-lock-type-face)            ; not XEmacs's java level-3 fruit salad
+     (3 (if (antlr-upcase-p (char-after (match-beginning 3)))
+            antlr-tokendef-face
+          antlr-ruledef-face) nil t)
+     (4 antlr-syntax-face nil t))
+     (,(lambda (limit)
+         (antlr-re-search-forward "^\\(\\sw+\\)[ \t]*\\(:\\)?" limit))
+     (1 (if (antlr-upcase-p (char-after (match-beginning 0)))
+            antlr-tokendef-face
+          antlr-ruledef-face) nil t)
+     (2 antlr-syntax-face nil t))
+     (,(lambda (limit)
+         ;; v:ruleref and v:"literal" is allowed...
+         (antlr-re-search-forward "\\(\\sw+\\)[ \t]*\\([=:]\\)?" limit))
+     (1 (if (match-beginning 2)
+            (if (eq (char-after (match-beginning 2)) ?=)
+                antlr-default-face
+              font-lock-variable-name-face)
+          (if (antlr-upcase-p (char-after (match-beginning 1)))
+              antlr-tokenref-face
+            antlr-ruleref-face)))
+     (2 antlr-default-face nil t))
+     (,(lambda (limit)
+         (antlr-re-search-forward "[|&:;(~]\\|)\\([*+?]\\|=>\\)?" limit))
+     (0 antlr-syntax-face))))
+  "Font-lock keywords for ANTLR's normal grammar code.
+See `antlr-font-lock-keywords-alist' for the keywords of actions.")
+
+(defvar antlr-font-lock-defaults
+  '(antlr-font-lock-keywords
+    nil nil ((?_ . "w") (?\( . ".") (?\) . ".")) beginning-of-defun)
+  "Font-lock defaults used for ANTLR syntax highlighting.
+The SYNTAX-ALIST element is also used to initialize
+`antlr-action-syntax-table'.")
+
+
+;;;===========================================================================
+;;;  Internal variables
+;;;===========================================================================
+
+(defvar antlr-mode-hook nil
+  "Hook called by `antlr-mode'.")
+
+(defvar antlr-mode-syntax-table
+  (let ((st (make-syntax-table)))
+    (c-populate-syntax-table st)
+    st)
+  "Syntax table used in `antlr-mode' buffers.
+If non-nil, it will be initialized in `antlr-mode'.")
+
+;; used for "in Java/C++ code" = syntactic-depth>0
+(defvar antlr-action-syntax-table
+  (let ((st (copy-syntax-table antlr-mode-syntax-table))
+        (slist (nth 3 antlr-font-lock-defaults)))
+    (while slist
+      (modify-syntax-entry (caar slist) (cdar slist) st)
+      (setq slist (cdr slist)))
+    st)
+  "Syntax table used for ANTLR action parsing.
+Initialized by `antlr-mode-syntax-table', changed by SYNTAX-ALIST in
+`antlr-font-lock-defaults'.  This table should be selected if you use
+`buffer-syntactic-context' and `buffer-syntactic-context-depth' in order
+not to confuse their context_cache.")
+
+(defvar antlr-mode-abbrev-table nil
+  "Abbreviation table used in `antlr-mode' buffers.")
+(define-abbrev-table 'antlr-mode-abbrev-table ())
+
+(defvar antlr-slow-cache-enabling-symbol 'loudly
+;; Emacs's font-lock changes buffer's tick counter, therefore this value should
+;; be a parameter of a font-lock function, but not any other variable of
+;; functions which call `antlr-slow-syntactic-context'.
+  "If value is a bound symbol, cache will be used even with text changes.
+This is no user option.  Used for `antlr-slow-syntactic-context'.")
+
+(defvar antlr-slow-cache-diff-threshold 5000
+  "Maximum distance between `point' and cache position for cache use.
+Used for `antlr-slow-syntactic-context'.")
+
+
+;;;;##########################################################################
+;;;;  The Code
+;;;;##########################################################################
+
+
+
+;;;===========================================================================
+;;;  Syntax functions -- Emacs vs XEmacs dependent, part 1
+;;;===========================================================================
+
+;; From help.el (XEmacs-21.1), without `copy-syntax-table'
+(defmacro antlr-with-syntax-table (syntab &rest body)
+  "Evaluate BODY with the syntax table SYNTAB."
+  `(let ((stab (syntax-table)))
+     (unwind-protect
+         (progn (set-syntax-table ,syntab) ,@body)
+       (set-syntax-table stab))))
+(put 'antlr-with-syntax-table 'lisp-indent-function 1)
+(put 'antlr-with-syntax-table 'edebug-form-spec '(form body))
+
+(defunx antlr-default-directory ()
+  :xemacs-and-try default-directory
+  "Return `default-directory'."
+  default-directory)
+
+;; Check Emacs-21.1 simple.el, `shell-command'.
+(defunx antlr-read-shell-command (prompt &optional initial-input history)
+  :xemacs-and-try read-shell-command
+  "Read a string from the minibuffer, using `shell-command-history'."
+  (read-from-minibuffer prompt initial-input nil nil
+                        (or history 'shell-command-history)))
+
+(defunx antlr-with-displaying-help-buffer (thunk &optional _name)
+  :xemacs-and-try with-displaying-help-buffer
+  "Make a help buffer and call `thunk' there."
+  (with-output-to-temp-buffer "*Help*"
+    (save-excursion (funcall thunk))))
+
+
+;;;===========================================================================
+;;;  Context cache
+;;;===========================================================================
+
+(defvar antlr-slow-context-cache nil "Internal.")
+
+;;;(defvar antlr-statistics-full-neg 0)
+;;;(defvar antlr-statistics-full-diff 0)
+;;;(defvar antlr-statistics-full-other 0)
+;;;(defvar antlr-statistics-cache 0)
+;;;(defvar antlr-statistics-inval 0)
+
+(defunx antlr-invalidate-context-cache (&rest _dummies)
+;; checkdoc-params: (dummies)
+  "Invalidate context cache for syntactical context information."
+  :XEMACS                               ; XEmacs bug workaround
+  (with-current-buffer (get-buffer-create " ANTLR XEmacs bug workaround")
+    (buffer-syntactic-context-depth)
+    nil)
+  :EMACS
+;;;  (incf antlr-statistics-inval)
+  (setq antlr-slow-context-cache nil))
+
+(defunx antlr-syntactic-context ()
+  "Return some syntactic context information.
+Return `string' if point is within a string, `block-comment' or
+`comment' is point is within a comment or the depth within all
+parenthesis-syntax delimiters at point otherwise.
+WARNING: this may alter `match-data'."
+  :XEMACS
+  (or (buffer-syntactic-context) (buffer-syntactic-context-depth))
+  :EMACS
+  (let ((orig (point)) diff state
+        ;; Arg, Emacs's (buffer-modified-tick) changes with font-lock.  Use
+        ;; hack that `loudly' is bound during font-locking => cache use will
+        ;; increase from 7% to 99.99% during font-locking.
+        (tick (or (boundp antlr-slow-cache-enabling-symbol)
+                  (buffer-modified-tick))))
+    (if (and (cdr antlr-slow-context-cache)
+             (>= (setq diff (- orig (cadr antlr-slow-context-cache))) 0)
+             (< diff antlr-slow-cache-diff-threshold)
+             (eq (current-buffer) (caar antlr-slow-context-cache))
+             (eq tick (cdar antlr-slow-context-cache)))
+        ;; (setq antlr-statistics-cache (1+ antlr-statistics-cache) ...)
+        (setq state (parse-partial-sexp (cadr antlr-slow-context-cache) orig
+                                        nil nil
+                                        (cddr antlr-slow-context-cache)))
+      (if (>= orig antlr-slow-cache-diff-threshold)
+          (beginning-of-defun)
+        (goto-char (point-min)))
+;;;      (cond ((and diff (< diff 0)) (incf antlr-statistics-full-neg))
+;;;         ((and diff (>= diff 3000)) (incf antlr-statistics-full-diff))
+;;;         (t (incf antlr-statistics-full-other)))
+      (setq state (parse-partial-sexp (point) orig)))
+    (goto-char orig)
+    (if antlr-slow-context-cache
+        (setcdr antlr-slow-context-cache (cons orig state))
+      (setq antlr-slow-context-cache
+            (cons (cons (current-buffer) tick)
+                  (cons orig state))))
+    (cond ((nth 3 state) 'string)
+          ((nth 4 state) 'comment)      ; block-comment? -- we don't care
+          (t (car state)))))
+
+;;;  (incf (aref antlr-statistics 2))
+;;;  (unless (and (eq (current-buffer)
+;;;                (caar antlr-slow-context-cache))
+;;;            (eq (buffer-modified-tick)
+;;;                (cdar antlr-slow-context-cache)))
+;;;    (incf (aref antlr-statistics 1))
+;;;    (setq antlr-slow-context-cache nil))
+;;;  (let* ((orig (point))
+;;;      (base (cadr antlr-slow-context-cache))
+;;;      (curr (cddr antlr-slow-context-cache))
+;;;      (state (cond ((eq orig (car curr)) (cdr curr))
+;;;                   ((eq orig (car base)) (cdr base))))
+;;;      diff diff2)
+;;;    (unless state
+;;;      (incf (aref antlr-statistics 3))
+;;;      (when curr
+;;;     (if (< (setq diff  (abs (- orig (car curr))))
+;;;            (setq diff2 (abs (- orig (car base)))))
+;;;         (setq state curr)
+;;;       (setq state base
+;;;             diff  diff2))
+;;;     (if (or (>= (1+ diff) (point)) (>= diff 3000))
+;;;         (setq state nil)))          ; start from bod/bob
+;;;      (if state
+;;;       (setq state
+;;;             (parse-partial-sexp (car state) orig nil nil (cdr state)))
+;;;     (if (>= orig 3000) (beginning-of-defun) (goto-char (point-min)))
+;;;     (incf (aref antlr-statistics 4))
+;;;     (setq cw (list orig (point) base curr))
+;;;     (setq state (parse-partial-sexp (point) orig)))
+;;;      (goto-char orig)
+;;;      (if antlr-slow-context-cache
+;;;       (setcdr (cdr antlr-slow-context-cache) (cons orig state))
+;;;     (setq antlr-slow-context-cache
+;;;           (cons (cons (current-buffer) (buffer-modified-tick))
+;;;                 (cons (cons orig state) (cons orig state))))))
+;;;    (cond ((nth 3 state) 'string)
+;;;       ((nth 4 state) 'comment)      ; block-comment? -- we don't care
+;;;       (t (car state)))))
+
+;;;    (beginning-of-defun)
+;;;    (let ((state (parse-partial-sexp (point) orig)))
+;;;      (goto-char orig)
+;;;      (cond ((nth 3 state) 'string)
+;;;         ((nth 4 state) 'comment)    ; block-comment? -- we don't care
+;;;         (t (car state))))))
+
+
+;;;===========================================================================
+;;;  Miscellaneous functions
+;;;===========================================================================
+
+(defun antlr-upcase-p (char)
+  "Non-nil, if CHAR is an uppercase character (if CHAR was a char)."
+  ;; in XEmacs, upcase only works for ASCII
+  (or (and (<= ?A char) (<= char ?Z))
+      (and (<= ?\300 char) (<= char ?\337)))) ; ?\327 is no letter
+
+(defun antlr-re-search-forward (regexp bound)
+  "Search forward from point for regular expression REGEXP.
+Set point to the end of the occurrence found, and return point.  Return
+nil if no occurrence was found.  Do not search within comments, strings
+and actions/semantic predicates.  BOUND bounds the search; it is a
+buffer position.  See also the functions `match-beginning', `match-end'
+and `replace-match'."
+  ;; WARNING: Should only be used with `antlr-action-syntax-table'!
+  (let ((continue t))
+    (while (and (re-search-forward regexp bound 'limit)
+                (save-match-data
+                  (if (eq (antlr-syntactic-context) 0)
+                      (setq continue nil)
+                    t))))
+    (if continue nil (point))))
+
+(defun antlr-search-forward (string)
+  "Search forward from point for STRING.
+Set point to the end of the occurrence found, and return point.  Return
+nil if no occurrence was found.  Do not search within comments, strings
+and actions/semantic predicates."
+  ;; WARNING: Should only be used with `antlr-action-syntax-table'!
+  (let ((continue t))
+    (while (and (search-forward string nil 'limit)
+                (if (eq (antlr-syntactic-context) 0) (setq continue nil) t)))
+    (if continue nil (point))))
+
+(defun antlr-search-backward (string)
+  "Search backward from point for STRING.
+Set point to the beginning of the occurrence found, and return point.
+Return nil if no occurrence was found.  Do not search within comments,
+strings and actions/semantic predicates."
+  ;; WARNING: Should only be used with `antlr-action-syntax-table'!
+  (let ((continue t))
+    (while (and (search-backward string nil 'limit)
+                (if (eq (antlr-syntactic-context) 0) (setq continue nil) t)))
+    (if continue nil (point))))
+
+(defsubst antlr-skip-sexps (count)
+  "Skip the next COUNT balanced expressions and the comments after it.
+Return position before the comments after the last expression."
+  (goto-char (or (ignore-errors-x (scan-sexps (point) count)) (point-max)))
+  (prog1 (point)
+    (antlr-c-forward-sws)))
+
+
+;;;===========================================================================
+;;;  font-lock
+;;;===========================================================================
+
+(defun antlr-font-lock-keywords ()
+  "Return font-lock keywords for current buffer.
+See `antlr-font-lock-additional-keywords', `antlr-language' and
+`antlr-font-lock-maximum-decoration'."
+  (if (eq antlr-font-lock-maximum-decoration 'none)
+      antlr-font-lock-additional-keywords
+    (append antlr-font-lock-additional-keywords
+            (eval (let ((major-mode antlr-language)) ; dynamic
+                        (font-lock-choose-keywords
+                         (cdr (assq antlr-language
+                                    antlr-font-lock-keywords-alist))
+                         (if (eq antlr-font-lock-maximum-decoration 'inherit)
+                             font-lock-maximum-decoration
+                           antlr-font-lock-maximum-decoration)))))))
+
+
+;;;===========================================================================
+;;;  imenu support
+;;;===========================================================================
+
+(defun antlr-grammar-tokens ()
+  "Return alist for tokens defined in current buffer."
+  (save-excursion (antlr-imenu-create-index-function t)))
+
+(defun antlr-imenu-create-index-function (&optional tokenrefs-only)
+  "Return imenu index-alist for ANTLR grammar files.
+IF TOKENREFS-ONLY is non-nil, just return alist with tokenref names."
+  (let ((items nil)
+        (classes nil)
+        (continue t))
+    ;; Using `imenu-progress-message' would require imenu for compilation, but
+    ;; nobody is missing these messages.  The generic imenu function searches
+    ;; backward, which is slower and more likely not to work during editing.
+    (antlr-with-syntax-table antlr-action-syntax-table
+      (antlr-invalidate-context-cache)
+      (goto-char (point-min))
+      (antlr-skip-file-prelude t)
+      (while continue
+        (if (looking-at "{") (antlr-skip-sexps 1))
+        (if (looking-at antlr-class-header-regexp)
+            (or tokenrefs-only
+                (push (cons (match-string 2)
+                            (if imenu-use-markers
+                                (copy-marker (match-beginning 2))
+                              (match-beginning 2)))
+                      classes))
+          (if (looking-at "p\\(ublic\\|rotected\\|rivate\\)")
+              (antlr-skip-sexps 1))
+          (when (looking-at "\\sw+")
+            (if tokenrefs-only
+                (if (antlr-upcase-p (char-after (point)))
+                    (push (list (match-string 0)) items))
+              (push (cons (match-string 0)
+                          (if imenu-use-markers
+                              (copy-marker (match-beginning 0))
+                            (match-beginning 0)))
+                    items))))
+        (if (setq continue (antlr-search-forward ";"))
+            (antlr-skip-exception-part t))))
+    (if classes
+        (cons (cons "Classes" (nreverse classes)) (nreverse items))
+      (nreverse items))))
+
+
+;;;===========================================================================
+;;;  Parse grammar files (internal functions)
+;;;===========================================================================
+
+(defun antlr-skip-exception-part (skip-comment)
+  "Skip exception part of current rule, i.e., everything after `;'.
+This also includes the options and tokens part of a grammar class
+header.  If SKIP-COMMENT is non-nil, also skip the comment after that
+part."
+  (let ((pos (point))
+        (class nil))
+    (antlr-c-forward-sws)
+    (while (looking-at "options\\>\\|tokens\\>")
+      (setq class t)
+      (setq pos (antlr-skip-sexps 2)))
+    (if class
+        ;; Problem: an action only belongs to a class def, not a normal rule.
+        ;; But checking the current rule type is too expensive => only expect
+        ;; an action if we have found an option or tokens part.
+        (if (looking-at "{") (setq pos (antlr-skip-sexps 1)))
+      (while (looking-at "exception\\>")
+        (setq pos (antlr-skip-sexps 1))
+        (when (looking-at "\\[")
+          (setq pos (antlr-skip-sexps 1)))
+        (while (looking-at "catch\\>")
+          (setq pos (antlr-skip-sexps 3)))))
+    (or skip-comment (goto-char pos))))
+
+(defun antlr-skip-file-prelude (skip-comment)
+  "Skip the file prelude: the header and file options.
+If SKIP-COMMENT is non-nil, also skip the comment after that part.
+Return the start position of the file prelude.
+
+Hack: if SKIP-COMMENT is `header-only' only skip header and return
+position before the comment after the header."
+  (let* ((pos (point))
+         (pos0 pos))
+    (antlr-c-forward-sws)
+    (if skip-comment (setq pos0 (point)))
+    (while (looking-at "header\\>[ \t]*\\(\"\\)?")
+      (setq pos (antlr-skip-sexps (if (match-beginning 1) 3 2))))
+    (if (eq skip-comment 'header-only)  ; a hack...
+        pos
+      (when (looking-at "options\\>")
+        (setq pos (antlr-skip-sexps 2)))
+      (or skip-comment (goto-char pos))
+      pos0)))
+
+(defun antlr-next-rule (arg skip-comment)
+  "Move forward to next end of rule.  Do it ARG many times.
+A grammar class header and the file prelude are also considered as a
+rule.  Negative argument ARG means move back to ARGth preceding end of
+rule.  The behavior is not defined when ARG is zero.  If SKIP-COMMENT
+is non-nil, move to beginning of the rule."
+  ;; WARNING: Should only be used with `antlr-action-syntax-table'!
+  ;; PRE: ARG<>0
+  (let ((pos (point))
+        (beg (point)))
+    ;; first look whether point is in exception part
+    (if (antlr-search-backward ";")
+        (progn
+          (setq beg (point))
+          (forward-char)
+          (antlr-skip-exception-part skip-comment))
+      (antlr-skip-file-prelude skip-comment))
+    (if (< arg 0)
+        (unless (and (< (point) pos) (zerop (incf arg)))
+          ;; if we have moved backward, we already moved one defun backward
+          (goto-char beg)               ; rewind (to ";" / point)
+          (while (and arg (<= (incf arg) 0))
+            (if (antlr-search-backward ";")
+                (setq beg (point))
+              (when (>= arg -1)
+                ;; try file prelude:
+                (setq pos (antlr-skip-file-prelude skip-comment))
+                (if (zerop arg)
+                    (if (>= (point) beg)
+                        (goto-char (if (>= pos beg) (point-min) pos)))
+                  (goto-char (if (or (>= (point) beg) (= (point) pos))
+                                 (point-min) pos))))
+              (setq arg nil)))
+          (when arg                     ; always found a ";"
+            (forward-char)
+            (antlr-skip-exception-part skip-comment)))
+      (if (<= (point) pos)              ; moved backward?
+          (goto-char pos)               ; rewind
+        (decf arg))                     ; already moved one defun forward
+      (unless (zerop arg)
+        (while (>= (decf arg) 0)
+          (antlr-search-forward ";"))
+        (antlr-skip-exception-part skip-comment)))))
+
+(defun antlr-outside-rule-p ()
+  "Non-nil if point is outside a grammar rule.
+Move to the beginning of the current rule if point is inside a rule."
+  ;; WARNING: Should only be used with `antlr-action-syntax-table'!
+  (let ((pos (point)))
+    (antlr-next-rule -1 nil)
+    (let ((between (or (bobp) (< (point) pos))))
+      (antlr-c-forward-sws)
+      (and between (> (point) pos) (goto-char pos)))))
+
+
+;;;===========================================================================
+;;;  Parse grammar files (commands)
+;;;===========================================================================
+;; No (interactive "_") in Emacs... use `zmacs-region-stays'.
+
+(defun antlr-inside-rule-p ()
+  "Non-nil if point is inside a grammar rule.
+A grammar class header and the file prelude are also considered as a
+rule."
+  (save-excursion
+    (antlr-with-syntax-table antlr-action-syntax-table
+      (not (antlr-outside-rule-p)))))
+
+(defunx antlr-end-of-rule (&optional arg)
+  "Move forward to next end of rule.  Do it ARG [default: 1] many times.
+A grammar class header and the file prelude are also considered as a
+rule.  Negative argument ARG means move back to ARGth preceding end of
+rule.  If ARG is zero, run `antlr-end-of-body'."
+  (interactive "_p")
+  (if (zerop arg)
+      (antlr-end-of-body)
+    (antlr-with-syntax-table antlr-action-syntax-table
+      (antlr-next-rule arg nil))))
+
+(defunx antlr-beginning-of-rule (&optional arg)
+  "Move backward to preceding beginning of rule.  Do it ARG many times.
+A grammar class header and the file prelude are also considered as a
+rule.  Negative argument ARG means move forward to ARGth next beginning
+of rule.  If ARG is zero, run `antlr-beginning-of-body'."
+  (interactive "_p")
+  (if (zerop arg)
+      (antlr-beginning-of-body)
+    (antlr-with-syntax-table antlr-action-syntax-table
+      (antlr-next-rule (- arg) t))))
+
+(defunx antlr-end-of-body (&optional msg)
+  "Move to position after the `;' of the current rule.
+A grammar class header is also considered as a rule.  With optional
+prefix arg MSG, move to `:'."
+  (interactive "_")
+  (antlr-with-syntax-table antlr-action-syntax-table
+    (let ((orig (point)))
+      (if (antlr-outside-rule-p)
+          (error "Outside an ANTLR rule"))
+      (let ((bor (point)))
+        (when (< (antlr-skip-file-prelude t) (point))
+          ;; Yes, we are in the file prelude
+          (goto-char orig)
+          (error (or msg "The file prelude is without `;'")))
+        (antlr-search-forward ";")
+        (when msg
+          (when (< (point)
+                   (progn (goto-char bor)
+                          (or (antlr-search-forward ":") (point-max))))
+            (goto-char orig)
+            (error msg))
+          (antlr-c-forward-sws))))))
+
+(defunx antlr-beginning-of-body ()
+  "Move to the first element after the `:' of the current rule."
+  (interactive "_")
+  (antlr-end-of-body "Class headers and the file prelude are without `:'"))
+
+
+;;;===========================================================================
+;;;  Literal normalization, Hide Actions
+;;;===========================================================================
+
+(defun antlr-downcase-literals (&optional transform)
+  "Convert all literals in buffer to lower case.
+If non-nil, TRANSFORM is used on literals instead of `downcase-region'."
+  (interactive)
+  (or transform (setq transform 'downcase-region))
+  (let ((literals 0))
+    (save-excursion
+      (goto-char (point-min))
+      (antlr-with-syntax-table antlr-action-syntax-table
+        (antlr-invalidate-context-cache)
+        (while (antlr-re-search-forward "\"\\(\\sw\\(\\sw\\|-\\)*\\)\"" nil)
+          (funcall transform (match-beginning 0) (match-end 0))
+          (incf literals))))
+    (message "Transformed %d literals" literals)))
+
+(defun antlr-upcase-literals ()
+  "Convert all literals in buffer to upper case."
+  (interactive)
+  (antlr-downcase-literals 'upcase-region))
+
+(defun antlr-hide-actions (arg &optional silent)
+  "Hide or unhide all actions in buffer.
+Hide all actions including arguments in brackets if ARG is 1 or if
+called interactively without prefix argument.  Hide all actions
+excluding arguments in brackets if ARG is 2 or higher.  Unhide all
+actions if ARG is 0 or negative.  See `antlr-action-visibility'.
+
+Display a message unless optional argument SILENT is non-nil."
+  (interactive "p")
+  (save-buffer-state-x
+    (if (> arg 0)
+        (let ((regexp (if (= arg 1) "[]}]" "}"))
+              (diff (and antlr-action-visibility
+                         (+ (max antlr-action-visibility 0) 2))))
+          (antlr-hide-actions 0 t)
+          (save-excursion
+            (goto-char (point-min))
+            (antlr-with-syntax-table antlr-action-syntax-table
+              (antlr-invalidate-context-cache)
+              (while (antlr-re-search-forward regexp nil)
+                (let ((beg (ignore-errors-x (scan-sexps (point) -1))))
+                  (when beg
+                    (if diff            ; braces are visible
+                        (if (> (point) (+ beg diff))
+                            (add-text-properties (1+ beg) (1- (point))
+                                                 '(invisible t intangible t)))
+                      ;; if actions is on line(s) of its own, hide WS
+                      (and (looking-at "[ \t]*$")
+                           (save-excursion
+                             (goto-char beg)
+                             (skip-chars-backward " \t")
+                             (and (bolp) (setq beg (point))))
+                           (beginning-of-line 2)) ; beginning of next line
+                      (add-text-properties beg (point)
+                                           '(invisible t intangible t))))))))
+          (or silent
+              (message "Hide all actions (%s arguments)...done"
+                       (if (= arg 1) "including" "excluding"))))
+      (remove-text-properties (point-min) (point-max)
+                              '(invisible nil intangible nil))
+      (or silent
+          (message "Unhide all actions (including arguments)...done")))))
+
+
+;;;===========================================================================
+;;;  Insert option: command
+;;;===========================================================================
+
+(defun antlr-insert-option (level option &optional location)
+  "Insert file/grammar/rule/subrule option near point.
+LEVEL determines option kind to insert: 1=file, 2=grammar, 3=rule,
+4=subrule.  OPTION is a string with the name of the option to insert.
+LOCATION can be specified for not calling `antlr-option-kind' twice.
+
+Inserting an option with this command works as follows:
+
+ 1. When called interactively, LEVEL is determined by the prefix
+    argument or automatically deduced without prefix argument.
+ 2. Signal an error if no option of that level could be inserted, e.g.,
+    if the buffer is read-only, the option area is outside the visible
+    part of the buffer or a subrule/rule option should be inserted with
+    point outside a subrule/rule.
+ 3. When called interactively, OPTION is read from the minibuffer with
+    completion over the known options of the given LEVEL.
+ 4. Ask user for confirmation if the given OPTION does not seem to be a
+    valid option to insert into the current file.
+ 5. Find a correct position to insert the option.
+ 6. Depending on the option, insert it the following way \(inserting an
+    option also means inserting the option section if necessary\):
+     - Insert the option and let user insert the value at point.
+     - Read a value (with completion) from the minibuffer, using a
+       previous value as initial contents, and insert option with value.
+ 7. Final action depending on the option.  For example, set the language
+    according to a newly inserted language option.
+
+The name of all options with a specification for their values are stored
+in `antlr-options-alists'.  The used specification also depends on the
+value of `antlr-tool-version', i.e., step 4 will warn you if you use an
+option that has been introduced in newer version of ANTLR, and step 5
+will offer completion using version-correct values.
+
+If the option already exists inside the visible part of the buffer, this
+command can be used to change the value of that option.  Otherwise, find
+a correct position where the option can be inserted near point.
+
+The search for a correct position is as follows:
+
+  * If search is within an area where options can be inserted, use the
+    position of point.  Inside the options section and if point is in
+    the middle of a option definition, skip the rest of it.
+  * If an options section already exists, insert the options at the end.
+    If only the beginning of the area is visible, insert at the
+    beginning.
+  * Otherwise, find the position where an options section can be
+    inserted and insert a new section before any comments.  If the
+    position before the comments is not visible, insert the new section
+    after the comments.
+
+This function also inserts \"options {...}\" and the \":\" if necessary,
+see `antlr-options-auto-colon'.  See also `antlr-options-assign-string'.
+
+This command might also set the mark like \\[set-mark-command] does, see
+`antlr-options-push-mark'."
+  (interactive (antlr-insert-option-interactive current-prefix-arg))
+  (barf-if-buffer-read-only)
+  (or location (setq location (cdr (antlr-option-kind level))))
+  (cond ((null level)
+         (error "Cannot deduce what kind of option to insert"))
+        ((atom location)
+         (error "Cannot insert any %s options around here"
+                (elt antlr-options-headings (1- level)))))
+  (let ((area (car location))
+        (place (cdr location)))
+    (cond ((null place)                 ; invisible
+           (error (if area
+                      "Invisible %s options, use %s to make them visible"
+                    "Invisible area for %s options, use %s to make it visible")
+                  (elt antlr-options-headings (1- level))
+                  (substitute-command-keys "\\[widen]")))
+          ((null area)                  ; without option part
+           (antlr-insert-option-do level option nil
+                                   (null (cdr place))
+                                   (car place)))
+          ((save-excursion              ; with option part, option visible
+             (goto-char (max (point-min) (car area)))
+             (re-search-forward (concat "\\(^\\|;\\)[ \t]*\\(\\<"
+                                        (regexp-quote option)
+                                        "\\>\\)[ \t\n]*\\(\\(=[ \t]?\\)[ \t]*\\(\\(\\sw\\|\\s_\\)+\\|\"\\([^\n\"\\]\\|[\\][^\n]\\)*\"\\)?\\)?")
+                                ;; 2=name, 3=4+5, 4="=", 5=value
+                                (min (point-max) (cdr area))
+                                t))
+           (antlr-insert-option-do level option
+                                   (cons (or (match-beginning 5)
+                                             (match-beginning 3))
+                                         (match-end 5))
+                                   (and (null (cdr place)) area)
+                                   (or (match-beginning 5)
+                                       (match-end 4)
+                                       (match-end 2))))
+          (t                            ; with option part, option not yet
+           (antlr-insert-option-do level option t
+                                   (and (null (cdr place)) area)
+                                   (car place))))))
+
+(defun antlr-insert-option-interactive (arg)
+  "Interactive specification for `antlr-insert-option'.
+Return \(LEVEL OPTION LOCATION)."
+  (barf-if-buffer-read-only)
+  (if arg (setq arg (prefix-numeric-value arg)))
+  (unless (memq arg '(nil 1 2 3 4))
+    (error "Valid prefix args: no=auto, 1=file, 2=grammar, 3=rule, 4=subrule"))
+  (let* ((kind (antlr-option-kind arg))
+         (level (car kind)))
+    (if (atom (cdr kind))
+        (list level nil (cdr kind))
+      (let* ((table (elt antlr-options-alists (1- level)))
+             (completion-ignore-case t)         ;dynamic
+             (input (completing-read (format "Insert %s option: "
+                                             (elt antlr-options-headings
+                                                  (1- level)))
+                                     table)))
+        (list level input (cdr kind))))))
+
+(defun antlr-options-menu-filter (level _menu-items)
+  "Return items for options submenu of level LEVEL."
+  ;; checkdoc-params: (menu-items)
+  (let ((active (if buffer-read-only
+                    nil
+                  (consp (cdr-safe (cdr (antlr-option-kind level)))))))
+    (mapcar (lambda (option)
+              (vector option
+                      (list 'antlr-insert-option level option)
+                      :active active))
+            (sort (mapcar 'car (elt antlr-options-alists (1- level)))
+                  'string-lessp))))
+
+
+;;;===========================================================================
+;;;  Insert option: determine section-kind
+;;;===========================================================================
+
+(defun antlr-option-kind (requested)
+  "Return level and location for option to insert near point.
+Call function `antlr-option-level' with argument REQUESTED.  If the
+result is nil, return \(REQUESTED \. error).  If the result has the
+non-nil value LEVEL, return \(LEVEL \. LOCATION) where LOCATION looks
+like \(AREA \. PLACE), see `antlr-option-location'."
+  (save-excursion
+    (save-restriction
+      (let ((min0 (point-min))          ; before `widen'!
+            (max0 (point-max))
+            (orig (point))
+            (level (antlr-option-level requested)) ; calls `widen'!
+            pos)
+        (cond ((null level)
+               (setq level requested))
+              ((eq level 1)             ; file options
+               (goto-char (point-min))
+               (setq pos (antlr-skip-file-prelude 'header-only)))
+              ((not (eq level 3))       ; grammar or subrule options
+               (setq pos (point))
+               (antlr-c-forward-sws))
+              ((looking-at "^\\(private[ \t\n]\\|public[ \t\n]\\|protected[ \t\n]\\)?[ \t\n]*\\(\\(\\sw\\|\\s_\\)+\\)[ \t\n]*\\(!\\)?[ \t\n]*\\(\\[\\)?")
+               ;; rule options, with complete rule header
+               (goto-char (or (match-end 4) (match-end 3)))
+               (setq pos (antlr-skip-sexps (if (match-end 5) 1 0)))
+               (when (looking-at "returns[ \t\n]*\\[")
+                 (goto-char (1- (match-end 0)))
+                 (setq pos (antlr-skip-sexps 1)))))
+        (cons level
+              (cond ((null pos) 'error)
+                    ((looking-at "options[ \t\n]*{")
+                     (goto-char (match-end 0))
+                     (setq pos (ignore-errors-x (scan-lists (point) 1 1)))
+                     (antlr-option-location orig min0 max0
+                                            (point)
+                                            (if pos (1- pos) (point-max))
+                                            t))
+                    (t
+                     (antlr-option-location orig min0 max0
+                                            pos (point)
+                                            nil))))))))
+
+(defun antlr-option-level (requested)
+  "Return level for option to insert near point.
+Remove any restrictions from current buffer and return level for the
+option to insert near point, i.e., 1, 2, 3, 4, or nil if no such option
+can be inserted.  If REQUESTED is non-nil, it is the only possible value
+to return except nil.  If REQUESTED is nil, return level for the nearest
+option kind, i.e., the highest number possible.
+
+If the result is 2, point is at the beginning of the class after the
+class definition.  If the result is 3 or 4, point is at the beginning of
+the rule/subrule after the init action.  Otherwise, the point position
+is undefined."
+  (widen)
+  (if (eq requested 1)
+      1
+    (antlr-with-syntax-table antlr-action-syntax-table
+      (antlr-invalidate-context-cache)
+      (let* ((orig (point))
+             (outsidep (antlr-outside-rule-p))
+             bor depth)
+        (if (eq (char-after) ?\{) (antlr-skip-sexps 1))
+        (setq bor (point))              ; beginning of rule (after init action)
+        (cond ((eq requested 2)                 ; grammar options required?
+               (let (boc)               ; beginning of class
+                 (goto-char (point-min))
+                 (while (and (<= (point) bor)
+                             (antlr-re-search-forward antlr-class-header-regexp
+                                                      nil))
+                   (if (<= (match-beginning 0) bor)
+                       (setq boc (match-end 0))))
+                 (when boc
+                   (goto-char boc)
+                   2)))
+              ((save-excursion          ; in region of file options?
+                 (goto-char (point-min))
+                 (antlr-skip-file-prelude t) ; ws/comment after: OK
+                 (< orig (point)))
+               (and (null requested) 1))
+              (outsidep                         ; outside rule not OK
+               nil)
+              ((looking-at antlr-class-header-regexp) ; rule = class def?
+               (goto-char (match-end 0))
+               (and (null requested) 2))
+              ((eq requested 3)                 ; rule options required?
+               (goto-char bor)
+               3)
+              ((setq depth (antlr-syntactic-grammar-depth orig bor))
+               (if (> depth 0)          ; move out of actions
+                   (goto-char (scan-lists (point) -1 depth)))
+               (set-syntax-table antlr-mode-syntax-table)
+               (antlr-invalidate-context-cache)
+               (if (eq (antlr-syntactic-context) 0) ; not in subrule?
+                   (unless (eq requested 4)
+                     (goto-char bor)
+                     3)
+                 (goto-char (1+ (scan-lists (point) -1 1)))
+                 4)))))))
+
+(defun antlr-option-location (orig min-vis max-vis min-area max-area withp)
+  "Return location for the options area.
+ORIG is the original position of `point', MIN-VIS is `point-min' and
+MAX-VIS is `point-max'.  If WITHP is non-nil, there exists an option
+specification and it starts after the brace at MIN-AREA and stops at
+MAX-AREA.  If WITHP is nil, there is no area and the region where it
+could be inserted starts at MIN-AREA and stops at MAX-AREA.
+
+The result has the form (AREA . PLACE).  AREA is (MIN-AREA . MAX-AREA)
+if WITHP is non-nil, and nil otherwise.  PLACE is nil if the area is
+invisible, (ORIG) if ORIG is inside the area, (MIN-AREA . beginning) for
+a visible start position and (MAX-AREA . end) for a visible end position
+where the beginning is preferred if WITHP is nil and the end if WITHP is
+non-nil."
+  (cons (and withp (cons min-area max-area))
+        (cond ((and (<= min-area orig) (<= orig max-area)
+                    (save-excursion
+                      (goto-char orig)
+                      (not (memq (antlr-syntactic-context)
+                                 '(comment block-comment)))))
+               ;; point in options area and not in comment
+               (list orig))
+              ((and (null withp) (<= min-vis min-area) (<= min-area max-vis))
+               ;; use start of options area (only if not `withp')
+               (cons min-area 'beginning))
+              ((and (<= min-vis max-area) (<= max-area max-vis))
+               ;; use end of options area
+               (cons max-area 'end))
+              ((and withp (<= min-vis min-area) (<= min-area max-vis))
+               ;; use start of options area (only if `withp')
+               (cons min-area 'beginning)))))
+
+(defun antlr-syntactic-grammar-depth (pos beg)
+  "Return syntactic context depth at POS.
+Move to POS and from there on to the beginning of the string or comment
+if POS is inside such a construct.  Then, return the syntactic context
+depth at point if the point position is smaller than BEG.
+WARNING: this may alter `match-data'."
+  (goto-char pos)
+  (let ((context (or (antlr-syntactic-context) 0)))
+    (while (and context (not (integerp context)))
+      (cond ((eq context 'string)
+             (setq context
+                   (and (search-backward "\"" nil t)
+                        (>= (point) beg)
+                        (or (antlr-syntactic-context) 0))))
+            ((memq context '(comment block-comment))
+             (setq context
+                   (and (re-search-backward "/[/*]" nil t)
+                        (>= (point) beg)
+                        (or (antlr-syntactic-context) 0))))))
+    context))
+
+
+;;;===========================================================================
+;;;  Insert options: do the insertion
+;;;===========================================================================
+
+(defun antlr-insert-option-do (level option old area pos)
+  "Insert option into buffer at position POS.
+Insert option of level LEVEL and name OPTION.  If OLD is non-nil, an
+options area is already exists.  If OLD looks like \(BEG \. END), the
+option already exists.  Then, BEG is the start position of the option
+value, the position of the `=' or nil, and END is the end position of
+the option value or nil.
+
+If the original point position was outside an options area, AREA is nil.
+Otherwise, and if an option specification already exists, AREA is a cons
+cell where the two values determine the area inside the braces."
+  (let* ((spec (cdr (assoc option (elt antlr-options-alists (1- level)))))
+         (value (antlr-option-spec level option (cdr spec) (consp old))))
+    (if (fboundp (car spec)) (funcall (car spec) 'before-input option))
+    ;; set mark (unless point was inside options area before)
+    (if (cond (area (eq antlr-options-push-mark t))
+              ((numberp antlr-options-push-mark)
+               (> (count-lines (min (point) pos) (max (point) pos))
+                  antlr-options-push-mark))
+              (antlr-options-push-mark))
+        (push-mark))
+    ;; read option value -----------------------------------------------------
+    (goto-char pos)
+    (if (null value)
+        ;; no option specification found
+        (if (y-or-n-p (format "Insert unknown %s option %s? "
+                              (elt antlr-options-headings (1- level))
+                              option))
+            (message "Insert value for %s option %s"
+                     (elt antlr-options-headings (1- level))
+                     option)
+          (error "Didn't insert unknown %s option %s"
+                 (elt antlr-options-headings (1- level))
+                 option))
+      ;; option specification found
+      (setq value (cdr value))
+      (if (car value)
+          (let ((initial (and (consp old) (cdr old)
+                              (buffer-substring (car old) (cdr old)))))
+            (setq value (apply (car value)
+                               (and initial
+                                    (if (eq (aref initial 0) ?\")
+                                        (read initial)
+                                      initial))
+                               (cdr value))))
+        (message "%s" (or (cadr value) ""))
+        (setq value nil)))
+    ;; insert value ----------------------------------------------------------
+    (if (consp old)
+        (antlr-insert-option-existing old value)
+      (if (consp area)
+          ;; Move outside string/comment if point is inside option spec
+          (antlr-syntactic-grammar-depth (point) (car area)))
+      (antlr-insert-option-space area old)
+      (or old (antlr-insert-option-area level))
+      (insert option " = ;")
+      (backward-char)
+      (if value (insert value)))
+    ;; final -----------------------------------------------------------------
+    (if (fboundp (car spec)) (funcall (car spec) 'after-insertion option))))
+
+(defun antlr-option-spec (level option specs existsp)
+  "Return version correct option value specification.
+Return specification for option OPTION of kind level LEVEL.  SPECS
+should correspond to the VALUE-SPEC... in `antlr-option-alists'.
+EXISTSP determines whether the option already exists."
+  (let (value)
+    (while (and specs (>= antlr-tool-version (caar specs)))
+      (setq value (pop specs)))
+    (cond (value)                       ; found correct spec
+          ((null specs) nil)            ; didn't find any specs
+          (existsp (car specs))         ; wrong version, but already present
+          ((y-or-n-p (format "Insert v%s %s option %s in v%s? "
+                             (antlr-version-string (caar specs))
+                             (elt antlr-options-headings (1- level))
+                             option
+                             (antlr-version-string antlr-tool-version)))
+           (car specs))
+          (t
+           (error "Didn't insert v%s %s option %s in v%s"
+                  (antlr-version-string (caar specs))
+                  (elt antlr-options-headings (1- level))
+                  option
+                  (antlr-version-string antlr-tool-version))))))
+
+(defun antlr-version-string (version)
+  "Format the Antlr version number VERSION, see `antlr-tool-version'."
+  (let ((version100 (/ version 100)))
+    (format "%d.%d.%d"
+            (/ version100 100) (mod version100 100) (mod version 100))))
+
+
+;;;===========================================================================
+;;;  Insert options: the details (used by `antlr-insert-option-do')
+;;;===========================================================================
+
+(defun antlr-insert-option-existing (old value)
+  "Insert option value VALUE at point for existing option.
+For OLD, see `antlr-insert-option-do'."
+  ;; no = => insert =
+  (unless (car old) (insert antlr-options-assign-string))
+  ;; with user input => insert if necessary
+  (when value
+    (if (cdr old)               ; with value
+        (if (string-equal value (buffer-substring (car old) (cdr old)))
+            (goto-char (cdr old))
+          (delete-region (car old) (cdr old))
+          (insert value))
+      (insert value)))
+  (unless (looking-at "\\([^\n=;{}/'\"]\\|'\\([^\n'\\]\\|\\\\.\\)*'\\|\"\\([^\n\"\\]\\|\\\\.\\)*\"\\)*;")
+    ;; stuff (no =, {, } or /) at point is not followed by ";"
+    (insert ";")
+    (backward-char)))
+
+(defun antlr-insert-option-space (area old)
+  "Find appropriate place to insert option, insert newlines/spaces.
+For AREA and OLD, see `antlr-insert-option-do'."
+  (let ((orig (point))
+        (open t))
+    (skip-chars-backward " \t")
+    (unless (bolp)
+      (let ((before (char-after (1- (point)))))
+        (goto-char orig)
+        (and old                        ; with existing options area
+             (consp area)               ; if point inside existing area
+             (not (eq before ?\;))      ; if not at beginning of option
+                                        ; => skip to end of option
+             (if (and (search-forward ";" (cdr area) t)
+                      (let ((context (antlr-syntactic-context)))
+                        (or (null context) (numberp context))))
+                 (setq orig (point))
+               (goto-char orig)))
+        (skip-chars-forward " \t")
+
+        (if (looking-at "$\\|//")
+            ;; just comment after point => skip (+ lines w/ same col comment)
+            (let ((same (if (> (match-end 0) (match-beginning 0))
+                            (current-column))))
+              (beginning-of-line 2)
+              (or (bolp) (insert "\n"))
+              (when (and same (null area)) ; or (consp area)?
+                (while (and (looking-at "[ \t]*\\(//\\)")
+                            (goto-char (match-beginning 1))
+                            (= (current-column) same))
+                  (beginning-of-line 2)
+                  (or (bolp) (insert "\n")))))
+          (goto-char orig)
+          (if (null old)
+              (progn (insert "\n") (antlr-indent-line))
+            (unless (eq (char-after (1- (point))) ?\ )
+              (insert " "))
+            (unless (eq (char-after (point)) ?\ )
+              (insert " ")
+              (backward-char))
+            (setq open nil)))))
+    (when open
+      (beginning-of-line 1)
+      (insert "\n")
+      (backward-char)
+      (antlr-indent-line))))
+
+(defun antlr-insert-option-area (level)
+  "Insert new options area for options of level LEVEL.
+Used by `antlr-insert-option-do'."
+  (insert "options {\n\n}")
+  (when (and antlr-options-auto-colon
+             (memq level '(3 4))
+             (save-excursion
+               (antlr-c-forward-sws)
+               (if (eq (char-after (point)) ?\{) (antlr-skip-sexps 1))
+               (not (eq (char-after (point)) ?\:))))
+    (insert "\n:")
+    (antlr-indent-line)
+    (end-of-line 0))
+  (backward-char 1)
+  (antlr-indent-line)
+  (beginning-of-line 0)
+  (antlr-indent-line))
+
+
+;;;===========================================================================
+;;;  Insert options: in `antlr-options-alists'
+;;;===========================================================================
+
+(defun antlr-read-value (initial-contents prompt
+                                          &optional as-string table table-x)
+  "Read a string from the minibuffer, possibly with completion.
+If INITIAL-CONTENTS is non-nil, insert it in the minibuffer initially.
+PROMPT is a string to prompt with, normally it ends in a colon and a
+space.  If AS-STRING is t or is a member \(comparison done with `eq') of
+`antlr-options-style', return printed representation of the user input,
+otherwise return the user input directly.
+
+If TABLE or TABLE-X is non-nil, read with completion.  The completion
+table is the resulting alist of TABLE-X concatenated with TABLE where
+TABLE can also be a function evaluation to an alist.
+
+Used inside `antlr-options-alists'."
+  (let* ((completion-ignore-case t)     ; dynamic
+         (table0 (and (or table table-x)
+                      (append table-x
+                              (if (functionp table) (funcall table) table))))
+         (input (if table0
+                    (completing-read prompt table0 nil nil initial-contents)
+                  (read-from-minibuffer prompt initial-contents))))
+    (if (and as-string
+             (or (eq as-string t)
+                 (cdr (assq as-string antlr-options-style))))
+        (format "%S" input)
+      input)))
+
+(defun antlr-read-boolean (initial-contents prompt &optional table)
+  "Read a boolean value from the minibuffer, with completion.
+If INITIAL-CONTENTS is non-nil, insert it in the minibuffer initially.
+PROMPT is a string to prompt with, normally it ends in a question mark
+and a space.  \"(true or false) \" is appended if TABLE is nil.
+
+Read with completion over \"true\", \"false\" and the keys in TABLE, see
+also `antlr-read-value'.
+
+Used inside `antlr-options-alists'."
+  (antlr-read-value initial-contents
+                    (if table prompt (concat prompt "(true or false) "))
+                    nil
+                    table '(("false") ("true"))))
+
+(defun antlr-language-option-extra (phase &rest _dummies)
+;; checkdoc-params: (dummies)
+  "Change language according to the new value of the \"language\" option.
+Call `antlr-mode' if the new language would be different from the value
+of `antlr-language', keeping the value of variable `font-lock-mode'.
+
+Called in PHASE `after-insertion', see `antlr-options-alists'."
+  (when (eq phase 'after-insertion)
+    (let ((new-language (antlr-language-option t)))
+      (or (null new-language)
+          (eq new-language antlr-language)
+          (let ((font-lock (and (boundp 'font-lock-mode) font-lock-mode)))
+            (if font-lock (font-lock-mode 0))
+            (antlr-mode)
+            (and font-lock (null font-lock-mode) (font-lock-mode 1)))))))
+
+(defun antlr-c++-mode-extra (phase option &rest _dummies)
+;; checkdoc-params: (option dummies)
+  "Warn if C++ option is used with the wrong language.
+Ask user \(\"y or n\"), if a C++ only option is going to be inserted but
+`antlr-language' has not the value `c++-mode'.
+
+Called in PHASE `before-input', see `antlr-options-alists'."
+  (and (eq phase 'before-input)
+       (not (eq antlr-language 'c++-mode))
+       (not (y-or-n-p (format "Insert C++ %s option? " option)))
+       (error "Didn't insert C++ %s option with language %s"
+              option (cadr (assq antlr-language antlr-language-alist)))))
+
+
+;;;===========================================================================
+;;;  Compute dependencies
+;;;===========================================================================
+
+(defun antlr-file-dependencies ()
+  "Return dependencies for grammar in current buffer.
+The result looks like \(FILE \(CLASSES \. SUPERS) VOCABS \. LANGUAGE)
+  where CLASSES = ((CLASS . CLASS-EVOCAB) ...),
+        SUPERS  = ((SUPER . USE-EVOCAB-P) ...), and
+        VOCABS  = ((EVOCAB ...) . (IVOCAB ...))
+
+FILE is the current buffer's file-name without directory part and
+LANGUAGE is the value of `antlr-language' in the current buffer.  Each
+EVOCAB is an export vocabulary and each IVOCAB is an import vocabulary.
+
+Each CLASS is a grammar class with its export vocabulary CLASS-EVOCAB.
+Each SUPER is a super-grammar class where USE-EVOCAB-P indicates whether
+its export vocabulary is used as an import vocabulary."
+  (unless buffer-file-name
+    (error "Grammar buffer does not visit a file"))
+  (let (classes export-vocabs import-vocabs superclasses default-vocab)
+    (antlr-with-syntax-table antlr-action-syntax-table
+      (goto-char (point-min))
+      (while (antlr-re-search-forward antlr-class-header-regexp nil)
+        ;; parse class definition --------------------------------------------
+        (let* ((class (match-string 2))
+               (sclass (match-string 4))
+               ;; export vocab defaults to class name (first grammar in file)
+               ;; or to the export vocab of the first grammar in file:
+               (evocab (or default-vocab class))
+               (ivocab nil))
+          (goto-char (match-end 0))
+          (antlr-c-forward-sws)
+          (while (looking-at "options\\>\\|\\(tokens\\)\\>")
+            (if (match-beginning 1)
+                (antlr-skip-sexps 2)
+              (goto-char (match-end 0))
+              (antlr-c-forward-sws)
+              ;; parse grammar option sections -------------------------------
+              (when (eq (char-after (point)) ?\{)
+                (let* ((beg (1+ (point)))
+                       (end (1- (antlr-skip-sexps 1)))
+                       (cont (point)))
+                (goto-char beg)
+                (if (re-search-forward "\\<exportVocab[ \t]*=[ \t]*\\([A-Za-z\300-\326\330-\337]\\sw*\\)" end t)
+                    (setq evocab (match-string 1)))
+                (goto-char beg)
+                (if (re-search-forward "\\<importVocab[ \t]*=[ \t]*\\([A-Za-z\300-\326\330-\337]\\sw*\\)" end t)
+                    (setq ivocab (match-string 1)))
+                (goto-char cont)))))
+          (unless (member sclass '("Parser" "Lexer" "TreeParser"))
+            (let ((super (assoc sclass superclasses)))
+              (if super
+                  (or ivocab (setcdr super t))
+                (push (cons sclass (null ivocab)) superclasses))))
+          ;; remember class with export vocabulary:
+          (push (cons class evocab) classes)
+          ;; default export vocab is export vocab of first grammar in file:
+          (or default-vocab (setq default-vocab evocab))
+          (or (member evocab export-vocabs) (push evocab export-vocabs))
+          (or (null ivocab)
+              (member ivocab import-vocabs) (push ivocab import-vocabs)))))
+    (if classes
+        (list* (file-name-nondirectory buffer-file-name)
+               (cons (nreverse classes) (nreverse superclasses))
+               (cons (nreverse export-vocabs) (nreverse import-vocabs))
+               antlr-language))))
+
+(defun antlr-directory-dependencies (dirname)
+  "Return dependencies for all grammar files in directory DIRNAME.
+The result looks like \((CLASS-SPEC ...) \. \(FILE-DEP ...))
+  where CLASS-SPEC = (CLASS (FILE \. EVOCAB) ...).
+
+FILE-DEP are the dependencies for each grammar file in DIRNAME, see
+`antlr-file-dependencies'.  For each grammar class CLASS, FILE is a
+grammar file in which CLASS is defined and EVOCAB is the name of the
+export vocabulary specified in that file."
+  (let ((grammar (directory-files dirname t "\\.g\\'")))
+    (when grammar
+      (let ((antlr-imenu-name nil)              ; dynamic-let: no imenu
+            (expanded-regexp
+             (concat (format (regexp-quote
+                              (cadr antlr-special-file-formats))
+                             ".+")
+                     "\\'"))
+            classes dependencies)
+        (with-temp-buffer
+          (dolist (file grammar)
+            (when (and (file-regular-p file)
+                       (null (string-match expanded-regexp file)))
+              (insert-file-contents file t nil nil t)
+              (normal-mode t)           ; necessary for major-mode, syntax
+                                        ; table and `antlr-language'
+              (when (derived-mode-p 'antlr-mode)
+                (let* ((file-deps (antlr-file-dependencies))
+                       (file (car file-deps)))
+                  (when file-deps
+                    (dolist (class-def (caadr file-deps))
+                      (let ((file-evocab (cons file (cdr class-def)))
+                            (class-spec (assoc (car class-def) classes)))
+                        (if class-spec
+                            (nconc (cdr class-spec) (list file-evocab))
+                          (push (list (car class-def) file-evocab)
+                                classes))))
+                    (push file-deps dependencies)))))))
+        (cons (nreverse classes) (nreverse dependencies))))))
+
+
+;;;===========================================================================
+;;;  Compilation: run ANTLR tool
+;;;===========================================================================
+
+(defun antlr-superclasses-glibs (supers classes)
+  "Compute the grammar lib option for the super grammars SUPERS.
+Look in CLASSES for the right grammar lib files for SUPERS.  SUPERS is
+part SUPER in the result of `antlr-file-dependencies'.  CLASSES is the
+part \(CLASS-SPEC ...) in the result of `antlr-directory-dependencies'.
+
+The result looks like \(OPTION WITH-UNKNOWN GLIB ...).  OPTION is the
+complete \"-glib\" option.  WITH-UNKNOWN is t if there is none or more
+than one grammar file for at least one super grammar.
+
+Each GLIB looks like \(GRAMMAR-FILE \. EVOCAB).  GRAMMAR-FILE is a file
+in which a super-grammar is defined.  EVOCAB is the value of the export
+vocabulary of the super-grammar or nil if it is not needed."
+  ;; If the superclass is defined in the same file, that file will be included
+  ;; with -glib again.  This will lead to a redefinition.  But defining a
+  ;; analyzer of the same class twice in a file will lead to an error anyway...
+  (let (glibs unknown)
+    (while supers
+      (let* ((super (pop supers))
+             (sup-files (cdr (assoc (car super) classes)))
+             (file (and sup-files (null (cdr sup-files)) (car sup-files))))
+        (or file (setq unknown t))      ; not exactly one file
+        (push (cons (or (car file)
+                        (format (car antlr-unknown-file-formats)
+                                (car super)))
+                    (and (cdr super)
+                         (or (cdr file)
+                             (format (cadr antlr-unknown-file-formats)
+                                     (car super)))))
+              glibs)))
+    (cons (if glibs (concat " -glib " (mapconcat 'car glibs ";")) "")
+          (cons unknown glibs))))
+
+(defun antlr-run-tool (command file &optional saved)
+  "Run Antlr took COMMAND on grammar FILE.
+When called interactively, COMMAND is read from the minibuffer and
+defaults to `antlr-tool-command' with a computed \"-glib\" option if
+necessary.
+
+Save all buffers first unless optional value SAVED is non-nil.  When
+called interactively, the buffers are always saved, see also variable
+`antlr-ask-about-save'."
+  (interactive (antlr-run-tool-interactive))
+  (or saved (save-some-buffers (not antlr-ask-about-save)))
+  (let ((default-directory (file-name-directory file)))
+    (compilation-start (concat command " " (file-name-nondirectory file))
+                       nil (lambda (_mode-name) "*Antlr-Run*"))))
+
+(defun antlr-run-tool-interactive ()
+  ;; code in `interactive' is not compiled
+  "Interactive specification for `antlr-run-tool'.
+Use prefix argument ARG to return \(COMMAND FILE SAVED)."
+  (let* ((supers (cdadr (save-excursion
+                          (save-restriction
+                            (widen)
+                            (antlr-file-dependencies)))))
+         (glibs ""))
+    (when supers
+      (save-some-buffers (not antlr-ask-about-save) nil)
+      (setq glibs (car (antlr-superclasses-glibs
+                        supers
+                        (car (antlr-directory-dependencies
+                              (antlr-default-directory)))))))
+    (list (antlr-read-shell-command "Run Antlr on current file with: "
+                                    (concat antlr-tool-command glibs " "))
+          buffer-file-name
+          supers)))
+
+
+;;;===========================================================================
+;;;  Makefile creation
+;;;===========================================================================
+
+(defun antlr-makefile-insert-variable (number pre post)
+  "Insert Makefile variable numbered NUMBER according to specification.
+Also insert strings PRE and POST before and after the variable."
+  (let ((spec (cadr antlr-makefile-specification)))
+    (when spec
+      (insert pre
+              (if number (format (cadr spec) number) (car spec))
+              post))))
+
+(defun antlr-insert-makefile-rules (&optional in-makefile)
+  "Insert Makefile rules in the current buffer at point.
+IN-MAKEFILE is non-nil, if the current buffer is the Makefile.  See
+command `antlr-show-makefile-rules' for detail."
+  (let* ((dirname (antlr-default-directory))
+         (deps0 (antlr-directory-dependencies dirname))
+         (classes (car deps0))          ; CLASS -> (FILE . EVOCAB) ...
+         (deps (cdr deps0))             ; FILE -> (c . s) (ev . iv) . LANGUAGE
+         (with-error nil)
+         (gen-sep (or (caddr (cadr antlr-makefile-specification)) " "))
+         (n (and (cdr deps) (cadr antlr-makefile-specification) 0)))
+    (or in-makefile (set-buffer standard-output))
+    (dolist (dep deps)
+      (let ((supers (cdadr dep))
+            (lang (cdr (assoc (cdddr dep) antlr-file-formats-alist))))
+        (if n (incf n))
+        (antlr-makefile-insert-variable n "" " =")
+        (if supers
+            (insert " "
+                    (format (cadr antlr-special-file-formats)
+                            (file-name-sans-extension (car dep)))))
+        (dolist (class-def (caadr dep))
+          (let ((sep gen-sep))
+            (dolist (class-file (cadr lang))
+              (insert sep (format class-file (car class-def)))
+              (setq sep " "))))
+        (dolist (evocab (caaddr dep))
+          (let ((sep gen-sep))
+            (dolist (vocab-file (cons (car antlr-special-file-formats)
+                                      (car lang)))
+              (insert sep (format vocab-file evocab))
+              (setq sep " "))))
+        (antlr-makefile-insert-variable n "\n$(" ")")
+        (insert ": " (car dep))
+        (dolist (ivocab (cdaddr dep))
+          (insert " " (format (car antlr-special-file-formats) ivocab)))
+        (let ((glibs (antlr-superclasses-glibs supers classes)))
+          (if (cadr glibs) (setq with-error t))
+          (dolist (super (cddr glibs))
+            (insert " " (car super))
+            (if (cdr super)
+                (insert " " (format (car antlr-special-file-formats)
+                                    (cdr super)))))
+          (insert "\n\t"
+                  (caddr antlr-makefile-specification)
+                  (car glibs)
+                  " $<\n"
+                  (car antlr-makefile-specification)))))
+    (if n
+        (let ((i 0))
+          (antlr-makefile-insert-variable nil "" " =")
+          (while (<= (incf i) n)
+            (antlr-makefile-insert-variable i " $(" ")"))
+          (insert "\n" (car antlr-makefile-specification))))
+    (if (string-equal (car antlr-makefile-specification) "\n")
+        (backward-delete-char 1))
+    (when with-error
+      (goto-char (point-min))
+      (insert antlr-help-unknown-file-text))
+    (unless in-makefile
+      (copy-region-as-kill (point-min) (point-max))
+      (goto-char (point-min))
+      (insert (format antlr-help-rules-intro dirname)))))
+
+;;;###autoload
+(defun antlr-show-makefile-rules ()
+  "Show Makefile rules for all grammar files in the current directory.
+If the `major-mode' of the current buffer has the value `makefile-mode',
+the rules are directory inserted at point.  Otherwise, a *Help* buffer
+is shown with the rules which are also put into the `kill-ring' for
+\\[yank].
+
+This command considers import/export vocabularies and grammar
+inheritance and provides a value for the \"-glib\" option if necessary.
+Customize variable `antlr-makefile-specification' for the appearance of
+the rules.
+
+If the file for a super-grammar cannot be determined, special file names
+are used according to variable `antlr-unknown-file-formats' and a
+commentary with value `antlr-help-unknown-file-text' is added.  The
+*Help* buffer always starts with the text in `antlr-help-rules-intro'."
+  (interactive)
+  (if (null (derived-mode-p 'makefile-mode))
+      (antlr-with-displaying-help-buffer 'antlr-insert-makefile-rules)
+    (push-mark)
+    (antlr-insert-makefile-rules t)))
+
+
+;;;===========================================================================
+;;;  Indentation
+;;;===========================================================================
+
+(defun antlr-indent-line ()
+  "Indent the current line as ANTLR grammar code.
+The indentation of grammar lines are calculated by `c-basic-offset',
+multiplied by:
+ - the level of the paren/brace/bracket depth,
+ - plus 0/2/1, depending on the position inside the rule: header, body,
+   exception part,
+ - minus 1 if `antlr-indent-item-regexp' matches the beginning of the
+   line starting from the first non-whitespace.
+
+Lines inside block comments are indented by `c-indent-line' according to
+`antlr-indent-comment'.
+
+Lines in actions except top-level actions in a header part or an option
+area are indented by `c-indent-line'.
+
+Lines in header actions are indented at column 0 if `antlr-language'
+equals to a key in `antlr-indent-at-bol-alist' and the line starting at
+the first non-whitespace is matched by the corresponding value.
+
+For the initialization of `c-basic-offset', see `antlr-indent-style' and,
+to a lesser extent, `antlr-tab-offset-alist'."
+  (save-restriction
+    (let ((orig (point))
+          (min0 (point-min))
+          bol boi indent syntax cc-syntax)
+      (widen)
+      (beginning-of-line)
+      (setq bol (point))
+      (if (< bol min0)
+          (error "Beginning of current line not visible"))
+      (skip-chars-forward " \t")
+      (setq boi (point))
+      ;; check syntax at beginning of indentation ----------------------------
+      (antlr-with-syntax-table antlr-action-syntax-table
+        (antlr-invalidate-context-cache)
+        (setq syntax (antlr-syntactic-context))
+        (cond ((symbolp syntax)
+               (setq indent nil))       ; block-comments, strings, (comments)
+              ((progn
+                 (antlr-next-rule -1 t)
+                 (if (antlr-search-forward ":") (< boi (1- (point))) t))
+               (setq indent 0))                 ; in rule header
+              ((if (antlr-search-forward ";") (< boi (point)) t)
+               (setq indent 2))                 ; in rule body
+              (t
+               (forward-char)
+               (antlr-skip-exception-part nil)
+               (setq indent (if (> (point) boi) 1 0))))) ; in exception part?
+      ;; check whether to use indentation engine of cc-mode ------------------
+      (antlr-invalidate-context-cache)
+      (goto-char boi)
+      (when (and indent (> syntax 0))
+        (cond ((> syntax 1)             ; block in action => use cc-mode
+               (setq indent nil))
+              ((and (= indent 0)
+                    (assq antlr-language antlr-indent-at-bol-alist)
+                    (looking-at (cdr (assq antlr-language
+                                           antlr-indent-at-bol-alist))))
+               (setq syntax 'bol))
+              ((setq cc-syntax (c-guess-basic-syntax))
+               (let ((cc cc-syntax) symbol)
+                 (while (setq symbol (pop cc))
+                   (when (cdr symbol)
+                     (or (memq (car symbol)
+                               antlr-disabling-cc-syntactic-symbols)
+                         (setq indent nil))
+                     (setq cc nil)))))))
+;;;             ((= indent 1)           ; exception part => use cc-mode
+;;;              (setq indent nil))
+;;;             ((save-restriction      ; not in option part => cc-mode
+;;;                (goto-char (scan-lists (point) -1 1))
+;;;                (skip-chars-backward " \t\n")
+;;;                (narrow-to-region (point-min) (point))
+;;;                (not (re-search-backward "\\<options\\'" nil t)))
+;;;              (setq indent nil)))))
+        ;; compute the corresponding indentation and indent --------------------
+      (if (null indent)
+          ;; Use the indentation engine of cc-mode
+          (progn
+            (goto-char orig)
+            (if (or (numberp syntax)
+                    (if (eq syntax 'string) nil (eq antlr-indent-comment t)))
+                (c-indent-line cc-syntax)))
+        ;; do it ourselves
+        (goto-char boi)
+        (unless (symbolp syntax)                ; direct indentation
+          ;;(antlr-invalidate-context-cache)
+          (incf indent (antlr-syntactic-context))
+          (and (> indent 0) (looking-at antlr-indent-item-regexp) (decf indent))
+          (setq indent (* indent c-basic-offset)))
+        ;; the usual major-mode indent stuff ---------------------------------
+        (setq orig (- (point-max) orig))
+        (unless (= (current-column) indent)
+          (delete-region bol boi)
+          (beginning-of-line)
+          (indent-to indent))
+        ;; If initial point was within line's indentation,
+        ;; position after the indentation.  Else stay at same point in text.
+        (if (> (- (point-max) orig) (point))
+            (goto-char (- (point-max) orig)))))))
+
+(defun antlr-indent-command (&optional arg)
+  "Indent the current line or insert tabs/spaces.
+With optional prefix argument ARG or if the previous command was this
+command, insert ARG tabs or spaces according to `indent-tabs-mode'.
+Otherwise, indent the current line with `antlr-indent-line'."
+  (interactive "*P")
+  (if (or arg (eq last-command 'antlr-indent-command))
+      (insert-tab arg)
+    (let ((antlr-indent-comment (and antlr-indent-comment t))) ; dynamic
+      (antlr-indent-line))))
+
+(defun antlr-electric-character (&optional arg)
+  "Insert the character you type and indent the current line.
+Insert the character like `self-insert-command' and indent the current
+line as `antlr-indent-command' does.  Do not indent the line if
+
+ * this command is called with a prefix argument ARG,
+ * there are characters except whitespaces between point and the
+   beginning of the line, or
+ * point is not inside a normal grammar code, { and } are also OK in
+   actions.
+
+This command is useful for a character which has some special meaning in
+ANTLR's syntax and influences the auto indentation, see
+`antlr-indent-item-regexp'."
+  (interactive "*P")
+  (if (or arg
+          (save-excursion (skip-chars-backward " \t") (not (bolp)))
+          (antlr-with-syntax-table antlr-action-syntax-table
+            (antlr-invalidate-context-cache)
+            (let ((context (antlr-syntactic-context)))
+              (not (and (numberp context)
+                        (or (zerop context)
+                            (memq last-command-event '(?\{ ?\}))))))))
+      (self-insert-command (prefix-numeric-value arg))
+    (self-insert-command (prefix-numeric-value arg))
+    (antlr-indent-line)))
+
+
+;;;===========================================================================
+;;;  Mode entry
+;;;===========================================================================
+
+(defun antlr-c-init-language-vars ()
+  "Like `c-init-language-vars-for' when using cc-mode before v5.29."
+  (let ((settings                       ; (cdr '(setq...)) will be optimized
+         (if (eq antlr-language 'c++-mode)
+             (cdr '(setq                ;' from `c++-mode' v5.20, v5.28
+                    c-keywords (c-identifier-re c-C++-keywords)
+                    c-conditional-key c-C++-conditional-key
+                    c-comment-start-regexp c-C++-comment-start-regexp
+                    c-class-key c-C++-class-key
+                    c-extra-toplevel-key c-C++-extra-toplevel-key
+                    c-access-key c-C++-access-key
+                    c-recognize-knr-p nil
+                    c-bitfield-key c-C-bitfield-key ; v5.28
+                    ))
+           (cdr '(setq                  ; from `java-mode' v5.20, v5.28
+                  c-keywords (c-identifier-re c-Java-keywords)
+                  c-conditional-key c-Java-conditional-key
+                  c-comment-start-regexp c-Java-comment-start-regexp
+                  c-class-key c-Java-class-key
+                  c-method-key nil
+                  c-baseclass-key nil
+                  c-recognize-knr-p nil
+                  c-access-key c-Java-access-key ; v5.20
+                  c-inexpr-class-key c-Java-inexpr-class-key ; v5.28
+                  )))))
+    (while settings
+      (when (boundp (car settings))
+        (ignore-errors
+          (set (car settings) (eval (cadr settings)))))
+      (setq settings (cddr settings)))))
+
+(defun antlr-language-option (search)
+  "Find language in `antlr-language-alist' for language option.
+If SEARCH is non-nil, find element for language option.  Otherwise, find
+the default language."
+  (let ((value
+         (and search
+              (save-excursion
+                (goto-char (point-min))
+                (re-search-forward (cdr antlr-language-limit-n-regexp)
+                                   (+ (point)
+                                      (car antlr-language-limit-n-regexp))
+                                   t))
+              (match-string 1)))
+        (seq antlr-language-alist)
+        r)
+    ;; Like (find VALUE antlr-language-alist :key 'cddr :test 'member)
+    (while seq
+      (setq r (pop seq))
+      (if (member value (cddr r))
+          (setq seq nil)                ; stop
+        (setq r nil)))                  ; no result yet
+    (car r)))
+
+;;;###autoload
+(define-derived-mode antlr-mode prog-mode
+  ;; FIXME: Since it uses cc-mode, it bumps into c-update-modeline's
+  ;; limitation to mode-name being a string.
+  ;; '("Antlr." (:eval (cadr (assq antlr-language antlr-language-alist))))
+  "Antlr"
+  "Major mode for editing ANTLR grammar files."
+  :abbrev-table antlr-mode-abbrev-table
+  (c-initialize-cc-mode)                ; cc-mode is required
+  (unless (fboundp 'c-forward-sws)      ; see above
+    (fset 'antlr-c-forward-sws 'c-forward-syntactic-ws))
+  ;; ANTLR specific ----------------------------------------------------------
+  (unless antlr-language
+    (set (make-local-variable 'antlr-language)
+         (or (antlr-language-option t) (antlr-language-option nil))))
+  (if (stringp (cadr (assq antlr-language antlr-language-alist)))
+      (setq mode-name
+            (concat "Antlr."
+                    (cadr (assq antlr-language antlr-language-alist)))))
+  ;; indentation, for the C engine -------------------------------------------
+  (setq c-buffer-is-cc-mode antlr-language)
+  (cond ((fboundp 'c-init-language-vars-for) ; cc-mode 5.30.5+
+         (c-init-language-vars-for antlr-language))
+        ((fboundp 'c-init-c-language-vars) ; cc-mode 5.30 to 5.30.4
+         (c-init-c-language-vars)       ; not perfect, but OK
+         (setq c-recognize-knr-p nil))
+        ((fboundp 'c-init-language-vars) ; cc-mode 5.29
+         (let ((init-fn 'c-init-language-vars))
+           (funcall init-fn)))          ; is a function in v5.29
+        (t                              ; cc-mode upto 5.28
+         (antlr-c-init-language-vars)))         ; do it myself
+  (c-basic-common-init antlr-language (or antlr-indent-style "gnu"))
+  (set (make-local-variable 'outline-regexp) "[^#\n\^M]")
+  (set (make-local-variable 'outline-level) 'c-outline-level) ;TODO: define own
+  (set (make-local-variable 'indent-line-function) 'antlr-indent-line)
+  (set (make-local-variable 'indent-region-function) nil)       ; too lazy
+  (setq comment-start "// "
+        comment-end ""
+        comment-start-skip "/\\*+ *\\|// *")
+  ;; various -----------------------------------------------------------------
+  (set (make-local-variable 'font-lock-defaults) antlr-font-lock-defaults)
+  (easy-menu-add antlr-mode-menu)
+  (set (make-local-variable 'imenu-create-index-function)
+       'antlr-imenu-create-index-function)
+  (set (make-local-variable 'imenu-generic-expression) t) ; fool stupid test
+  (and antlr-imenu-name                         ; there should be a global variable...
+       (fboundp 'imenu-add-to-menubar)
+       (imenu-add-to-menubar
+        (if (stringp antlr-imenu-name) antlr-imenu-name "Index")))
+  (antlr-set-tabs))
+
+;; A smarter version of `group-buffers-menu-by-mode-then-alphabetically' (in
+;; XEmacs) could use the following property.  The header of the submenu would
+;; be "Antlr" instead of "Antlr.C++" or (not and!) "Antlr.Java".
+(put 'antlr-mode 'mode-name "Antlr")
+
+;;;###autoload
+(defun antlr-set-tabs ()
+  "Use ANTLR's convention for TABs according to `antlr-tab-offset-alist'.
+Used in `antlr-mode'.  Also a useful function in `java-mode-hook'."
+  (if buffer-file-name
+      (let ((alist antlr-tab-offset-alist) elem)
+        (while alist
+          (setq elem (pop alist))
+          (and (or (null (car elem)) (eq (car elem) major-mode))
+               (or (null (cadr elem))
+                   (string-match (cadr elem) buffer-file-name))
+               (setq tab-width (caddr elem)
+                     indent-tabs-mode (cadddr elem)
+                     alist nil))))))
+
+(add-to-list 'auto-mode-alist '("\\.g4\\'" . antlr-mode))
+
+(provide 'antlr-mode)
+
+;;; Local IspellPersDict: .ispell_antlr
+
+;;; antlr-mode.el ends here
-- 
GitLab