comparison src/elisp/urweb-move.el @ 350:3a1e36b14105

First sort-of-working run of urweb-mode
author Adam Chlipala <adamc@hcoop.net>
date Sun, 12 Oct 2008 10:04:17 -0400
parents
children 9390c55b9f1f
comparison
equal deleted inserted replaced
349:beb72f8a7218 350:3a1e36b14105
1 ;;; urweb-move.el --- Buffer navigation functions for urweb-mode
2
3 ;; Based on urweb-mode:
4 ;; Copyright (C) 1999, 2000, 2004 Stefan Monnier <monnier@gnu.org>
5 ;;
6 ;; Modified for urweb-mode:
7 ;; Copyright (C) 2008 Adam Chlipala <adamc@hcoop.net>
8 ;;
9 ;; This program is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2 of the License, or
12 ;; (at your option) any later version.
13 ;;
14 ;; This program is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
18 ;;
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with this program; if not, write to the Free Software
21 ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22
23
24 ;;; Commentary:
25
26
27 ;;; Code:
28
29 (eval-when-compile (require 'cl))
30 (require 'urweb-util)
31 (require 'urweb-defs)
32
33 (defsyntax urweb-internal-syntax-table
34 '((?_ . "w")
35 (?' . "w")
36 (?. . "w"))
37 "Syntax table used for internal urweb-mode operation."
38 :copy urweb-mode-syntax-table)
39
40 ;;;
41 ;;; various macros
42 ;;;
43
44 (defmacro urweb-with-ist (&rest r)
45 (let ((ost-sym (make-symbol "oldtable")))
46 `(let ((,ost-sym (syntax-table))
47 (case-fold-search nil)
48 (parse-sexp-lookup-properties t)
49 (parse-sexp-ignore-comments t))
50 (unwind-protect
51 (progn (set-syntax-table urweb-internal-syntax-table) . ,r)
52 (set-syntax-table ,ost-sym)))))
53 (def-edebug-spec urweb-with-ist t)
54
55 (defmacro urweb-move-if (&rest body)
56 (let ((pt-sym (make-symbol "point"))
57 (res-sym (make-symbol "result")))
58 `(let ((,pt-sym (point))
59 (,res-sym ,(cons 'progn body)))
60 (unless ,res-sym (goto-char ,pt-sym))
61 ,res-sym)))
62 (def-edebug-spec urweb-move-if t)
63
64 (defmacro urweb-point-after (&rest body)
65 `(save-excursion
66 ,@body
67 (point)))
68 (def-edebug-spec urweb-point-after t)
69
70 ;;
71
72 (defvar urweb-op-prec
73 (urweb-preproc-alist
74 '((("UNION" "INTERSECT" "EXCEPT") . 0)
75 (("AND" "OR") . 1)
76 ((">" ">=" "<>" "<" "<=" "=") . 4)
77 (("+" "-" "^") . 6)
78 (("/" "*" "%") . 7)
79 (("++" "--") 8)
80 (("NOT") 9)
81 (("~" "$") 10)))
82 "Alist of Ur/Web infix operators and their precedence.")
83
84 (defconst urweb-syntax-prec
85 (urweb-preproc-alist
86 `(((";" ",") . 20)
87 (("=>" "d=" "=of") . (65 . 40))
88 ("|" . (47 . 30))
89 (("case" "of" "fn") . 45)
90 (("if" "then" "else" ) . 50)
91 (("<-") . 55)
92 ("||" . 70)
93 ("&&" . 80)
94 ((":" "::" ":::" ":>") . 90)
95 ("->" . 95)
96 ("with" . 100)
97 (,(cons "end" urweb-begin-syms) . 10000)))
98 "Alist of pseudo-precedence of syntactic elements.")
99
100 (defun urweb-op-prec (op dir)
101 "Return the precedence of OP or nil if it's not an infix.
102 DIR should be set to BACK if you want to precedence w.r.t the left side
103 and to FORW for the precedence w.r.t the right side.
104 This assumes that we are `looking-at' the OP."
105 (when op
106 (let ((sprec (cdr (assoc op urweb-syntax-prec))))
107 (cond
108 ((consp sprec) (if (eq dir 'back) (car sprec) (cdr sprec)))
109 (sprec sprec)
110 (t
111 (let ((prec (cdr (assoc op urweb-op-prec))))
112 (when prec (+ prec 100))))))))
113
114 ;;
115
116 (defun urweb-forward-spaces () (forward-comment 100000))
117 (defun urweb-backward-spaces () (forward-comment -100000))
118
119
120 ;;
121 ;; moving forward around matching symbols
122 ;;
123
124 (defun urweb-looking-back-at (re)
125 (save-excursion
126 (when (= 0 (skip-syntax-backward "w_")) (backward-char))
127 (looking-at re)))
128
129 (defun urweb-find-match-forward (this match)
130 "Only works for word matches."
131 (let ((level 1)
132 (forward-sexp-function nil)
133 (either (concat this "\\|" match)))
134 (while (> level 0)
135 (forward-sexp 1)
136 (while (not (or (eobp) (urweb-looking-back-at either)))
137 (condition-case () (forward-sexp 1) (error (forward-char 1))))
138 (setq level
139 (cond
140 ((and (eobp) (> level 1)) (error "Unbalanced"))
141 ((urweb-looking-back-at this) (1+ level))
142 ((urweb-looking-back-at match) (1- level))
143 (t (error "Unbalanced")))))
144 t))
145
146 (defun urweb-find-match-backward (this match)
147 (let ((level 1)
148 (forward-sexp-function nil)
149 (either (concat this "\\|" match)))
150 (while (> level 0)
151 (backward-sexp 1)
152 (while (not (or (bobp) (looking-at either)))
153 (condition-case () (backward-sexp 1) (error (backward-char 1))))
154 (setq level
155 (cond
156 ((and (bobp) (> level 1)) (error "Unbalanced"))
157 ((looking-at this) (1+ level))
158 ((looking-at match) (1- level))
159 (t (error "Unbalanced")))))
160 t))
161
162 ;;;
163 ;;; read a symbol, including the special "op <sym>" case
164 ;;;
165
166 (defmacro urweb-move-read (&rest body)
167 (let ((pt-sym (make-symbol "point")))
168 `(let ((,pt-sym (point)))
169 ,@body
170 (when (/= (point) ,pt-sym)
171 (buffer-substring-no-properties (point) ,pt-sym)))))
172 (def-edebug-spec urweb-move-read t)
173
174 (defun urweb-poly-equal-p ()
175 (< (urweb-point-after (re-search-backward urweb-=-starter-re nil 'move))
176 (urweb-point-after (re-search-backward "=" nil 'move))))
177
178 (defun urweb-nested-of-p ()
179 (< (urweb-point-after
180 (re-search-backward urweb-non-nested-of-starter-re nil 'move))
181 (urweb-point-after (re-search-backward "\\<case\\>" nil 'move))))
182
183 (defun urweb-forward-sym-1 ()
184 (or (/= 0 (skip-syntax-forward "'w_"))
185 (/= 0 (skip-syntax-forward ".'"))))
186 (defun urweb-forward-sym ()
187 (let ((sym (urweb-move-read (urweb-forward-sym-1))))
188 (cond
189 ((equal "op" sym)
190 (urweb-forward-spaces)
191 (concat "op " (or (urweb-move-read (urweb-forward-sym-1)) "")))
192 ((equal sym "=")
193 (save-excursion
194 (urweb-backward-sym-1)
195 (if (urweb-poly-equal-p) "=" "d=")))
196 ((equal sym "of")
197 (save-excursion
198 (urweb-backward-sym-1)
199 (if (urweb-nested-of-p) "of" "=of")))
200 ;; ((equal sym "datatype")
201 ;; (save-excursion
202 ;; (urweb-backward-sym-1)
203 ;; (urweb-backward-spaces)
204 ;; (if (eq (preceding-char) ?=) "=datatype" sym)))
205 (t sym))))
206
207 (defun urweb-backward-sym-1 ()
208 (or (/= 0 (skip-syntax-backward ".'"))
209 (/= 0 (skip-syntax-backward "'w_"))))
210 (defun urweb-backward-sym ()
211 (let ((sym (urweb-move-read (urweb-backward-sym-1))))
212 (when sym
213 ;; FIXME: what should we do if `sym' = "op" ?
214 (let ((point (point)))
215 (urweb-backward-spaces)
216 (if (equal "op" (urweb-move-read (urweb-backward-sym-1)))
217 (concat "op " sym)
218 (goto-char point)
219 (cond
220 ((string= sym "=") (if (urweb-poly-equal-p) "=" "d="))
221 ((string= sym "of") (if (urweb-nested-of-p) "of" "=of"))
222 ;; ((string= sym "datatype")
223 ;; (save-excursion (urweb-backward-spaces)
224 ;; (if (eq (preceding-char) ?=) "=datatype" sym)))
225 (t sym)))))))
226
227
228 (defun urweb-backward-sexp (prec)
229 "Move one sexp backward if possible, or one char else.
230 Returns t if the move indeed moved through one sexp and nil if not.
231 PREC is the precedence currently looked for."
232 (let ((parse-sexp-lookup-properties t)
233 (parse-sexp-ignore-comments t))
234 (urweb-backward-spaces)
235 (let* ((op (urweb-backward-sym))
236 (op-prec (urweb-op-prec op 'back))
237 match)
238 (cond
239 ((not op)
240 (let ((point (point)))
241 (ignore-errors (let ((forward-sexp-function nil)) (backward-sexp 1)))
242 (if (/= point (point)) t (ignore-errors (backward-char 1)) nil)))
243 ;; stop as soon as precedence is smaller than `prec'
244 ((and prec op-prec (>= prec op-prec)) nil)
245 ;; special rules for nested constructs like if..then..else
246 ((and (or (not prec) (and prec op-prec))
247 (setq match (second (assoc op urweb-close-paren))))
248 (urweb-find-match-backward (concat "\\<" op "\\>") match))
249 ;; don't back over open-parens
250 ((assoc op urweb-open-paren) nil)
251 ;; infix ops precedence
252 ((and prec op-prec) (< prec op-prec))
253 ;; [ prec = nil ] a new operator, let's skip the sexps until the next
254 (op-prec (while (urweb-move-if (urweb-backward-sexp op-prec))) t)
255 ;; special symbols indicating we're getting out of a nesting level
256 ((string-match urweb-sexp-head-symbols-re op) nil)
257 ;; if the op was not alphanum, then we still have to do the backward-sexp
258 ;; this reproduces the usual backward-sexp, but it might be bogus
259 ;; in this case since !@$% is a perfectly fine symbol
260 (t t))))) ;(or (string-match "\\sw" op) (urweb-backward-sexp prec))
261
262 (defun urweb-forward-sexp (prec)
263 "Moves one sexp forward if possible, or one char else.
264 Returns T if the move indeed moved through one sexp and NIL if not."
265 (let ((parse-sexp-lookup-properties t)
266 (parse-sexp-ignore-comments t))
267 (urweb-forward-spaces)
268 (let* ((op (urweb-forward-sym))
269 (op-prec (urweb-op-prec op 'forw))
270 match)
271 (cond
272 ((not op)
273 (let ((point (point)))
274 (ignore-errors (let ((forward-sexp-function nil)) (forward-sexp 1)))
275 (if (/= point (point)) t (forward-char 1) nil)))
276 ;; stop as soon as precedence is smaller than `prec'
277 ((and prec op-prec (>= prec op-prec)) nil)
278 ;; special rules for nested constructs like if..then..else
279 ((and (or (not prec) (and prec op-prec))
280 (setq match (cdr (assoc op urweb-open-paren))))
281 (urweb-find-match-forward (first match) (second match)))
282 ;; don't forw over close-parens
283 ((assoc op urweb-close-paren) nil)
284 ;; infix ops precedence
285 ((and prec op-prec) (< prec op-prec))
286 ;; [ prec = nil ] a new operator, let's skip the sexps until the next
287 (op-prec (while (urweb-move-if (urweb-forward-sexp op-prec))) t)
288 ;; special symbols indicating we're getting out of a nesting level
289 ((string-match urweb-sexp-head-symbols-re op) nil)
290 ;; if the op was not alphanum, then we still have to do the backward-sexp
291 ;; this reproduces the usual backward-sexp, but it might be bogus
292 ;; in this case since !@$% is a perfectly fine symbol
293 (t t))))) ;(or (string-match "\\sw" op) (urweb-backward-sexp prec))
294
295 (defun urweb-in-word-p ()
296 (and (eq ?w (char-syntax (or (char-before) ? )))
297 (eq ?w (char-syntax (or (char-after) ? )))))
298
299 (defun urweb-user-backward-sexp (&optional count)
300 "Like `backward-sexp' but tailored to the Ur/Web syntax."
301 (interactive "p")
302 (unless count (setq count 1))
303 (urweb-with-ist
304 (let ((point (point)))
305 (if (< count 0) (urweb-user-forward-sexp (- count))
306 (when (urweb-in-word-p) (forward-word 1))
307 (dotimes (i count)
308 (unless (urweb-backward-sexp nil)
309 (goto-char point)
310 (error "Containing expression ends prematurely")))))))
311
312 (defun urweb-user-forward-sexp (&optional count)
313 "Like `forward-sexp' but tailored to the Ur/Web syntax."
314 (interactive "p")
315 (unless count (setq count 1))
316 (urweb-with-ist
317 (let ((point (point)))
318 (if (< count 0) (urweb-user-backward-sexp (- count))
319 (when (urweb-in-word-p) (backward-word 1))
320 (dotimes (i count)
321 (unless (urweb-forward-sexp nil)
322 (goto-char point)
323 (error "Containing expression ends prematurely")))))))
324
325 ;;(defun urweb-forward-thing ()
326 ;; (if (= ?w (char-syntax (char-after))) (forward-word 1) (forward-char 1)))
327
328 (defun urweb-backward-arg () (urweb-backward-sexp 1000))
329 (defun urweb-forward-arg () (urweb-forward-sexp 1000))
330
331
332 (provide 'urweb-move)
333
334 ;;; urweb-move.el ends here