Home | History | Annotate | Line # | Download | only in lisp
      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.
     88 See `gmpasm-mode' docstring for how this is used.
     89 
     90 Commenting 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 
    106 Add ELEMENT to LIST-VAR as the second element in the list, if it isn't
    107 already in the list.  If LIST-VAR is nil, then ELEMENT is just added as the
    108 sole element in the list.
    109 
    110 This is like `add-to-list', but it puts the new value second in the list.
    111 
    112 The first cons cell is copied rather than changed in-place, so references to
    113 the 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 
    128 Remove ELEMENT from LIST-VAR, using `copy-sequence' and `delete'.
    129 This is vaguely like `add-to-list', but the element is removed from the list.
    130 The list is copied rather than changed in-place, so references to it elsewhere
    131 aren'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
    167 outside quotes, but not inside.  Omitting a syntax entry ensures that when
    168 inside quotes emacs treats parentheses and apostrophes the same way that m4
    169 does.  When outside quotes this is not quite right, but having it right when
    170 nesting expressions is more important.
    171 
    172 '*', '!' or '|' aren't setup as comment syntax either, on CPUs which use
    173 these for comments.  The GMP macro setups don't set them in m4 changecom(),
    174 since that prevents them being used in eval() expressions, and on that basis
    175 they don't change the way quotes and parentheses are treated by m4 and
    176 should 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 
    206 The keywords are m4 builtins and some of the GMP macros used in asm files.
    207 L doesn't look good fontified, so it's omitted.
    208 
    209 The right assembler comment regexp is added dynamically buffer-local (with
    210 dnl 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
    228 commenting appropriate for the CPU by looking for something matching
    229 `gmpasm-comment-start-regexp' at the start of a line, or \"#\" is used if
    230 there's no match (if \"#\" isn't what you want, type in a desired comment
    231 and 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
    235 gets `comment-start' and dnl added as buffer-local fill prefixes.
    236 
    237 Font locking has the m4 builtins, some of the GMP macros, m4 dnl commenting,
    238 and assembler commenting (based on the `comment-start' determined).
    239 
    240 Note that `gmpasm-comment-start-regexp' is only matched as a whole word, so
    241 the `C' in it is only matched as a whole word, not on something that happens
    242 to start with `C'.  Also it's only the particular `comment-start' determined
    243 that'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 
    373 Comment or uncomment each line in the region using `dnl'.
    374 With \\[universal-argument] prefix arg, uncomment each line in region.
    375 This 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