3 ;;;; this is a simple major mode for working with parrot assembler
4 ;;;; (and, to a certain extent, parrot imc) files.
6 ;;;; first off: this file is 'it works for me' quality, use at your
11 ;;;; 1) highlighting for labels, comments and ops which modify program
12 ;;;; flow (if, bsr, jsr, etc.). I have intentionally kept the
13 ;;;; highlighting to a minimum (highlighitng loses it's point when you
14 ;;;; highlight _everything_), however if someone wants i'll add in
15 ;;;; different highlighting levels ala cperl-mode.
17 ;;;; 2) simple indentation (but it kills tabs, which i think is a good
18 ;;;; thing so i'm not going to fix it (yes, it'm just justifying my
19 ;;;; lazyness (i just realized that this is going to create a *lot* of
20 ;;;; whitespace diffs ... hmmm ...)))
22 ;;;; 3) a simple function for following branches. see the doc string
23 ;;;; for pasm-follow-branch. By default this is bound to "C-c C-j".
25 ;;;; 4) a function for passing the curent buffer to assembler.pl and
26 ;;;; passing the output of that to the parrot interpreter and putting
27 ;;;; the output in another window. see the doc string for
28 ;;;; pasm-assemble-and-run-buffer (dont't forget to set PERL5LIB). By
29 ;;;; default this is bound to "C-c C-c".
31 ;;;; COPYRIGHT (C) 2002 Edward Marco Baringer. All Rights Reserved.
32 ;;;; This file is free software. It may be used, redistributed
33 ;;;; and/or modified under the terms of the Perl Artistic License
34 ;;;; (see http://www.perl.com/perl/misc/Artistic.html)
37 (defvar *pasm-mode-syntax-table
* nil
)
39 (defvar *pasm-mode-keymap
* nil
)
41 (defvar *pasm-labeled-branching-ops
*
42 '("bsr" "branch" "jsr" "jump" "eq" "ne" "lt" "le" "gt" "ge" "if" "unless")
43 "All the pasm ops which jump to a particular label, in other words
44 all the ops which can change control flow minus 'ret' and 'end'")
46 (defvar *pasm-label-regexp
* "[A-Za-z_][A-Za-z_0-9]*:")
48 (defvar *pasm-assembler-path
* nil
49 "The path of the assembler. This will be passed as is to your shell,
50 so either assembler.pl is in PATH or you need to use an absolute name")
52 (defvar *pasm-parrot-path
* nil
53 "The path of the parrot interpreter. As in *pasm-assembler-path*
54 this will be passed as is to your shell (via shell-command-on-region's
55 third arg) and so unless parrot in your PATH this should be an
58 (unless *pasm-mode-syntax-table
*
59 (setq *pasm-mode-syntax-table
* (make-syntax-table))
60 (modify-syntax-entry ?
\\ "\\" *pasm-mode-syntax-table
*)
61 (modify-syntax-entry ?
# "<" *pasm-mode-syntax-table
*)
62 (modify-syntax-entry ?
\n ">" *pasm-mode-syntax-table
*)
63 (modify-syntax-entry ?
: "_" *pasm-mode-syntax-table
*))
65 (unless *pasm-mode-keymap
*
66 ;; please, someone tell how i should really do this...
67 (let ((inner-keymap (make-sparse-keymap)))
68 (define-key inner-keymap
(kbd "C-c") 'pasm-assemble-and-run-buffer
)
69 (define-key inner-keymap
(kbd "C-j") 'pasm-follow-branch
)
70 (setq *pasm-mode-keymap
* (make-sparse-keymap))
71 (define-key *pasm-mode-keymap
* (kbd "\C-c") inner-keymap
)
72 (define-key *pasm-mode-keymap
* (kbd "TAB") 'pasm-indent-function
)))
74 (setq pasm-font-lock-keywords
`(;; labels
75 ;; NB: i hearby decree that labels
76 ;; must be the first thing on a line,
77 ;; the assembler be damned.
78 (,(concat "^" *pasm-label-regexp
*) . font-lock-constant-face
)
79 ;; assembler directives
80 ("^\\s-*\\.[a-zA-Z]*" . font-lock-builtin-face
)
81 ;; ops that jump: bsr, branch, jsr,
82 ;; jump, eq, ne, lt, le, gt, ge, if,
83 ;; unless, ret and goto
84 ("\\<\\(b\\(sr\\|ranch\\)\\|e\\(q\\|nd\\)\\|g[te]\\|if\\|j\\(ump\\|sr\\)\\|l[te]\\|ne\\|ret\\|unless\\|goto\\)\\>" .
85 font-lock-keyword-face
)
86 ;; imcc temporary registers
87 ("\\$\\(I\\|S\\|P\\|N\\)[0-9]+" .
88 font-lock-variable-name-face
)
90 ("\\<\\(I\\|S\\|P\\|N\\)\\([0-9]\\|[12][0-9]\\|3[01]\\)\\>" .
91 font-lock-variable-name-face
)
92 ;; basic types: int, string, pmc and float
93 ("\\<\\(int\\|string\\|pmc\\|float\\)\\>" .
94 font-lock-type-face
)))
97 "Simple Emacs mode for editing Parrot Assembler"
99 (kill-all-local-variables)
100 (setq major-mode
'pasm-mode
)
101 (setq mode-name
"PASM")
102 (set-syntax-table *pasm-mode-syntax-table
*)
103 (make-local-variable 'paragraph-start
)
104 (setq paragraph-start
(concat "^$\\|" page-delimiter
))
105 (make-local-variable 'paragraph-separate
)
106 (setq paragraph-separate paragraph-start
)
107 (make-local-variable 'indent-line-function
)
108 (setq indent-line-function
'pasm-indent-line-function
)
109 (make-local-variable 'require-final-newline
)
110 (setq require-final-newline t
)
111 (make-local-variable 'comment-start
)
112 (setq comment-start
"# ")
113 (make-local-variable 'comment-end
)
114 (setq comment-end
"")
115 (make-local-variable 'comment-start-skip
)
116 (setq comment-start-skip
"#+ *")
117 (make-local-variable 'font-lock-defaults
)
118 (setq font-lock-defaults
'(pasm-font-lock-keywords))
120 (use-local-map *pasm-mode-keymap
*)
121 (run-hooks 'pasm-mode-hook
))
123 (defun pasm-indent-line-function ()
127 ;;(delete-region (point) (+ (point) (current-indentation)))
128 (delete-horizontal-space)
130 ((looking-at "[A-Za-z_][A-Za-z_0-9]*:")
135 (defun beginning-of-line-point (&optional n
)
136 "Return the point at the beginning of the current line. N gets
137 passed to beginning-of-line if you want"
139 (beginning-of-line n
)
142 (defun pasm-indent-function ()
143 "This differs from pasm-indent-line-function in that if we end up at
144 the beginning of a line (which doesn't have a label) we want to be
145 moved forward to column 8"
147 (pasm-indent-line-function)
148 ;; how do we check if we're at the beginnign of a line? there must
149 ;; be a function for this
150 (unless (or (looking-at "[A-Za-z_][A-Za-z_0-9]*:")
151 (/= (beginning-of-line-point) (point)))
154 (defun pasm-assemble-and-run-buffer ()
155 "Pretend the current buffer is pasm code and send it to assemble.pl,
156 send the output of that to parrot and send the output of that to the
159 This relies on the variables *pasm-assembler-path* to find
160 assembler.pl and *pasm-parrot-path* to find the parrot interpreter. If
161 there are any args you want passed to the assembler or the parrot
162 interpreter just append them to the respective variable. Note that to
163 whatever value these vars have the string \" -- - \" will be appended.
165 NB: You need to add <parrot-root-dir>/lib to your PERL5LIB var for
166 this to work (or you need to be lucky (which i guess we could say
169 (let ((max-mini-window-height 0))
170 (shell-command-on-region (point-min)
172 (concat *pasm-assembler-path
* " -- - | "
173 *pasm-parrot-path
* " -- -")
174 (get-buffer-create "*Parrot output*")))
175 (let ((current-buffer (current-buffer)))
176 (switch-to-buffer-other-window (get-buffer "*Parrot output*"))
177 (switch-to-buffer-other-window current-buffer
)))
179 (defun pasm-follow-branch ()
180 "Look at the current op, it it's a branching op we jump to the
181 proper label (assuming it exists). In order to determine whether the
182 current op is branching or not we rely on the value of
183 *pasm-labeled-branching-ops*"
185 (let ((jump-to-point nil
))
186 ;; jump-to-point and the save-excursion are so that if we're not
187 ;; on a branching op line or if the op to jump to isn't defined we
188 ;; don't move the point around unneccessarily (this would be very
189 ;; confusing, trust me)
193 (when (looking-at (concat "^" *pasm-label-regexp
*))
194 (search-forward ":")))
195 (let ((op (buffer-substring (1- (search-forward-regexp "[^ \t\n\r]"))
196 (1- (search-forward-regexp "[^a-z_]")))))
197 (when (member op
*pasm-labeled-branching-ops
*)
198 ;; the label to jump to is the last sequence of [a-zA-Z_0-9]+
199 ;; chars in this instruction
201 (search-backward-regexp "[^a-zA-Z_0-9]")
203 (let ((label (buffer-substring (point) (1- (search-forward-regexp "[^a-zA-Z_0-9]")))))
204 ;; label is the name (minus the traling ':') of the label
206 (beginning-of-buffer)
207 (setq jump-to-point
(search-forward (concat label
":")))))))
209 (goto-char jump-to-point
)
210 (message "Not on branching op or undefined label"))))