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