Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
marvel
GitHub Repository: marvel/qnf
Path: blob/master/elisp/gas-mode.el
987 views
1
;; gas-mode.el --- mode for editing assembler code
2
3
;; Copyright (C) 2007 Heike C. Zimmerer
4
;; Time-stamp: <2007-12-27 18:14:08 hcz>
5
6
;; Author: Heike C. Zimmerer <[email protected]>
7
;; Created: 20 Feb 2007
8
;; Version: 1.10 2009-2-25 hcz
9
;; Keywords: languages
10
11
12
13
;; This file is written for GNU Emacs, and uses the same license
14
;; terms; however, it is an add-on and not part of it.
15
16
;; GNU Emacs is free software; you can redistribute it and/or modify
17
;; it under the terms of the GNU General Public License as published by
18
;; the Free Software Foundation; either version 2, or (at your option)
19
;; any later version.
20
21
;; GNU Emacs is distributed in the hope that it will be useful,
22
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
23
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24
;; GNU General Public License for more details.
25
26
;; You should have received a copy of the GNU General Public License
27
;; along with GNU Emacs; see the file COPYING. If not, write to the
28
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
29
;; Boston, MA 02110-1301, USA.
30
31
;;; Commentary:
32
33
;; To use this mode, put gas-mode.el somewhere on your load-path.
34
;; Then add this to your .emacs:
35
;;
36
;; (require 'gas-mode)
37
;; (add-to-list 'auto-mode-alist '("\\.S\\'" . gas-mode))
38
39
;; gas-mode recognizes gas syntax (including embedded C preprocessor
40
;; directives). It does a limited amount of parsing, so it can do
41
;; some fancy things with syntactic elements (like labels). It,
42
;; however does not know about the peculiarities of the special
43
;; processor you're writing code for (there are just too many of
44
;; them), so, for example, it can't tell register names from labels.
45
;; Also, its scope is limited to the file you are editing.
46
;;
47
;; Symbol highlighting: For a symbol to be recognized as such, it must
48
;; be in a field where (as per gas syntax) symbols can be placed
49
;; (i.e. label field, argument field, some directives). If point
50
;; rests on such a symbol and there are more of it in the current
51
;; buffer, it is highlighted and you can move forward and backward
52
;; between all places where that symbol is referenced or defined with
53
;; forward-sexp and backward-sexp.
54
;;
55
;; This is different from a simple string search in that only those
56
;; places are considered where the symbol is actually used. For local
57
;; labels, gas-mode resolves which references are associated with
58
;; which location and only highlights those that fit. The
59
;; highlighting is different for different types of symbols; see the
60
;; customization buffer for the gas-symbol-* faces for short
61
;; explanations of their meanings.
62
;;
63
;; Special forms of local labels (like `55$') are not (yet?)
64
;; supported.
65
66
;; A special feature may need some explanation: C passthroughs.
67
;; Assembler code is often used to write functions that are later
68
;; called by C programs. For this to work, you usually have to
69
;; maintain two files, one containing the assembler source, the other
70
;; holding the C interface declaration. C passthroughs allow you to
71
;; move the C declaration part into the assembler file next to the
72
;; function it belongs to.
73
;;
74
;; From the assembler's point of view, C passthroughs are just C syntax
75
;; comments with some small syntactic sugar added, like:
76
;;
77
;; /*C
78
;; int a_declaration(void);
79
;; extern volatile int another_declaration;
80
;;
81
;; /# and this will be passed as comment #/
82
;; */
83
;;
84
;; Note the `/*C' at the beginning and the `/# ... #/' for the nested
85
;; comment.
86
;;
87
;; It is then up to the Make process to generate a .h file, which
88
;; carries the declarations and the comment, changing the "/#" and "#/"
89
;; into "/*" and "*/".
90
;;
91
;; This may be done by including a line similar to the following into
92
;; your Makefile (assuming $(ASFILES) is a list of your assembly
93
;; language files) (and don't forget to use a TAB for the white space
94
;; which introduces the action lines (<TAB> echo ..., <TAB>sed -n ...):
95
;;
96
;; asm-C-defs.h: $(ASFILES)
97
;; echo '/* Definitions of assembly language functions */' > $@
98
;; echo '/* (automatically created by make) */' >> $@
99
;; sed -n '/[/][*]C/,/[*][/]/{s|/[*]C||;s|[*]/||;s|/#|/*|;s|#/|*/|;p}' \
100
;; $^ >> $@
101
;;
102
;; (Note the above code requires the "/*C" and "*/" to be on a line of
103
;; their own.) gas-mode recognizes this kind of comment by proper
104
;; syntax highlighting. Symbol highlighting is also supported. For a
105
;; symbol to be highlighted within C passthrough code, it must be
106
;; defined to be global (because only then it is visible to an
107
;; external C program) in the same buffer.
108
109
;;
110
;; This mode runs `gas-mode-hook' when initialization is complete.
111
;;
112
113
;; Bugs:
114
;;
115
;; Most probably, yes. You'll tell me (<[email protected]>).
116
;;
117
;; This code is *not tested at all* for syntaxes where
118
;; `gas-commant-char' differs from `?;'.
119
120
;;; Change Log:
121
;;
122
;; 2007-05-26 1st public release (hcz).
123
;;
124
;; 2007-05-30 docstrings, commentary,
125
;; open string recognition with gas-next-token/limit-re.
126
;;
127
;; 2007-12-05 Bug with Intel syntax in arguemnt field fixed
128
;;
129
;; 2007-12-25 Indents fixed for C comments without closing '*/'
130
;;
131
;;; Code:
132
133
(defgroup gas nil
134
"Mode for editing gas syntax assembler code."
135
:link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
136
:group 'languages)
137
138
(defcustom gas-comment-char ?\;
139
"The comment start character assumed by gas mode."
140
141
:type 'character
142
; :set 'gas-set-gas-comment-char
143
:group 'gas)
144
145
(defcustom gas-opcode-column 14
146
"The opcode column."
147
:type 'integer
148
:group 'gas)
149
150
(defcustom gas-argument-column 20
151
"The column for the arguments (if any)."
152
:type 'integer
153
:group 'gas)
154
155
(defcustom gas-comment-column 36
156
"The column where end of line asm comments go to."
157
:type 'integer
158
:group 'gas)
159
160
(defcustom gas-comment-char-starts-comment nil
161
"Always jump to comment column when a variable `gas-comment-char' is typed.
162
163
It t, starts/expands a comment if appropriate. When
164
unset (nil), use `gas-comment-dwim' (usually bound to M-;) to get there."
165
:type 'boolean
166
:group 'gas)
167
168
(defcustom gas-indent-current-field-only nil
169
"If nil, 'indent' indents all fileds on the current line.
170
Else only the current field is affected."
171
:type 'boolean
172
:group 'gas)
173
174
;; (defcustom gas-preserve-trailing-whitespace nil
175
;; "If nil, (re-)indenting removes trailing white space."
176
;; :type 'boolean
177
;; :group 'gas)
178
(setq gas-preserve-trailing-whitespace nil) ; (currently?) non-functional.
179
180
(defcustom gas-enable-symbol-highlight t
181
"Enable symbol recognition and highlighing.
182
183
When t and if point is on a symbol, some limited parsing data is
184
collected and all occurences of this symbol in the buffer get
185
highlighted according to the results (defined, global, etc.). As
186
long as a symbol is highlighted, `forward-sexp' and `backward-sexp'
187
move to the next/previous occurence of the same symbol in the
188
same buffer."
189
:type 'boolean
190
:group 'gas)
191
192
(defcustom gas-use-C-passthrough t
193
"When true, C passthrough comments are recognized.
194
195
This kind of comment is introduced by the starting sequence
196
\"/*C\" and is meant to be processed later by an external
197
program \(see the introducing comment in gas-mode.el for an
198
example) into C source code. Within these passthrough-comments,
199
the combination /# ... #/ is available for nested comments which
200
will be later changed into real C comments (/* ... */) by the
201
same external program."
202
:type 'boolean
203
:group 'gas)
204
205
(defcustom gas-defun-regexp "\n\\([;#].*\\|.*[*]/[ \t]*\\|\\|[ \t]*\\|[ \t]+\\..*\\)\n\\([^ \t\n;]+:\\)"
206
"Regexp used to recognize the beginning of a defun.
207
208
The default value describes a line which is either empty, a
209
full-line left-justified comment or a directive, followed by a
210
line starting with a label. Note that the character \";\" in the
211
regexp will be replaced by the actual comment character described
212
by variable `gas-comment-char'."
213
:type 'regexp
214
; :set 'gas-set-comment-regexp
215
:group 'gas)
216
217
218
219
(defcustom gas-defun-regexp-subexp 2
220
"The subexp in `gas-defun-regexp to jump to."
221
:type 'integer
222
:group 'gas)
223
224
(defcustom gas-C-indent 3
225
"Indent to use with C style comments."
226
:group 'gas
227
:type 'integer)
228
229
(defcustom gas-C-comment-end-column 0
230
"Where to indent a C comment end (\"*/\") if it starts a line."
231
:group 'gas
232
:type 'integer)
233
234
(defcustom gas-symbol-highlight-delay 0.5
235
"After this many seconds symbols get highlighted.
236
237
Number of seconds of idle time (a float) to wait before a symbol
238
gets highlighted."
239
:group 'gas
240
:type 'float)
241
242
(defgroup gas-faces nil
243
"Faces used by gas-mode."
244
:group 'gas)
245
246
(defface gas-builtin
247
'((((class color) (background light)) (:foreground "maroon"))
248
(t (:foreground "yellow")))
249
"Face to use for Gas buitins."
250
:group 'gas-faces)
251
252
(defface gas-symbol-ok
253
'((((class color) (background light)) (:background "#e0ffe0"))
254
(((class color) (background dark)) (:background "#001f00"))
255
(t (:foreground "yellow" :background "blue")))
256
"Face to use for symbols where exactly 1 definition was found."
257
:group 'gas-faces)
258
259
(defface gas-symbol-error
260
'((((class color) (background light))
261
(:background "#ffffe8" :foreground "red" :weight bold))
262
(((class color) (background dark))
263
(:background "#181800" :foreground "red" :weight bold))
264
(t (:foreground "yellow" :background "red")))
265
"Face to use when highlighting symbols with more than 1 definition."
266
:group 'gas-faces)
267
268
(defface gas-symbol-global
269
'((((class color) (background light)) (:background "#d0f8ff"))
270
(((class color) (background dark)) (:background "#00383f"))
271
(t (:foreground "yellow" :background "blue")))
272
"Face to use when highlighting global symbols."
273
:group 'gas-faces)
274
275
(defface gas-symbol-undef
276
'((((class color) (background light))
277
(:background "#ffffe8" :foreground "maroon"))
278
(((class color) (background dark))
279
(:background "#181800"))
280
(t (:foreground "yellow" :background "red")))
281
"Face to use for symbols defined as global when no definition
282
was found."
283
:group 'gas-faces)
284
285
(defface gas-symbol-global-undef
286
'((((class color) (background light))
287
(:background "#d0f8ff" :foreground "red"))
288
(((class color) (background dark))
289
(:background "#002840" :foreground "red"))
290
(t (:foreground "yellow" :background "red")))
291
"Face to use for symbols when no definition is found."
292
:group 'gas-faces)
293
294
(defface gas-passthrough-code
295
'((((class color) (background light)) (:foreground "magenta4"))
296
(((class color) (background dark)) (:foreground "magenta2"))
297
(t (:foreground "magenta1" :background "cyan")))
298
"Marks passthrough code."
299
:group 'gas-faces)
300
301
(defface gas-passthrough-comment
302
'((((class color) (background light)) (:foreground "turquoise4"))
303
(((class color) (background dark)) (:foreground "turquoise2"))
304
(t (:foreground "turquoise1")))
305
"Marks passthrough comments."
306
:group 'gas-faces)
307
308
(defvar gas-builtin-face 'gas-builtin)
309
(defvar gas-symbol-ok-face 'gas-symbol-ok)
310
(defvar gas-symbol-error-face 'gas-symbol-error)
311
(defvar gas-symbol-global-face 'gas-symbol-global)
312
(defvar gas-symbol-undef-face 'gas-symbol-undef)
313
(defvar gas-symbol-global-undef-face 'gas-symbol-global-undef)
314
(defvar gas-passthrough-code-face 'gas-passthrough-code)
315
(defvar gas-passthrough-comment-face 'gas-passthrough-comment)
316
317
(defconst gas-max-lines-in-cache 500
318
"Maximum number of parsed lines in cache.
319
320
I don't expect much impact from this on performance (the line
321
cache is emptied on any buffer change anyway). Play around with
322
this value if you suspect memory may be your problem.")
323
324
(defconst gas-max-labels-in-cache 300
325
"Maximum number of symbols in highlight cache.
326
Reduce this if memory footprint grows too high (very unlikely).")
327
328
(defconst gas-re-sym "\\([_$A-Za-z][_0-9$A-Za-z]*\\)"
329
"Regexp defining a valid symbol as a subexpression.")
330
331
(defconst gas-skip-sym "_0-9$A-Za-z"
332
"The valid characters for a symbol as used in `skip-chars-*' functions.")
333
334
(defconst gas-re-nosym"[^_0-9$A-Za-z]"
335
"Regexp defining the character set not allowed in a symbol.")
336
337
(defvar gas-mode-syntax-table
338
(let ((st (make-syntax-table)))
339
(modify-syntax-entry ?\n "> b" st)
340
(modify-syntax-entry ?. "_" st)
341
(modify-syntax-entry ?/ ". 124b" st)
342
(modify-syntax-entry ?* ". 23" st)
343
st)
344
"Syntax table used while in gas mode.")
345
346
(defvar gas-mode-abbrev-table nil
347
"Abbrev table used while in Gas mode.")
348
(define-abbrev-table 'gas-mode-abbrev-table ())
349
350
(defvar gas-mode-map
351
(let ((map (make-sparse-keymap)))
352
;; Note that the comment character isn't set up until gas-mode is called.
353
(define-key map ":" 'gas-colon)
354
(define-key map "\M-;" 'gas-comment)
355
(define-key map ";" 'gas-comment-char)
356
(define-key map "#" 'gas-hash)
357
(define-key map (kbd "<S-iso-lefttab>") 'gas-indent-backward)
358
(define-key map "\C-c;" 'comment-region)
359
(define-key map "\C-j" 'newline-and-indent)
360
(define-key map "\C-m" 'newline-and-indent)
361
map)
362
"Keymap for Gas mode.")
363
364
(defconst gas-equ (regexp-opt '(".equ" ".set" ".eqv" ".equiv" ".set"))
365
"Regex matching all operators which define a symbol.")
366
367
(defconst gas-indents
368
'((C-comment-end . gas-C-comment-end-column)
369
(C-comment-start . 0)
370
(C-comment . gas-get-C-relative-indent)
371
(cpp-macro-def . 0)
372
(cpp-argument . 0)
373
(label . 0)
374
(opcode . gas-opcode-column)
375
(argument . gas-argument-column)
376
(asm-comment . gas-get-asm-comment-column))
377
"Fields and their indents.
378
The cdr (the indent) may either be a number, a symbol bound to a
379
number, or a symbol bound to a function yielding the value.")
380
381
(defconst gas-parse-sequences
382
'(
383
(starting-asm-line
384
. ((cpp-macro-def . cpp-macro-def)
385
(label . label)
386
(empty-label . label)
387
(asm-comment . asm-comment)
388
(opcode . opcode)))
389
(label
390
. ((opcode . opcode)
391
(asm-comment . asm-comment)
392
(garbage . garbage)))
393
(opcode
394
. ((asm-comment . asm-comment)
395
(argument . argument)
396
(eol-ws . eol-ws)
397
(garbage . garbage)))
398
(argument
399
. ((asm-comment . asm-comment)
400
(eol-ws . eol-ws)
401
(garbage . garbage)))
402
(asm-comment
403
. ((eol-ws . eol-ws)))
404
(cpp-argument
405
. ((eol-ws . eol-ws)
406
(garbage . garbage)))
407
(cpp-macro-def
408
. ((cpp-argument . cpp-argument)
409
(garbage . garbage)))
410
(cpp-argument
411
. ((eol-ws)
412
(garbage . garbage)))
413
(C-comment-start
414
. ((C-comment . C-comment)))
415
(starting-within-C-comment
416
. ((C-comment-end . C-comment-end)
417
(C-comment . C-comment)))
418
(C-comment
419
. ((C-comment-end . C-comment-end)
420
(eol-ws . eol-ws)
421
(garbage . garbage)))
422
(garbage
423
. ((eol-ws . eol-ws)))
424
(eol-ws
425
. fini))
426
"Mapping from the field type we're on to the field types to check next.
427
car - type of field we're on (IOW, the one just handled)
428
cdr - ordered list of (first (most special) check first):
429
car - token to match
430
cdr - next field type iff match." )
431
432
(defconst gas-patterns
433
'((cpp-macro-def
434
"[ \t]*\\(\\(#[^ \t\n]*\\)\\)"
435
(0 1 1 1)
436
"/[*]")
437
(cpp-argument
438
"[ \t]*\\(\\([ \t]*\\([^ \t\n]\\)+\\)+\\)"
439
(0 1 1 1)
440
"/[*]")
441
(label
442
"[ \t]*\\([^ :\t\n]+:\\)"
443
(0 1 1 1)
444
"/[*]\\|;")
445
(opcode
446
"[ \t]*\\([^ \t\n]+\\)"
447
(0 1 1 1)
448
"/[*]\\|;")
449
(argument
450
"[ \t]*\\(\\([ \t]*\\([^ \t\n]\\)+\\)+\\)"
451
(0 1 1 1)
452
"/[*]\\|;")
453
(garbage
454
"[ \t]*\\(\\([ \t]*\\([^ \t\n]\\)+\\)+\\)"
455
(0 1 1 1)
456
"/[*]\\|;")
457
(asm-comment
458
"[ \t]*\\(\\(;+\\)\\([ \t]*[^ \t\n]+\\)*\\)"
459
(0 1 1 1)
460
nil)
461
(C-inline-comment
462
"[ \t]*\\(/[*].*?[*]/\\)"
463
(0 1 1 1)
464
nil)
465
(C-comment-start
466
"[ \t]*\\(/[*]C?\\)"
467
(0 1 1 1)
468
nil)
469
(empty-label
470
;; a field of at least 1 white space: no fill at start, nil
471
;; text field at start, fill at end
472
" [ \t]*"
473
(0 0 0 nil)
474
"/[*]\\|;")
475
(C-comment-end
476
"[ \t]*\\([*]/\\)"
477
(0 1 1 1)
478
nil)
479
(C-comment
480
;; match the entire line (except for trailing whitespace)
481
"[ \t]*\\(\\([ \t]*[^ \t]+\\)*\\)"
482
(0 1 1 1)
483
"[*]/")
484
;; fill only: zero or more white space, nil text field at
485
;; end
486
(eol-ws
487
"[ \t]+\\( ?\\)"
488
(0 1 1 1) ; last element always empty
489
nil))
490
"An alist of parse patterns.
491
492
Each entry holds 4 elements (SYMBOL REGEXP SUBEXPS TERMINATE-RE):
493
494
SYMBOL - designator (a symbol) under which it will be
495
referenced.
496
497
REGEXP - the regexp to match against,
498
499
SUBEXPS - a list (BEG-COL TEXT-COL END-COL END-OF-FIELD) of at
500
which subexpression of REGEXP to find beg-col, text-col,
501
end-col (see `gas-parse-line-really') and the end of the
502
field,
503
504
TERMINATE-RE - a regexp, the start of which (if it matches and
505
if outside a \"..\" string) unconditionally terminates the
506
field.
507
508
Every occurence of the character \";\" in both regexps is
509
replaced by variable `gas-comment-char' before use.")
510
511
(defconst gas-elmt-types
512
'(type subtype beg-col text-col end-col text modified)
513
"The elements of a gas syntax field.")
514
515
(defconst gas-builtin-keywords (concat "^\\(\\(\\sw\\|\\s_\\)+:?\\)?[ \t]+\\("
516
(regexp-opt '(
517
".Abort" ".ABORT" ".Align" ".Altmacro" ".Ascii" ".Asciz"
518
".Balign" ".Byte" ".Comm"
519
".Data" ".Def" ".Desc" ".Dim" ".Double" ".Eject"
520
".Else" ".Elseif" ".End" ".Endef" ".Endfunc" ".Endif"
521
".Equ" ".Equiv" ".Eqv"
522
".Err" ".Error" ".Exitm" ".Extern" ".Fail"
523
".File" ".Fill" ".Float" ".Func"
524
".Global" ".Hidden" ".hword" ".Ident"
525
".If" ".ifb" ".ifc" ".ifeq" ".ifeqs"
526
".ifge" ".ifle" ".ifgt" ".iflt"
527
".ifnb" ".ifnc" ".ifndef" ".ifdef" ".ifnotdef" ".ifne" ".ifnes"
528
".Incbin" ".Include" ".Int"
529
".Internal" ".Irp" ".Irpc" ".Lcomm" ".Lflags" ".Line"
530
".Linkonce" ".List" ".Ln"
531
".Long" ".Macro" ".MRI" ".Noaltmacro"
532
".Nolist" ".Octa" ".Org" ".P2align" ".PopSection" ".Previous"
533
".Print" ".Protected" ".Psize" ".Purgem" ".PushSection"
534
".Quad" ".Rept" ".Sbttl" ".Scl" ".Section" ".Set" ".Short"
535
".Single" ".Size" ".Skip" ".Sleb128" ".Space" ".Stab" ".String"
536
".Struct" ".SubSection" ".Symver" ".Tag" ".Text" ".Title" ".Type"
537
".Uleb128" ".Val" ".Version" ".VTableEntry" ".VTableInherit"
538
".Warning" ".Weak" ".Weakref" ".Word" ".Deprecated"))
539
"\\)[ \t\n]"))
540
541
(defvar gas-font-lock-keywords
542
(append
543
(list
544
'(gas-return-passthrough-code-hi . (0 gas-passthrough-code-face t))
545
'(gas-return-passthrough-comment-hi . (0 gas-passthrough-comment-face t))
546
'(gas-return-gas-hi-ok . (0 gas-symbol-ok-face t))
547
'(gas-return-gas-hi-global . (0 gas-symbol-global-face t))
548
'(gas-return-gas-hi-error . (0 gas-symbol-error-face t))
549
'(gas-return-gas-hi-undef . (0 gas-symbol-undef-face t))
550
'(gas-return-gas-hi-global-undef . (0 gas-symbol-global-undef-face t))
551
(list gas-builtin-keywords 3 'gas-builtin-face)
552
'("^\\(\\(\\sw\\|\\s_\\)+\\)\\>:?" 1 font-lock-function-name-face))
553
cpp-font-lock-keywords)
554
"Additional expressions to highlight in gas mode.")
555
(put 'gas-mode 'font-lock-defaults '(gas-font-lock-keywords))
556
557
;;;###autoload
558
559
(defun gas-mode ()
560
"Major mode for editing assembler code.
561
562
Commands:
563
564
\(Some of these commands may exhibit slightly different behaviour if point
565
is on a C syntax line.)
566
567
\\[indent-for-tab-command] indent the field(s) point is on. If it already is in its position,
568
move on to the next field on the line.
569
570
\\[gas-indent-backward] Move to the previous field.
571
572
\\[gas-comment] When no region is active, starts a comment sequence:
573
- If a comment is present and point is not at its start, jump there.
574
- Else start a comment. If there already is one, increase its comment level.
575
What that is and what it does, is best explained when you try
576
it out: Move to en empty line, then type \\[gas-comment] and
577
then repeatedly \\[gas-comment] or \\[gas-comment-char].
578
Calls \\[gas-comment-dwim] (see below) if the region is active.
579
580
\\[forward-sexp] If you're on a highlighted symbol, jump to its next
581
occurence. Else do `forward-sexp' like in text mode.
582
583
\\[backward-sexp] If you're on a highlighted symbol, jump to its previous
584
occurence. Else do `backward-sexp' like in text mode.
585
586
\\[gas-comment-dwim] If the region starts at the leading white space
587
before a comment, all full-line comments in region will be
588
removed. If the region starts on a comment, comments will be
589
removed, but comments with leading white space will be left
590
untouched. Else insert triple variable `gas-comment-char's before all
591
lines in region.
592
593
\\[fill-paragraph] beautifies the paragraph around
594
point, i.e. it adjusts all assembly syntax fields to their
595
standard positions.
596
597
\\[indent-region] beautifies the region, i.e., adjusts all fields in region.
598
599
600
The following characters have a special meaning in special cases:
601
602
\\[gas-colon] if it terminates a label: outdent the label and
603
move to opcode column. Else, just insert \\[gas-colon] as usual.
604
605
\\[gas-comment-char] The value which introduces an asm style comment.
606
If typed in in a row after \\[gas-comment], behaves as an alias to \\[gas-comment]. else
607
just insert \\[gas-comment-char].
608
Can be customized to always act as alias (`gas-comment-char-starts-comment').
609
610
Alternatively, you may use a File Variable to make it buffer local
611
(which allows you to use different syntaxes in the same session).
612
Note: Setting it to a value other than ?\; has not yet been tested.
613
614
\\[gas-hash] If it starts a preprocessor directive: Outdent it to first
615
column.
616
617
Customization: Entry on this mode runs `gas-mode-hook'.
618
The customization group is called 'gas'.
619
620
Special commands:
621
\\{gas-mode-map}"
622
(interactive)
623
(kill-all-local-variables)
624
(setq mode-name "gas")
625
(setq major-mode 'gas-mode)
626
(setq local-abbrev-table gas-mode-abbrev-table)
627
(setq gas-comment-string (string gas-comment-char))
628
(setq gas-comment-re (regexp-quote gas-comment-string))
629
(set (make-local-variable 'indent-line-function) 'gas-indent)
630
(set (make-local-variable 'indent-region-function) 'gas-indent-region)
631
(set (make-local-variable 'forward-sexp-function) 'gas-forward-sexp)
632
(set (make-local-variable 'fill-paragraph-function) 'gas-fill-paragraph)
633
(set (make-local-variable 'font-lock-defaults) '(gas-font-lock-keywords))
634
(set (make-local-variable 'gas-local-comment-char) gas-comment-char)
635
(set (make-local-variable 'beginning-of-defun-function)
636
'gas-beginning-of-defun)
637
(set (make-local-variable 'end-of-defun-function)
638
'gas-end-of-defun)
639
(set (make-local-variable 'font-lock-keywords-case-fold-search) t)
640
(use-local-map (nconc (make-sparse-keymap) gas-mode-map))
641
(local-set-key (vector gas-comment-char) 'gas-comment-char)
642
(set-syntax-table (make-syntax-table gas-mode-syntax-table))
643
(modify-syntax-entry gas-comment-char "< b")
644
(dolist (var '(gas-line-cache gas-globals-cache gas-locals-cache
645
gas-hi-valid gas-hi-sym-list gas-hi-global gas-hi-undef
646
gas-hi-error gas-hi-ok gas-changed gas-highlights gas-hi
647
gas-pass-code-hi gas-pass-comment-hi gas-symbol-timer
648
gas-highlights-error gas-symbol-highlight-beg
649
gas-symbol-highlight-end gas-doing-comment
650
after-change-functions))
651
(set (make-local-variable var) nil))
652
(add-to-list 'after-change-functions 'gas-after-change)
653
(add-hook 'pre-command-hook 'gas-symbol-pre-command)
654
(gas-start-symbol-timer)
655
(run-mode-hooks 'gas-mode-hook)
656
;; scan buffer for extra regions to highlight:
657
(gas-symbol-highlight))
658
659
(defun gas-dbg ()
660
"You won't need this unless you're debugging `gas-mode'."
661
(interactive)
662
(setq debug-on-error t)
663
;(setq debug-on-quit t)
664
(setq debug-items '(indent))
665
(switch-to-buffer-other-frame "*Messages*")
666
(switch-to-buffer-other-frame "*scratch*")
667
(info "Elisp")
668
(switch-to-buffer-other-frame "*scratch*")
669
(find-file-other-frame "x.S")
670
(switch-to-buffer-other-frame "x.S")
671
(switch-to-buffer-other-frame (get-buffer-create "*gas-dbg*"))
672
(find-file-other-frame "gas-mode.el")
673
(column-number-mode t)
674
(switch-to-buffer-other-frame "gas-mode.el"))
675
676
(defun dmsg (condition &rest args)
677
"Helper function, outputs debug messages into a buffer of their own.
678
679
If DEBUG-ITEMS (a symbol or a list) has a non-empty
680
intersection with CONDITION (a symbol or a list)', apply `format'
681
to ARGS and insert the result at the end of the buffer
682
`gas-dbg' (which is created if non-existing).
683
684
Currently defined symbols are: 'wip (work in progress),
685
'hi (highlighting), 'parser, 'cursor, 'sym (symbol highlighting),
686
'cursor."
687
(when (and (boundp 'debug-items) debug-items)
688
(when (not (listp condition))
689
(setq condition (list condition)))
690
(when (not (listp debug-items))
691
(setq debug-items (list debug-items)))
692
(when (or (eq condition '(all))
693
(catch 'found
694
(dolist (c condition)
695
(when (member c debug-items)
696
(throw 'found t)))))
697
(let ((contents (apply 'format args)))
698
(save-current-buffer
699
(set-buffer (get-buffer-create "*gas-dbg*"))
700
(goto-char (point-max))
701
(newline)
702
(insert contents)
703
(goto-char (point-max))
704
;(recenter -1)
705
)))))
706
707
(defun gas-change-comment-regexp (str)
708
"Return STR with all \";\"s replaced by (regexp-quote variable `gas-comment-char')."
709
(when str
710
(setq str (replace-regexp-in-string
711
".*\\[[^]]*\\(;\\)" gas-comment-string str t t 1))
712
(replace-regexp-in-string ";" (regexp-quote gas-comment-string) str t t)))
713
714
(defun gas-change-comment-string (str)
715
"Return STR with all \";\"s replaced by variable `gas-comment-char'."
716
(when str
717
(replace-regexp-in-string ";" gas-comment-string str t t)))
718
719
(defun gas-set-patterns (comment-char)
720
"Replace `gas-patterns' by a copy, replacing ?\; by COMMENT-CHAR.
721
Also sets variable `gas-comment-char' to COMMENT-CHAR."
722
(set (make-local-variable 'gas-comment-char) comment-char)
723
(set (make-local-variable 'gas-comment-string) (string gas-comment-char))
724
(set (make-local-variable 'gas-comment-re) (regexp-quote gas-comment-string))
725
(kill-local-variable 'gas-patterns)
726
(let (result)
727
(dolist (pattern gas-patterns)
728
(add-to-list 'result
729
(list
730
(car pattern)
731
(gas-change-comment-regexp (nth 1 pattern))
732
(nth 2 pattern)
733
(gas-change-comment-regexp (nth 3 pattern)))))
734
(set (make-local-variable 'gas-patterns) result)
735
(dolist (sym '(gas-defun-regexp))
736
(kill-local-variable sym)
737
(let ((result (gas-change-comment-regexp (eval sym))))
738
(set (make-local-variable sym) result)))))
739
740
(defun gas-after-change (beg end len)
741
"Invalidate saved parser state.
742
Argument BEG BEG, END, and LEN, athough saved in `gas-changed' for debugging purposes, are not used."
743
(setq gas-globals-cache nil)
744
(setq gas-hi-valid nil)
745
(setq gas-locals-cache nil)
746
(setq gas-changed (append gas-changed (list (current-buffer) beg end len))))
747
748
(defun gas-symbol-pre-command()
749
(setq gas-hi nil))
750
751
(defun gas-start-symbol-timer (&optional stop)
752
"Schedule a timer for symbol highlighting (if not already scheduled).
753
Optional STOP, if non-nil, means remove from schedule."
754
(if (and stop gas-symbol-timer)
755
(progn
756
(cancel-timer gas-symbol-timer)
757
(setq gas-symbol-timer nil))
758
(unless (and gas-symbol-timer
759
(memq gas-symbol-timer timer-idle-list))
760
(setq gas-symbol-timer
761
(run-with-idle-timer gas-symbol-highlight-delay
762
t
763
'gas-symbol-highlight-maybe)))))
764
765
(defun gas-return-passthrough-hi (pos what)
766
"Return next passthrough match (if any).
767
See highlight.el for POS and WHAT."
768
(let ((curpoint (point)))
769
(catch 'found
770
(dolist (match what)
771
(when (and (< curpoint (cdr match))
772
(> pos (car match)))
773
(when (< curpoint (car match))
774
(goto-char (car match)))
775
(when (re-search-forward ".+" (min pos (cdr match)) t)
776
(dmsg 'hi "match: %s, data: '%s'" match (match-string 0))
777
(throw 'found t)))
778
(when (>= (car match) pos)
779
(throw 'found nil))))))
780
781
(defun gas-return-passthrough-code-hi (pos)
782
"Return next passthrough code match (if any).
783
See highlight.el for documentation on POS."
784
(gas-return-passthrough-hi pos gas-pass-code-hi))
785
786
(defun gas-return-passthrough-comment-hi (pos)
787
"Return next passthrough comment match (if any).
788
See highlight.el for documentaion on POS ."
789
(gas-return-passthrough-hi pos gas-pass-comment-hi))
790
791
(defun gas-passthrough-highlight ()
792
"Compute blocks of C passthroughs to be highlighted."
793
(goto-char (point-min))
794
(setq gas-pass-code-hi nil)
795
(setq gas-pass-comment-hi nil)
796
(let (beg end limit)
797
(while (and (re-search-forward "/[*]C" nil t)
798
(setq beg (point))
799
(setq limit
800
(save-excursion
801
(and (re-search-forward "[*]/" nil t)
802
(- (point) 2)))))
803
(while (and beg
804
(setq end (and (re-search-forward "/#" limit t)
805
(point))))
806
(add-to-list 'gas-pass-code-hi (cons beg (- end 2)) t)
807
(forward-char 2)
808
(when
809
(setq beg (and (re-search-forward "#/" limit t) (point)))
810
(add-to-list 'gas-pass-comment-hi (cons (- end 2) beg) t)))
811
(when beg
812
(add-to-list 'gas-pass-code-hi (cons beg limit) t)))))
813
814
(defun gas-return-highlight (pos hi-list)
815
"Called through the gas-return-gas-hi-* functions by highlight.el.
816
If there's a match of POS against one of the entries in HI-LIST,
817
return match data. Else, return nil."
818
(when gas-hi-valid
819
(dmsg 'hi "gas-return-highlight, pos: %s, point: %s, list: %s" pos (point) hi-list)
820
(catch 'found
821
(dolist (match hi-list)
822
(if (< (car match) pos)
823
(when (>= (car match) (point))
824
(dmsg 'hi "gas-return-highlight, match: %s" match)
825
(goto-char (car match))
826
(throw 'found (re-search-forward ".+" (min pos (cadr match)) t)))
827
(dmsg 'hi "gas-return-highlight: nope.")
828
(throw 'found nil)
829
nil)))))
830
831
(defun gas-return-gas-hi-global (pos)
832
"Check POS against the entries in the list `gas-hi-global'.
833
Called by highlight.el."
834
(gas-return-highlight pos gas-hi-global))
835
836
(defun gas-return-gas-hi-undef (pos)
837
"Check POS against the entries in the list `gas-hi-undef'.
838
Called by highlight.el."
839
(gas-return-highlight pos gas-hi-undef))
840
841
(defun gas-return-gas-hi-error (pos)
842
"Check POS against the entries in the list `gas-hi-error'.
843
Called by highlight.el."
844
(gas-return-highlight pos gas-hi-error))
845
846
(defun gas-return-gas-hi-ok (pos)
847
"Check POS against the entries in the list `gas-hi-ok'.
848
Called by highlight.el."
849
(gas-return-highlight pos gas-hi-ok))
850
851
(defun gas-return-gas-hi-global-undef (pos)
852
"Check POS against the entries in the list `gas-hi-undef'.
853
Called by highlight.el."
854
(gas-return-highlight pos gas-hi-global-undef))
855
856
857
(defun gas-qualify-symbol (sym-re slist lflags)
858
"The common part of `gas-scan-global-symbol and `gas-scan-local-symbol'.
859
860
Gets called with point on a line where a label match may be
861
found. It expects SYM-RE to be a regexp describing the
862
label. Adds on match the match it finds to SLIST (a symbol bound
863
to a list of matches) and the kind of match (as a symbol, like
864
'def for a definition or 'duplicate for a duplicate definition)
865
to LFLAGS."
866
(save-excursion
867
(let ((eol (line-end-position))
868
(bol (line-beginning-position)))
869
(setq case-fold-search nil)
870
(if (gas-C-comment-p)
871
(progn
872
(backward-char)
873
(while (re-search-forward
874
(format "%s\\(%s\\)\\(%s\\|$\\)"
875
gas-re-nosym sym-re gas-re-nosym)
876
eol t)
877
(let ((beg (match-beginning 1))
878
(end (match-end 1)))
879
(when (gas-C-passthrough-code-p)
880
(add-to-list lflags 'C-ref t)
881
(add-to-list slist (list 'C-ref beg end) t)))))
882
;; not C style:
883
(let* ((fields (gas-parsed))
884
(lbl (gas-nth 'text 'label fields))
885
(arg (gas-nth 'text 'argument fields))
886
(type 'ref)
887
(argno 0))
888
(when (and lbl
889
(string-match (concat "^" sym-re ":?$") lbl))
890
(setq nlabels (1+ nlabels))
891
(if (member 'def (eval lflags))
892
(add-to-list lflags 'duplicate t)
893
(add-to-list lflags 'def t))
894
(move-to-column 0)
895
(looking-at "[^:]+:")
896
(add-to-list slist
897
(list 'def (match-beginning 0) (match-end 0)) t))
898
(when arg
899
(move-to-column (- (gas-nth 'text-col 'argument fields) 1))
900
(let ((type 'ref)
901
(eo-arg (+ bol (gas-nth 'end-col 'argument fields) 2))
902
(opcode (gas-nth 'text 'opcode fields)))
903
(while (re-search-forward
904
(format "%s\\(%s\\)\\(%s\\|$\\)"
905
gas-re-nosym sym-re gas-re-nosym)
906
eo-arg t)
907
(let ((beg (match-beginning 1))
908
(end (match-end 1)))
909
;; first argument?
910
(if (and (= argno 0) ; yes
911
(string-match gas-equ opcode)) ; assignment?
912
(progn
913
(setq type 'def) ; yes
914
(if (member 'def (eval lflags))
915
(add-to-list lflags 'duplicate t)
916
(add-to-list lflags 'def t)))
917
(add-to-list lflags 'ref t)) ; no
918
(when (string-match ".global" opcode)
919
(add-to-list lflags 'global t))
920
(add-to-list slist (list type beg end) t)
921
(setq argno (1+ argno))))))))))
922
;; skip past parsed part:
923
(end-of-line))
924
925
(defun gas-scan-global-symbol (sym)
926
"Scan the buffer vor valid occurences of the global symbol SYM.
927
Called by `gas-symbol-highlight'."
928
(goto-char (point-min))
929
(let* (sym-list
930
flags
931
qualifiers
932
(nlabels 0)
933
(sym-re (regexp-quote sym))
934
(re (format "\\(^\\|%s\\)\\(%s\\)\\(%s\\|$\\)"
935
gas-re-nosym sym-re gas-re-nosym)))
936
(while (re-search-forward re nil t)
937
(when (input-pending-p)
938
(throw 'event-abort nil))
939
(goto-char (match-beginning 2))
940
(dmsg 'sym "global: qualified1: %s" sym-list)
941
(gas-qualify-symbol sym-re 'sym-list 'flags))
942
(dmsg 'sym "global: sym-list: %s" sym-list)
943
(unless (equal sym-list '(nil))
944
(list sym flags sym-list))))
945
946
(defun gas-scan-local-symbol (orig-sym)
947
"Scan the buffer vor valid occurences of the local symbol SYM.
948
Called by `gas-symbol-highlight'.
949
Argument ORIG-SYM is the complete symbol (as written)."
950
(when (string-match "^\\(.*\\)\\([:bf]\\)" orig-sym)
951
(let* ((search-lo (point-min)) ; location of previous duplicate
952
search-mid ; label pos
953
(search-hi (point-max)) ; location of next duplicate
954
(sym (match-string 1 orig-sym))
955
(sym-kind (match-string 2 orig-sym))
956
(sym-re (regexp-quote sym))
957
(lbl-re (concat "^\\(" sym-re ":" "\\)" ))
958
(nlabels 0)
959
sym-list
960
flags
961
qualifiers
962
searches)
963
;; determine region where the label is valid
964
(save-excursion
965
(when (equal sym-kind "b")
966
(re-search-backward lbl-re nil t)) ; skip label
967
(when (re-search-backward lbl-re nil t)
968
(setq search-lo (match-end 1)))
969
(goto-char search-lo)
970
(when (re-search-forward lbl-re nil t)
971
(setq search-mid (match-beginning 1)))
972
(when (re-search-forward lbl-re nil t)
973
(setq search-hi (match-beginning 1))))
974
(let ((lo search-hi) ; first match
975
(hi search-lo) ; end of last match
976
(search-params
977
(if search-mid
978
(list
979
(list (concat sym-re "f") search-lo search-mid)
980
(list (concat sym-re ":")
981
search-mid (+ search-mid (length sym) 2))
982
(list (concat sym-re "b") search-mid search-hi))
983
(list
984
(list (concat sym-re "f") search-lo search-hi)
985
(list (concat sym-re "b") search-lo search-hi)))))
986
(dolist (param search-params)
987
(when (input-pending-p)
988
(throw 'event-abort nil))
989
(let* ((sym-re (car param))
990
(search-re (concat gas-re-nosym
991
"\\(" sym-re "\\)" gas-re-nosym))
992
(limit (caddr param)))
993
(goto-char (- (cadr param) 1))
994
(while (and (< (point) limit)
995
(re-search-forward search-re limit t))
996
(setq lo (min lo (match-beginning 1)))
997
(setq hi (max hi (match-end 1)))
998
(goto-char (match-beginning 1))
999
(gas-qualify-symbol sym-re 'sym-list 'flags))))
1000
(dmsg 'sym "local: sym-list: %s" sym-list)
1001
(unless (equal sym-list '(nil))
1002
(list sym lo hi flags sym-list))))))
1003
1004
(defun gas-sym-invalidate ()
1005
"Invalidate all symbol scan results."
1006
(setq gas-hi-sym-list nil)
1007
(setq gas-hi-global nil)
1008
(setq gas-hi-global-undef nil)
1009
(setq gas-hi-undef nil)
1010
(setq gas-hi-error nil)
1011
(setq gas-hi-ok nil))
1012
1013
(defun gas-symbol-highlight ()
1014
"Get symbol point is on, look for match and highlight it.
1015
For use with the idle timer."
1016
;; To debug this part, stop the idle timer mechanism first:
1017
;; (gas-start-symbol-timer 'stop)
1018
;; then eval it.
1019
(save-match-data
1020
(save-excursion
1021
(gas-passthrough-highlight))
1022
(save-excursion
1023
(let* ((fields (gas-parsed))
1024
(curpoint (point))
1025
(case-fold-orig case-fold-search)
1026
(pointpos (gas-get-pointpos fields))
1027
sym-list)
1028
(catch 'event-abort
1029
(dmsg 'hi "pointpos=%s, fields=%s" pointpos fields)
1030
;; skip to the symbol's start
1031
(skip-chars-backward gas-skip-sym)
1032
(when (member (car pointpos) '(label argument C-comment))
1033
(setq case-fold-search nil)
1034
(setq sym-list
1035
(catch 'found
1036
;; local label?
1037
(if (and (not (eq (car pointpos) 'C-comment))
1038
(looking-at (concat "\\(\\([0-9]+\\)\\([bf:]\\)\\)"
1039
gas-re-nosym)))
1040
(let ((lbl (match-string 2)))
1041
;; local label
1042
(dolist (entry gas-locals-cache)
1043
(when (input-pending-p)
1044
(throw 'event-abort nil))
1045
(and (equal (car entry) lbl)
1046
(>= (nth 1 entry) curpoint)
1047
(<= (nth 2 entry) curpoint)
1048
(dolist (sym (nthcdr 3 entry))
1049
(and (>= (nth 1 entry) curpoint)
1050
(<= (nth 2 entry) curpoint)
1051
(throw 'found (nthcdr 3 entry)))))
1052
nil)
1053
;; not found in cache
1054
(let ((entry (gas-scan-local-symbol (match-string 1))))
1055
(when entry
1056
(when (> (length gas-locals-cache)
1057
gas-max-labels-in-cache)
1058
(dmsg 'sym "gas-locals-cache truncated.")
1059
(nbutlast gas-locals-cache
1060
(/ (* gas-max-labels-in-cache 3) 4)))
1061
(add-to-list 'gas-locals-cache entry)
1062
(throw 'found (nthcdr 3 entry)))))
1063
;; global label:
1064
(when (looking-at (format "\\(%s+\\)%s"
1065
gas-re-sym gas-re-nosym))
1066
(setq lbl (match-string 1))
1067
(when (setq entry (assoc lbl gas-globals-cache))
1068
(throw 'found (cdr entry)))
1069
;; not found in cache
1070
(let ((entry (gas-scan-global-symbol lbl)))
1071
(when entry
1072
(when (> (length gas-globals-cache)
1073
gas-max-labels-in-cache)
1074
(dmsg 'sym "gas-globals-cache truncated.")
1075
(nbutlast gas-globals-cache
1076
(/ (* gas-max-labels-in-cache 3) 4)))
1077
(add-to-list 'gas-globals-cache entry)
1078
(throw 'found (cdr entry))))))))
1079
1080
(dmsg 'sym "gas-symbol-highlight: Matched: %s" sym-list)
1081
(gas-sym-invalidate)
1082
(setq gas-hi-sym-list sym-list)
1083
(let ((flags (car sym-list))
1084
target-list)
1085
(dolist (sym (cadr sym-list))
1086
(when (input-pending-p)
1087
(throw 'event-abort nil))
1088
(setq target-list
1089
(cond
1090
((member 'duplicate flags) 'gas-hi-error)
1091
((and (member 'global flags) (member 'def flags))
1092
'gas-hi-global)
1093
((member 'global flags) 'gas-hi-global-undef)
1094
((eq (car sym) 'C-ref) nil)
1095
((and (member 'def flags) (member 'ref flags))
1096
'gas-hi-ok)
1097
((> (length (cadr sym-list)) 1) 'gas-hi-undef)))
1098
(dmsg 'sym "target-list, sym: %s, list: %s" sym target-list)
1099
(when target-list
1100
(add-to-list target-list (cdr sym) t))))
1101
(setq gas-hi-valid t)
1102
(font-lock-fontify-buffer)))
1103
(setq case-fold-search case-fold-orig)))))
1104
1105
(defun gas-symbol-highlight-maybe ()
1106
"Check for a symbol at point to be highlighted.
1107
For use with the idle timer."
1108
(unless (or (not gas-enable-symbol-highlight ) (input-pending-p))
1109
(undo-boundary) ; probably redundant
1110
(gas-symbol-highlight)
1111
(undo-boundary))) ; -"-
1112
1113
1114
(defun gas-C-comment-p ()
1115
"True if we're editing a C syntax comment (the one enclosed in /* */)."
1116
(save-excursion
1117
(let ((current (point)))
1118
(cond ((not (re-search-backward "/\\*" 0 t)) nil)
1119
((not (re-search-forward "\\*/" current t)))))))
1120
1121
;; format of a C passthrough:
1122
;; --- asm
1123
;; /*C
1124
;; --- C passthrough code
1125
;; /#
1126
;; --- C passthrough comment
1127
;; #/
1128
;; ---- C passthrough code
1129
;; [... passthrough code, comment as above ...]
1130
;; */
1131
;; --- asm
1132
(defun gas-C-passthrough-p ()
1133
"True if we're editing a C passthrough (a C style comment enclosed in /*C ...
1134
*/)"
1135
(when gas-use-C-passthrough
1136
(save-excursion
1137
(let ((current (point)))
1138
(and (re-search-backward "/\\*C" 0 t)
1139
(not (re-search-forward "\\*/" current t)))))))
1140
1141
(defun gas-C-passthrough-comment-p ()
1142
"True if we're editing a C passthrough comment (/# ...
1143
#/ within a /*C ... */ comment)"
1144
(when gas-use-C-passthrough
1145
(save-excursion
1146
(let ((current (point)))
1147
(when (gas-C-passthrough-p)
1148
(save-restriction
1149
(prog2
1150
(gas-narrow-to-C-comment)
1151
(and (re-search-backward "/\\#" 0 t)
1152
(not (re-search-forward "[#*]/" current t))))))))))
1153
1154
1155
(defun gas-C-passthrough-code-p ()
1156
"True if we're editing C passthrough code.
1157
i.e. we're within a /*C ...*/ comment but not within /# ... #/."
1158
(when gas-use-C-passthrough
1159
(and (gas-C-passthrough-p)
1160
(not (gas-C-passthrough-comment-p)))))
1161
1162
(defun gas-C-comment-really-p ()
1163
"True if we're in a standard C commont or in a nested passthrough C comment."
1164
(and (gas-C-comment-p)
1165
(not (gas-C-passthrough-code-p))))
1166
1167
(defun gas-comment-p ()
1168
"True if we're editing some kind of comment."
1169
(or (looking-back (concat gas-comment-string ".*"))
1170
(gas-C-comment-p)))
1171
1172
(defun gas-narrow-to-C-comment ()
1173
"Narrow region to the C comment point resides in."
1174
(unless (gas-C-comment-p)
1175
(error "Not within C comment"))
1176
(save-excursion
1177
(let* ((beg (re-search-backward ".*/[*]"))
1178
(end (re-search-forward "[*]/" nil t)))
1179
(when end
1180
(narrow-to-region beg end)))))
1181
1182
1183
(defun gas-token-pattern (cur-check)
1184
"Return a list how to parse for token `CUR-CHECK'.
1185
The returned list holds three elements, see the documentation
1186
of `gas-patterns' for an explanation."
1187
(let ((pattern (cdr (assq cur-check gas-patterns))))
1188
(when (not pattern)
1189
(t (error "Gas internal: illegal pattern %s requested"
1190
cur-check)))
1191
pattern))
1192
1193
(defun gas-next-token (check-list)
1194
"Parse for one of the tokens in list `CHECK-LIST'.
1195
`CHECK-LIST' is a list of cons cells (TOKEN-SYMBOL . RESULTING-TYPE).
1196
1197
Look up each TOKEN-SYMBOL in `gas-patterns' in order, then match
1198
the text at point against the two regexps found there. On match,
1199
eat the match (advance point past it) and return the resulting
1200
field with its car set to RESULTING-TYPE (which is the same as
1201
TOKEN-SYMBOL in nearly all cases except for the final parser
1202
state, 'fini (and may be nil if same)). If none matches,
1203
return nil.
1204
1205
Valid TOKEN-SYMBOLS and RESULTING-TYPEs are the ones listed in
1206
'gas-patterns'."
1207
1208
(save-match-data
1209
(catch 'got-token
1210
(dolist (cur-check check-list)
1211
(save-restriction
1212
(let* ((pattern-list (gas-token-pattern (car cur-check)))
1213
(regexp (pop pattern-list))
1214
(subexps (pop pattern-list))
1215
(limit-re (pop pattern-list))
1216
limit
1217
(subtype nil))
1218
(setq limit (line-end-position))
1219
(when (and limit-re
1220
(looking-at
1221
(format "\\(\\(\"[^\n\"]*\"\\)\\|[^\n\"]\\)*?\\(%s\\)"
1222
limit-re)))
1223
(setq limit (match-beginning 3)))
1224
(when (>= limit (point))
1225
(narrow-to-region (point) limit)
1226
(when (looking-at regexp) ; match pattern
1227
(cond
1228
((eq (cdr cur-check) 'asm-comment)
1229
;; set subtype to the number of consecutive comment-chars
1230
(setq subtype (- (match-end 2) (match-beginning 2))))
1231
((eq (cdr cur-check) 'argument)
1232
;; set subtype to be a list holding the positions of the
1233
;; individual subexpressions (relative to start of text)
1234
(let ((arg-beg (point))
1235
(arg-end (match-end 0)))
1236
(setq subtype '(0))
1237
(save-excursion
1238
(save-match-data
1239
(while (re-search-forward "," arg-end t)
1240
(add-to-list 'subtype (- (point) arg-beg 1) t)))))))
1241
(goto-char (min limit (match-end 0)))
1242
(throw 'got-token
1243
(list (cdr cur-check)
1244
subtype
1245
(- (min limit (match-beginning (pop subexps))) curbol)
1246
(- (min limit (match-beginning (pop subexps))) curbol)
1247
(- (min limit (match-end (pop subexps))) curbol)
1248
(let ((subexp (pop subexps)))
1249
(and subexp
1250
(not (equal (match-string subexp) ""))
1251
(match-string subexp)))
1252
nil))))))))))
1253
1254
1255
1256
1257
1258
1259
(defun gas-parse-line-really ()
1260
"Parse the line point is on, element by element.
1261
The returned list holds a list of syntactic elements (fields)
1262
found, in the order of appearance. Each syntactic element is
1263
represented by a list holding 7 elements:
1264
1265
'(TYPE SUBTYPE BEG-COL TEXT-COL END-COL TEXT MODIFIED)
1266
0 - 'type: ('label, 'opcode ...)
1267
1 - 'subtype: (additional information required by some types)
1268
2 - 'beg-col: first slot (column #) (usually occupied by white
1269
space before text)
1270
3 - 'text-col: first text slot (column #)
1271
4 - 'end-col: first free slot after field (column #)
1272
5 - 'text: text contents (nil if empty)
1273
6 - 'modified: nil (will later reflect if rearrangement (indentation)
1274
is required / was done).
1275
1276
Terminology: A single list of these 7 elements is called a
1277
`field' throughout gas-mode.el. `fields' as symbol name (or part
1278
of a symbol name) means it stands for a variable bound to a list
1279
of `field' lists which, in order of appearance, describe a
1280
complete source line.
1281
1282
Note that text-col may lie outside (on the right side of) the
1283
range beg-col ... end-col if there's no actual text (it is the
1284
column where text would have to go to).
1285
1286
Don't assume a fixed element position except for 'type, which is
1287
always the car of the list. Instead, use `gas-nth' to extract
1288
elements by their symbolic names ('type, 'subtype, ...)."
1289
1290
;; We use syntax tables neither here nor much throughout the whole
1291
;; gas-mode, since assembler code, being line oriented and based on
1292
;; number and position of elements on the line, fits regexps better
1293
;; than syntax tables (IMHO). The only drawback is that this
1294
;; approach makes dealing with inline C-style comments embedded
1295
;; between asm fields (who the hell does that?) somewhat clumsy,
1296
;; but not by much.
1297
(let (field
1298
(f-type 'start)
1299
pushed-f-type
1300
(C-inline-comment-level 0)
1301
fields)
1302
1303
(save-excursion
1304
(setq cureol (line-end-position))
1305
(beginning-of-line)
1306
(setq curbol (point))
1307
1308
(setq f-type
1309
(if (gas-C-comment-p)
1310
(progn
1311
(setq pushed-f-type 'starting-asm-line)
1312
'starting-within-C-comment)
1313
'starting-asm-line))
1314
(while f-type
1315
;; always check for C-comment first (except we're already
1316
;; inside)
1317
(setq field
1318
(and (not (gas-C-comment-p))
1319
(gas-next-token
1320
'((C-inline-comment . C-inline-comment)
1321
(C-comment-start . C-comment-start)))))
1322
(unless field
1323
;; dispatch based on previous field
1324
(let ((next-check (cdr (assq f-type gas-parse-sequences))))
1325
(when (not next-check)
1326
(error "Gas-mode internal: gas-parse-line: %s" f-type))
1327
(when (not (eq next-check 'fini))
1328
;; parse:
1329
(setq field (gas-next-token next-check)))
1330
(when (eq (car field) 'garbage)
1331
(error "Gas-mode internal: garbage: %s" field))
1332
(dmsg 'parser "parsed a field: %s->%s" f-type field)))
1333
(when (eq (car field) 'C-inline-comment)
1334
(setcar field (list 'C-inline-comment C-inline-comment-level))
1335
(setq C-inline-comment-level (1+ C-inline-comment-level)))
1336
(when (and (eq 'asm-comment (car field))
1337
(assq 'opcode fields))
1338
(gas-set-nth 'subtype field 1))
1339
(when field
1340
(add-to-list 'fields field t))
1341
(when (eq (car field) 'C-comment-start) ; push state
1342
(setq pushed-f-type f-type))
1343
(unless (and field (listp (car field))) ; not C-inline-comment
1344
(setq f-type (car field)))
1345
(when (eq f-type 'C-comment-end) ; pop state
1346
(setq f-type pushed-f-type))
1347
(when (listp f-type)
1348
(setq f-type (car f-type)))))
1349
1350
1351
(dmsg '(parser cursor) "parsed all fields: %s" fields)
1352
fields))
1353
1354
1355
(defun gas-get-field (field-type field-list &optional offset)
1356
"Return field of specified type.
1357
1358
Return field (a list) with first element (syntactic type) eq
1359
to FIELD-TYPE from FIELD-LIST, nil if no such field.
1360
1361
Optional OFFSET >= 0: return next field, OFFSET < 0: return
1362
previous field. If OFFSET is given. both fields (FIELD-TYPE and
1363
the one before/after) must exist, else nil is returned."
1364
(if offset
1365
(let (f-types
1366
f-type)
1367
(dolist (field field-list)
1368
(add-to-list 'f-types (car field) t))
1369
(if (> offset 0)
1370
(while (not (eq (pop ftypes) field-type)))
1371
(while (and (setq f-type (nth 1 f-types))
1372
f-type
1373
(not (eq f-type field-type)))
1374
(pop f-types)))
1375
(gas-get-field (car f-types) field-list))
1376
(assq field-type field-list)))
1377
1378
(defun gas-nth (component field-from &optional field-list)
1379
"Get one of the components of a field.
1380
1381
COMPONENT is the component to be returned. FIELD-FROM is either
1382
a list containing a field, or a symbol designating a field (see
1383
`gas-parse-line-really' for both).
1384
1385
In the latter case, optional FIELD-LIST must be supplied as a
1386
list of fields where FIELD-FROM is extracted from."
1387
(let ((field-from (if field-list
1388
(gas-get-field field-from field-list)
1389
field-from)))
1390
(nth (gas-elmt-n component) field-from)))
1391
1392
(defun gas-elmt-n (elmt)
1393
"Return the numeric index of field component ELMT."
1394
(when (symbolp elmt)
1395
(- (length gas-elmt-types) (length (memq elmt gas-elmt-types)))))
1396
1397
(defun gas-set-nth (elmt field val-or-fields &optional val)
1398
"Set a single field component.
1399
1400
ELMT is the content to set the field to.
1401
If FIELD is a list containing a single field, set its ELMT (a
1402
symbol) to VAL-OR-FIELDS. If FIELD is a symbol designating a
1403
field and VAL-OR-FIELDS is a list of such fields (as returned by
1404
`gas-parsed'), set ELMT in the FIELD field of VAL-OR-FIELDS to
1405
VAL."
1406
(let ((curfield field))
1407
(if val
1408
(setq curfield (assq field val-or-fields))
1409
(setq val val-or-fields))
1410
(setcar (nthcdr (gas-elmt-n elmt) curfield) val)))
1411
1412
(defun gas-rearrange (fields &optional elmt f-type val)
1413
"Rearrange FIELDS so they fit together without gaps or overlapping.
1414
1415
FIELDS is the field list describing the current line.
1416
1417
Optional ELMT designates a field component in the field specified
1418
by (eq to) F-TYPE, which has to be changed before rearrangement
1419
to VAL. VAL may be a variable, a symbol which evaluates to the
1420
taget value, or bound to a function returning the target type of
1421
the component.
1422
1423
If the line did change, return the field type (the car) of the
1424
first field that has changed, else nil."
1425
(let ((field-slot 0) ; next free slot for a field
1426
(text-slot 0) ; next column where text should go to
1427
(end-slot 0)
1428
did-change)
1429
(dolist (field fields)
1430
(let ((old-col (gas-nth 'text-col field))
1431
(text (gas-nth 'text field)))
1432
1433
(gas-set-nth 'beg-col field field-slot)
1434
;; insert new value if needed
1435
(when (and val (eq f-type (gas-nth 'type field)))
1436
(gas-set-nth elmt field val))
1437
;; determine text column
1438
(let ((tcol (gas-nth 'text-col field)))
1439
;; if the current text column is nil and we are reordering:
1440
;; put in its default value.
1441
(when (or (eq elmt 'all)
1442
(and (not val) (eq f-type (car field))))
1443
(setq tcol (cdr (assq (car field) gas-indents))))
1444
;; if column is a symbol: replace it by its value
1445
(when (and tcol (symbolp tcol))
1446
(setq tcol (if (fboundp tcol)
1447
(funcall tcol)
1448
(eval tcol))))
1449
;; if we still have got no target value: use the next free
1450
;; text slot
1451
(unless tcol
1452
(setq tcol text-slot))
1453
;; set text col to max (current, next-free-slot)
1454
(setq tcol (max tcol text-slot))
1455
(gas-set-nth 'text-col field tcol)
1456
;; update `modified' flags:
1457
(when (and text
1458
(not (eq tcol old-col)))
1459
(unless did-change
1460
(setq did-change (car field)))
1461
(gas-set-nth 'modified field t))
1462
(when text
1463
(setq field-slot (+ (gas-nth 'text-col field) (length text))))
1464
(setq end-slot (max end-slot (or (gas-nth 'end-col field) 0)))
1465
(gas-set-nth 'end-col field field-slot)
1466
;; determine next free slot
1467
(setq text-slot (max tcol text-slot))
1468
(when (/= 0 field-slot)
1469
(setq text-slot (max text-slot (1+ field-slot)))))))
1470
(gas-set-nth 'end-col 'eol-ws fields end-slot)
1471
did-change))
1472
1473
1474
(defun gas-reset-modified (fields)
1475
"Clears the `modified' flag on all fields in FIELDS."
1476
(dolist (field fields)
1477
(gas-set-nth 'modified field nil)))
1478
1479
(defun gas-get-pointpos (ffields)
1480
"Determine the field in FFIELDS point is on.
1481
Returns a list (F-TYPE SUBTYPE OFFSET-TO-FIELD-TEXT-BEGIN ABS-COLUMN EOL).
1482
EOL may bei either the symbol 'eol or nil"
1483
(let* ((curcol (current-column))
1484
(fields (copy-tree ffields))
1485
(ppos (list (caar fields) curcol)))
1486
(catch 'pos-found
1487
(dolist (field fields)
1488
;; first, look for a field match
1489
(when (and (gas-nth 'text field)
1490
(>= curcol (gas-nth 'beg-col field)) ; field start
1491
(<= curcol (gas-nth 'end-col field))) ; field end
1492
(setq ppos (list (car field) (- curcol (gas-nth 'text-col field))))
1493
(throw 'pos-found t)))
1494
;; no field match: look at position relative to text slot
1495
(gas-rearrange fields 'all)
1496
(dolist (field fields)
1497
(when (or (<= curcol (gas-nth 'text-col field))
1498
(eq (car field) 'eol-ws))
1499
(setq ppos (list (car field) (- curcol (gas-nth 'text-col field))))
1500
(throw 'pos-found t))))
1501
(setq ppos (append ppos (list curcol)))
1502
(if (>= curcol (gas-nth 'end-col 'eol-ws fields))
1503
(append ppos '(eol))
1504
ppos)))
1505
1506
(defun gas-get-C-relative-indent ()
1507
"Return indent of the closest previous non-blank line in current C comment."
1508
(if (not (gas-C-comment-p))
1509
gas-C-indent
1510
(save-restriction
1511
(gas-narrow-to-C-comment)
1512
(let ((col
1513
(save-excursion
1514
(catch 'found
1515
(if (looking-at "[ \t]*#/")
1516
;; end passthrough: same indent as start
1517
(while (= 0 (forward-line -1))
1518
(when (looking-at "[ \t]*/#")
1519
(throw 'found (current-indentation))))
1520
(while (= 0 (forward-line -1))
1521
(when (looking-at "[ \t]*/[*#]")
1522
(throw 'found (+ gas-C-indent (current-indentation))))
1523
(when (looking-at "[ \t]*[^ \t\n]")
1524
(throw 'found (current-indentation)))))
1525
gas-C-indent))))
1526
(when (gas-C-passthrough-code-p)
1527
(if (looking-at "[ \t\n]*}[ \t\n]*;")
1528
(setq col (max 0 (- col gas-C-indent)))))
1529
col))))
1530
1531
(defun gas-add-missing-fields (parsed-fields)
1532
"Complete parser result by adding in empty but allowed fields.
1533
1534
Determine line syntax from the parse result in PARSED-FIELDS,
1535
then add in empty fields for syntactic components not used in the
1536
source line but allowed by the syntax, so later code can safely
1537
assume they are present."
1538
;; Let's qualify what we've got
1539
(let (existing-types mandatory line-syntax new-fields f-type cur-type)
1540
;; determine mandatory fields:
1541
(setq mandatory
1542
(cond ((not parsed-fields) ; empty line
1543
'(label opcode argument asm-comment eol-ws))
1544
((assq 'C-comment parsed-fields)
1545
'(C-comment-start C-comment C-comment-end eol-ws))
1546
((assq 'cpp-macro-def parsed-fields)
1547
'(cpp-macro-def cpp-argument eol-ws))
1548
(t
1549
(let ((subtype (gas-nth 'subtype 'asm-comment parsed-fields)))
1550
(cond
1551
((eq subtype 3) '(asm-comment eol-ws))
1552
((eq subtype 2) '(label asm-comment eol-ws))
1553
(t '(label opcode argument asm-comment eol-ws)))))))
1554
(dmsg '(parser cursor) "mandatory: %s" mandatory)
1555
;; We're going through all fields in `fields', inserting mandatory
1556
;; elements (from `mandatory') if not present
1557
(dolist (field parsed-fields)
1558
(setq f-type (car field))
1559
(if (member f-type mandatory)
1560
;; we have a mandatory field. Insert missing fields (if
1561
;; any) before it, then insert the field at its place.
1562
(progn
1563
;; insert an empty field for each mandatory but
1564
;; not already exisiting element before the one
1565
;; we're dealing with.
1566
(while (progn
1567
(setq cur-type (pop mandatory))
1568
(not (eq cur-type f-type)))
1569
(dmsg 'parser "adding missing mandatory: %s" cur-type)
1570
(add-to-list 'new-fields
1571
(list cur-type nil nil nil nil nil nil) t))
1572
;; now insert field
1573
(dmsg 'parser "adding existing mandatory: %s" field)
1574
(add-to-list 'new-fields field t))
1575
;; we have a non-mandatory field. Pass it through,
1576
;; keeping its place if possible.
1577
(add-to-list 'new-fields field t)))
1578
;; done with parsed fields.
1579
;; add mandatory elements left out at right
1580
(while (setq cur-type (pop mandatory))
1581
(dmsg 'rearranged "adding left-over: %s" cur-type)
1582
(add-to-list 'new-fields
1583
(list cur-type nil nil nil nil nil nil) t))
1584
(dmsg 'parser "new-fields=%s" new-fields)
1585
new-fields))
1586
1587
(defun gas-parsed (&optional lineno)
1588
"Return fields on line LINENO or, if nil, on current line.
1589
Return cached data, if available. Else, call
1590
`gas-parse-line-really', store result into the gas-line-cache and
1591
return it."
1592
(dmsg 'parser "gas-changed: %s, lenght gas-line-cache: %s"
1593
gas-changed (length gas-line-cache))
1594
(unless (and (eq gas-comment-char ?\;)
1595
(eq gas-local-comment-char gas-comment-char))
1596
(setq gas-local-comment-char gas-comment-char)
1597
(gas-set-patterns gas-comment-char))
1598
(when gas-changed
1599
(setq gas-line-cache nil)
1600
(setq gas-changed nil))
1601
(when (> (length gas-line-cache) gas-max-lines-in-cache)
1602
(dmsg 'parser "gas-line-cache truncated.")
1603
(nbutlast gas-line-cache (/ (* gas-max-lines-in-cache 3) 4)))
1604
(let* ((curline (or lineno (line-number-at-pos)))
1605
(cached (assq curline gas-line-cache)))
1606
(if cached
1607
(cadr cached)
1608
(let (fields)
1609
(save-excursion
1610
(goto-line curline)
1611
(setq fields (gas-parse-line-really))
1612
(setq fields (gas-add-missing-fields fields))
1613
(gas-rearrange fields)
1614
(dmsg 'parser "->gas-line-cache: %s" fields)
1615
(add-to-list 'gas-line-cache (list curline fields)))
1616
fields))))
1617
1618
(defun gas-put-parsed (fields lineno)
1619
"Put FIELDS into gas-line-cache at LINENO.
1620
Overwrites the previous entry for that line (if any)."
1621
(gas-reset-modified fields)
1622
(let ((current (assq lineno gas-line-cache)))
1623
(if current
1624
(setcdr current (list fields))
1625
(add-to-list 'gas-line-cache (list curline fields)))))
1626
1627
(defun gas-put-out (&optional fields)
1628
"Replace the current scrren line by the one described by FIELDS."
1629
(when (not fields)
1630
(setq fields (gas-parsed)))
1631
(dmsg 'indent "put-out, fields: %s" fields)
1632
(combine-after-change-calls
1633
(save-excursion
1634
(beginning-of-line)
1635
(delete-region (point) (line-end-position))
1636
(dolist (field fields)
1637
(when (and (gas-nth 'text field)
1638
(or gas-preserve-trailing-whitespace
1639
(not (eq (car field) 'eol-ws))))
1640
(indent-to (gas-nth 'text-col field))
1641
(insert (gas-nth 'text field)))))))
1642
1643
(defun gas-get-asm-comment-column ()
1644
"Get comment column according to asm comment subtype on current line.
1645
The caller must provide the variable `fields' (bound to current
1646
line's parsed content)."
1647
(let ((subtype (gas-nth 'subtype 'asm-comment fields)))
1648
(cond ((not subtype) gas-comment-column)
1649
((= subtype 1) gas-comment-column)
1650
((= subtype 2) gas-opcode-column)
1651
(t 0))))
1652
1653
(defun gas-set-point-to-field (f-type fields)
1654
"Postition point to the start of the field of type F-TYPE.
1655
FIELDS is a field list reflecting the current line."
1656
(let ((target-col (gas-nth 'text-col f-type fields)))
1657
(move-to-column target-col)
1658
(indent-to target-col)
1659
(when (eq f-type 'asm-comment)
1660
(skip-chars-forward (format " %c" gas-comment-char))
1661
(when (not (looking-back " "))
1662
(insert-char ?\s 1)))))
1663
1664
(defun gas-set-point (pointpos fields &optional direction always)
1665
"Set point to a field of the current line.
1666
1667
POINTPOS is the current field as returned by a call to
1668
`gas-get-pointpos', FIELDS the parsed content of the current
1669
line.
1670
1671
If DIRECTION is nil or 'stay, set it to the beginning of
1672
the current field. If DIRECTION is 'end-of-field, set it to the
1673
end of the current field. If DIRECTION equals 'backward or
1674
'forward, and ALWAYS is nil, set it to the beginning of the
1675
next/previous field only if the field is unchanged, else to the
1676
beginning of the current. Finally, if DIRECTION equals 'backward
1677
or'forward and ALWAYS is non-nil, move point unconditionally to
1678
the beginning of the next/previous field."
1679
(let ((existing '(line-begin))
1680
f-type)
1681
(dmsg 'indent "(gas-set-point %s %s %s)" pointpos direction always)
1682
(dolist (field fields)
1683
(setq existing (append existing (list (car field)))))
1684
(add-to-list 'existing 'line-end t)
1685
(when (eq direction 'backward)
1686
(setq existing (nreverse existing)))
1687
(setq f-type (car pointpos))
1688
(when (and (eq direction 'forward)
1689
(eq (nth 3 pointpos) 'eol))
1690
;; special forward skipping rules for certain fields:
1691
(cond ((and (eq f-type 'C-comment-start) (= (nth 2 pointpos) 0))
1692
;; move on:
1693
nil)
1694
((memq f-type '(label opcode argument))
1695
(cond ((>= (nth 2 pointpos) gas-comment-column)
1696
(setq f-type 'eol-ws)) ; next: line-end
1697
((and (not (gas-nth 'text 'opcode fields))
1698
(>= (nth 2 pointpos) gas-opcode-column))
1699
(setq f-type 'argument)))) ; next: comment
1700
(t (setq f-type 'eol-ws)))) ; next: line-end
1701
;; rules for skipping backward:
1702
(when (eq direction 'backward)
1703
(when (= (nth 2 pointpos) 0)
1704
(setq f-type (car (last existing 2)))) ;next: line-begin
1705
(and (> (nth 1 pointpos) 0)
1706
(or (not (eq f-type 'asm-comment))
1707
(not (looking-back ";[ \t]")))
1708
(setq direction 'stay)))
1709
;; move to next field if told to do so:
1710
(when (and (not (memq direction '(pos stay end-of-field))) ; no - forbidden
1711
(or always ; yes - do always
1712
(and direction ; maybe - do only if field unchanged
1713
(not (gas-nth 'modified f-type fields)))))
1714
(setq f-type (cadr (member f-type existing))))
1715
(dmsg 'indent "gas-set-point next:%s, fields: %s" f-type fields)
1716
;; set point
1717
(if (eq f-type 'line-begin)
1718
(progn
1719
(if (bobp)
1720
(beep))
1721
(beginning-of-line))
1722
(when (eq f-type 'line-end)
1723
(when (eobp)
1724
(beep)
1725
(error "End of buffer"))
1726
(setq f-type 'eol-ws)
1727
(setq direction 'end-of-field))
1728
(gas-put-out)
1729
;; and set point there:
1730
(let ((where (gas-nth 'text-col f-type fields)))
1731
(cond ((eq direction 'pos)
1732
(setq where (+ (gas-nth 'text-col f-type fields)
1733
(cadr pointpos))))
1734
((eq direction 'end-of-field)
1735
(setq where (max where (gas-nth 'end-col f-type fields)))))
1736
(move-to-column where t)
1737
(indent-to where))
1738
(when (eq f-type 'asm-comment)
1739
(skip-chars-forward (format " %c" gas-comment-char))
1740
(when (not (looking-back " "))
1741
(insert-char ?\s 1))))))
1742
1743
(defun gas-indent (&optional direction always ffields)
1744
"Indent current line.
1745
1746
For lines carrying asm syntax, `gas-indent-current-field-only'
1747
determines if only the current field or the entire line is
1748
affected.
1749
1750
See `gas-set-point' for a description of DIRECTION and ALWAYS.
1751
FFIELDS, if present, is a list describing the fields on the
1752
current line as returned by `gas-parsed'."
1753
(let* ((fields (or ffields (gas-parsed)))
1754
rearranged-type
1755
pointpos)
1756
(setq pointpos (gas-get-pointpos fields))
1757
(dmsg 'indent "gas-indent, fields: %s" fields)
1758
(dmsg 'indent "gas-indent, pointpos: %s" pointpos)
1759
(when (and (not direction)
1760
(not (gas-C-comment-p))
1761
(eq this-command 'newline-and-indent))
1762
;; go to first non-empty field. If none, to opcode.
1763
(setq pointpos
1764
(list
1765
(or (catch 'field-found
1766
(dolist (field fields)
1767
(when (gas-nth 'text field)
1768
(throw 'field-found (car field)))))
1769
'opcode)
1770
0 gas-opcode-column))
1771
(setq direction 'stay)
1772
(setq always t))
1773
(setq rearranged-type
1774
(cond
1775
((and (eq (caar fields) 'C-comment-start)
1776
(or (save-excursion (beginning-of-line)(looking-at "[ \t]*$"))
1777
(and (eq (car pointpos) 'C-comment) (= (nth 1 pointpos) 0))))
1778
;; at start of C comment text (poosibly empty): in/outdent
1779
(let ((n_indents (/ (nth 2 pointpos) gas-C-indent)))
1780
(if (eq direction 'backward)
1781
(setq n_indents (max 0 (1- n_indents)))
1782
(setq n_indents (1+ n_indents)))
1783
(gas-rearrange fields 'text-col 'C-comment
1784
(* gas-C-indent n_indents))
1785
(setq direction 'stay)
1786
'C-comment))
1787
(gas-indent-current-field-only
1788
(gas-rearrange fields 'text-col (car pointpos)
1789
(cdr (assq (car pointpos) gas-indents))))
1790
(t (gas-rearrange fields 'all))))
1791
(dmsg 'indent "re-indented, fields: %s" fields)
1792
(gas-put-parsed fields (line-number-at-pos))
1793
(gas-put-out)
1794
(cond (always (gas-set-point pointpos fields direction always))
1795
(rearranged-type (gas-set-point-to-field rearranged-type fields))
1796
((and (eq (car pointpos) 'C-comment) (not direction)
1797
(> (cadr pointpos) 0))
1798
(gas-set-point pointpos fields 'pos always))
1799
((and (not direction) (>= (cadr pointpos) 0))
1800
(gas-set-point pointpos fields 'forward always))
1801
(t (gas-set-point pointpos fields direction always)))
1802
fields))
1803
1804
(defun gas-indent-region (&optional from to)
1805
"Indent all fields in region.
1806
1807
If optional FROM and TO are given, they are used instead of point
1808
and mark for the region's end points."
1809
(interactive)
1810
(unless from (setq from (min (point) (mark))))
1811
(unless to (setq to (max (point) (mark))))
1812
(combine-after-change-calls
1813
(save-excursion
1814
(goto-char from)
1815
(while (and (not (eobp)) (< (point) to))
1816
(dmsg 'indent "indent-region, line:%s" (line-number-at-pos))
1817
(if (gas-C-comment-p)
1818
(beginning-of-line 2) ; skip C comments
1819
(let ((fields (gas-parsed)))
1820
(gas-rearrange fields 'all)
1821
(gas-put-out fields)
1822
(beginning-of-line 2)))))))
1823
1824
(defun gas-indent-backward ()
1825
"Indent, then move to previous field.
1826
While in C-comment, remove one level of indentation."
1827
(interactive)
1828
(gas-indent 'backward 'always))
1829
1830
(defun gas-forward-sexp (n)
1831
"On a highlighted symbol, move to next (previous if N < 9).
1832
Else do `forward-sexp' as usual."
1833
(gas-sym-invalidate)
1834
(interactive)
1835
(gas-symbol-highlight)
1836
(if gas-hi-sym-list
1837
(unless
1838
(catch 'found-one
1839
(if (< 0 n)
1840
(dolist (match (cadr gas-hi-sym-list))
1841
(when (> (cadr match) (point))
1842
(goto-char (cadr match))
1843
(throw 'found-one t)))
1844
(dolist (match (reverse (cadr gas-hi-sym-list)))
1845
(when (< (nth 2 match) (point))
1846
(goto-char (cadr match))
1847
(throw 'found-one t)))))
1848
(beep))
1849
(let (forward-sexp-function)
1850
(forward-sexp n))))
1851
1852
1853
(defun gas-beginning-of-defun ()
1854
"Skip to the beginning of the current block.
1855
The block delimiter is described by `gas-defun-regexp'."
1856
(interactive)
1857
(let* ((beg (save-excursion
1858
(re-search-backward gas-defun-regexp nil t))))
1859
(when beg
1860
(goto-char (match-beginning gas-defun-regexp-subexp)))))
1861
1862
(defun gas-end-of-defun ()
1863
"Skip to the beginning of the next block.
1864
The block delimiter is described by `gas-defun-regexp'."
1865
(interactive)
1866
(let ((beg (save-excursion
1867
(re-search-forward gas-defun-regexp nil t))))
1868
(when beg
1869
(goto-char (match-beginning gas-defun-regexp-subexp)))))
1870
1871
1872
(defun gas-hash ()
1873
"Insert a hash mark. If it start a macro, delete the indentaion."
1874
(interactive)
1875
(when (and (not (gas-comment-p))
1876
(looking-back "^[ \t]*"))
1877
(beginning-of-line)
1878
(delete-horizontal-space))
1879
(call-interactively 'self-insert-command))
1880
1881
1882
(defun gas-colon ()
1883
"Insert a colon; if it follows a label, delete the label's indentation."
1884
(interactive)
1885
(call-interactively 'self-insert-command)
1886
;; check if colon belongs to a label field
1887
(let* ((fields (gas-parsed))
1888
(pointpos (gas-get-pointpos fields)))
1889
(dmsg 'indent "gas-colon, fields: %s" fields)
1890
(dmsg 'indent "gas-colon, pointpos: %s" pointpos)
1891
(when (eq (car pointpos) 'label)
1892
(beginning-of-line)
1893
(gas-indent 'forward t))))
1894
1895
1896
;; Obsolete since Emacs-22.1.
1897
(defalias 'gas-newline 'newline-and-indent)
1898
1899
(defun gas-comment-region-dwim (&optional from to)
1900
"De-comment region if at start of comment, make comment out of region if not.
1901
Optional argument FROM Optional FROM and TO mark the region."
1902
(unless from
1903
(setq from (min (point) (mark)))
1904
(setq to (max (point) (mark))))
1905
(when (save-excursion (goto-char to) (bolp))
1906
(setq to (1- to)))
1907
(save-excursion
1908
(let ((mmax (progn (goto-char to) (point-marker)))
1909
(mmin (progn (goto-char from) (point-marker))))
1910
1911
(if (gas-C-passthrough-p)
1912
(let* (beg end end-match
1913
(pos (looking-at "[ \t\n]*/#"))
1914
(de-comment (or pos (gas-C-passthrough-comment-p))))
1915
(save-restriction
1916
(gas-narrow-to-C-comment)
1917
(if de-comment
1918
(progn
1919
(when (gas-C-passthrough-comment-p)
1920
(insert " #/\n"))
1921
(while (re-search-forward
1922
"\\(\n[ \t]*\\)?/#\\(\n[ \t]*\\)?" mmax t)
1923
(replace-match "")
1924
(when (re-search-forward
1925
"\\(\n[ \t]*\\)?[#]/\\(\n[ \t]*\\)?" mmax t)
1926
(replace-match "")))
1927
(let* ((end (save-excursion
1928
(re-search-forward "[#*]/" nil t)))
1929
(end-match (match-string 0))
1930
(beg (save-excursion
1931
(re-search-forward "/#" nil t))))
1932
(when (and (equal end-match "#/")
1933
(or (not beg)
1934
(> beg end)))
1935
;; we have a comment close outside the selcted
1936
;; area which misses the matching comment open
1937
(goto-char mmax)
1938
(insert "\n/#\n"))))
1939
;; do C en-comment
1940
(insert " /#\n ") ;
1941
(while (and (save-excursion
1942
(setq end (re-search-forward "[*#]/" nil t)))
1943
(setq beg (re-search-forward "/#" mmax t)))
1944
(replace-match "#/ /# ")
1945
(re-search-forward "[#*]/" mmax t)
1946
(if (equal end-match "*/")
1947
(replace-match " #/\n*/")
1948
(replace-match "#/ /#")))
1949
(let* ((end (save-excursion
1950
(re-search-forward "[#*]/" nil t)))
1951
(end-match (match-string 0))
1952
(beg (save-excursion
1953
(re-search-forward "/#" nil t))))
1954
(when (and (equal end-match "*/")
1955
(or (not beg)
1956
(> beg end)))
1957
;; we have no comment close outside the selcted
1958
;; area but comment is open
1959
(goto-char mmax)
1960
(backward-char 2)
1961
(insert "\n #/\n"))))))
1962
;; skip to the start of the asm comment if we're on one
1963
(when (looking-back (format "%c+[ \t]*" gas-comment-char))
1964
(goto-char (match-beginning 0))
1965
(skip-chars-backward gas-comment-string))
1966
(save-restriction
1967
(narrow-to-region (point) to)
1968
(let ((triple-comment (concat (make-string 3 gas-comment-char) " "))
1969
comment-pattern
1970
(de-comment
1971
;; de-comment if there's a comment after point or
1972
;; at the start of the next non-empty line
1973
(looking-at (format "[ \t]*%c\\|.*\n[ \t\n]*\%c"
1974
gas-comment-char gas-comment-char))))
1975
(when de-comment
1976
(goto-char (match-end 0))
1977
(skip-chars-backward gas-comment-string)
1978
(looking-at (format "\n?\\([ \t]\\)*\\(%c+\\)\\([ \t]?\\)"
1979
gas-comment-char))
1980
(let ((pre-pattern (match-string 1))
1981
(mid-pattern (regexp-quote (match-string 2)))
1982
(post-pattern (match-string 3)))
1983
(setq pre-pattern
1984
(if (and pre-pattern
1985
(string-match "[ \t]" pre-pattern))
1986
"\\([ \t]*"
1987
"\\("))
1988
(setq post-pattern
1989
(if (and post-pattern
1990
(string-match "[ \t]" post-pattern))
1991
"[ \t]?\\)"
1992
(format "\\)\\([^%c]\\|$\\)" gas-comment-char)))
1993
(setq comment-pattern
1994
(concat pre-pattern mid-pattern post-pattern))))
1995
(catch 'fini
1996
(while t
1997
(cond ((gas-C-comment-p) nil)
1998
((looking-at "[ \t]/[*]") nil)
1999
((not de-comment) (insert triple-comment))
2000
(t (when (looking-at comment-pattern)
2001
(replace-match "" nil t nil 1))))
2002
;; forward-line returns t even after it just
2003
;; moved to eol (end of narrowed region):
2004
(unless (and (= 0 (forward-line)) (bolp))
2005
(throw 'fini t)))))))
2006
(setq mmax nil)
2007
(setq mmin nil))))
2008
2009
(defun gas-backward-indent ()
2010
"Skip to previous asm field. Within C style comments, decrease indentation."
2011
(interactive)
2012
(gas-indent 'backward 'always))
2013
2014
(defun gas-comment ()
2015
"Start/expand a comment.
2016
Suggested usage: while writing your code, trigger `gas-comment'
2017
repeatedly until you are satisfied with the kind of comment."
2018
(interactive)
2019
(setq gas-doing-comment t)
2020
(if mark-active
2021
(gas-comment-region-dwim)
2022
(let* ((fields (save-excursion (gas-parsed)))
2023
(curcol (current-column))
2024
(pointpos (gas-get-pointpos fields))
2025
(subtype (gas-nth 'subtype 'asm-comment fields)))
2026
(dmsg 'indent "gas-comment, pointpos: %s, fields. %s"
2027
pointpos fields)
2028
(cond
2029
((gas-C-passthrough-comment-p) (insert-char gas-comment-char 1))
2030
((gas-C-passthrough-code-p)
2031
(when (looking-back "/[*]C[ \t]*")
2032
(insert "\n"))
2033
(if (looking-back "[ \t]")
2034
(insert "/# #/ ")
2035
(insert " /# #/ "))
2036
(backward-char 5))
2037
((gas-C-comment-p)
2038
(if (and gas-use-C-passthrough
2039
(looking-back "\\(/[*] ?\\)[ \t\n]*") )
2040
(save-excursion (replace-match "/*C" nil nil nil 1))
2041
(insert-char gas-comment-char 1)))
2042
((and subtype (>= subtype 3))
2043
(move-to-column (gas-nth 'text-col 'asm-comment fields))
2044
(looking-at ";+ ?")
2045
(replace-match "")
2046
(insert "/*")
2047
(backward-char)
2048
(gas-indent 'forward 'always)
2049
(let ((curindent (current-indentation)))
2050
(insert "\n\n")
2051
(indent-to curindent)
2052
(insert "*/")
2053
(forward-line -1)
2054
(indent-to (+ curindent gas-C-indent))))
2055
((looking-back "[ \t]*#.*" (line-beginning-position))
2056
(insert "/* */ ")
2057
(backward-char 5))
2058
(t
2059
(let ((target-col (gas-nth 'text-col 'asm-comment fields)))
2060
(when target-col
2061
(move-to-column target-col)))
2062
(when (or (not subtype)
2063
(and (<= (current-column) curcol)
2064
(save-excursion
2065
(skip-chars-forward (format "%c \t" gas-comment-char))
2066
(>= (current-column) curcol))))
2067
(insert-char gas-comment-char 1)
2068
(setq fields (gas-parsed)))
2069
(gas-rearrange fields 'text-col 'asm-comment)
2070
(gas-put-out fields)
2071
(gas-set-point-to-field 'asm-comment fields))))))
2072
2073
(defun gas-fill-paragraph (arg)
2074
"Beautify asm code block. Within comments, fill current paragraph.
2075
Calls `fill-paragraph' if within a C comment with ARG passed
2076
through (which is ignored otherwise)."
2077
(let ((curcol (current-column))
2078
(fill-paragraph-function nil)
2079
; non-empty comment:
2080
(asm-comment-re (format "\\([ \t]*\\(%c+\\)\\)[ \t]*[^ \t%c\n]\n?"
2081
gas-comment-char gas-comment-char))
2082
(empty-line-re "\\([^ \t]+:\\)?[ \t]*$"))
2083
(cond ((gas-C-comment-really-p)
2084
(let* ((beg (save-excursion
2085
(re-search-backward "/#\\|/[*][^C]\\([ \t]*\n\\)?")
2086
(match-end 0)))
2087
(end (save-excursion
2088
(re-search-forward "\\(\n[ \t]*\\)?[*#]/")
2089
(match-beginning 0))))
2090
(save-restriction
2091
(narrow-to-region beg end)
2092
(fill-paragraph arg))))
2093
((gas-C-passthrough-code-p)
2094
(let* ((beg (save-excursion
2095
(re-search-backward "\\(#/\\|/[*]C\\)\\([ \t]*\n\\)?")
2096
(match-end 0)))
2097
(end (save-excursion
2098
(re-search-forward "\\(\n[ \t]*\\)?[*#]/")
2099
(match-beginning 0))))
2100
(goto-char beg)
2101
(while (< (point) end)
2102
(let ((fields (gas-parsed)))
2103
(gas-rearrange fields 'all)
2104
(gas-put-out fields)
2105
(beginning-of-line 2)))))
2106
((save-excursion (beginning-of-line) (looking-at asm-comment-re))
2107
(let* ((fill-prefix (concat (match-string 1) " "))
2108
(re (format "[^%c\n]*\\%s[ \t]*[^ \t%c\n]\n?" gas-comment-char
2109
(match-string 2) gas-comment-char))
2110
(beg (save-excursion
2111
(beginning-of-line)
2112
(while (save-excursion
2113
(beginning-of-line)
2114
(looking-at re))
2115
(end-of-line 0))
2116
(point)))
2117
(end (save-excursion
2118
(end-of-line)
2119
(while (save-excursion
2120
(beginning-of-line 2)
2121
(looking-at re))
2122
(end-of-line 2))
2123
(point))))
2124
(save-restriction
2125
(narrow-to-region beg end)
2126
(fill-paragraph arg))))
2127
(t
2128
(let ((beg (save-excursion
2129
(beginning-of-line)
2130
(while (save-excursion
2131
(beginning-of-line 0)
2132
(not (or (looking-at asm-comment-re)
2133
(looking-at empty-line-re)
2134
(gas-comment-p))))
2135
(beginning-of-line 0))
2136
(point)))
2137
(end (save-excursion
2138
(end-of-line)
2139
(while (save-excursion
2140
(beginning-of-line 2)
2141
(not (or (looking-at asm-comment-re)
2142
(looking-at empty-line-re)
2143
(gas-comment-p))))
2144
(end-of-line 2))
2145
(point))))
2146
(gas-indent-region beg end))))
2147
(move-to-column curcol)) t)
2148
2149
(defun gas-comment-char ()
2150
"Handle comment character.
2151
2152
If we're in a comment start sequence (either introduced by the
2153
last command being `gas-comment' or by
2154
`gas-comment-char-starts-comment' being customized to a non-nil
2155
value and typing 'gas-comment-char'), increment the level of the
2156
comment just started.
2157
2158
Else, if `gas-comment-char-starts-comment' is customized to a
2159
non-nil value, start comment.
2160
2161
Else just insert variable `gas-comment-char'."
2162
(interactive)
2163
(if (and gas-comment-char-starts-comment
2164
(not (gas-C-comment-p)))
2165
(gas-comment)
2166
(if (and gas-doing-comment
2167
(member last-command '(gas-comment-char gas-comment)))
2168
(gas-comment)
2169
(setq gas-doing-comment nil)
2170
(insert-char gas-comment-char 1))))
2171
2172
(provide 'gas-mode)
2173
2174
;;; gas-mode.el ends here
2175
2176