Some fixes to follow coding conventions.
[bpt/emacs.git] / lisp / progmodes / antlr-mode.el
1 ;;; antlr-mode.el --- major mode for ANTLR grammar files
2
3 ;; Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
4 ;;
5 ;; Author: Christoph.Wedler@sap.com
6 ;; Version: 1.4
7 ;; X-URL: http://www.fmi.uni-passau.de/~wedler/antlr-mode/
8
9 ;; This file is part of GNU Emacs.
10
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)
14 ;; any later version.
15
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.
20
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.
25
26 ;;; Commentary:
27
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>.
31
32 ;; This package provides the following features:
33 ;; * Syntax coloring (via font-lock) for grammar symbols and the code in
34 ;; actions. The latter depends on the language settings.
35 ;; * Indentation for the current line (TAB) and selected region (C-M-\).
36 ;; * Support for imenu/speedbar: menu "Index" (Parser, Lexer, TreeParser).
37 ;; * Commands to move to previous/next rule, beginning/end of rule body etc.
38 ;; * Commands to hide/unhide actions, upcase/downcase literals.
39 ;; * Run ANTLR from within Emacs, create Makefile dependencies.
40
41 ;; LANGUAGE SETTINGS. This mode needs to know which language is used in
42 ;; actions and semantic predicated of the grammar. This information is used
43 ;; for syntax coloring and the creation of the Makefile dependencies. It is
44 ;; stored in variable `antlr-language' and automatically set according to
45 ;; ANTLR's file option "language", see `antlr-language-alist'. The supported
46 ;; languages are "Java" (java-mode) and "Cpp" (c++-mode).
47
48 ;; INDENTATION. This package supports ANTLR's (intended) indentation style
49 ;; which is based on a simple paren/brace/bracket depth-level calculation, see
50 ;; `antlr-indent-line'. The indentation engine of cc-mode is only used inside
51 ;; block comments (it is not easy to use it for actions, esp if they come early
52 ;; in the rule body). By default, this package defines a tab width of 4 to be
53 ;; consistent to both ANTLR's conventions (TABs usage) and the
54 ;; `c-indentation-style' "java" which sets `c-basic-offset' to 4, see
55 ;; `antlr-tab-offset-alist'. You might want to set this variable to nil.
56
57 ;; SYNTAX COLORING comes in three phases. First, comments and strings are
58 ;; highlighted. Second, the grammar code is highlighted according to
59 ;; `antlr-font-lock-additional-keywords' (rule refs: dark blue, token refs:
60 ;; dark orange, definition: bold blue). Third, actions, semantic predicates
61 ;; and arguments are highlighted according to the usual font-lock keywords of
62 ;; `antlr-language', see also `antlr-font-lock-maximum-decoration'. We define
63 ;; special font-lock faces for the grammar code to allow you to distinguish
64 ;; ANTLR keywords from Java/C++ keywords.
65
66 ;; MAKEFILE CREATION. Command \\[antlr-show-makefile-rules] shows/inserts the
67 ;; dependencies for all grammar files in the current directory. It considers
68 ;; import/export vocabularies and grammar inheritance and provides a value for
69 ;; the -glib option if necessary (which you have to edit if the super-grammar
70 ;; is not in the same directory).
71
72 ;; TODO. Support to insert/change file/grammar/rule/subrule options. imenu
73 ;; support for method definitions in actions is not really planned (you can
74 ;; send be a patch, though). This mode would become too dependent on cc-mode
75 ;; or I would have to do a lot of language-dependent things myself...
76
77 ;; Bug fixes, bug reports, improvements, and suggestions are strongly
78 ;; appreciated. Please check the newest version first:
79 ;; http://www.fmi.uni-passau.de/~wedler/antlr-mode/changes.html
80
81 ;;; Installation:
82
83 ;; This file requires Emacs-20.3, XEmacs-20.4 or higher and package cc-mode.
84
85 ;; If antlr-mode is not part of your distribution, put this file into your
86 ;; load-path and the following into your ~/.emacs:
87 ;; (autoload 'antlr-mode "antlr-mode" nil t)
88 ;; (setq auto-mode-alist (cons '("\\.g\\'" . antlr-mode) auto-mode-alist))
89 ;; (add-hook 'speedbar-load-hook ; would be too late in antlr-mode.el
90 ;; (lambda () (speedbar-add-supported-extension ".g")))
91
92 ;; If you edit ANTLR's source files, you might also want to use
93 ;; (autoload 'antlr-set-tabs "antlr-mode")
94 ;; (add-hook 'java-mode-hook 'antlr-set-tabs)
95
96 ;; I strongly recommend to use font-lock with a support mode like fast-lock,
97 ;; lazy-lock or better jit-lock (Emacs-21.1+) / lazy-shot (XEmacs).
98
99 ;; To customize, use `M-x customize-group RET antlr RET' or the custom browser
100 ;; (Emacs->Programming->Languages->Antlr).
101
102 ;;; Code:
103
104 (provide 'antlr-mode)
105 (eval-when-compile ; required and optional libraries
106 (require 'cc-mode)
107 (defvar outline-level) (defvar imenu-use-markers)
108 (defvar imenu-create-index-function))
109 (eval-when-compile ; Emacs: cl, easymenu
110 (require 'cl)
111 (require 'easymenu))
112 (eval-when-compile ; XEmacs: Emacs vars
113 (defvar inhibit-point-motion-hooks) (defvar deactivate-mark))
114
115 (eval-and-compile ; XEmacs functions, simplified
116 (if (featurep 'xemacs)
117 (defalias 'antlr-scan-sexps 'scan-sexps)
118 (defalias 'antlr-scan-sexps 'antlr-scan-sexps-internal))
119 (if (fboundp 'default-directory)
120 (defalias 'antlr-default-directory 'default-directory)
121 (defun antlr-default-directory () default-directory))
122 (if (fboundp 'read-shell-command)
123 (defalias 'antlr-read-shell-command 'read-shell-command)
124 (defun antlr-read-shell-command (prompt &optional initial-input history)
125 (read-from-minibuffer prompt initial-input nil nil
126 (or history 'shell-command-history))))
127 (if (fboundp 'with-displaying-help-buffer)
128 (defalias 'antlr-with-displaying-help-buffer 'with-displaying-help-buffer)
129 (defun antlr-with-displaying-help-buffer (thunk &optional name)
130 (with-output-to-temp-buffer "*Help*"
131 (save-excursion (funcall thunk)))))
132 (if (and (fboundp 'buffer-syntactic-context)
133 (fboundp 'buffer-syntactic-context-depth))
134 (progn
135 (defalias 'antlr-invalidate-context-cache 'antlr-xemacs-bug-workaround)
136 (defalias 'antlr-syntactic-context 'antlr-fast-syntactic-context))
137 (defalias 'antlr-invalidate-context-cache 'ignore)
138 (defalias 'antlr-syntactic-context 'antlr-slow-syntactic-context)))
139
140
141
142 ;;;;##########################################################################
143 ;;;; Variables
144 ;;;;##########################################################################
145
146
147 (defgroup antlr nil
148 "Major mode for ANTLR grammar files."
149 :group 'languages
150 :link '(emacs-commentary-link "antlr-mode.el")
151 :link '(url-link "http://www.fmi.uni-passau.de/~wedler/antlr-mode/")
152 :prefix "antlr-")
153
154 (defconst antlr-version "1.4"
155 "ANTLR major mode version number.")
156
157
158 ;;;===========================================================================
159 ;;; Controlling ANTLR's code generator (language option)
160 ;;;===========================================================================
161
162 (defvar antlr-language nil
163 "Major mode corresponding to ANTLR's \"language\" option.
164 Set via `antlr-language-alist'. The only useful place to change this
165 buffer-local variable yourself is in `antlr-mode-hook' or in the \"local
166 variable list\" near the end of the file, see
167 `enable-local-variables'.")
168
169 (defcustom antlr-language-alist
170 '((java-mode "Java" nil "\"Java\"" "Java")
171 (c++-mode "C++" "\"Cpp\"" "Cpp"))
172 "List of ANTLR's supported languages.
173 Each element in this list looks like
174 (MAJOR-MODE MODELINE-STRING OPTION-VALUE...)
175
176 MAJOR-MODE, the major mode of the code in the grammar's actions, is the
177 value of `antlr-language' if the first group in the string matched by
178 REGEXP in `antlr-language-limit-n-regexp' is one of the OPTION-VALUEs.
179 An OPTION-VALUE of nil denotes the fallback element. MODELINE-STRING is
180 also displayed in the modeline next to \"Antlr\"."
181 :group 'antlr
182 :type '(repeat (group :value (java-mode "")
183 (function :tag "Major mode")
184 (string :tag "Modeline string")
185 (repeat :tag "ANTLR language option" :inline t
186 (choice (const :tag "Default" nil)
187 string )))))
188
189 (defcustom antlr-language-limit-n-regexp
190 '(3000 . "language[ \t]*=[ \t]*\\(\"?[A-Z][A-Za-z_]*\"?\\)")
191 "Used to set a reasonable value for `antlr-language'.
192 Looks like (LIMIT . REGEXP). Search for REGEXP from the beginning of
193 the buffer to LIMIT and use the first group in the matched string to set
194 the language according to `antlr-language-alist'."
195 :group 'antlr
196 :type '(cons (choice :tag "Limit" (const :tag "No" nil) (integer :value 0))
197 regexp))
198
199
200 ;;;===========================================================================
201 ;;; Hide/Unhide, Indent/Tabs
202 ;;;===========================================================================
203
204 (defcustom antlr-action-visibility 3
205 "Visibility of actions when command `antlr-hide-actions' is used.
206 If nil, the actions with their surrounding braces are hidden. If a
207 number, do not hide the braces, only hide the contents if its length is
208 greater than this number."
209 :group 'antlr
210 :type '(choice (const :tag "Completely hidden" nil)
211 (integer :tag "Hidden if longer than" :value 3)))
212
213 (defcustom antlr-indent-comment 'tab
214 "*Non-nil, if the indentation should touch lines in block comments.
215 If nil, no continuation line of a block comment is changed. If t, they
216 are changed according to `c-indentation-line'. When not nil and not t,
217 they are only changed by \\[antlr-indent-command]."
218 :group 'antlr
219 :type '(radio (const :tag "No" nil)
220 (const :tag "Always" t)
221 (sexp :tag "With TAB" :format "%t" :value tab)))
222
223 (defcustom antlr-tab-offset-alist
224 '((antlr-mode nil 4 nil)
225 (java-mode "antlr" 4 nil))
226 "Alist to determine whether to use ANTLR's convention for TABs.
227 Each element looks like (MAJOR-MODE REGEXP TAB-WIDTH INDENT-TABS-MODE).
228 The first element whose MAJOR-MODE is nil or equal to `major-mode' and
229 whose REGEXP is nil or matches `buffer-file-name' is used to set
230 `tab-width' and `indent-tabs-mode'. This is useful to support both
231 ANTLR's and Java's indentation styles. Used by `antlr-set-tabs'."
232 :group 'antlr
233 :type '(repeat (group :value (antlr-mode nil 8 nil)
234 (choice (const :tag "All" nil)
235 (function :tag "Major mode"))
236 (choice (const :tag "All" nil) regexp)
237 (integer :tag "Tab width")
238 (boolean :tag "Indent-tabs-mode"))))
239
240 (defvar antlr-indent-item-regexp
241 "[]}):;|&]\\|default[ \t]*:\\|case[ \t]+\\('\\\\?.'\\|[0-9]+\\|[A-Za-z_][A-Za-z_0-9]*\\)[ \t]*:" ; & is local ANTLR extension
242 "Regexp matching lines which should be indented by one TAB less.
243 See command \\[antlr-indent-command].")
244
245
246 ;;;===========================================================================
247 ;;; Run tool, create Makefile dependencies
248 ;;;===========================================================================
249
250 (defcustom antlr-tool-command "java antlr.Tool"
251 "*Command used in \\[antlr-run-tool] to run the Antlr tool.
252 This variable should include all options passed to Antlr except the
253 option \"-glib\" which is automatically suggested if necessary."
254 :group 'antlr
255 :type 'string)
256
257 (defcustom antlr-ask-about-save t
258 "*If not nil, \\[antlr-run-tool] asks which buffers to save.
259 Otherwise, it saves all modified buffers before running without asking."
260 :group 'antlr
261 :type 'boolean)
262
263 (defcustom antlr-makefile-specification
264 '("\n" ("GENS" "GENS%d" " \\\n\t") "$(ANTLR)")
265 "*Variable to specify the appearance of the generated makefile rules.
266 This variable influences the output of \\[antlr-show-makefile-rules].
267 It looks like (RULE-SEP GEN-VAR-SPEC COMMAND).
268
269 RULE-SEP is the string to separate different makefile rules. COMMAND is
270 a string with the command which runs the Antlr tool, it should include
271 all options except the option \"-glib\" which is automatically added
272 if necessary.
273
274 If GEN-VAR-SPEC is nil, each target directly consists of a list of
275 files. If GEN-VAR-SPEC looks like (GEN-VAR GEN-VAR-FORMAT GEN-SEP), a
276 Makefile variable is created for each rule target.
277
278 Then, GEN-VAR is a string with the name of the variable which contains
279 the file names of all makefile rules. GEN-VAR-FORMAT is a format string
280 producing the variable of each target with substitution COUNT/%d where
281 COUNT starts with 1. GEN-SEP is used to separate long variable values."
282 :group 'antlr
283 :type '(list (string :tag "Rule separator")
284 (choice
285 (const :tag "Direct targets" nil)
286 (list :tag "Variables for targets"
287 (string :tag "Variable for all targets")
288 (string :tag "Format for each target variable")
289 (string :tag "Variable separator")))
290 (string :tag "ANTLR command")))
291
292 (defvar antlr-file-formats-alist
293 '((java-mode ("%sTokenTypes.java") ("%s.java"))
294 (c++-mode ("%sTokenTypes.hpp") ("%s.cpp" "%s.hpp")))
295 "Language dependent formats which specify generated files.
296 Each element in this list looks looks like
297 (MAJOR-MODE (VOCAB-FILE-FORMAT...) (CLASS-FILE-FORMAT...)).
298
299 The element whose MAJOR-MODE is equal to `antlr-language' is used to
300 specify the generated files which are language dependent. See variable
301 `antlr-special-file-formats' for language independent files.
302
303 VOCAB-FILE-FORMAT is a format string, it specifies with substitution
304 VOCAB/%s the generated file for each export vocabulary VOCAB.
305 CLASS-FILE-FORMAT is a format string, it specifies with substitution
306 CLASS/%s the generated file for each grammar class CLASS.")
307
308 (defvar antlr-special-file-formats '("%sTokenTypes.txt" "expanded%s.g")
309 "Language independent formats which specify generated files.
310 The value looks like (VOCAB-FILE-FORMAT EXPANDED-GRAMMAR-FORMAT).
311
312 VOCAB-FILE-FORMAT is a format string, it specifies with substitution
313 VOCAB/%s the generated or input file for each export or import
314 vocabulary VOCAB, respectively. EXPANDED-GRAMMAR-FORMAT is a format
315 string, it specifies with substitution GRAMMAR/%s the constructed
316 grammar file if the file GRAMMAR.g contains a grammar class which
317 extends a class other than \"Lexer\", \"Parser\" or \"TreeParser\".
318
319 See variable `antlr-file-formats-alist' for language dependent
320 formats.")
321
322 (defvar antlr-unknown-file-formats '("?%s?.g" "?%s?")
323 "*Formats which specify the names of unknown files.
324 The value looks like (SUPER-GRAMMAR-FILE-FORMAT SUPER-EVOCAB-FORMAT).
325
326 SUPER-GRAMMAR-FORMAT is a format string, it specifies with substitution
327 SUPER/%s the name of a grammar file for Antlr's option \"-glib\" if no
328 grammar file in the current directory defines the class SUPER or if it
329 is defined more than once. SUPER-EVOCAB-FORMAT is a format string, it
330 specifies with substitution SUPER/%s the name for the export vocabulary
331 of above mentioned class SUPER.")
332
333 (defvar antlr-help-unknown-file-text
334 "## The following rules contain filenames of the form
335 ## \"?SUPERCLASS?.g\" (and \"?SUPERCLASS?TokenTypes.txt\")
336 ## where SUPERCLASS is not found to be defined in any grammar file of
337 ## the current directory or is defined more than once. Please replace
338 ## these filenames by the grammar files (and their exportVocab).\n\n"
339 "String indicating the existence of unknown files in the Makefile.
340 See \\[antlr-show-makefile-rules] and `antlr-unknown-file-formats'.")
341
342 (defvar antlr-help-rules-intro
343 "The following Makefile rules define the dependencies for all (non-
344 expanded) grammars in directory \"%s\".\n
345 They are stored in the kill-ring, i.e., you can insert them with C-y
346 into your Makefile. You can also invoke M-x antlr-show-makefile-rules
347 from within a Makefile to insert them directly.\n\n\n"
348 "Introduction to use with \\[antlr-show-makefile-rules].
349 It is a format string and used with substitution DIRECTORY/%s where
350 DIRECTORY is the name of the current directory.")
351
352
353 ;;;===========================================================================
354 ;;; Menu
355 ;;;===========================================================================
356
357 (defcustom antlr-imenu-name t
358 "*Non-nil, if a \"Index\" menu should be added to the menubar.
359 If it is a string, it is used instead \"Index\". Requires package
360 imenu."
361 :group 'antlr
362 :type '(choice (const :tag "No menu" nil)
363 (const :tag "Index menu" t)
364 (string :tag "Other menu name")))
365
366 (defvar antlr-mode-map
367 (let ((map (make-sparse-keymap)))
368 (define-key map "\t" 'antlr-indent-command)
369 (define-key map "\e\C-a" 'antlr-beginning-of-rule)
370 (define-key map "\e\C-e" 'antlr-end-of-rule)
371 (define-key map "\C-c\C-a" 'antlr-beginning-of-body)
372 (define-key map "\C-c\C-e" 'antlr-end-of-body)
373 (define-key map "\C-c\C-f" 'c-forward-into-nomenclature)
374 (define-key map "\C-c\C-b" 'c-backward-into-nomenclature)
375 (define-key map "\C-c\C-c" 'comment-region)
376 (define-key map "\C-c\C-v" 'antlr-hide-actions)
377 (define-key map "\C-c\C-r" 'antlr-run-tool)
378 ;; I'm too lazy to define my own:
379 (define-key map "\ea" 'c-beginning-of-statement)
380 (define-key map "\ee" 'c-end-of-statement)
381 map)
382 "Keymap used in `antlr-mode' buffers.")
383
384 (easy-menu-define antlr-mode-menu
385 antlr-mode-map
386 "Major mode menu."
387 '("Antlr"
388 ["Indent Line" antlr-indent-command
389 :active (not buffer-read-only)]
390 ["Indent for Comment" indent-for-comment
391 :active (not buffer-read-only)]
392 ["Comment Out Region" comment-region
393 :active (and (not buffer-read-only)
394 (c-region-is-active-p))]
395 ["Uncomment Region"
396 (comment-region (region-beginning) (region-end) '(4))
397 :active (and (not buffer-read-only)
398 (c-region-is-active-p))]
399 "---"
400 ["Backward Rule" antlr-beginning-of-rule t]
401 ["Forward Rule" antlr-end-of-rule t]
402 ["Start of Rule Body" antlr-beginning-of-body
403 :active (antlr-inside-rule-p)]
404 ["End of Rule Body" antlr-end-of-body
405 :active (antlr-inside-rule-p)]
406 "---"
407 ["Backward Statement" c-beginning-of-statement t]
408 ["Forward Statement" c-end-of-statement t]
409 ["Backward Into Nomencl." c-backward-into-nomenclature t]
410 ["Forward Into Nomencl." c-forward-into-nomenclature t]
411 "---"
412 ["Hide Actions (incl. Args)" antlr-hide-actions t]
413 ["Hide Actions (excl. Args)" (antlr-hide-actions 2) t]
414 ["Unhide All Actions" (antlr-hide-actions 0) t]
415 "---"
416 ["Run Tool on Grammar" antlr-run-tool t]
417 ["Show Makefile Rules" antlr-show-makefile-rules t]))
418
419
420 ;;;===========================================================================
421 ;;; font-lock
422 ;;;===========================================================================
423
424 (defcustom antlr-font-lock-maximum-decoration 'inherit
425 "*The maximum decoration level for fontifying actions.
426 Value `none' means, do not fontify actions, just normal grammar code
427 according to `antlr-font-lock-additional-keywords'. Value `inherit'
428 means, use value of `font-lock-maximum-decoration'. Any other value is
429 interpreted as in `font-lock-maximum-decoration' with no level-0
430 fontification, see `antlr-font-lock-keywords-alist'.
431
432 While calculating the decoration level for actions, `major-mode' is
433 bound to `antlr-language'. For example, with value
434 ((java-mode . 2) (c++-mode . 0))
435 Java actions are fontified with level 2 and C++ actions are not
436 fontified at all."
437 :type '(choice (const :tag "none" none)
438 (const :tag "inherit" inherit)
439 (const :tag "default" nil)
440 (const :tag "maximum" t)
441 (integer :tag "level" 1)
442 (repeat :menu-tag "mode specific" :tag "mode specific"
443 :value ((t . t))
444 (cons :tag "Instance"
445 (radio :tag "Mode"
446 (const :tag "all" t)
447 (symbol :tag "name"))
448 (radio :tag "Decoration"
449 (const :tag "default" nil)
450 (const :tag "maximum" t)
451 (integer :tag "level" 1))))))
452
453 (defconst antlr-no-action-keywords nil
454 ;; Using nil directly won't work (would use highest level, see
455 ;; `font-lock-choose-keywords'), but a non-symbol, i.e., (list), at `car'
456 ;; would break Emacs-21.0:
457 "Empty font-lock keywords for actions.
458 Do not change the value of this constant.")
459
460 (defvar antlr-font-lock-keywords-alist
461 '((java-mode
462 antlr-no-action-keywords
463 java-font-lock-keywords-1 java-font-lock-keywords-2
464 java-font-lock-keywords-3)
465 (c++-mode
466 antlr-no-action-keywords
467 c++-font-lock-keywords-1 c++-font-lock-keywords-2
468 c++-font-lock-keywords-3))
469 "List of font-lock keywords for actions in the grammar.
470 Each element in this list looks like
471 (MAJOR-MODE KEYWORD...)
472
473 If `antlr-language' is equal to MAJOR-MODE, the KEYWORDs are the
474 font-lock keywords according to `font-lock-defaults' used for the code
475 in the grammar's actions and semantic predicates, see
476 `antlr-font-lock-maximum-decoration'.")
477
478 (defvar antlr-font-lock-keyword-face 'antlr-font-lock-keyword-face)
479 (defface antlr-font-lock-keyword-face
480 '((((class color) (background light)) (:foreground "black" :bold t)))
481 "ANTLR keywords."
482 :group 'antlr)
483
484 (defvar antlr-font-lock-ruledef-face 'antlr-font-lock-ruledef-face)
485 (defface antlr-font-lock-ruledef-face
486 '((((class color) (background light)) (:foreground "blue" :bold t)))
487 "ANTLR rule references (definition)."
488 :group 'antlr)
489
490 (defvar antlr-font-lock-tokendef-face 'antlr-font-lock-tokendef-face)
491 (defface antlr-font-lock-tokendef-face
492 '((((class color) (background light)) (:foreground "blue" :bold t)))
493 "ANTLR token references (definition)."
494 :group 'antlr)
495
496 (defvar antlr-font-lock-ruleref-face 'antlr-font-lock-ruleref-face)
497 (defface antlr-font-lock-ruleref-face
498 '((((class color) (background light)) (:foreground "blue4")))
499 "ANTLR rule references (usage)."
500 :group 'antlr)
501
502 (defvar antlr-font-lock-tokenref-face 'antlr-font-lock-tokenref-face)
503 (defface antlr-font-lock-tokenref-face
504 '((((class color) (background light)) (:foreground "orange4")))
505 "ANTLR token references (usage)."
506 :group 'antlr)
507
508 (defvar antlr-font-lock-literal-face 'antlr-font-lock-literal-face)
509 (defface antlr-font-lock-literal-face
510 '((((class color) (background light)) (:foreground "brown4" :bold t)))
511 "ANTLR literal tokens consisting merely of letter-like characters."
512 :group 'antlr)
513
514 (defvar antlr-font-lock-additional-keywords
515 `((antlr-invalidate-context-cache)
516 ("\\$setType[ \t]*(\\([A-Za-z\300-\326\330-\337]\\sw*\\))"
517 (1 antlr-font-lock-tokendef-face))
518 ("\\$\\sw+" (0 font-lock-keyword-face))
519 ;; the tokens are already fontified as string/docstrings:
520 (,(lambda (limit)
521 (antlr-re-search-forward "\"\\(\\sw\\(\\sw\\|-\\)*\\)\"" limit))
522 (1 antlr-font-lock-literal-face t)
523 ,@(and (string-match "XEmacs" emacs-version)
524 '((0 nil)))) ; XEmacs bug workaround
525 (,(lambda (limit)
526 (antlr-re-search-forward
527 "^\\(class\\)[ \t]+\\([A-Za-z\300-\326\330-\337]\\sw*\\)[ \t]+\\(extends\\)[ \t]+\\([A-Za-z\300-\326\330-\337]\\sw*\\)[ \t]*;" limit))
528 (1 antlr-font-lock-keyword-face)
529 (2 antlr-font-lock-ruledef-face)
530 (3 antlr-font-lock-keyword-face)
531 (4 (if (member (match-string 4) '("Lexer" "Parser" "TreeParser"))
532 'antlr-font-lock-keyword-face
533 'font-lock-type-face)))
534 (,(lambda (limit)
535 (antlr-re-search-forward
536 "\\<\\(header\\|options\\|tokens\\|exception\\|catch\\|returns\\)\\>"
537 limit))
538 (1 antlr-font-lock-keyword-face))
539 (,(lambda (limit)
540 (antlr-re-search-forward
541 "^\\(private\\|public\\|protected\\)\\>\\([ \t]+\\(\\sw+\\)\\)?"
542 limit))
543 (1 font-lock-type-face) ; not XEmacs' java level-3 fruit salad
544 (3 (if (antlr-upcase-p (char-after (match-beginning 3)))
545 'antlr-font-lock-tokendef-face
546 'antlr-font-lock-ruledef-face) nil t))
547 (,(lambda (limit)
548 (antlr-re-search-forward "^\\sw+" limit))
549 (0 (if (antlr-upcase-p (char-after (match-beginning 0)))
550 'antlr-font-lock-tokendef-face
551 'antlr-font-lock-ruledef-face) nil t))
552 (,(lambda (limit)
553 ;; not only before a rule ref, also before a literal
554 (antlr-re-search-forward "\\<\\(\\sw+\\)[ \t]*:" limit))
555 (1 font-lock-variable-name-face))
556 (,(lambda (limit)
557 (antlr-re-search-forward "\\<\\(\\sw+[ \t]*=[ \t]*\\)?\\(\\sw+[ \t]*:[ \t]*\\)?\\(\\sw+\\)" limit))
558 ;;(1 antlr-font-lock-default-face nil t) ; fool java-font-lock-keywords
559 (3 (if (antlr-upcase-p (char-after (match-beginning 3)))
560 'antlr-font-lock-tokenref-face
561 'antlr-font-lock-ruleref-face))))
562 "Font-lock keywords for ANTLR's normal grammar code.
563 See `antlr-font-lock-keywords-alist' for the keywords of actions.")
564
565 (defvar antlr-font-lock-defaults
566 '(antlr-font-lock-keywords
567 nil nil ((?_ . "w") (?\( . ".") (?\) . ".")) beginning-of-defun)
568 "Font-lock defaults used for ANTLR syntax coloring.
569 The SYNTAX-ALIST element is also used to initialize
570 `antlr-action-syntax-table'.")
571
572
573 ;;;===========================================================================
574 ;;; Internal variables
575 ;;;===========================================================================
576
577 (defvar antlr-mode-hook nil
578 "Hook called by `antlr-mode'.")
579
580 (defvar antlr-mode-syntax-table nil
581 "Syntax table used in `antlr-mode' buffers.
582 If non-nil, it will be initialized in `antlr-mode'.")
583
584 ;; used for "in Java/C++ code" = syntactic-depth>0
585 (defvar antlr-action-syntax-table nil
586 "Syntax table used for ANTLR action parsing.
587 Initialized by `antlr-mode-syntax-table', changed by SYNTAX-ALIST in
588 `antlr-font-lock-defaults'. This table should be selected if you use
589 `buffer-syntactic-context' and `buffer-syntactic-context-depth' in order
590 not to confuse their context_cache.")
591
592 (defvar antlr-mode-abbrev-table nil
593 "Abbreviation table used in `antlr-mode' buffers.")
594 (define-abbrev-table 'antlr-mode-abbrev-table ())
595
596
597
598 ;;;;##########################################################################
599 ;;;; The Code
600 ;;;;##########################################################################
601
602
603 ;;;===========================================================================
604 ;;; Syntax functions -- Emacs vs XEmacs dependent
605 ;;;===========================================================================
606
607 ;; From help.el (XEmacs-21.1), without `copy-syntax-table'
608 (defmacro antlr-with-syntax-table (syntab &rest body)
609 "Evaluate BODY with the syntax table SYNTAB."
610 `(let ((stab (syntax-table)))
611 (unwind-protect
612 (progn (set-syntax-table ,syntab) ,@body)
613 (set-syntax-table stab))))
614 (put 'antlr-with-syntax-table 'lisp-indent-function 1)
615 (put 'antlr-with-syntax-table 'edebug-form-spec '(form body))
616
617 (defun antlr-scan-sexps-internal (from count &optional dummy no-error)
618 ;; checkdoc-params: (from count dummy)
619 "Like `scan-sexps' but with additional arguments.
620 When optional arg NO-ERROR is non-nil, `scan-sexps' will return nil
621 instead of signaling an error."
622 (if no-error
623 (condition-case nil
624 (scan-sexps from count)
625 (t nil))
626 (scan-sexps from count)))
627
628 (defun antlr-xemacs-bug-workaround (&rest dummies)
629 ;; checkdoc-params: (dummies)
630 "Invalidate context_cache for syntactical context information."
631 ;; XEmacs bug workaround
632 (save-excursion
633 (set-buffer (get-buffer-create " ANTLR XEmacs bug workaround"))
634 (buffer-syntactic-context-depth))
635 nil)
636
637 (defun antlr-fast-syntactic-context ()
638 "Return some syntactic context information.
639 Return `string' if point is within a string, `block-comment' or
640 `comment' is point is within a comment or the depth within all
641 parenthesis-syntax delimiters at point otherwise.
642 WARNING: this may alter `match-data'."
643 (or (buffer-syntactic-context) (buffer-syntactic-context-depth)))
644
645 (defun antlr-slow-syntactic-context ()
646 "Return some syntactic context information.
647 Return `string' if point is within a string, `block-comment' or
648 `comment' is point is within a comment or the depth within all
649 parenthesis-syntax delimiters at point otherwise.
650 WARNING: this may alter `match-data'."
651 (let ((orig (point)))
652 (beginning-of-defun)
653 (let ((state (parse-partial-sexp (point) orig)))
654 (goto-char orig)
655 (cond ((nth 3 state) 'string)
656 ((nth 4 state) 'comment) ; block-comment? -- we don't care
657 (t (car state))))))
658
659
660 ;;;===========================================================================
661 ;;; Misc functions
662 ;;;===========================================================================
663
664 (defun antlr-upcase-p (char)
665 "Non-nil, if CHAR is an uppercase character (if CHAR was a char)."
666 ;; in XEmacs, upcase only works for ASCII
667 (or (and (<= ?A char) (<= char ?Z))
668 (and (<= ?\300 char) (<= char ?\337)))) ; ?\327 is no letter
669
670 (defun antlr-re-search-forward (regexp bound)
671 "Search forward from point for regular expression REGEXP.
672 Set point to the end of the occurrence found, and return point. Return
673 nil if no occurrence was found. Do not search within comments, strings
674 and actions/semantic predicates. BOUND bounds the search; it is a
675 buffer position. See also the functions `match-beginning', `match-end'
676 and `replace-match'."
677 ;; WARNING: Should only be used with `antlr-action-syntax-table'!
678 (let ((continue t))
679 (while (and (re-search-forward regexp bound 'limit)
680 (save-match-data
681 (if (eq (antlr-syntactic-context) 0)
682 (setq continue nil)
683 t))))
684 (if continue nil (point))))
685
686 (defun antlr-search-forward (string)
687 "Search forward from point for STRING.
688 Set point to the end of the occurrence found, and return point. Return
689 nil if no occurrence was found. Do not search within comments, strings
690 and actions/semantic predicates."
691 ;; WARNING: Should only be used with `antlr-action-syntax-table'!
692 (let ((continue t))
693 (while (and (search-forward string nil 'limit)
694 (if (eq (antlr-syntactic-context) 0) (setq continue nil) t)))
695 (if continue nil (point))))
696
697 (defun antlr-search-backward (string)
698 "Search backward from point for STRING.
699 Set point to the beginning of the occurrence found, and return point.
700 Return nil if no occurrence was found. Do not search within comments,
701 strings and actions/semantic predicates."
702 ;; WARNING: Should only be used with `antlr-action-syntax-table'!
703 (let ((continue t))
704 (while (and (search-backward string nil 'limit)
705 (if (eq (antlr-syntactic-context) 0) (setq continue nil) t)))
706 (if continue nil (point))))
707
708 (defsubst antlr-skip-sexps (count)
709 "Skip the next COUNT balanced expressions and the comments after it.
710 Return position before the comments after the last expression."
711 (goto-char (or (antlr-scan-sexps (point) count nil t) (point-max)))
712 (prog1 (point)
713 (c-forward-syntactic-ws)))
714
715
716 ;;;===========================================================================
717 ;;; font-lock
718 ;;;===========================================================================
719
720 (defun antlr-font-lock-keywords ()
721 "Return font-lock keywords for current buffer.
722 See `antlr-font-lock-additional-keywords', `antlr-language' and
723 `antlr-font-lock-maximum-decoration'."
724 (if (eq antlr-font-lock-maximum-decoration 'none)
725 antlr-font-lock-additional-keywords
726 (append antlr-font-lock-additional-keywords
727 (eval (let ((major-mode antlr-language)) ; dynamic
728 (font-lock-choose-keywords
729 (cdr (assq antlr-language
730 antlr-font-lock-keywords-alist))
731 (if (eq antlr-font-lock-maximum-decoration 'inherit)
732 font-lock-maximum-decoration
733 antlr-font-lock-maximum-decoration)))))))
734
735
736 ;;;===========================================================================
737 ;;; imenu support
738 ;;;===========================================================================
739
740 (defun antlr-imenu-create-index-function ()
741 "Return imenu index-alist for ANTLR grammar files."
742 (let ((items nil)
743 (classes nil)
744 (semi (point-max)))
745 ;; Using `imenu-progress-message' would require imenu for compilation --
746 ;; nobody is missing these messages...
747 (antlr-with-syntax-table antlr-action-syntax-table
748 ;; We stick to the imenu standard and search backwards, although I don't
749 ;; think this is right. It is slower and more likely not to work during
750 ;; editing (you are more likely to add functions to the end of the file).
751 (while semi
752 (goto-char semi)
753 (if (setq semi (antlr-search-backward ";"))
754 (progn (forward-char) (antlr-skip-exception-part t))
755 (antlr-skip-file-prelude t))
756 (if (looking-at "{") (antlr-skip-sexps 1))
757 (if (looking-at "class[ \t]+\\([A-Za-z\300-\326\330-\337]\\sw*\\)[ \t]+extends[ \t]+\\([A-Za-z\300-\326\330-\337]\\sw*\\)[ \t]*;")
758 (push (cons (match-string 1)
759 (if imenu-use-markers
760 (copy-marker (match-beginning 1))
761 (match-beginning 1)))
762 classes)
763 (if (looking-at "p\\(ublic\\|rotected\\|rivate\\)")
764 (antlr-skip-sexps 1))
765 (when (looking-at "\\sw+")
766 (push (cons (match-string 0)
767 (if imenu-use-markers
768 (copy-marker (match-beginning 0))
769 (match-beginning 0)))
770 items)))))
771 (if classes (cons (cons "Classes" classes) items) items)))
772
773
774 ;;;===========================================================================
775 ;;; Parse grammar files (internal functions)
776 ;;;===========================================================================
777
778 (defun antlr-skip-exception-part (skip-comment)
779 "Skip exception part of current rule, i.e., everything after `;'.
780 This also includes the options and tokens part of a grammar class
781 header. If SKIP-COMMENT is non-nil, also skip the comment after that
782 part."
783 (let ((pos (point))
784 (class nil))
785 (c-forward-syntactic-ws)
786 (while (looking-at "options\\>\\|tokens\\>")
787 (setq class t)
788 (setq pos (antlr-skip-sexps 2)))
789 (if class
790 ;; Problem: an action only belongs to a class def, not a normal rule.
791 ;; But checking the current rule type is too expensive => only expect
792 ;; an action if we have found an option or tokens part.
793 (if (looking-at "{") (setq pos (antlr-skip-sexps 1)))
794 (while (looking-at "exception\\>")
795 (setq pos (antlr-skip-sexps 1))
796 (if (looking-at "\\[") (setq pos (antlr-skip-sexps 1)))
797 (while (looking-at "catch\\>")
798 (setq pos (antlr-skip-sexps 3)))))
799 (or skip-comment (goto-char pos))))
800
801 (defun antlr-skip-file-prelude (skip-comment)
802 "Skip the file prelude: the header and file options.
803 If SKIP-COMMENT is non-nil, also skip the comment after that part.
804 Return the start position of the file prelude."
805 (let* ((pos (point))
806 (pos0 pos))
807 (c-forward-syntactic-ws)
808 (if skip-comment (setq pos0 (point)))
809 (while (looking-at "header\\>[ \t]*\\(\"\\)?")
810 (setq pos (antlr-skip-sexps (if (match-beginning 1) 3 2))))
811 (if (looking-at "options\\>") (setq pos (antlr-skip-sexps 2)))
812 (or skip-comment (goto-char pos))
813 pos0))
814
815 (defun antlr-next-rule (arg skip-comment)
816 "Move forward to next end of rule. Do it ARG many times.
817 A grammar class header and the file prelude are also considered as a
818 rule. Negative argument ARG means move back to ARGth preceding end of
819 rule. The behavior is not defined when ARG is zero. If SKIP-COMMENT
820 is non-nil, move to beginning of the rule."
821 ;; WARNING: Should only be used with `antlr-action-syntax-table'!
822 ;; PRE: ARG<>0
823 (let ((pos (point))
824 (beg (point)))
825 ;; first look whether point is in exception part
826 (if (antlr-search-backward ";")
827 (progn
828 (setq beg (point))
829 (forward-char)
830 (antlr-skip-exception-part skip-comment))
831 (antlr-skip-file-prelude skip-comment))
832 (if (< arg 0)
833 (unless (and (< (point) pos) (zerop (incf arg)))
834 ;; if we have moved backward, we already moved one defun backward
835 (goto-char beg) ; rewind (to ";" / point)
836 (while (and arg (<= (incf arg) 0))
837 (if (antlr-search-backward ";")
838 (setq beg (point))
839 (when (>= arg -1)
840 ;; try file prelude:
841 (setq pos (antlr-skip-file-prelude skip-comment))
842 (if (zerop arg)
843 (if (>= (point) beg)
844 (goto-char (if (>= pos beg) (point-min) pos)))
845 (goto-char (if (or (>= (point) beg) (= (point) pos))
846 (point-min) pos))))
847 (setq arg nil)))
848 (when arg ; always found a ";"
849 (forward-char)
850 (antlr-skip-exception-part skip-comment)))
851 (if (<= (point) pos) ; moved backward?
852 (goto-char pos) ; rewind
853 (decf arg)) ; already moved one defun forward
854 (unless (zerop arg)
855 (while (>= (decf arg) 0)
856 (antlr-search-forward ";"))
857 (antlr-skip-exception-part skip-comment)))))
858
859 (defun antlr-outside-rule-p ()
860 "Non-nil if point is outside a grammar rule.
861 Move to the beginning of the current rule if point is inside a rule."
862 ;; WARNING: Should only be used with `antlr-action-syntax-table'!
863 (let ((pos (point)))
864 (antlr-next-rule -1 nil)
865 (let ((between (or (bobp) (< (point) pos))))
866 (c-forward-syntactic-ws)
867 (and between (> (point) pos) (goto-char pos)))))
868
869
870 ;;;===========================================================================
871 ;;; Parse grammar files (commands)
872 ;;;===========================================================================
873 ;; No (interactive "_") in Emacs... use `zmacs-region-stays'.
874
875 (defun antlr-inside-rule-p ()
876 "Non-nil if point is inside a grammar rule.
877 A grammar class header and the file prelude are also considered as a
878 rule."
879 (save-excursion
880 (antlr-with-syntax-table antlr-action-syntax-table
881 (not (antlr-outside-rule-p)))))
882
883 (defun antlr-end-of-rule (&optional arg)
884 "Move forward to next end of rule. Do it ARG [default: 1] many times.
885 A grammar class header and the file prelude are also considered as a
886 rule. Negative argument ARG means move back to ARGth preceding end of
887 rule. If ARG is zero, run `antlr-end-of-body'."
888 (interactive "p")
889 (if (zerop arg)
890 (antlr-end-of-body)
891 (antlr-with-syntax-table antlr-action-syntax-table
892 (antlr-next-rule arg nil))
893 (setq zmacs-region-stays t)))
894
895 (defun antlr-beginning-of-rule (&optional arg)
896 "Move backward to preceding beginning of rule. Do it ARG many times.
897 A grammar class header and the file prelude are also considered as a
898 rule. Negative argument ARG means move forward to ARGth next beginning
899 of rule. If ARG is zero, run `antlr-beginning-of-body'."
900 (interactive "p")
901 (if (zerop arg)
902 (antlr-beginning-of-body)
903 (antlr-with-syntax-table antlr-action-syntax-table
904 (antlr-next-rule (- arg) t))
905 (setq zmacs-region-stays t)))
906
907 (defun antlr-end-of-body (&optional msg)
908 "Move to position after the `;' of the current rule.
909 A grammar class header is also considered as a rule. With optional
910 prefix arg MSG, move to `:'."
911 (interactive)
912 (antlr-with-syntax-table antlr-action-syntax-table
913 (let ((orig (point)))
914 (if (antlr-outside-rule-p)
915 (error "Outside an ANTLR rule"))
916 (let ((bor (point)))
917 (when (< (antlr-skip-file-prelude t) (point))
918 ;; Yes, we are in the file prelude
919 (goto-char orig)
920 (error (or msg "The file prelude is without `;'")))
921 (antlr-search-forward ";")
922 (when msg
923 (when (< (point)
924 (progn (goto-char bor)
925 (or (antlr-search-forward ":") (point-max))))
926 (goto-char orig)
927 (error msg))
928 (c-forward-syntactic-ws)))))
929 (setq zmacs-region-stays t))
930
931 (defun antlr-beginning-of-body ()
932 "Move to the first element after the `:' of the current rule."
933 (interactive)
934 (antlr-end-of-body "Class headers and the file prelude are without `:'"))
935
936
937 ;;;===========================================================================
938 ;;; Literal normalization, Hide Actions
939 ;;;===========================================================================
940
941 (defun antlr-downcase-literals (&optional transform)
942 "Convert all literals in buffer to lower case.
943 If non-nil, TRANSFORM is used on literals instead of `downcase-region'."
944 (interactive)
945 (or transform (setq transform 'downcase-region))
946 (let ((literals 0))
947 (save-excursion
948 (goto-char (point-min))
949 (antlr-with-syntax-table antlr-action-syntax-table
950 (antlr-invalidate-context-cache)
951 (while (antlr-re-search-forward "\"\\(\\sw\\(\\sw\\|-\\)*\\)\"" nil)
952 (funcall transform (match-beginning 0) (match-end 0))
953 (incf literals))))
954 (message "Transformed %d literals" literals)))
955
956 (defun antlr-upcase-literals ()
957 "Convert all literals in buffer to upper case."
958 (interactive)
959 (antlr-downcase-literals 'upcase-region))
960
961 (defun antlr-hide-actions (arg &optional silent)
962 "Hide or unhide all actions in buffer.
963 Hide all actions including arguments in brackets if ARG is 1 or if
964 called interactively without prefix argument. Hide all actions
965 excluding arguments in brackets if ARG is 2 or higher. Unhide all
966 actions if ARG is 0 or negative. See `antlr-action-visibility'.
967
968 Display a message unless optional argument SILENT is non-nil."
969 (interactive "p")
970 ;; from Emacs/lazy-lock: `save-buffer-state'
971 (let ((modified (buffer-modified-p))
972 (buffer-undo-list t) (inhibit-read-only t)
973 (inhibit-point-motion-hooks t) deactivate-mark ; Emacs only
974 before-change-functions after-change-functions
975 buffer-file-name buffer-file-truename)
976 (if (> arg 0)
977 (let ((regexp (if (= arg 1) "[]}]" "}"))
978 (diff (and antlr-action-visibility
979 (+ (max antlr-action-visibility 0) 2))))
980 (antlr-hide-actions 0 t)
981 (save-excursion
982 (goto-char (point-min))
983 (antlr-with-syntax-table antlr-action-syntax-table
984 (antlr-invalidate-context-cache)
985 (while (antlr-re-search-forward regexp nil)
986 (let ((beg (antlr-scan-sexps (point) -1 nil t)))
987 (when beg
988 (if diff ; braces are visible
989 (if (> (point) (+ beg diff))
990 (add-text-properties (1+ beg) (1- (point))
991 '(invisible t intangible t)))
992 ;; if actions is on line(s) of its own, hide WS
993 (and (looking-at "[ \t]*$")
994 (save-excursion
995 (goto-char beg)
996 (skip-chars-backward " \t")
997 (and (bolp) (setq beg (point))))
998 (beginning-of-line 2)) ; beginning of next line
999 (add-text-properties beg (point)
1000 '(invisible t intangible t))))))))
1001 (or silent
1002 (message "Hide all actions (%s arguments)...done"
1003 (if (= arg 1) "including" "excluding"))))
1004 (remove-text-properties (point-min) (point-max)
1005 '(invisible nil intangible nil))
1006 (or silent
1007 (message "Unhide all actions (including arguments)...done")))
1008 (and (not modified) (buffer-modified-p)
1009 (set-buffer-modified-p nil))))
1010
1011
1012 ;;;===========================================================================
1013 ;;; Compute dependencies
1014 ;;;===========================================================================
1015
1016 (defun antlr-file-dependencies ()
1017 "Return dependencies for grammar in current buffer.
1018 The result looks like (FILE (CLASSES . SUPERS) VOCABS . LANGUAGE)
1019 where CLASSES = ((CLASS . CLASS-EVOCAB) ...),
1020 SUPERS = ((SUPER . USE-EVOCAB-P) ...), and
1021 VOCABS = ((EVOCAB ...) . (IVOCAB ...))
1022
1023 FILE is the current buffer's file-name without directory part and
1024 LANGUAGE is the value of `antlr-language' in the current buffer. Each
1025 EVOCAB is an export vocabulary and each IVOCAB is an import vocabulary.
1026
1027 Each CLASS is a grammar class with its export vocabulary CLASS-EVOCAB.
1028 Each SUPER is a super-grammar class where USE-EVOCAB-P indicates whether
1029 its export vocabulary is used as an import vocabulary."
1030 (unless buffer-file-name
1031 (error "Grammar buffer does not visit a file"))
1032 (let (classes exportVocabs importVocabs superclasses default-vocab)
1033 (antlr-with-syntax-table antlr-action-syntax-table
1034 (goto-char (point-min))
1035 (while (antlr-re-search-forward "class[ \t]+\\([A-Za-z\300-\326\330-\337]\\sw*\\)[ \t]+extends[ \t]+\\([A-Za-z\300-\326\330-\337]\\sw*\\)[ \t]*;" nil)
1036 ;; parse class definition --------------------------------------------
1037 (let* ((class (match-string 1))
1038 (sclass (match-string 2))
1039 ;; export vocab defaults to class name (first grammar in file)
1040 ;; or to the export vocab of the first grammar in file:
1041 (evocab (or default-vocab class))
1042 (ivocab nil))
1043 (goto-char (match-end 0))
1044 (c-forward-syntactic-ws)
1045 (while (looking-at "options\\>\\|\\(tokens\\)\\>")
1046 (if (match-beginning 1)
1047 (antlr-skip-sexps 2)
1048 (goto-char (match-end 0))
1049 (c-forward-syntactic-ws)
1050 ;; parse grammar option section --------------------------------
1051 (when (eq (char-after (point)) ?\{)
1052 (let* ((beg (1+ (point)))
1053 (end (1- (antlr-skip-sexps 1)))
1054 (cont (point)))
1055 (goto-char beg)
1056 (if (re-search-forward "\\<exportVocab[ \t]*=[ \t]*\\([A-Za-z\300-\326\330-\337]\\sw*\\)" end t)
1057 (setq evocab (match-string 1)))
1058 (goto-char beg)
1059 (if (re-search-forward "\\<importVocab[ \t]*=[ \t]*\\([A-Za-z\300-\326\330-\337]\\sw*\\)" end t)
1060 (setq ivocab (match-string 1)))
1061 (goto-char cont)))))
1062 (unless (member sclass '("Parser" "Lexer" "TreeParser"))
1063 (let ((super (assoc sclass superclasses)))
1064 (if super
1065 (or ivocab (setcdr super t))
1066 (push (cons sclass (null ivocab)) superclasses))))
1067 ;; remember class with export vocabulary:
1068 (push (cons class evocab) classes)
1069 ;; default export vocab is export vocab of first grammar in file:
1070 (or default-vocab (setq default-vocab evocab))
1071 (or (member evocab exportVocabs) (push evocab exportVocabs))
1072 (or (null ivocab)
1073 (member ivocab importVocabs) (push ivocab importVocabs)))))
1074 (if classes
1075 (list* (file-name-nondirectory buffer-file-name)
1076 (cons (nreverse classes) (nreverse superclasses))
1077 (cons (nreverse exportVocabs) (nreverse importVocabs))
1078 antlr-language))))
1079
1080 (defun antlr-directory-dependencies (dirname)
1081 "Return dependencies for all grammar files in directory DIRNAME.
1082 The result looks like ((CLASS-SPEC ...) . (FILE-DEP ...))
1083 where CLASS-SPEC = (CLASS (FILE . EVOCAB) ...).
1084
1085 FILE-DEP are the dependencies for each grammar file in DIRNAME, see
1086 `antlr-file-dependencies'. For each grammar class CLASS, FILE is a
1087 grammar file in which CLASS is defined and EVOCAB is the name of the
1088 export vocabulary specified in that file."
1089 (let ((grammar (directory-files dirname t "\\.g\\'")))
1090 (when grammar
1091 (let ((temp-buffer (get-buffer-create
1092 (generate-new-buffer-name " *temp*")))
1093 (antlr-imenu-name nil) ; dynamic-let: no imenu
1094 (expanded-regexp (concat (format (regexp-quote
1095 (cadr antlr-special-file-formats))
1096 ".+")
1097 "\\'"))
1098 classes dependencies)
1099 (unwind-protect
1100 (save-excursion
1101 (set-buffer temp-buffer)
1102 (widen) ; just in case...
1103 (dolist (file grammar)
1104 (when (and (file-regular-p file)
1105 (null (string-match expanded-regexp file)))
1106 (insert-file-contents file t nil nil t)
1107 (normal-mode t) ; necessary for major-mode, syntax
1108 ; table and `antlr-language'
1109 (when (eq major-mode 'antlr-mode)
1110 (let* ((file-deps (antlr-file-dependencies))
1111 (file (car file-deps)))
1112 (when file-deps
1113 (dolist (class-def (caadr file-deps))
1114 (let ((file-evocab (cons file (cdr class-def)))
1115 (class-spec (assoc (car class-def) classes)))
1116 (if class-spec
1117 (nconc (cdr class-spec) (list file-evocab))
1118 (push (list (car class-def) file-evocab)
1119 classes))))
1120 (push file-deps dependencies)))))))
1121 (kill-buffer temp-buffer))
1122 (cons (nreverse classes) (nreverse dependencies))))))
1123
1124
1125 ;;;===========================================================================
1126 ;;; Compilation: run ANTLR tool
1127 ;;;===========================================================================
1128
1129 (defun antlr-superclasses-glibs (supers classes)
1130 "Compute the grammar lib option for the super grammars SUPERS.
1131 Look in CLASSES for the right grammar lib files for SUPERS. SUPERS is
1132 part SUPER in the result of `antlr-file-dependencies'. CLASSES is the
1133 part (CLASS-SPEC ...) in the result of `antlr-directory-dependencies'.
1134
1135 The result looks like (OPTION WITH-UNKNOWN GLIB ...). OPTION is the
1136 complete \"-glib\" option. WITH-UNKNOWN has value t iff there is none
1137 or more than one grammar file for at least one super grammar.
1138
1139 Each GLIB looks like (GRAMMAR-FILE . EVOCAB). GRAMMAR-FILE is a file in
1140 which a super-grammar is defined. EVOCAB is the value of the export
1141 vocabulary of the super-grammar or nil if it is not needed."
1142 ;; If the superclass is defined in the same file, that file will be included
1143 ;; with -glib again. This will lead to a redefinition. But defining a
1144 ;; analyzer of the same class twice in a file will lead to an error anyway...
1145 (let (glibs unknown)
1146 (while supers
1147 (let* ((super (pop supers))
1148 (sup-files (cdr (assoc (car super) classes)))
1149 (file (and sup-files (null (cdr sup-files)) (car sup-files))))
1150 (or file (setq unknown t)) ; not exactly one file
1151 (push (cons (or (car file)
1152 (format (car antlr-unknown-file-formats)
1153 (car super)))
1154 (and (cdr super)
1155 (or (cdr file)
1156 (format (cadr antlr-unknown-file-formats)
1157 (car super)))))
1158 glibs)))
1159 (cons (if glibs (concat " -glib " (mapconcat 'car glibs ";")) "")
1160 (cons unknown glibs))))
1161
1162 (defun antlr-run-tool (command file &optional saved)
1163 "Run Antlr took COMMAND on grammar FILE.
1164 When called interactively, COMMAND is read from the minibuffer and
1165 defaults to `antlr-tool-command' with a computed \"-glib\" option if
1166 necessary.
1167
1168 Save all buffers first unless optional value SAVED is non-nil. When
1169 called interactively, the buffers are always saved, see also variable
1170 `antlr-ask-about-save'."
1171 (interactive
1172 ;; code in `interactive' is not compiled: do not use cl macros (`cdadr')
1173 (let* ((supers (cdr (cadr (save-excursion
1174 (save-restriction
1175 (widen)
1176 (antlr-file-dependencies))))))
1177 (glibs ""))
1178 (when supers
1179 (save-some-buffers (not antlr-ask-about-save) nil)
1180 (setq glibs (car (antlr-superclasses-glibs
1181 supers
1182 (car (antlr-directory-dependencies
1183 (antlr-default-directory)))))))
1184 (list (antlr-read-shell-command "Run Antlr on current file with: "
1185 (concat antlr-tool-command glibs " "))
1186 buffer-file-name
1187 supers)))
1188 (or saved (save-some-buffers (not antlr-ask-about-save)))
1189 (let ((default-directory (file-name-directory file)))
1190 (require 'compile) ; only `compile' autoload
1191 (compile-internal (concat command " " (file-name-nondirectory file))
1192 "No more errors" "Antlr-Run")))
1193
1194
1195 ;;;===========================================================================
1196 ;;; Makefile creation
1197 ;;;===========================================================================
1198
1199 (defun antlr-makefile-insert-variable (number pre post)
1200 "Insert Makefile variable numbered NUMBER according to specification.
1201 Also insert strings PRE and POST before and after the variable."
1202 (let ((spec (cadr antlr-makefile-specification)))
1203 (when spec
1204 (insert pre
1205 (if number (format (cadr spec) number) (car spec))
1206 post))))
1207
1208 (defun antlr-insert-makefile-rules (&optional in-makefile)
1209 "Insert Makefile rules in the current buffer at point.
1210 IN-MAKEFILE is non-nil, if the current buffer is the Makefile. See
1211 command `antlr-show-makefile-rules' for detail."
1212 (let* ((dirname (antlr-default-directory))
1213 (deps0 (antlr-directory-dependencies dirname))
1214 (classes (car deps0)) ; CLASS -> (FILE . EVOCAB) ...
1215 (deps (cdr deps0)) ; FILE -> (c . s) (ev . iv) . LANGUAGE
1216 (with-error nil)
1217 (gen-sep (or (caddr (cadr antlr-makefile-specification)) " "))
1218 (n (and (cdr deps) (cadr antlr-makefile-specification) 0)))
1219 (or in-makefile (set-buffer standard-output))
1220 (dolist (dep deps)
1221 (let ((supers (cdadr dep))
1222 (lang (cdr (assoc (cdddr dep) antlr-file-formats-alist))))
1223 (if n (incf n))
1224 (antlr-makefile-insert-variable n "" " =")
1225 (if supers
1226 (insert " "
1227 (format (cadr antlr-special-file-formats)
1228 (file-name-sans-extension (car dep)))))
1229 (dolist (class-def (caadr dep))
1230 (let ((sep gen-sep))
1231 (dolist (class-file (cadr lang))
1232 (insert sep (format class-file (car class-def)))
1233 (setq sep " "))))
1234 (dolist (evocab (caaddr dep))
1235 (let ((sep gen-sep))
1236 (dolist (vocab-file (cons (car antlr-special-file-formats)
1237 (car lang)))
1238 (insert sep (format vocab-file evocab))
1239 (setq sep " "))))
1240 (antlr-makefile-insert-variable n "\n$(" ")")
1241 (insert ": " (car dep))
1242 (dolist (ivocab (cdaddr dep))
1243 (insert " " (format (car antlr-special-file-formats) ivocab)))
1244 (let ((glibs (antlr-superclasses-glibs supers classes)))
1245 (if (cadr glibs) (setq with-error t))
1246 (dolist (super (cddr glibs))
1247 (insert " " (car super))
1248 (if (cdr super)
1249 (insert " " (format (car antlr-special-file-formats)
1250 (cdr super)))))
1251 (insert "\n\t"
1252 (caddr antlr-makefile-specification)
1253 (car glibs)
1254 " $<\n"
1255 (car antlr-makefile-specification)))))
1256 (if n
1257 (let ((i 0))
1258 (antlr-makefile-insert-variable nil "" " =")
1259 (while (<= (incf i) n)
1260 (antlr-makefile-insert-variable i " $(" ")"))
1261 (insert "\n" (car antlr-makefile-specification))))
1262 (if (string-equal (car antlr-makefile-specification) "\n")
1263 (backward-delete-char 1))
1264 (when with-error
1265 (goto-char (point-min))
1266 (insert antlr-help-unknown-file-text))
1267 (unless in-makefile
1268 (copy-region-as-kill (point-min) (point-max))
1269 (goto-char (point-min))
1270 (insert (format antlr-help-rules-intro dirname)))))
1271
1272 ;;;###autoload
1273 (defun antlr-show-makefile-rules ()
1274 "Show Makefile rules for all grammar files in the current directory.
1275 If the `major-mode' of the current buffer has the value `makefile-mode',
1276 the rules are directory inserted at point. Otherwise, a *Help* buffer
1277 is shown with the rules which are also put into the `kill-ring' for
1278 \\[yank].
1279
1280 This command considers import/export vocabularies and grammar
1281 inheritance and provides a value for the \"-glib\" option if necessary.
1282 Customize variable `antlr-makefile-specification' for the appearance of
1283 the rules.
1284
1285 If the file for a super-grammar cannot be determined, special file names
1286 are used according to variable `antlr-unknown-file-formats' and a
1287 commentary with value `antlr-help-unknown-file-text' is added. The
1288 *Help* buffer always starts with the text in `antlr-help-rules-intro'."
1289 (interactive)
1290 (if (null (eq major-mode 'makefile-mode))
1291 (antlr-with-displaying-help-buffer 'antlr-insert-makefile-rules)
1292 (push-mark)
1293 (antlr-insert-makefile-rules t)))
1294
1295
1296 ;;;===========================================================================
1297 ;;; Indentation
1298 ;;;===========================================================================
1299
1300 (defun antlr-indent-line ()
1301 "Indent the current line as ANTLR grammar code.
1302 The indentation of non-comment lines are calculated by `c-basic-offset',
1303 multiplied by:
1304 - the level of the paren/brace/bracket depth,
1305 - plus 0/2/1, depending on the position inside the rule: header, body,
1306 exception part,
1307 - minus 1 if `antlr-indent-item-regexp' matches the beginning of the
1308 line starting from the first non-blank.
1309
1310 Lines inside block comments are not changed or indented by
1311 `c-indent-line', see `antlr-indent-comment'."
1312 (let ((orig (point)) bol boi indent syntax)
1313 (beginning-of-line)
1314 (setq bol (point))
1315 (skip-chars-forward " \t")
1316 (setq boi (point))
1317 ;; check syntax at beginning of indentation ------------------------------
1318 (antlr-with-syntax-table antlr-action-syntax-table
1319 (antlr-invalidate-context-cache)
1320 (cond ((symbolp (setq syntax (antlr-syntactic-context)))
1321 (setq indent nil)) ; block-comments, strings, (comments)
1322 ((eq (char-after) ?#) ; cpp directive
1323 (setq syntax 'cpp)
1324 (setq indent 0)) ; indentation at 0
1325 ((progn
1326 (antlr-next-rule -1 t)
1327 (if (antlr-search-forward ":") (< boi (1- (point))) t))
1328 (setq indent 0)) ; in rule header
1329 ((if (antlr-search-forward ";") (< boi (point)) t)
1330 (setq indent 2)) ; in rule body
1331 (t
1332 (forward-char)
1333 (antlr-skip-exception-part nil)
1334 (setq indent (if (> (point) boi) 1 0))))) ; in exception part?
1335 ;; compute the corresponding indentation and indent ----------------------
1336 (if (null indent)
1337 (progn
1338 (goto-char orig)
1339 (and (eq antlr-indent-comment t)
1340 (not (eq syntax 'string))
1341 (c-indent-line)))
1342 ;; do it ourselves
1343 (goto-char boi)
1344 (unless (symbolp syntax) ; direct indentation
1345 (antlr-invalidate-context-cache)
1346 (incf indent (antlr-syntactic-context))
1347 (and (> indent 0) (looking-at antlr-indent-item-regexp) (decf indent))
1348 (setq indent (* indent c-basic-offset)))
1349 ;; the usual major-mode indent stuff -----------------------------------
1350 (setq orig (- (point-max) orig))
1351 (unless (= (current-column) indent)
1352 (delete-region bol boi)
1353 (beginning-of-line)
1354 (indent-to indent))
1355 ;; If initial point was within line's indentation,
1356 ;; position after the indentation. Else stay at same point in text.
1357 (if (> (- (point-max) orig) (point))
1358 (goto-char (- (point-max) orig))))))
1359
1360 (defun antlr-indent-command (&optional arg)
1361 "Indent the current line or insert tabs/spaces.
1362 With optional prefix argument ARG or if the previous command was this
1363 command, insert ARG tabs or spaces according to `indent-tabs-mode'.
1364 Otherwise, indent the current line with `antlr-indent-line'."
1365 (interactive "P")
1366 (if (or arg (eq last-command 'antlr-indent-command))
1367 (insert-tab arg)
1368 (let ((antlr-indent-comment (and antlr-indent-comment t))) ; dynamic
1369 (antlr-indent-line))))
1370
1371
1372 ;;;===========================================================================
1373 ;;; Mode entry
1374 ;;;===========================================================================
1375
1376 (defun antlr-c-common-init ()
1377 "Like `c-common-init' except menu, auto-hungry and c-style stuff."
1378 ;; X/Emacs 20 only
1379 (make-local-variable 'paragraph-start)
1380 (make-local-variable 'paragraph-separate)
1381 (make-local-variable 'paragraph-ignore-fill-prefix)
1382 (make-local-variable 'require-final-newline)
1383 (make-local-variable 'parse-sexp-ignore-comments)
1384 (make-local-variable 'indent-line-function)
1385 (make-local-variable 'indent-region-function)
1386 (make-local-variable 'comment-start)
1387 (make-local-variable 'comment-end)
1388 (make-local-variable 'comment-column)
1389 (make-local-variable 'comment-start-skip)
1390 (make-local-variable 'comment-multi-line)
1391 (make-local-variable 'outline-regexp)
1392 (make-local-variable 'outline-level)
1393 (make-local-variable 'adaptive-fill-regexp)
1394 (make-local-variable 'adaptive-fill-mode)
1395 (make-local-variable 'imenu-generic-expression) ;set in the mode functions
1396 (and (boundp 'comment-line-break-function)
1397 (make-local-variable 'comment-line-break-function))
1398 ;; Emacs 19.30 and beyond only, AFAIK
1399 (if (boundp 'fill-paragraph-function)
1400 (progn
1401 (make-local-variable 'fill-paragraph-function)
1402 (setq fill-paragraph-function 'c-fill-paragraph)))
1403 ;; now set their values
1404 (setq paragraph-start (concat page-delimiter "\\|$")
1405 paragraph-separate paragraph-start
1406 paragraph-ignore-fill-prefix t
1407 require-final-newline t
1408 parse-sexp-ignore-comments t
1409 indent-line-function 'c-indent-line
1410 indent-region-function 'c-indent-region
1411 outline-regexp "[^#\n\^M]"
1412 outline-level 'c-outline-level
1413 comment-column 32
1414 comment-start-skip "/\\*+ *\\|// *"
1415 comment-multi-line nil
1416 comment-line-break-function 'c-comment-line-break-function
1417 adaptive-fill-regexp nil
1418 adaptive-fill-mode nil)
1419 ;; we have to do something special for c-offsets-alist so that the
1420 ;; buffer local value has its own alist structure.
1421 (setq c-offsets-alist (copy-alist c-offsets-alist))
1422 ;; setup the comment indent variable in a Emacs version portable way
1423 ;; ignore any byte compiler warnings you might get here
1424 (make-local-variable 'comment-indent-function)
1425 (setq comment-indent-function 'c-comment-indent))
1426
1427 (defun antlr-language-for-option (option-value)
1428 "Find element in `antlr-language-alist' for OPTION-VALUE."
1429 ;; Like (find OPTION-VALUE antlr-language-alist :key 'cddr :test 'member)
1430 (let ((seq antlr-language-alist)
1431 r)
1432 (while seq
1433 (setq r (pop seq))
1434 (if (member option-value (cddr r))
1435 (setq seq nil) ; stop
1436 (setq r nil))) ; no result yet
1437 r))
1438
1439 ;;;###autoload
1440 (defun antlr-mode ()
1441 "Major mode for editing ANTLR grammar files.
1442 \\{antlr-mode-map}"
1443 (interactive)
1444 (c-initialize-cc-mode) ; for java syntax table
1445 (kill-all-local-variables)
1446 ;; ANTLR specific ----------------------------------------------------------
1447 (setq major-mode 'antlr-mode
1448 mode-name "Antlr")
1449 (setq local-abbrev-table antlr-mode-abbrev-table)
1450 (unless antlr-mode-syntax-table
1451 (setq antlr-mode-syntax-table (make-syntax-table))
1452 (c-populate-syntax-table antlr-mode-syntax-table))
1453 (set-syntax-table antlr-mode-syntax-table)
1454 (unless antlr-action-syntax-table
1455 (let ((slist (nth 3 antlr-font-lock-defaults)))
1456 (setq antlr-action-syntax-table
1457 (copy-syntax-table antlr-mode-syntax-table))
1458 (while slist
1459 (modify-syntax-entry (caar slist) (cdar slist)
1460 antlr-action-syntax-table)
1461 (setq slist (cdr slist)))))
1462 (use-local-map antlr-mode-map)
1463 (make-local-variable 'antlr-language)
1464 (unless antlr-language
1465 (save-excursion
1466 (goto-char (point-min))
1467 (setq antlr-language
1468 (car (or (and (re-search-forward (cdr antlr-language-limit-n-regexp)
1469 (car antlr-language-limit-n-regexp)
1470 t)
1471 (antlr-language-for-option (match-string 1)))
1472 (antlr-language-for-option nil))))))
1473 (if (stringp (cadr (assq antlr-language antlr-language-alist)))
1474 (setq mode-name
1475 (concat "Antlr."
1476 (cadr (assq antlr-language antlr-language-alist)))))
1477 ;; indentation, for the C engine -------------------------------------------
1478 (antlr-c-common-init)
1479 (setq indent-line-function 'antlr-indent-line
1480 indent-region-function nil) ; too lazy
1481 (setq comment-start "// "
1482 comment-end "")
1483 (c-set-style "java")
1484 (if (eq antlr-language 'c++-mode)
1485 (setq c-conditional-key c-C++-conditional-key
1486 c-comment-start-regexp c-C++-comment-start-regexp
1487 c-class-key c-C++-class-key
1488 c-extra-toplevel-key c-C++-extra-toplevel-key
1489 c-access-key c-C++-access-key
1490 c-recognize-knr-p nil)
1491 (setq c-conditional-key c-Java-conditional-key
1492 c-comment-start-regexp c-Java-comment-start-regexp
1493 c-class-key c-Java-class-key
1494 c-method-key nil
1495 c-baseclass-key nil
1496 c-recognize-knr-p nil
1497 c-access-key (and (boundp 'c-Java-access-key) c-Java-access-key))
1498 (and (boundp 'c-inexpr-class-key) (boundp 'c-Java-inexpr-class-key)
1499 (setq c-inexpr-class-key c-Java-inexpr-class-key)))
1500 ;; various -----------------------------------------------------------------
1501 (make-local-variable 'font-lock-defaults)
1502 (setq font-lock-defaults antlr-font-lock-defaults)
1503 (easy-menu-add antlr-mode-menu)
1504 (make-local-variable 'imenu-create-index-function)
1505 (setq imenu-create-index-function 'antlr-imenu-create-index-function)
1506 (make-local-variable 'imenu-generic-expression)
1507 (setq imenu-generic-expression t) ; fool stupid test
1508 (and antlr-imenu-name ; there should be a global variable...
1509 (fboundp 'imenu-add-to-menubar)
1510 (imenu-add-to-menubar
1511 (if (stringp antlr-imenu-name) antlr-imenu-name "Index")))
1512 (antlr-set-tabs)
1513 (run-hooks 'antlr-mode-hook))
1514
1515 ;; A smarter version of `group-buffers-menu-by-mode-then-alphabetically' (in
1516 ;; XEmacs) could use the following property. The header of the submenu would
1517 ;; be "Antlr" instead of "Antlr.C++" or (not and!) "Antlr.Java".
1518 (put 'antlr-mode 'mode-name "Antlr")
1519
1520 ;;;###autoload
1521 (defun antlr-set-tabs ()
1522 "Use ANTLR's convention for TABs according to `antlr-tab-offset-alist'.
1523 Used in `antlr-mode'. Also a useful function in `java-mode-hook'."
1524 (if buffer-file-name
1525 (let ((alist antlr-tab-offset-alist) elem)
1526 (while alist
1527 (setq elem (pop alist))
1528 (and (or (null (car elem)) (eq (car elem) major-mode))
1529 (or (null (cadr elem))
1530 (string-match (cadr elem) buffer-file-name))
1531 (setq tab-width (caddr elem)
1532 indent-tabs-mode (cadddr elem)
1533 alist nil))))))
1534
1535 ; LocalWords: antlr ANother ANTLR's Cpp Lexer TreeParser esp refs VALUEs ea ee
1536 ; LocalWords: Java's Nomencl ruledef tokendef ruleref tokenref setType ader ev
1537 ; LocalWords: ivate syntab lexer treeparser lic rotected rivate bor boi AFAIK
1538 ; LocalWords: slist knr inexpr unhide jit GENS SEP GEN sTokenTypes hpp cpp DEP
1539 ; LocalWords: VOCAB EVOCAB Antlr's TokenTypes exportVocab incl excl SUPERS gen
1540 ; LocalWords: VOCABS IVOCAB exportVocabs importVocabs superclasses vocab kens
1541 ; LocalWords: sclass evocab ivocab importVocab deps glibs supers sep dep lang
1542
1543 ;;; antlr-mode.el ends here