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