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