More robust symbol reading (instead of specializing for quack)
[geiser.git] / elisp / geiser-syntax.el
blob9f4da8667c5228ce222fd2b20132b6d1d0f9844b
1 ;;; geiser-syntax.el -- utilities for parsing scheme syntax
3 ;; Copyright (C) 2009, 2010 Jose Antonio Ortega Ruiz
5 ;; This program is free software; you can redistribute it and/or
6 ;; modify it under the terms of the Modified BSD License. You should
7 ;; have received a copy of the license along with this program. If
8 ;; not, see <http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5>.
10 ;; Start date: Sun Feb 08, 2009 15:03
14 (require 'geiser-impl)
15 (require 'geiser-popup)
16 (require 'geiser-base)
18 (require 'scheme)
20 (eval-when-compile (require 'cl))
23 ;;; Indentation:
25 (defmacro geiser-syntax--scheme-indent (&rest pairs)
26 `(progn ,@(mapcar (lambda (p)
27 `(put ',(car p) 'scheme-indent-function ',(cadr p)))
28 pairs)))
30 (geiser-syntax--scheme-indent
31 (begin0 1)
32 (c-declare 0)
33 (c-lambda 2)
34 (case-lambda 0)
35 (case-lambda: 0)
36 (catch defun)
37 (class defun)
38 (class* defun)
39 (compound-unit/sig 0)
40 (define: defun)
41 (dynamic-wind 0)
42 (for/fold 2)
43 (instantiate 2)
44 (interface 1)
45 (lambda: 1)
46 (lambda/kw 1)
47 (let*-values 1)
48 (let*-values: 1)
49 (let+ 1)
50 (let: 1)
51 (letrec: 1)
52 (letrec* 1)
53 (letrec-values 1)
54 (letrec-values: 1)
55 (let-values 1)
56 (let-values: 1)
57 (let/cc: 1)
58 (let/ec 1)
59 (match defun)
60 (mixin 2)
61 (module defun)
62 (opt-lambda 1)
63 (parameterize 1)
64 (parameterize-break 1)
65 (parameterize* 1)
66 (pmatch defun)
67 (quasisyntax/loc 1)
68 (receive 2)
69 (send* 1)
70 (sigaction 1)
71 (syntax-case 2)
72 (syntax/loc 1)
73 (type-case defun)
74 (unit defun)
75 (unit/sig 2)
76 (unless 1)
77 (when 1)
78 (while 1)
79 (with-fluid* 1)
80 (with-fluids 1)
81 (with-fluids* 1)
82 (with-handlers 1)
83 (with-handlers: 1)
84 (with-method 1)
85 (with-syntax 1))
88 ;;; Extra syntax keywords
89 (defconst geiser-syntax--keywords
90 '(("\\[\\(else\\)\\>" . 1)
91 ("(\\(parameterize\\)\\>" . 1)))
93 (font-lock-add-keywords 'scheme-mode geiser-syntax--keywords)
95 (geiser-impl--define-caller geiser-syntax--impl-kws keywords ()
96 "A variable (or thunk returning a value) giving additional,
97 implementation-specific entries for font-lock-keywords.")
99 (defun geiser-syntax--add-kws ()
100 (when (not (and (boundp 'quack-mode) quack-mode))
101 (let ((kw (geiser-syntax--impl-kws geiser-impl--implementation)))
102 (when kw (font-lock-add-keywords nil kw)))))
105 ;;; A simple scheme reader
107 (defvar geiser-syntax--read/buffer-limit nil)
109 (defsubst geiser-syntax--read/eos ()
110 (or (eobp)
111 (and geiser-syntax--read/buffer-limit
112 (<= geiser-syntax--read/buffer-limit (point)))))
114 (defsubst geiser-syntax--read/next-char ()
115 (unless (geiser-syntax--read/eos)
116 (forward-char)
117 (char-after)))
119 (defsubst geiser-syntax--read/token (token)
120 (geiser-syntax--read/next-char)
121 (if (listp token) token (list token)))
123 (defsubst geiser-syntax--read/elisp ()
124 (ignore-errors (read (current-buffer))))
126 (defun geiser-syntax--read/symbol ()
127 (with-syntax-table scheme-mode-syntax-table
128 (when (re-search-forward "\\(\\sw\\|\\s_\\)+" nil t)
129 (make-symbol (match-string-no-properties 0)))))
131 (defun geiser-syntax--read/matching (open close)
132 (let ((count 1)
133 (p (1+ (point))))
134 (while (and (> count 0)
135 (geiser-syntax--read/next-char))
136 (cond ((looking-at-p open) (setq count (1+ count)))
137 ((looking-at-p close) (setq count (1- count)))))
138 (buffer-substring-no-properties p (point))))
140 (defsubst geiser-syntax--read/unprintable ()
141 (geiser-syntax--read/token
142 (cons 'unprintable (geiser-syntax--read/matching "<" ">"))))
144 (defun geiser-syntax--read/skip-comment ()
145 (while (and (geiser-syntax--read/next-char)
146 (nth 8 (syntax-ppss))))
147 (geiser-syntax--read/next-token))
149 (defun geiser-syntax--read/next-token ()
150 (skip-syntax-forward "->")
151 (if (geiser-syntax--read/eos) '(eob)
152 (case (char-after)
153 (?\; (geiser-syntax--read/skip-comment))
154 ((?\( ?\[) (geiser-syntax--read/token 'lparen))
155 ((?\) ?\]) (geiser-syntax--read/token 'rparen))
156 (?. (if (memq (car (syntax-after (1+ (point)))) '(0 11 12))
157 (geiser-syntax--read/token 'dot)
158 (cons 'atom (geiser-syntax--read/elisp))))
159 (?\# (case (geiser-syntax--read/next-char)
160 ('nil '(eob))
161 (?| (geiser-syntax--read/skip-comment))
162 (?: (if (geiser-syntax--read/next-char)
163 (cons 'kwd (geiser-syntax--read/symbol))
164 '(eob)))
165 (?\\ (cons 'char (geiser-syntax--read/elisp)))
166 (?\( (geiser-syntax--read/token 'vectorb))
167 (?\< (geiser-syntax--read/unprintable))
168 ((?' ?` ?,) (geiser-syntax--read/next-token))
169 (t (let ((tok (geiser-syntax--read/symbol)))
170 (cond ((equal (symbol-name tok) "t") '(boolean . :t))
171 ((equal (symbol-name tok) "f") '(boolean . :f))
172 (tok (cons 'atom tok))
173 (t (geiser-syntax--read/next-token)))))))
174 (?\' (geiser-syntax--read/token '(quote . quote)))
175 (?\` (geiser-syntax--read/token
176 `(backquote . ,backquote-backquote-symbol)))
177 (?, (if (eq (geiser-syntax--read/next-char) ?@)
178 (geiser-syntax--read/token
179 `(splice . ,backquote-splice-symbol))
180 `(unquote . ,backquote-unquote-symbol)))
181 (?\" (cons 'string (geiser-syntax--read/elisp)))
182 (t (cons 'atom (geiser-syntax--read/symbol))))))
184 (defsubst geiser-syntax--read/match (&rest tks)
185 (let ((token (geiser-syntax--read/next-token)))
186 (if (memq (car token) tks) token
187 (error "Unexpected token: %s" token))))
189 (defsubst geiser-syntax--read/skip-until (&rest tks)
190 (let (token)
191 (while (and (not (memq (car token) tks))
192 (not (eq (car token) 'eob)))
193 (setq token (geiser-syntax--read/next-token)))
194 token))
196 (defsubst geiser-syntax--read/try (&rest tks)
197 (let ((p (point))
198 (tk (ignore-errors (apply 'geiser-syntax--read/match tks))))
199 (unless tk (goto-char p))
200 tk))
202 (defun geiser-syntax--read/list ()
203 (cond ((geiser-syntax--read/try 'dot)
204 (let ((tail (geiser-syntax--read)))
205 (geiser-syntax--read/skip-until 'eob 'rparen)
206 tail))
207 ((geiser-syntax--read/try 'rparen 'eob) nil)
208 (t (cons (geiser-syntax--read)
209 (geiser-syntax--read/list)))))
211 (defun geiser-syntax--read ()
212 (let ((token (geiser-syntax--read/next-token))
213 (max-lisp-eval-depth (max max-lisp-eval-depth 3000)))
214 (case (car token)
215 (eob nil)
216 (lparen (geiser-syntax--read/list))
217 (vectorb (apply 'vector (geiser-syntax--read/list)))
218 ((quote backquote unquote splice) (list (cdr token)
219 (geiser-syntax--read)))
220 (kwd (make-symbol (format ":%s" (cdr token))))
221 (unprintable (format "#<%s>" (cdr token)))
222 ((char string atom) (cdr token))
223 (boolean (cdr token))
224 (t (error "Reading scheme syntax: unexpected token: %s" token)))))
226 (defun geiser-syntax--read-from-string (string &optional start end)
227 (when (stringp string)
228 (let* ((start (or start 0))
229 (end (or end (length string)))
230 (max-lisp-eval-depth (max max-lisp-eval-depth (- end start))))
231 (with-temp-buffer
232 (save-excursion (insert string))
233 (cons (ignore-errors (geiser-syntax--read)) (point))))))
235 (defsubst geiser-syntax--form-after-point (&optional boundary)
236 (let ((geiser-syntax--read/buffer-limit (and (numberp boundary) boundary)))
237 (save-excursion (values (geiser-syntax--read) (point)))))
239 (defun geiser-syntax--mapconcat (fun lst sep)
240 (cond ((null lst) "")
241 ((not (listp lst)) (format ".%s%s" sep (funcall fun lst)))
242 ((null (cdr lst)) (format "%s" (funcall fun (car lst))))
243 (t (format "%s%s%s"
244 (funcall fun (car lst))
246 (geiser-syntax--mapconcat fun (cdr lst) sep)))))
248 (defun geiser-syntax--display (a)
249 (cond ((null a) "()")
250 ((eq a :t) "#t")
251 ((eq a :f) "#f")
252 ((geiser-syntax--keywordp a) (format "#%s" a))
253 ((symbolp a) (format "%s" a))
254 ((equal a "...") "...")
255 ((stringp a) (format "%S" a))
256 ((and (listp a) (symbolp (car a))
257 (equal (symbol-name (car a)) "quote"))
258 (format "'%s" (geiser-syntax--display (cadr a))))
259 ((listp a)
260 (format "(%s)"
261 (geiser-syntax--mapconcat 'geiser-syntax--display a " ")))
262 (t (format "%s" a))))
265 ;;; Code parsing:
267 (defsubst geiser-syntax--symbol-at-point ()
268 (and (not (nth 8 (syntax-ppss)))
269 (car (geiser-syntax--read-from-string (thing-at-point 'symbol)))))
271 (defsubst geiser-syntax--skip-comment/string ()
272 (let ((pos (nth 8 (syntax-ppss))))
273 (goto-char (or pos (point)))
274 pos))
276 (defsubst geiser-syntax--nesting-level ()
277 (or (nth 0 (syntax-ppss)) 0))
279 (defsubst geiser-syntax--pair-length (p)
280 (if (cdr (last p)) (1+ (safe-length p)) (length p)))
282 (defun geiser-syntax--shallow-form (boundary)
283 (when (looking-at-p "\\s(")
284 (save-excursion
285 (forward-char)
286 (let ((elems))
287 (ignore-errors
288 (while (< (point) boundary)
289 (skip-syntax-forward "-<>")
290 (when (<= (point) boundary)
291 (forward-sexp)
292 (let ((s (thing-at-point 'symbol)))
293 (unless (equal "." s)
294 (push (car (geiser-syntax--read-from-string s)) elems))))))
295 (nreverse elems)))))
297 (defsubst geiser-syntax--keywordp (s)
298 (and s (symbolp s) (string-match "^:.+" (symbol-name s))))
300 (defsubst geiser-syntax--symbol-eq (s0 s1)
301 (and (symbolp s0) (symbolp s1) (equal (symbol-name s0) (symbol-name s1))))
303 (defun geiser-syntax--scan-sexps (&optional begin)
304 (let* ((fst (geiser-syntax--symbol-at-point))
305 (smth (or fst (not (looking-at-p "[\s \s)\s>\s<\n]"))))
306 (path (and fst `((,fst 0)))))
307 (save-excursion
308 (while (not (zerop (geiser-syntax--nesting-level)))
309 (let ((boundary (point)))
310 (geiser-syntax--skip-comment/string)
311 (backward-up-list)
312 (let ((form (geiser-syntax--shallow-form boundary)))
313 (when (and (listp form) (car form) (symbolp (car form)))
314 (let* ((len (geiser-syntax--pair-length form))
315 (pos (if smth (1- len) (progn (setq smth t) len)))
316 (prev (and (> pos 1) (nth (1- pos) form)))
317 (prev (and (geiser-syntax--keywordp prev)
318 (list prev))))
319 (push `(,(car form) ,pos ,@prev) path)))))))
320 (mapcar (lambda (e)
321 (cons (substring-no-properties (format "%s" (car e))) (cdr e)))
322 (nreverse path))))
324 (defsubst geiser-syntax--binding-form-p (bfs sbfs f)
325 (and (symbolp f)
326 (let ((f (symbol-name f)))
327 (or (member f '("define" "define*" "define-syntax"
328 "syntax-rules" "lambda" "case-lambda"
329 "let" "let*" "let-values" "let*-values"
330 "letrec" "letrec*" "parameterize"))
331 (member f bfs)
332 (member f sbfs)))))
334 (defsubst geiser-syntax--binding-form*-p (sbfs f)
335 (and (symbolp f)
336 (let ((f (symbol-name f)))
337 (or (member f '("let*" "let*-values" "letrec" "letrec*"))
338 (member f sbfs)))))
340 (defsubst geiser-syntax--if-symbol (x) (and (symbolp x) x))
341 (defsubst geiser-syntax--if-list (x) (and (listp x) x))
343 (defsubst geiser-syntax--normalize (vars)
344 (mapcar (lambda (i)
345 (let ((i (if (listp i) (car i) i)))
346 (and (symbolp i) (symbol-name i))))
347 vars))
349 (defun geiser-syntax--linearize (form)
350 (cond ((not (listp form)) (list form))
351 ((null form) nil)
352 (t (cons (car form) (geiser-syntax--linearize (cdr form))))))
354 (defun geiser-syntax--scan-locals (bfs sbfs form nesting locals)
355 (if (or (null form) (not (listp form)))
356 (geiser-syntax--normalize locals)
357 (if (not (geiser-syntax--binding-form-p bfs sbfs (car form)))
358 (geiser-syntax--scan-locals bfs sbfs
359 (car (last form))
360 (1- nesting) locals)
361 (let* ((head (car form))
362 (name (geiser-syntax--if-symbol (cadr form)))
363 (names (if name (geiser-syntax--if-list (caddr form))
364 (geiser-syntax--if-list (cadr form))))
365 (bns (and name
366 (geiser-syntax--binding-form-p bfs sbfs (car names))))
367 (rest (if (and name (not bns)) (cdddr form) (cddr form)))
368 (use-names (and (or rest
369 (< nesting 1)
370 (geiser-syntax--binding-form*-p sbfs head))
371 (not bns))))
372 (when name (push name locals))
373 (when (geiser-syntax--symbol-eq head 'case-lambda)
374 (dolist (n (and (> nesting 0) (caar (last form))))
375 (when n (push n locals)))
376 (setq rest (and (> nesting 0) (cdr form)))
377 (setq use-names nil))
378 (when (geiser-syntax--symbol-eq head 'syntax-rules)
379 (dolist (n (and (> nesting 0) (cdaar (last form))))
380 (when n (push n locals)))
381 (setq rest (and (> nesting 0) (cdr form))))
382 (when use-names
383 (dolist (n (geiser-syntax--linearize names))
384 (let ((xs (if (and (listp n) (listp (car n))) (car n) (list n))))
385 (dolist (x xs) (when x (push x locals))))))
386 (dolist (f (butlast rest))
387 (when (and (listp f)
388 (geiser-syntax--symbol-eq (car f) 'define)
389 (cadr f))
390 (push (cadr f) locals)))
391 (geiser-syntax--scan-locals bfs sbfs
392 (car (last (or rest names)))
393 (1- nesting)
394 locals)))))
396 (defun geiser-syntax--locals-around-point (bfs sbfs)
397 (when (eq major-mode 'scheme-mode)
398 (save-excursion
399 (let ((sym (unless (geiser-syntax--skip-comment/string)
400 (thing-at-point 'symbol))))
401 (skip-syntax-forward "->")
402 (let ((boundary (point))
403 (nesting (geiser-syntax--nesting-level)))
404 (while (not (zerop (geiser-syntax--nesting-level)))
405 (backward-up-list))
406 (multiple-value-bind (form end)
407 (geiser-syntax--form-after-point boundary)
408 (delete sym
409 (geiser-syntax--scan-locals bfs
410 sbfs
411 form
412 (1- nesting)
413 '()))))))))
416 ;;; Fontify strings as Scheme code:
418 (defun geiser-syntax--font-lock-buffer ()
419 (let ((name " *geiser font lock*"))
420 (or (get-buffer name)
421 (let ((buffer (get-buffer-create name)))
422 (set-buffer buffer)
423 (let ((geiser-default-implementation
424 (or geiser-default-implementation
425 (car geiser-active-implementations))))
426 (scheme-mode))
427 buffer))))
429 (defun geiser-syntax--scheme-str (str)
430 (save-current-buffer
431 (set-buffer (geiser-syntax--font-lock-buffer))
432 (erase-buffer)
433 (insert str)
434 (let ((font-lock-verbose nil)) (font-lock-fontify-buffer))
435 (buffer-string)))
438 (provide 'geiser-syntax)
439 ;;; geiser-syntax.el ends here