Tizen 2.1 base
[external/gmp.git] / mpn / lisp / gmpasm-mode.el
1 ;;; gmpasm-mode.el -- GNU MP asm and m4 editing mode.
2
3
4 ;; Copyright 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
5 ;;
6 ;; This file is part of the GNU MP Library.
7 ;;
8 ;; The GNU MP Library is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU Lesser General Public License as published by
10 ;; the Free Software Foundation; either version 3 of the License, or (at your
11 ;; option) any later version.
12 ;;
13 ;; The GNU MP Library is distributed in the hope that it will be useful, but
14 ;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 ;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16 ;; License for more details.
17 ;;
18 ;; You should have received a copy of the GNU Lesser General Public License
19 ;; along with the GNU MP Library.  If not, see http://www.gnu.org/licenses/.
20
21
22 ;;; Commentary:
23 ;;
24 ;; gmpasm-mode is a major mode for editing m4 processed assembler code and
25 ;; m4 macro files in GMP.  It's similar to m4-mode, but has a number of
26 ;; settings better suited to GMP.
27 ;;
28 ;;
29 ;; Install
30 ;; -------
31 ;;
32 ;; To make M-x gmpasm-mode available, put gmpasm-mode.el somewhere in your
33 ;; load-path and the following in your .emacs
34 ;;
35 ;;      (autoload 'gmpasm-mode "gmpasm-mode" nil t)
36 ;;
37 ;; To use gmpasm-mode automatically on all .asm and .m4 files, put the
38 ;; following in your .emacs
39 ;;
40 ;;      (add-to-list 'auto-mode-alist '("\\.asm\\'" . gmpasm-mode))
41 ;;      (add-to-list 'auto-mode-alist '("\\.m4\\'" . gmpasm-mode))
42 ;;
43 ;; To have gmpasm-mode only on gmp files, try instead something like the
44 ;; following, which uses it only in a directory starting with "gmp", or a
45 ;; sub-directory of such.
46 ;;
47 ;;      (add-to-list 'auto-mode-alist
48 ;;                   '("/gmp.*/.*\\.\\(asm\\|m4\\)\\'" . gmpasm-mode))
49 ;;
50 ;; Byte compiling will slightly speed up loading.  If you want a docstring
51 ;; in the autoload you can use M-x update-file-autoloads if you set it up
52 ;; right.
53 ;;
54 ;;
55 ;; Emacsen
56 ;; -------
57 ;;
58 ;; GNU Emacs 20.x, 21.x and XEmacs 20.x all work well.  GNU Emacs 19.x
59 ;; should work if replacements for the various 20.x-isms are available,
60 ;; though comment-region with "C" doesn't do the right thing.
61
62
63 ;;; Code:
64
65 (defgroup gmpasm nil
66   "GNU MP m4 and asm editing."
67   :prefix "gmpasm-"
68   :group 'languages)
69
70 (defcustom gmpasm-mode-hook nil
71   "*Hook called by `gmpasm-mode'."
72   :type 'hook
73   :group 'gmpasm)
74
75 (defcustom gmpasm-comment-start-regexp "\\([#;!@*|C]\\|//\\)"
76   "*Regexp matching possible comment styles.
77 See `gmpasm-mode' docstring for how this is used.
78
79 Commenting styles within GMP include
80   #   - alpha, i386, i960, vax, traditional unix
81   ;   - a29k, clipper, hppa, m88k, ppc
82   !   - sh, sparc, z8000
83   |   - m68k
84   @   - arm
85   *   - cray
86   C   - GMP m4, see mpn/asm-defs.m4
87   //  - ia64"
88   :type 'regexp
89   :group 'gmpasm)
90
91
92 (defun gmpasm-add-to-list-second (list-var element)
93   "(gmpasm-add-to-list-second LIST-VAR ELEMENT)
94
95 Add ELEMENT to LIST-VAR as the second element in the list, if it isn't
96 already in the list.  If LIST-VAR is nil, then ELEMENT is just added as the
97 sole element in the list.
98
99 This is like `add-to-list', but it puts the new value second in the list.
100
101 The first cons cell is copied rather than changed in-place, so references to
102 the list elsewhere won't be affected."
103
104   (if (member element (symbol-value list-var))
105       (symbol-value list-var)
106     (set list-var
107          (if (symbol-value list-var)
108              (cons (car (symbol-value list-var))
109                    (cons element
110                          (cdr (symbol-value list-var))))
111            (list element)))))
112
113
114 (defun gmpasm-remove-from-list (list-var element)
115   "(gmpasm-remove-from-list LIST-VAR ELEMENT)
116
117 Remove ELEMENT from LIST-VAR, using `copy-sequence' and `delete'.
118 This is vaguely like `add-to-list', but the element is removed from the list.
119 The list is copied rather than changed in-place, so references to it elsewhere
120 aren't affected."
121
122 ;; Only the portion of the list up to the removed element needs to be
123 ;; copied, but there's no need to bother arranging that, since this function
124 ;; is only used for a couple of initializations.
125
126   (set list-var (delete element (copy-sequence (symbol-value list-var)))))
127
128
129 (defvar gmpasm-mode-map
130   (let ((map (make-sparse-keymap)))
131
132     ;; assembler and dnl commenting
133     (define-key map "\C-c\C-c" 'comment-region)
134     (define-key map "\C-c\C-d" 'gmpasm-comment-region-dnl)
135
136     ;; kill an M-x compile, since it's not hard to put m4 into an infinite
137     ;; loop
138     (define-key map "\C-c\C-k" 'kill-compilation)
139
140     map)
141   "Keymap for `gmpasm-mode'.")
142
143
144 (defvar gmpasm-mode-syntax-table
145   (let ((table (make-syntax-table)))
146     ;; underscore left as a symbol char, like C mode
147
148     ;; m4 quotes
149     (modify-syntax-entry ?`  "('"  table)
150     (modify-syntax-entry ?'  ")`"  table)
151
152     table)
153   "Syntax table used in `gmpasm-mode'.
154
155 '#' and '\n' aren't set as comment syntax.  In m4 these are a comment
156 outside quotes, but not inside.  Omitting a syntax entry ensures that when
157 inside quotes emacs treats parentheses and apostrophes the same way that m4
158 does.  When outside quotes this is not quite right, but having it right when
159 nesting expressions is more important.
160
161 '*', '!' or '|' aren't setup as comment syntax either, on CPUs which use
162 these for comments.  The GMP macro setups don't set them in m4 changecom(),
163 since that prevents them being used in eval() expressions, and on that basis
164 they don't change the way quotes and parentheses are treated by m4 and
165 should be treated by emacs.")
166
167
168 (defvar gmpasm-font-lock-keywords
169   (eval-when-compile
170     (list
171      (cons
172       (concat
173        "\\b"
174        (regexp-opt
175         '("deflit" "defreg" "defframe" "defframe_pushl"
176           "define_not_for_expansion"
177           "m4_error" "m4_warning"
178           "ASM_START" "ASM_END"
179           "PROLOGUE" "PROLOGUE_GP" "MULFUNC_PROLOGUE" "EPILOGUE"
180           "DATASTART" "DATAEND"
181           "forloop"
182           "TEXT" "DATA" "ALIGN" "W32" "FLOAT64"
183           "builtin" "changecom" "changequote" "changeword" "debugfile"
184           "debugmode" "decr" "define" "defn" "divert" "divnum" "dumpdef"
185           "errprint" "esyscmd" "eval" "__file__" "format" "gnu" "ifdef"
186           "ifelse" "include" "incr" "index" "indir" "len" "__line__"
187           "m4exit" "m4wrap" "maketemp" "patsubst" "popdef" "pushdef"
188           "regexp" "shift" "sinclude" "substr" "syscmd" "sysval"
189           "traceoff" "traceon" "translit" "undefine" "undivert" "unix")
190         t)
191        "\\b") 'font-lock-keyword-face)))
192
193   "`font-lock-keywords' for `gmpasm-mode'.
194
195 The keywords are m4 builtins and some of the GMP macros used in asm files.
196 L doesn't look good fontified, so it's omitted.
197
198 The right assembler comment regexp is added dynamically buffer-local (with
199 dnl too).")
200
201
202 ;; Initialized if gmpasm-mode finds filladapt loaded.
203 (defvar gmpasm-filladapt-token-table nil
204   "Filladapt token table used in `gmpasm-mode'.")
205 (defvar gmpasm-filladapt-token-match-table nil
206   "Filladapt token match table used in `gmpasm-mode'.")
207 (defvar gmpasm-filladapt-token-conversion-table nil
208   "Filladapt token conversion table used in `gmpasm-mode'.")
209
210
211 ;;;###autoload
212 (defun gmpasm-mode ()
213   "A major mode for editing GNU MP asm and m4 files.
214
215 \\{gmpasm-mode-map}
216 `comment-start' and `comment-end' are set buffer-local to assembler
217 commenting appropriate for the CPU by looking for something matching
218 `gmpasm-comment-start-regexp' at the start of a line, or \"#\" is used if
219 there's no match (if \"#\" isn't what you want, type in a desired comment
220 and do \\[gmpasm-mode] to reinitialize).
221
222 `adaptive-fill-regexp' is set buffer-local to the standard regexp with
223 `comment-start' and dnl added.  If filladapt.el has been loaded it similarly
224 gets `comment-start' and dnl added as buffer-local fill prefixes.
225
226 Font locking has the m4 builtins, some of the GMP macros, m4 dnl commenting,
227 and assembler commenting (based on the `comment-start' determined).
228
229 Note that `gmpasm-comment-start-regexp' is only matched as a whole word, so
230 the `C' in it is only matched as a whole word, not on something that happens
231 to start with `C'.  Also it's only the particular `comment-start' determined
232 that's added for filling etc, not the whole `gmpasm-comment-start-regexp'.
233
234 `gmpasm-mode-hook' is run after initializations are complete."
235
236   (interactive)
237   (kill-all-local-variables)
238   (setq major-mode 'gmpasm-mode
239         mode-name  "gmpasm")
240   (use-local-map gmpasm-mode-map)
241   (set-syntax-table gmpasm-mode-syntax-table)
242   (setq fill-column 76)
243
244   ;; Short instructions might fit with 32, but anything with labels or
245   ;; expressions soon needs the comments pushed out to column 40.
246   (setq comment-column 40)
247
248   ;; Don't want to find out the hard way which dumb assemblers don't like a
249   ;; missing final newline.
250   (set (make-local-variable 'require-final-newline) t)
251
252   ;; The first match of gmpasm-comment-start-regexp at the start of a line
253   ;; determines comment-start, or "#" if no match.
254   (set (make-local-variable 'comment-start)
255        (save-excursion
256          (goto-char (point-min))
257          (if (re-search-forward
258               (concat "^\\(" gmpasm-comment-start-regexp "\\)\\(\\s-\\|$\\)")
259               nil t)
260              (match-string 1)
261            "#")))
262   (set (make-local-variable 'comment-end) "")
263
264   ;; If comment-start ends in an alphanumeric then \b is used to match it
265   ;; only as a separate word.  The test is for an alphanumeric rather than
266   ;; \w since we might try # or ! as \w characters but without wanting \b on
267   ;; them.
268   (let ((comment-regexp
269          (concat (regexp-quote comment-start)
270                  (if (string-match "[a-zA-Z0-9]\\'" comment-start) "\\b"))))
271
272     ;; Whitespace is required before a comment-start so m4 $# doesn't match
273     ;; when comment-start is "#".
274     (set (make-local-variable 'comment-start-skip)
275          (concat "\\(^\\|\\s-\\)\\(\\<dnl\\>\\|" comment-regexp "\\)[ \t]*"))
276
277     ;; Comment fontification based on comment-start, and always with dnl.
278     ;; Same treatment of a space before "#" as in comment-start-skip, but
279     ;; don't fontify that space.
280     (add-to-list (make-local-variable 'gmpasm-font-lock-keywords)
281                  (list (concat "\\(^\\|\\s-\\)\\(\\(\\<dnl\\>\\|"
282                                comment-regexp
283                                "\\).*$\\)")
284                        2 'font-lock-comment-face))
285
286     (set (make-local-variable 'font-lock-defaults)
287          '(gmpasm-font-lock-keywords
288            t             ; no syntactic fontification (of strings etc)
289            nil           ; no case-fold
290            ((?_ . "w"))  ; _ part of a word while fontifying
291            ))
292
293     ;; Paragraphs are separated by blank lines, or lines with only dnl or
294     ;; comment-start.
295     (set (make-local-variable 'paragraph-separate)
296          (concat "[ \t\f]*\\(\\(" comment-regexp "\\|dnl\\)[ \t]*\\)*$"))
297     (set (make-local-variable 'paragraph-start)
298          (concat "\f\\|" paragraph-separate))
299
300     ;; Some sort of "def...(" m4 define, possibly with ` for quoting.
301     ;; Could do something with PROLOGUE here, but in GMP the filename is
302     ;; enough, it's not normally necessary to say the function name.
303     (set (make-local-variable 'add-log-current-defun-header-regexp)
304          "^def[a-z0-9_]+(`?\\([a-zA-Z0-9_]+\\)")
305
306     ;; Adaptive fill gets dnl and comment-start as comment style prefixes on
307     ;; top of the standard regexp (which has # and ; already actually).
308     (set (make-local-variable 'adaptive-fill-regexp)
309          (concat "[ \t]*\\(\\("
310                  comment-regexp
311                  "\\|dnl\\|[-|#;>*]+\\|(?[0-9]+[.)]\\)[ \t]*\\)*"))
312     (set (make-local-variable 'adaptive-fill-first-line-regexp)
313          "\\`\\([ \t]*dnl\\)?[ \t]*\\'")
314
315     (when (fboundp 'filladapt-mode)
316       (unless gmpasm-filladapt-token-table
317         (setq gmpasm-filladapt-token-table
318               filladapt-token-table)
319         (setq gmpasm-filladapt-token-match-table
320               filladapt-token-match-table)
321         (setq gmpasm-filladapt-token-conversion-table
322               filladapt-token-conversion-table)
323
324         ;; Numbered bullet points like "2.1" get matched at the start of a
325         ;; line when it's really something like "2.1 cycles/limb", so remove
326         ;; this from the list.  The regexp for "1.", "2." etc is left
327         ;; though.
328         (gmpasm-remove-from-list 'gmpasm-filladapt-token-table
329                                  '("[0-9]+\\(\\.[0-9]+\\)+[ \t]"
330                                    bullet))
331
332         ;; "%" as a comment prefix interferes with register names on some
333         ;; CPUs, like %eax on x86, so remove this.
334         (gmpasm-remove-from-list 'gmpasm-filladapt-token-table
335                                  '("%+" postscript-comment))
336
337         (add-to-list 'gmpasm-filladapt-token-match-table
338                      '(gmpasm-comment gmpasm-comment))
339         (add-to-list 'gmpasm-filladapt-token-conversion-table
340                      '(gmpasm-comment . exact)))
341
342       (set (make-local-variable 'filladapt-token-table)
343            gmpasm-filladapt-token-table)
344       (set (make-local-variable 'filladapt-token-match-table)
345            gmpasm-filladapt-token-match-table)
346       (set (make-local-variable 'filladapt-token-conversion-table)
347            gmpasm-filladapt-token-conversion-table)
348
349       ;; Add dnl and comment-start as fill prefixes.
350       ;; Comments in filladapt.el say filladapt-token-table must begin
351       ;; with ("^" beginning-of-line), so put our addition second.
352       (gmpasm-add-to-list-second 'filladapt-token-table
353                                  (list (concat "dnl[ \t]\\|" comment-regexp)
354                                        'gmpasm-comment))))
355
356   (run-hooks 'gmpasm-mode-hook))
357
358
359 (defun gmpasm-comment-region-dnl (beg end &optional arg)
360   "(gmpasm-comment-region-dnl BEG END &optional ARG)
361
362 Comment or uncomment each line in the region using `dnl'.
363 With \\[universal-argument] prefix arg, uncomment each line in region.
364 This is `comment-region', but using \"dnl\"."
365
366   (interactive "r\nP")
367   (let ((comment-start "dnl")
368         (comment-end ""))
369     (comment-region beg end arg)))
370
371
372 (provide 'gmpasm-mode)
373
374 ;;; gmpasm-mode.el ends here