comparison lisp/progmodes/antlr-mode.el @ 26542:0d41332e3819

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