1 ;;; antlr-mode.el --- Major mode for ANTLR grammar files
3 ;; Copyright (C) 1999 Free Software Foundation, Inc.
5 ;; Author: Christoph.Wedler@sap.com
6 ;; Version: $Id: antlr-mode.el,v 1.2 1999/12/16 19:30:34 wedler Exp $
7 ;; X-URL: http://www.fmi.uni-passau.de/~wedler/antlr-mode/
9 ;; This file is part of GNU Emacs.
11 ;; GNU Emacs is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING. If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
28 ;; Major mode for editing ANTLR grammar files, i.e., files ending with `.g'.
29 ;; ANTLR is ANother Tool for Language Recognition (an excellent alternative to
30 ;; lex/yacc), see <http://www.ANTLR.org> and <news:comp.compilers.tools.pccts>.
32 ;; Variable `antlr-language' is set according to the language in actions and
33 ;; semantic predicates of the grammar (see ANTLR's file option "language").
34 ;; The supported languages are "Java" (java-mode) and "Cpp" (c++-mode). This
35 ;; package uses features of the Emacs package cc-mode.
37 ;; This package provides the following features:
38 ;; * Indentation for the current line (TAB) and selected region (C-M-\).
39 ;; * Syntax coloring (via font-lock) with language dependent coloring.
40 ;; * Support for imenu/speedbar: menu "Index" (Parser, Lexer, TreeParser).
41 ;; * Direct move to previous/next rule, beginning/end of rule body etc.
43 ;; INDENTATION. This package supports ANTLR's (intended) indentation style
44 ;; which is based on a simple paren/brace/bracket depth-level calculation, see
45 ;; `antlr-indent-line'. The indentation engine of cc-mode is only used inside
46 ;; block comments (it is not easy to use it for actions, esp if they come early
47 ;; in the rule body). By default, this package uses TABs for a basic offset of
48 ;; 4 to be consistent to both ANTLR's conventions (TABs usage) and the
49 ;; `c-indentation-style' "java" which sets `c-basic-offset' to 4, see
50 ;; `antlr-tab-offset-alist'.
52 ;; SYNTAX COLORING comes in three phases. First, comments and strings are
53 ;; highlighted. Second, the grammar code is highlighted according to
54 ;; `antlr-font-lock-additional-keywords' (rule refs: blue, token refs: brown,
55 ;; definition: ditto+bold). Third, actions, semantic predicates and arguments
56 ;; are highlighted according to the usual font-lock keywords of
57 ;; `antlr-language', see also `antlr-font-lock-maximum-decoration'. We define
58 ;; special font-lock faces for the grammar code to allow you to distinguish
59 ;; ANTLR keywords from Java/C++ keywords.
61 ;; Bug fixes, bug reports, improvements, and suggestions are strongly
62 ;; appreciated. Please check the newest version first:
63 ;; http://www.fmi.uni-passau.de/~wedler/antlr-mode/changes.html
67 ;; This file requires Emacs-20.3, XEmacs-20.4 or higher.
69 ;; If antlr-mode is not part of your distribution, put this file into your
70 ;; load-path and the following into your ~/.emacs:
71 ;; (autoload 'antlr-mode "antlr-mode" nil t)
72 ;; (setq auto-mode-alist (cons '("\\.g\\'" . antlr-mode) auto-mode-alist))
73 ;; (add-hook 'speedbar-load-hook ; would be too late in antlr-mode.el
74 ;; (lambda () (speedbar-add-supported-extension ".g")))
76 ;; If you edit ANTLR's source files, you might also want to use
77 ;; (autoload 'antlr-set-tabs "antlr-mode")
78 ;; (add-hook 'java-mode-hook 'antlr-set-tabs)
80 ;; To customize, use `M-x customize-group RET antlr RET' or the custom browser
81 ;; (Emacs->Programming->Languages->Antlr).
86 (eval-when-compile (require 'cl))
87 (require 'easymenu) ; Emacs
88 (eval-when-compile (require 'cc-mode)) ; shut up most warnings
91 (if (string-match "XEmacs" emacs-version)
92 (defalias 'antlr-scan-sexps 'scan-sexps)
93 (defalias 'antlr-scan-sexps 'antlr-scan-sexps-internal))
94 (if (and (fboundp 'buffer-syntactic-context)
95 (fboundp 'buffer-syntactic-context-depth))
97 (defalias 'antlr-invalidate-context-cache 'antlr-xemacs-bug-workaround)
98 (defalias 'antlr-syntactic-context 'antlr-fast-syntactic-context))
99 (defalias 'antlr-invalidate-context-cache 'ignore)
100 (defalias 'antlr-syntactic-context 'antlr-slow-syntactic-context)))
104 ;;;;##########################################################################
106 ;;;;##########################################################################
110 "Major mode for ANTLR grammar files."
112 :link '(emacs-commentary-link "antlr-mode.el")
113 :link '(url-link "http://www.fmi.uni-passau.de/~wedler/antlr-mode/")
116 (defconst antlr-version "1.2"
117 "ANTLR major mode version number.")
120 ;;;===========================================================================
121 ;;; Controlling ANTLR's code generator (language option)
122 ;;;===========================================================================
124 (defvar antlr-language nil
125 "Major mode corresponding to ANTLR's \"language\" option.
126 Set via `antlr-language-alist'. The only useful place to change this
127 buffer-local variable yourself is in `antlr-mode-hook' or in the \"local
128 variable list\" near the end of the file, see
129 `enable-local-variables'.")
131 (defcustom antlr-language-alist
132 '((java-mode "Java" nil "Java")
133 (c++-mode "C++" "Cpp"))
134 "List of ANTLR's supported languages.
135 Each element in this list looks like
136 (MAJOR-MODE MODELINE-STRING OPTION-VALUE...)
138 MAJOR-MODE, the major mode of the code in the grammar's actions, is the
139 value of `antlr-language' if the first regexp group matched by REGEXP in
140 `antlr-language-limit-n-regexp' is one of the OPTION-VALUEs. An
141 OPTION-VALUE of nil denotes the fallback element. MODELINE-STRING is
142 also displayed in the modeline next to \"Antlr\"."
144 :type '(repeat (group :value (java-mode "")
145 (function :tag "Major mode")
146 (string :tag "Modeline string")
147 (repeat :tag "ANTLR language option" :inline t
148 (choice (const :tag "Default" nil)
151 (defcustom antlr-language-limit-n-regexp
152 '(3000 . "language[ \t]*=[ \t]*\"\\([A-Z][A-Za-z_]*\\)\"")
153 "Used to set a reasonable value for `antlr-language'.
154 Looks like (LIMIT . REGEXP). Search for REGEXP from the beginning of
155 the buffer to LIMIT to set the language according to
156 `antlr-language-alist'."
158 :type '(cons (choice :tag "Limit" (const :tag "No" nil) (integer :value 0))
162 ;;;===========================================================================
164 ;;;===========================================================================
166 (defcustom antlr-indent-comment 'tab
167 "*Non-nil, if the indentation should touch lines in block comments.
168 If nil, no continuation line of a block comment is changed. If t, they
169 are changed according to `c-indentation-line'. When not nil and not t,
170 they are only changed by \\[antlr-indent-command]."
172 :type '(radio (const :tag "No" nil)
173 (const :tag "Always" t)
174 (sexp :tag "With TAB" :format "%t" :value tab)))
176 (defcustom antlr-tab-offset-alist
177 '((antlr-mode nil 4 t)
178 (java-mode "antlr" 4 t))
179 "Alist to determine whether to use ANTLR's convention for TABs.
180 Each element looks like (MAJOR-MODE REGEXP TAB-WIDTH INDENT-TABS-MODE).
181 The first element whose MAJOR-MODE is nil or equal to `major-mode' and
182 whose REGEXP is nil or matches `buffer-file-name' is used to set
183 `tab-width' and `indent-tabs-mode'. This is useful to support both
184 ANTLR's and Java's indentation styles. Used by `antlr-set-tabs'."
186 :type '(repeat (group :value (antlr-mode nil 8 nil)
187 (choice (const :tag "All" nil)
188 (function :tag "Major mode"))
189 (choice (const :tag "All" nil) regexp)
190 (integer :tag "Tab width")
191 (boolean :tag "Indent-tabs-mode"))))
193 (defvar antlr-indent-item-regexp
194 "[]}):;|&]\\|default[ \t]*:\\|case[ \t]+\\('\\\\?.'\\|[0-9]+\\|[A-Za-z_][A-Za-z_0-9]*\\)[ \t]*:" ; & is local ANTLR extension
195 "Regexp matching lines which should be indented by one TAB less.
196 See command \\[antlr-indent-command].")
199 ;;;===========================================================================
201 ;;;===========================================================================
203 (defcustom antlr-imenu-name t
204 "*Non-nil, if a \"Index\" menu should be added to the menubar.
205 If it is a string, it is used instead \"Index\". Requires package
208 :type '(choice (const :tag "No menu" nil)
209 (const :tag "Index menu" t)
210 (string :tag "Other menu name")))
212 (defvar antlr-mode-map
213 (let ((map (make-sparse-keymap)))
214 (define-key map "\t" 'antlr-indent-command)
215 (define-key map "\e\C-a" 'antlr-beginning-of-rule)
216 (define-key map "\e\C-e" 'antlr-end-of-rule)
217 (define-key map "\C-c\C-a" 'antlr-beginning-of-body)
218 (define-key map "\C-c\C-e" 'antlr-end-of-body)
219 (define-key map "\C-c\C-f" 'c-forward-into-nomenclature)
220 (define-key map "\C-c\C-b" 'c-backward-into-nomenclature)
221 (define-key map "\C-c\C-c" 'comment-region)
222 ;; I'm too lazy to define my own:
223 (define-key map "\ea" 'c-beginning-of-statement)
224 (define-key map "\ee" 'c-end-of-statement)
226 "Keymap used in `antlr-mode' buffers.")
228 (easy-menu-define antlr-mode-menu
232 ["Indent Line" antlr-indent-command
233 :active (not buffer-read-only)]
234 ["Indent for Comment" indent-for-comment
235 :active (not buffer-read-only)]
236 ["Comment Out Region" comment-region
237 :active (and (not buffer-read-only)
238 (c-region-is-active-p))]
240 (comment-region (region-beginning) (region-end) '(4))
241 :active (and (not buffer-read-only)
242 (c-region-is-active-p))]
244 ["Backward Rule" antlr-beginning-of-rule t]
245 ["Forward Rule" antlr-end-of-rule t]
246 ["Start of Rule Body" antlr-beginning-of-body
247 :active (antlr-inside-rule-p)]
248 ["End of Rule Body" antlr-end-of-body
249 :active (antlr-inside-rule-p)]
251 ["Backward Statement" c-beginning-of-statement t]
252 ["Forward Statement" c-end-of-statement t]
253 ["Backward Into Nomencl." c-backward-into-nomenclature t]
254 ["Forward Into Nomencl." c-forward-into-nomenclature t]))
257 ;;;===========================================================================
259 ;;;===========================================================================
261 (defcustom antlr-font-lock-maximum-decoration 'inherit
262 "*The maximum decoration level for fontifying actions.
263 Value `none' means, do not fontify actions, just normal grammar code
264 according to `antlr-font-lock-additional-keywords'. Value `inherit'
265 means, use value of `font-lock-maximum-decoration'. Any other value is
266 interpreted as in `font-lock-maximum-decoration' with no level-0
267 fontification, see `antlr-font-lock-keywords-alist'.
269 While calculating the decoration level for actions, `major-mode' is
270 bound to `antlr-language'. For example, with value
271 ((java-mode . 2) (c++-mode . 0))
272 Java actions are fontified with level 2 and C++ actions are not
274 :type '(choice (const :tag "none" none)
275 (const :tag "inherit" inherit)
276 (const :tag "default" nil)
277 (const :tag "maximum" t)
278 (integer :tag "level" 1)
279 (repeat :menu-tag "mode specific" :tag "mode specific"
281 (cons :tag "Instance"
284 (symbol :tag "name"))
285 (radio :tag "Decoration"
286 (const :tag "default" nil)
287 (const :tag "maximum" t)
288 (integer :tag "level" 1))))))
290 (defvar antlr-font-lock-keywords-alist
292 (list) ; nil won't work (would use level-3)
293 java-font-lock-keywords-1 java-font-lock-keywords-2
294 java-font-lock-keywords-3)
296 (list) ; nil won't work (would use level-3)
297 c++-font-lock-keywords-1 c++-font-lock-keywords-2
298 c++-font-lock-keywords-3))
299 "List of font-lock keywords for actions in the grammar.
300 Each element in this list looks like
301 (MAJOR-MODE KEYWORD...)
303 If `antlr-language' is equal to MAJOR-MODE, the KEYWORDs are the
304 font-lock keywords according to `font-lock-defaults' used for the code
305 in the grammar's actions and semantic predicates, see
306 `antlr-font-lock-maximum-decoration'.")
308 (defvar antlr-font-lock-keyword-face 'antlr-font-lock-keyword-face)
309 (defface antlr-font-lock-keyword-face
310 '((((class color) (background light)) (:foreground "black" :bold t)))
314 (defvar antlr-font-lock-ruledef-face 'antlr-font-lock-ruledef-face)
315 (defface antlr-font-lock-ruledef-face
316 '((((class color) (background light)) (:foreground "blue" :bold t)))
317 "ANTLR rule references (definition)."
320 (defvar antlr-font-lock-tokendef-face 'antlr-font-lock-tokendef-face)
321 (defface antlr-font-lock-tokendef-face
322 '((((class color) (background light)) (:foreground "brown3" :bold t)))
323 "ANTLR token references (definition)."
326 (defvar antlr-font-lock-ruleref-face 'antlr-font-lock-ruleref-face)
327 (defface antlr-font-lock-ruleref-face
328 '((((class color) (background light)) (:foreground "blue4")))
329 "ANTLR rule references (usage)."
332 (defvar antlr-font-lock-tokenref-face 'antlr-font-lock-tokenref-face)
333 (defface antlr-font-lock-tokenref-face
334 '((((class color) (background light)) (:foreground "brown4")))
335 "ANTLR token references (usage)."
338 (defvar antlr-font-lock-literal-face 'antlr-font-lock-literal-face)
339 (defface antlr-font-lock-literal-face
340 '((((class color) (background light)) (:foreground "brown4" :bold t)))
341 "ANTLR literal tokens consisting merely of letter-like characters."
344 (defvar antlr-font-lock-additional-keywords
345 `((antlr-invalidate-context-cache)
346 ("\\$setType[ \t]*(\\([A-Z\300-\326\330-\337]\\sw*\\))"
347 (1 antlr-font-lock-tokendef-face))
348 ("\\$\\sw+" (0 font-lock-keyword-face))
349 ;; the tokens are already fontified as string/docstrings:
351 (antlr-re-search-forward "\"\\(\\sw\\(\\sw\\|-\\)*\\)\"" limit))
352 (1 antlr-font-lock-literal-face t))
354 (antlr-re-search-forward
355 "^\\(class\\)[ \t]+\\([A-Z\300-\326\330-\337]\\sw*\\)[ \t]+\\(extends\\)[ \t]+\\([A-Z\300-\326\330-\337]\\sw*\\)[ \t]*;" limit))
356 (1 antlr-font-lock-keyword-face)
357 (2 antlr-font-lock-ruledef-face)
358 (3 antlr-font-lock-keyword-face)
359 (4 (if (member (match-string 4) '("Lexer" "Parser" "TreeParser"))
360 'antlr-font-lock-keyword-face
361 'font-lock-type-face)))
363 (antlr-re-search-forward
364 "\\<\\(header\\|options\\|tokens\\|exception\\|catch\\|returns\\)\\>"
366 (1 antlr-font-lock-keyword-face))
368 (antlr-re-search-forward
369 "^\\(private\\|public\\|protected\\)\\>\\([ \t]+\\(\\sw+\\)\\)?"
371 (1 font-lock-type-face) ; not XEmacs' java level-3 fruit salad
372 (3 (if (antlr-upcase-p (char-after (match-beginning 3)))
373 'antlr-font-lock-tokendef-face
374 'antlr-font-lock-ruledef-face) nil t))
376 (antlr-re-search-forward "^\\sw+" limit))
377 (0 (if (antlr-upcase-p (char-after (match-beginning 0)))
378 'antlr-font-lock-tokendef-face
379 'antlr-font-lock-ruledef-face) nil t))
381 ;; not only before a rule ref, also before a literal
382 (antlr-re-search-forward "\\<\\(\\sw+\\)[ \t]*:" limit))
383 (1 font-lock-variable-name-face))
385 (antlr-re-search-forward "\\<\\(\\sw+[ \t]*=[ \t]*\\)?\\(\\sw+[ \t]*:[ \t]*\\)?\\(\\sw+\\)" limit))
386 ;;(1 antlr-font-lock-default-face nil t) ; fool java-font-lock-keywords
387 (3 (if (antlr-upcase-p (char-after (match-beginning 3)))
388 'antlr-font-lock-tokenref-face
389 'antlr-font-lock-ruleref-face))))
390 "Font-lock keywords for ANTLR's normal grammar code.
391 See `antlr-font-lock-keywords-alist' for the keywords of actions.")
393 (defvar antlr-font-lock-defaults
394 '(antlr-font-lock-keywords
395 nil nil ((?_ . "w") (?\( . ".") (?\) . ".")) beginning-of-defun)
396 "Font-lock defaults used for ANTLR syntax coloring.
397 The SYNTAX-ALIST element is also used to initialize
398 `antlr-action-syntax-table'.")
401 ;;;===========================================================================
402 ;;; Internal variables
403 ;;;===========================================================================
405 (defvar antlr-mode-hook nil
406 "Hook called by `antlr-mode'.")
408 ;; used for "in Java/C++ code" = syntactic-depth>0
409 (defvar antlr-action-syntax-table nil
410 "Syntax table used for ANTLR action parsing.
411 Initialized by `java-mode-syntax-table', i.e., the syntax table used for
412 grammar files, changed by SYNTAX-ALIST in `antlr-font-lock-defaults'.
413 This table should be selected if you use `buffer-syntactic-context' and
414 `buffer-syntactic-context-depth' in order not to confuse their
417 (defvar antlr-mode-abbrev-table nil
418 "Abbreviation table used in `antlr-mode' buffers.")
419 (define-abbrev-table 'antlr-mode-abbrev-table ())
423 ;;;;##########################################################################
425 ;;;;##########################################################################
428 ;;;===========================================================================
429 ;;; Syntax functions -- Emacs vs XEmacs dependent
430 ;;;===========================================================================
432 ;; From help.el (XEmacs-21.1)
433 (defmacro antlr-with-syntax-table (syntab &rest body)
434 `(let ((stab (syntax-table)))
436 (progn (set-syntax-table (copy-syntax-table ,syntab)) ,@body)
437 (set-syntax-table stab))))
438 (put 'antlr-with-syntax-table 'lisp-indent-function 1)
439 (put 'antlr-with-syntax-table 'edebug-form-spec '(form body))
441 (defun antlr-scan-sexps-internal (from count &optional dummy no-error)
442 ;; checkdoc-params: (from count dummy)
443 "Like `scan-sexps' but with additional arguments.
444 When optional arg NO-ERROR is non-nil, `scan-sexps' will return nil
445 instead of signaling an error."
448 (scan-sexps from count)
450 (scan-sexps from count)))
452 (defun antlr-xemacs-bug-workaround (&rest dummies)
453 ;; checkdoc-params: (dummies)
454 "Invalidate context_cache for syntactical context information."
455 ;; XEmacs bug workaround
457 (set-buffer (get-buffer-create " ANTLR XEmacs bug workaround"))
458 (buffer-syntactic-context-depth))
461 (defun antlr-fast-syntactic-context ()
462 "Return some syntactic context information.
463 Return `string' if point is within a string, `block-comment' or
464 `comment' is point is within a comment or the depth within all
465 parenthesis-syntax delimiters at point otherwise.
466 WARNING: this may alter `match-data'."
467 (or (buffer-syntactic-context) (buffer-syntactic-context-depth)))
469 (defun antlr-slow-syntactic-context ()
470 "Return some syntactic context information.
471 Return `string' if point is within a string, `block-comment' or
472 `comment' is point is within a comment or the depth within all
473 parenthesis-syntax delimiters at point otherwise.
474 WARNING: this may alter `match-data'."
475 (let ((orig (point)))
477 (let ((state (parse-partial-sexp (point) orig)))
479 (cond ((nth 3 state) 'string)
480 ((nth 4 state) 'comment) ; block-comment? -- we don't care
484 ;;;===========================================================================
486 ;;;===========================================================================
488 (defun antlr-upcase-p (char)
489 "Non-nil, if CHAR is an uppercase character (if CHAR was a char)."
490 ;; in XEmacs, upcase only works for ASCII
491 (or (and (<= ?A char) (<= char ?Z))
492 (and (<= ?\300 char) (<= char ?\337)))) ; ?\327 is no letter
494 (defun antlr-re-search-forward (regexp bound)
495 "Search forward from point for regular expression REGEXP.
496 Set point to the end of the occurrence found, and return point. Return
497 nil if no occurrence was found. Do not search within comments, strings
498 and actions/semantic predicates. BOUND bounds the search; it is a
499 buffer position. See also the functions `match-beginning', `match-end'
500 and `replace-match'."
501 ;; WARNING: Should only be used with `antlr-action-syntax-table'!
503 (while (and (re-search-forward regexp bound 'limit)
505 (if (eq (antlr-syntactic-context) 0) (setq continue nil) t))))
506 (if continue nil (point))))
508 (defun antlr-search-forward (string)
509 "Search forward from point for STRING.
510 Set point to the end of the occurrence found, and return point. Return
511 nil if no occurrence was found. Do not search within comments, strings
512 and actions/semantic predicates."
513 ;; WARNING: Should only be used with `antlr-action-syntax-table'!
515 (while (and (search-forward string nil 'limit)
516 (if (eq (antlr-syntactic-context) 0) (setq continue nil) t)))
517 (if continue nil (point))))
519 (defun antlr-search-backward (string)
520 "Search backward from point for STRING.
521 Set point to the beginning of the occurrence found, and return point.
522 Return nil if no occurrence was found. Do not search within comments,
523 strings and actions/semantic predicates."
524 ;; WARNING: Should only be used with `antlr-action-syntax-table'!
526 (while (and (search-backward string nil 'limit)
527 (if (eq (antlr-syntactic-context) 0) (setq continue nil) t)))
528 (if continue nil (point))))
530 (defsubst antlr-skip-sexps (count)
531 "Skip the next COUNT balanced expressions and the comments after it.
532 Return position before the comments after the last expression."
533 (goto-char (or (antlr-scan-sexps (point) count nil t) (point-max)))
535 (c-forward-syntactic-ws)))
538 ;;;===========================================================================
540 ;;;===========================================================================
542 (defun antlr-font-lock-keywords ()
543 "Return font-lock keywords for current buffer.
544 See `antlr-font-lock-additional-keywords', `antlr-language' and
545 `antlr-font-lock-maximum-decoration'."
546 (if (eq antlr-font-lock-maximum-decoration 'none)
547 antlr-font-lock-additional-keywords
548 (append antlr-font-lock-additional-keywords
549 (eval (let ((major-mode antlr-language)) ; dynamic
550 (font-lock-choose-keywords
551 (cdr (assq antlr-language
552 antlr-font-lock-keywords-alist))
553 (if (eq antlr-font-lock-maximum-decoration 'inherit)
554 font-lock-maximum-decoration
555 antlr-font-lock-maximum-decoration)))))))
558 ;;;===========================================================================
560 ;;;===========================================================================
562 (defun antlr-imenu-create-index-function ()
563 "Return imenu index-alist for ANTLR grammar files."
571 ;; Using `imenu-progress-message' would require imenu for compilation --
572 ;; nobody is missing these messages...
573 (antlr-with-syntax-table antlr-action-syntax-table
574 ;; We stick to the imenu standard and search backwards, although I don't
575 ;; think this is right. It is slower and more likely not to work during
576 ;; editing (you are more likely to add functions to the end of the file).
579 (if (setq semi (antlr-search-backward ";"))
580 (progn (forward-char) (antlr-skip-exception-part t))
581 (antlr-skip-file-prelude t))
582 (if (looking-at "{") (antlr-skip-sexps 1))
583 (if (looking-at "class[ \t]+\\([A-Z\300-\326\330-\337]\\sw*\\)[ \t]+extends[ \t]+\\([A-Z\300-\326\330-\337]\\sw*\\)[ \t]*;")
585 (push (cons (match-string 1)
586 (if imenu-use-markers
587 (copy-marker (match-beginning 1))
588 (match-beginning 1)))
591 (let ((super (match-string 2)))
592 (cond ((string-equal super "Parser")
593 (setq parser (nconc items parser)))
594 ((string-equal super "Lexer")
595 (setq lexer (nconc items lexer)))
596 ((string-equal super "TreeParser")
597 (setq treeparser (nconc items treeparser)))
599 (setq misc (nconc items misc))))
601 (if (looking-at "p\\(ublic\\|rotected\\|rivate\\)")
602 (antlr-skip-sexps 1))
603 (when (looking-at "\\sw+")
604 (push (cons (match-string 0)
605 (if imenu-use-markers
606 (copy-marker (match-beginning 0))
607 (match-beginning 0)))
609 (or items ; outside any class
610 (prog1 (setq items misc) (setq misc nil))
611 (prog1 (setq items parser) (setq parser nil))
612 (prog1 (setq items lexer) (setq lexer nil))
613 (prog1 (setq items treeparser) (setq treeparser nil)))
614 (if misc (push (cons "Miscellaneous" misc) items))
615 (if treeparser (push (cons "TreeParser" treeparser) items))
616 (if lexer (push (cons "Lexer" lexer) items))
617 (if parser (push (cons "Parser" parser) items))
618 (if classes (cons (cons "Classes" classes) items) items)))
621 ;;;===========================================================================
622 ;;; Parse grammar files (internal functions)
623 ;;;===========================================================================
625 (defun antlr-skip-exception-part (skip-comment)
626 "Skip exception part of current rule, i.e., everything after `;'.
627 This also includes the options and tokens part of a grammar class
628 header. If SKIP-COMMENT is non-nil, also skip the comment after that
632 (c-forward-syntactic-ws)
633 (while (looking-at "options\\>\\|tokens\\>")
635 (setq pos (antlr-skip-sexps 2)))
637 ;; Problem: an action only belongs to a class def, not a normal rule.
638 ;; But checking the current rule type is too expensive => only expect
639 ;; an action if we have found an option or tokens part.
640 (if (looking-at "{") (setq pos (antlr-skip-sexps 1)))
641 (while (looking-at "exception\\>")
642 (setq pos (antlr-skip-sexps 1))
643 (if (looking-at "\\[") (setq pos (antlr-skip-sexps 1)))
644 (while (looking-at "catch\\>")
645 (setq pos (antlr-skip-sexps 3)))))
646 (or skip-comment (goto-char pos))))
648 (defun antlr-skip-file-prelude (skip-comment)
649 "Skip the file prelude: the header and file options.
650 If SKIP-COMMENT is non-nil, also skip the comment after that part."
653 (c-forward-syntactic-ws)
654 (if skip-comment (setq pos0 (point)))
655 (if (looking-at "header\\>") (setq pos (antlr-skip-sexps 2)))
656 (if (looking-at "options\\>") (setq pos (antlr-skip-sexps 2)))
657 (or skip-comment (goto-char pos))
660 (defun antlr-next-rule (arg skip-comment)
661 "Move forward to next end of rule. Do it ARG many times.
662 A grammar class header and the file prelude are also considered as a
663 rule. Negative argument ARG means move back to ARGth preceding end of
664 rule. The behavior is not defined when ARG is zero. If SKIP-COMMENT
665 is non-nil, move to beginning of the rule."
666 ;; WARNING: Should only be used with `antlr-action-syntax-table'!
670 ;; first look whether point is in exception part
671 (if (antlr-search-backward ";")
675 (antlr-skip-exception-part skip-comment))
676 (antlr-skip-file-prelude skip-comment))
678 (unless (and (< (point) pos) (zerop (incf arg)))
679 ;; if we have moved backward, we already moved one defun backward
680 (goto-char beg) ; rewind (to ";" / point)
681 (while (and arg (<= (incf arg) 0))
682 (if (antlr-search-backward ";")
686 (setq pos (antlr-skip-file-prelude skip-comment))
689 (goto-char (if (>= pos beg) (point-min) pos)))
690 (goto-char (if (or (>= (point) beg) (= (point) pos))
693 (when arg ; always found a ";"
695 (antlr-skip-exception-part skip-comment)))
696 (if (<= (point) pos) ; moved backward?
697 (goto-char pos) ; rewind
698 (decf arg)) ; already moved one defun forward
700 (while (>= (decf arg) 0)
701 (antlr-search-forward ";"))
702 (antlr-skip-exception-part skip-comment)))))
704 (defun antlr-outside-rule-p ()
705 "Non-nil if point is outside a grammar rule.
706 Move to the beginning of the current rule if point is inside a rule."
707 ;; WARNING: Should only be used with `antlr-action-syntax-table'!
709 (antlr-next-rule -1 nil)
710 (let ((between (or (bobp) (< (point) pos))))
711 (c-forward-syntactic-ws)
712 (and between (> (point) pos) (goto-char pos)))))
715 ;;;===========================================================================
716 ;;; Parse grammar files (commands)
717 ;;;===========================================================================
718 ;; No (interactive "_") in Emacs... use `zmacs-region-stays'.
720 (defun antlr-inside-rule-p ()
721 "Non-nil if point is inside a grammar rule.
722 A grammar class header and the file prelude are also considered as a
725 (antlr-with-syntax-table antlr-action-syntax-table
726 (not (antlr-outside-rule-p)))))
728 (defun antlr-end-of-rule (&optional arg)
729 "Move forward to next end of rule. Do it ARG [default: 1] many times.
730 A grammar class header and the file prelude are also considered as a
731 rule. Negative argument ARG means move back to ARGth preceding end of
732 rule. If ARG is zero, run `antlr-end-of-body'."
736 (antlr-with-syntax-table antlr-action-syntax-table
737 (antlr-next-rule arg nil))
738 (setq zmacs-region-stays t)))
740 (defun antlr-beginning-of-rule (&optional arg)
741 "Move backward to preceding beginning of rule. Do it ARG many times.
742 A grammar class header and the file prelude are also considered as a
743 rule. Negative argument ARG means move forward to ARGth next beginning
744 of rule. If ARG is zero, run `antlr-beginning-of-body'."
747 (antlr-beginning-of-body)
748 (antlr-with-syntax-table antlr-action-syntax-table
749 (antlr-next-rule (- arg) t))
750 (setq zmacs-region-stays t)))
752 (defun antlr-end-of-body (&optional msg)
753 "Move to position after the `;' of the current rule.
754 A grammar class header is also considered as a rule. With optional
755 prefix arg MSG, move to `:'."
757 (antlr-with-syntax-table antlr-action-syntax-table
758 (let ((orig (point)))
759 (if (antlr-outside-rule-p)
760 (error "Outside an ANTLR rule"))
762 (when (< (antlr-skip-file-prelude t) (point))
763 ;; Yes, we are in the file prelude
765 (error (or msg "The file prelude is without `;'")))
766 (antlr-search-forward ";")
769 (progn (goto-char bor)
770 (or (antlr-search-forward ":") (point-max))))
773 (c-forward-syntactic-ws)))))
774 (setq zmacs-region-stays t))
776 (defun antlr-beginning-of-body ()
777 "Move to the first element after the `:' of the current rule."
779 (antlr-end-of-body "Class headers and the file prelude are without `:'"))
782 ;;;===========================================================================
784 ;;;===========================================================================
786 (defun antlr-indent-line ()
787 "Indent the current line as ANTLR grammar code.
788 The indentation of non-comment lines are calculated by `c-basic-offset',
790 - the level of the paren/brace/bracket depth,
791 - plus 0/2/1, depending on the position inside the rule: header, body,
793 - minus 1 if `antlr-indent-item-regexp' matches the beginning of the
794 line starting from the first non-blank.
796 Lines inside block comments are not changed or indented by
797 `c-indent-line', see `antlr-indent-comment'."
798 (let ((orig (point)) bol boi indent syntax)
801 (skip-chars-forward " \t")
803 ;; check syntax at beginning of indentation ------------------------------
804 (antlr-with-syntax-table antlr-action-syntax-table
805 (antlr-invalidate-context-cache)
806 (cond ((symbolp (setq syntax (antlr-syntactic-context)))
807 (setq indent nil)) ; block-comments, strings, (comments)
809 (antlr-next-rule -1 t)
810 (if (antlr-search-forward ":") (< boi (1- (point))) t))
811 (setq indent 0)) ; in rule header
812 ((if (antlr-search-forward ";") (< boi (point)) t)
813 (setq indent 2)) ; in rule body
816 (antlr-skip-exception-part nil)
817 (setq indent (if (> (point) boi) 1 0))))) ; in exception part?
818 ;; compute the corresponding indentation and indent ----------------------
822 (and (eq antlr-indent-comment t)
823 (not (eq syntax 'string))
827 (antlr-invalidate-context-cache)
828 (incf indent (antlr-syntactic-context))
829 (and (> indent 0) (looking-at antlr-indent-item-regexp) (decf indent))
830 (setq indent (* indent c-basic-offset))
831 ;; the usual major-mode indent stuff:
832 (setq orig (- (point-max) orig))
833 (unless (= (current-column) indent)
834 (delete-region bol boi)
837 ;; If initial point was within line's indentation,
838 ;; position after the indentation. Else stay at same point in text.
839 (if (> (- (point-max) orig) (point))
840 (goto-char (- (point-max) orig))))))
842 (defun antlr-indent-command (&optional arg)
843 "Indent the current line or insert tabs/spaces.
844 With optional prefix argument ARG or if the previous command was this
845 command, insert ARG tabs or spaces according to `indent-tabs-mode'.
846 Otherwise, indent the current line with `antlr-indent-line'."
848 (if (or arg (eq last-command 'antlr-indent-command))
850 (let ((antlr-indent-comment (and antlr-indent-comment t))) ; dynamic
851 (antlr-indent-line))))
854 ;;;===========================================================================
856 ;;;===========================================================================
858 (defun antlr-c-common-init ()
859 "Like `c-common-init' except menu, auto-hungry and c-style stuff."
861 (make-local-variable 'paragraph-start)
862 (make-local-variable 'paragraph-separate)
863 (make-local-variable 'paragraph-ignore-fill-prefix)
864 (make-local-variable 'require-final-newline)
865 (make-local-variable 'parse-sexp-ignore-comments)
866 (make-local-variable 'indent-line-function)
867 (make-local-variable 'indent-region-function)
868 (make-local-variable 'comment-start)
869 (make-local-variable 'comment-end)
870 (make-local-variable 'comment-column)
871 (make-local-variable 'comment-start-skip)
872 (make-local-variable 'comment-multi-line)
873 (make-local-variable 'outline-regexp)
874 (make-local-variable 'outline-level)
875 (make-local-variable 'adaptive-fill-regexp)
876 (make-local-variable 'adaptive-fill-mode)
877 (make-local-variable 'imenu-generic-expression) ;set in the mode functions
878 (and (boundp 'comment-line-break-function)
879 (make-local-variable 'comment-line-break-function))
880 ;; Emacs 19.30 and beyond only, AFAIK
881 (if (boundp 'fill-paragraph-function)
883 (make-local-variable 'fill-paragraph-function)
884 (setq fill-paragraph-function 'c-fill-paragraph)))
885 ;; now set their values
886 (setq paragraph-start (concat page-delimiter "\\|$")
887 paragraph-separate paragraph-start
888 paragraph-ignore-fill-prefix t
889 require-final-newline t
890 parse-sexp-ignore-comments t
891 indent-line-function 'c-indent-line
892 indent-region-function 'c-indent-region
893 outline-regexp "[^#\n\^M]"
894 outline-level 'c-outline-level
896 comment-start-skip "/\\*+ *\\|// *"
897 comment-multi-line nil
898 comment-line-break-function 'c-comment-line-break-function
899 adaptive-fill-regexp nil
900 adaptive-fill-mode nil)
901 ;; we have to do something special for c-offsets-alist so that the
902 ;; buffer local value has its own alist structure.
903 (setq c-offsets-alist (copy-alist c-offsets-alist))
904 ;; setup the comment indent variable in a Emacs version portable way
905 ;; ignore any byte compiler warnings you might get here
906 (make-local-variable 'comment-indent-function)
907 (setq comment-indent-function 'c-comment-indent))
909 (defun antlr-language-for-option (option-value)
910 "Find element in `antlr-language-alist' for OPTION-VALUE."
911 ;; Like (find OPTION-VALUE antlr-language-alist :key 'cddr :test 'member)
912 (let ((seq antlr-language-alist)
916 (if (member option-value (cddr r))
917 (setq seq nil) ; stop
918 (setq r nil))) ; no result yet
923 "Major mode for editing ANTLR grammar files.
926 (c-initialize-cc-mode) ; for java syntax table
927 (kill-all-local-variables)
928 ;; ANTLR specific ----------------------------------------------------------
929 (setq major-mode 'antlr-mode
931 (setq local-abbrev-table antlr-mode-abbrev-table)
932 (set-syntax-table java-mode-syntax-table)
933 (unless antlr-action-syntax-table
934 (let ((slist (nth 3 antlr-font-lock-defaults)))
935 (setq antlr-action-syntax-table
936 (copy-syntax-table java-mode-syntax-table))
938 (modify-syntax-entry (caar slist) (cdar slist)
939 antlr-action-syntax-table)
940 (setq slist (cdr slist)))))
941 (use-local-map antlr-mode-map)
942 (make-local-variable 'antlr-language)
943 (unless antlr-language
945 (goto-char (point-min))
947 (car (or (and (re-search-forward (cdr antlr-language-limit-n-regexp)
948 (car antlr-language-limit-n-regexp)
950 (antlr-language-for-option (match-string 1)))
951 (antlr-language-for-option nil))))))
952 (if (stringp (cadr (assq antlr-language antlr-language-alist)))
955 (cadr (assq antlr-language antlr-language-alist)))))
956 ;; indentation, for the C engine -------------------------------------------
957 (antlr-c-common-init)
958 (setq indent-line-function 'antlr-indent-line
959 indent-region-function nil) ; too lazy
960 (setq comment-start "// "
963 (if (eq antlr-language 'c++-mode)
964 (setq c-conditional-key c-C++-conditional-key
965 c-comment-start-regexp c-C++-comment-start-regexp
966 c-class-key c-C++-class-key
967 c-extra-toplevel-key c-C++-extra-toplevel-key
968 c-access-key c-C++-access-key
969 c-recognize-knr-p nil)
970 (setq c-conditional-key c-Java-conditional-key
971 c-comment-start-regexp c-Java-comment-start-regexp
972 c-class-key c-Java-class-key
975 c-recognize-knr-p nil
976 c-access-key c-Java-access-key)
977 (and (boundp 'c-inexpr-class-key) (boundp 'c-Java-inexpr-class-key)
978 (setq c-inexpr-class-key c-Java-inexpr-class-key)))
979 ;; various -----------------------------------------------------------------
980 (make-local-variable 'font-lock-defaults)
981 (setq font-lock-defaults antlr-font-lock-defaults)
982 (easy-menu-add antlr-mode-menu)
983 (make-local-variable 'imenu-create-index-function)
984 (setq imenu-create-index-function 'antlr-imenu-create-index-function)
985 (make-local-variable 'imenu-generic-expression)
986 (setq imenu-generic-expression t) ; fool stupid test
987 (and antlr-imenu-name ; there should be a global variable...
988 (fboundp 'imenu-add-to-menubar)
989 (imenu-add-to-menubar
990 (if (stringp antlr-imenu-name) antlr-imenu-name "Index")))
992 (run-hooks 'antlr-mode-hook))
995 (defun antlr-set-tabs ()
996 "Use ANTLR's convention for TABs according to `antlr-tab-offset-alist'.
997 Used in `antlr-mode'. Also a useful function in `java-mode-hook'."
999 (let ((alist antlr-tab-offset-alist) elem)
1001 (setq elem (pop alist))
1002 (and (or (null (car elem)) (eq (car elem) major-mode))
1003 (or (null (cadr elem))
1004 (string-match (cadr elem) buffer-file-name))
1005 (setq tab-width (caddr elem)
1006 indent-tabs-mode (cadddr elem)
1009 ;;; antlr-mode.el ends here
1011 ; LocalWords: antlr ANother ANTLR's Cpp Lexer TreeParser esp refs VALUEs ea ee
1012 ; LocalWords: Java's Nomencl ruledef tokendef ruleref tokenref setType ader
1013 ; LocalWords: ivate syntab lexer treeparser lic rotected rivate bor boi AFAIK
1014 ; LocalWords: slist knr inexpr