Oops: font-lock-ensure is from the future
[geiser.git] / elisp / geiser-syntax.el
blob022c4a0cf04bc7762a083db3198561fd6341562f
1 ;;; geiser-syntax.el -- utilities for parsing scheme syntax
3 ;; Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 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 (and-let* 1)
32 (case-lambda 0)
33 (catch defun)
34 (class defun)
35 (dynamic-wind 0)
36 (guard 1)
37 (let*-values 1)
38 (let-values 1)
39 (let/ec 1)
40 (letrec* 1)
41 (match 1)
42 (match-lambda 0)
43 (match-lambda* 0)
44 (match-let 1)
45 (match-let* 1)
46 (match-letrec 1)
47 (opt-lambda 1)
48 (parameterize 1)
49 (parameterize* 1)
50 (receive 2)
51 (require-extension 0)
52 (syntax-case 2)
53 (test-approximate 1)
54 (test-assert 1)
55 (test-eq 1)
56 (test-equal 1)
57 (test-eqv 1)
58 (test-group-with-cleanup 1)
59 (test-runner-on-bad-count! 1)
60 (test-runner-on-bad-end-name! 1)
61 (test-runner-on-final! 1)
62 (test-runner-on-group-begin! 1)
63 (test-runner-on-group-end! 1)
64 (test-runner-on-test-begin! 1)
65 (test-runner-on-test-end! 1)
66 (test-with-runner 1)
67 (unless 1)
68 (when 1)
69 (while 1)
70 (with-exception-handler 1)
71 (with-syntax 1))
74 ;;; Extra syntax keywords
76 (defconst geiser-syntax--builtin-keywords
77 '("and-let*"
78 "cut"
79 "cute"
80 "define-condition-type"
81 "define-immutable-record-type"
82 "define-record-type"
83 "define-values"
84 "letrec*"
85 "match"
86 "match-lambda"
87 "match-lambda*"
88 "match-let"
89 "match-let*"
90 "match-letrec"
91 "parameterize"
92 "receive"
93 "require-extension"
94 "set!"
95 "syntax-case"
96 "test-approximate"
97 "test-assert"
98 "test-begin"
99 "test-end"
100 "test-eq"
101 "test-equal"
102 "test-eqv"
103 "test-error"
104 "test-group"
105 "test-group-with-cleanup"
106 "test-with-runner"
107 "unless"
108 "when"
109 "with-exception-handler"
110 "with-input-from-file"
111 "with-output-to-file"))
113 (defun geiser-syntax--simple-keywords (keywords)
114 "Return `font-lock-keywords' to highlight scheme KEYWORDS.
115 KEYWORDS should be a list of strings."
116 (when keywords
117 `((,(format "[[(]%s\\>" (regexp-opt keywords 1)) . 1))))
119 (defun geiser-syntax--keywords ()
120 (append
121 (geiser-syntax--simple-keywords geiser-syntax--builtin-keywords)
122 `(("\\[\\(else\\)\\>" . 1)
123 (,(rx "(" (group "define-syntax-rule") eow (* space)
124 (? "(") (? (group (1+ word))))
125 (1 font-lock-keyword-face)
126 (2 font-lock-function-name-face nil t)))))
128 (font-lock-add-keywords 'scheme-mode (geiser-syntax--keywords))
130 (geiser-impl--define-caller geiser-syntax--impl-kws keywords ()
131 "A variable (or thunk returning a value) giving additional,
132 implementation-specific entries for font-lock-keywords.")
134 (geiser-impl--define-caller geiser-syntax--case-sensitive case-sensitive ()
135 "A flag saying whether keywords are case sensitive.")
137 (defun geiser-syntax--add-kws ()
138 (when (not (and (boundp 'quack-mode) quack-mode))
139 (let ((kw (geiser-syntax--impl-kws geiser-impl--implementation))
140 (cs (geiser-syntax--case-sensitive geiser-impl--implementation)))
141 (when kw (font-lock-add-keywords nil kw))
142 (setq font-lock-keywords-case-fold-search (not cs)))))
145 ;;; A simple scheme reader
147 (defvar geiser-syntax--read/buffer-limit nil)
149 (defsubst geiser-syntax--read/eos ()
150 (or (eobp)
151 (and geiser-syntax--read/buffer-limit
152 (<= geiser-syntax--read/buffer-limit (point)))))
154 (defsubst geiser-syntax--read/next-char ()
155 (unless (geiser-syntax--read/eos)
156 (forward-char)
157 (char-after)))
159 (defsubst geiser-syntax--read/token (token)
160 (geiser-syntax--read/next-char)
161 (if (listp token) token (list token)))
163 (defsubst geiser-syntax--read/elisp ()
164 (ignore-errors (read (current-buffer))))
166 (defun geiser-syntax--read/symbol ()
167 (with-syntax-table scheme-mode-syntax-table
168 (when (re-search-forward "\\(\\sw\\|\\s_\\)+" nil t)
169 (make-symbol (match-string-no-properties 0)))))
171 (defun geiser-syntax--read/matching (open close)
172 (let ((count 1)
173 (p (1+ (point))))
174 (while (and (> count 0)
175 (geiser-syntax--read/next-char))
176 (cond ((looking-at-p open) (setq count (1+ count)))
177 ((looking-at-p close) (setq count (1- count)))))
178 (buffer-substring-no-properties p (point))))
180 (defsubst geiser-syntax--read/unprintable ()
181 (geiser-syntax--read/token
182 (cons 'unprintable (geiser-syntax--read/matching "<" ">"))))
184 (defun geiser-syntax--read/skip-comment ()
185 (while (and (geiser-syntax--read/next-char)
186 (nth 8 (syntax-ppss))))
187 (geiser-syntax--read/next-token))
189 (defun geiser-syntax--read/next-token ()
190 (skip-syntax-forward "->")
191 (if (geiser-syntax--read/eos) '(eob)
192 (case (char-after)
193 (?\; (geiser-syntax--read/skip-comment))
194 ((?\( ?\[) (geiser-syntax--read/token 'lparen))
195 ((?\) ?\]) (geiser-syntax--read/token 'rparen))
196 (?. (if (memq (car (syntax-after (1+ (point)))) '(0 11 12))
197 (geiser-syntax--read/token 'dot)
198 (cons 'atom (geiser-syntax--read/elisp))))
199 (?\# (case (geiser-syntax--read/next-char)
200 ('nil '(eob))
201 (?| (geiser-syntax--read/skip-comment))
202 (?: (if (geiser-syntax--read/next-char)
203 (cons 'kwd (geiser-syntax--read/symbol))
204 '(eob)))
205 (?\\ (cons 'char (geiser-syntax--read/elisp)))
206 (?\( (geiser-syntax--read/token 'vectorb))
207 (?\< (geiser-syntax--read/unprintable))
208 ((?' ?` ?,) (geiser-syntax--read/next-token))
209 (t (let ((tok (geiser-syntax--read/symbol)))
210 (cond ((equal (symbol-name tok) "t") '(boolean . :t))
211 ((equal (symbol-name tok) "f") '(boolean . :f))
212 (tok (cons 'atom tok))
213 (t (geiser-syntax--read/next-token)))))))
214 (?\' (geiser-syntax--read/token '(quote . quote)))
215 (?\` (geiser-syntax--read/token
216 `(backquote . ,backquote-backquote-symbol)))
217 (?, (if (eq (geiser-syntax--read/next-char) ?@)
218 (geiser-syntax--read/token
219 `(splice . ,backquote-splice-symbol))
220 `(unquote . ,backquote-unquote-symbol)))
221 (?\" (cons 'string (geiser-syntax--read/elisp)))
222 (t (cons 'atom (geiser-syntax--read/symbol))))))
224 (defsubst geiser-syntax--read/match (&rest tks)
225 (let ((token (geiser-syntax--read/next-token)))
226 (if (memq (car token) tks) token
227 (error "Unexpected token: %s" token))))
229 (defsubst geiser-syntax--read/skip-until (&rest tks)
230 (let (token)
231 (while (and (not (memq (car token) tks))
232 (not (eq (car token) 'eob)))
233 (setq token (geiser-syntax--read/next-token)))
234 token))
236 (defsubst geiser-syntax--read/try (&rest tks)
237 (let ((p (point))
238 (tk (ignore-errors (apply 'geiser-syntax--read/match tks))))
239 (unless tk (goto-char p))
240 tk))
242 (defun geiser-syntax--read/list ()
243 (cond ((geiser-syntax--read/try 'dot)
244 (let ((tail (geiser-syntax--read)))
245 (geiser-syntax--read/skip-until 'eob 'rparen)
246 tail))
247 ((geiser-syntax--read/try 'rparen 'eob) nil)
248 (t (cons (geiser-syntax--read)
249 (geiser-syntax--read/list)))))
251 (defun geiser-syntax--read ()
252 (let ((token (geiser-syntax--read/next-token))
253 (max-lisp-eval-depth (max max-lisp-eval-depth 3000)))
254 (case (car token)
255 (eob nil)
256 (lparen (geiser-syntax--read/list))
257 (vectorb (apply 'vector (geiser-syntax--read/list)))
258 ((quote backquote unquote splice) (list (cdr token)
259 (geiser-syntax--read)))
260 (kwd (make-symbol (format ":%s" (cdr token))))
261 (unprintable (format "#<%s>" (cdr token)))
262 ((char string atom) (cdr token))
263 (boolean (cdr token))
264 (t (error "Reading scheme syntax: unexpected token: %s" token)))))
266 (defun geiser-syntax--read-from-string (string &optional start end)
267 (when (stringp string)
268 (let* ((start (or start 0))
269 (end (or end (length string)))
270 (max-lisp-eval-depth (min 20000
271 (max max-lisp-eval-depth (- end start)))))
272 (with-temp-buffer
273 (save-excursion (insert string))
274 (cons (ignore-errors (geiser-syntax--read)) (point))))))
276 (defun geiser-syntax--form-from-string (s)
277 (car (geiser-syntax--read-from-string s)))
279 (defsubst geiser-syntax--form-after-point (&optional boundary)
280 (let ((geiser-syntax--read/buffer-limit (and (numberp boundary) boundary)))
281 (save-excursion (values (geiser-syntax--read) (point)))))
283 (defun geiser-syntax--mapconcat (fun lst sep)
284 (cond ((null lst) "")
285 ((not (listp lst)) (format ".%s%s" sep (funcall fun lst)))
286 ((null (cdr lst)) (format "%s" (funcall fun (car lst))))
287 (t (format "%s%s%s"
288 (funcall fun (car lst))
290 (geiser-syntax--mapconcat fun (cdr lst) sep)))))
293 ;;; Code parsing:
295 (defsubst geiser-syntax--symbol-at-point ()
296 (and (not (nth 8 (syntax-ppss)))
297 (car (geiser-syntax--read-from-string (thing-at-point 'symbol)))))
299 (defsubst geiser-syntax--skip-comment/string ()
300 (let ((pos (nth 8 (syntax-ppss))))
301 (goto-char (or pos (point)))
302 pos))
304 (defsubst geiser-syntax--nesting-level ()
305 (or (nth 0 (syntax-ppss)) 0))
307 (defun geiser-syntax--pop-to-top ()
308 (ignore-errors
309 (while (> (geiser-syntax--nesting-level) 0) (backward-up-list))))
311 (defsubst geiser-syntax--in-string-p ()
312 (nth 3 (syntax-ppss)))
314 (defsubst geiser-syntax--pair-length (p)
315 (if (cdr (last p)) (1+ (safe-length p)) (length p)))
317 (defun geiser-syntax--shallow-form (boundary)
318 (when (looking-at-p "\\s(")
319 (save-excursion
320 (forward-char)
321 (let ((elems))
322 (ignore-errors
323 (while (< (point) boundary)
324 (skip-syntax-forward "-<>")
325 (when (<= (point) boundary)
326 (forward-sexp)
327 (let ((s (thing-at-point 'symbol)))
328 (unless (equal "." s)
329 (push (car (geiser-syntax--read-from-string s)) elems))))))
330 (nreverse elems)))))
332 (defsubst geiser-syntax--keywordp (s)
333 (and s (symbolp s) (string-match "^:.+" (symbol-name s))))
335 (defsubst geiser-syntax--symbol-eq (s0 s1)
336 (and (symbolp s0) (symbolp s1) (equal (symbol-name s0) (symbol-name s1))))
338 (defun geiser-syntax--scan-sexps (&optional begin)
339 (let* ((fst (geiser-syntax--symbol-at-point))
340 (smth (or fst (not (looking-at-p "[\s \s)\s>\s<\n]"))))
341 (path (and fst `((,fst 0)))))
342 (save-excursion
343 (while (> (or (geiser-syntax--nesting-level) 0) 0)
344 (let ((boundary (point)))
345 (geiser-syntax--skip-comment/string)
346 (backward-up-list)
347 (let ((form (geiser-syntax--shallow-form boundary)))
348 (when (and (listp form) (car form) (symbolp (car form)))
349 (let* ((len (geiser-syntax--pair-length form))
350 (pos (if smth (1- len) (progn (setq smth t) len)))
351 (prev (and (> pos 1) (nth (1- pos) form)))
352 (prev (and (geiser-syntax--keywordp prev)
353 (list prev))))
354 (push `(,(car form) ,pos ,@prev) path)))))))
355 (mapcar (lambda (e)
356 (cons (substring-no-properties (format "%s" (car e))) (cdr e)))
357 (nreverse path))))
359 (defsubst geiser-syntax--binding-form-p (bfs sbfs f)
360 (and (symbolp f)
361 (let ((f (symbol-name f)))
362 (or (member f '("define" "define*" "define-syntax"
363 "syntax-rules" "lambda" "case-lambda"
364 "let" "let*" "let-values" "let*-values"
365 "letrec" "letrec*" "parameterize"))
366 (member f bfs)
367 (member f sbfs)))))
369 (defsubst geiser-syntax--binding-form*-p (sbfs f)
370 (and (symbolp f)
371 (let ((f (symbol-name f)))
372 (or (member f '("let*" "let*-values" "letrec" "letrec*"))
373 (member f sbfs)))))
375 (defsubst geiser-syntax--if-symbol (x) (and (symbolp x) x))
376 (defsubst geiser-syntax--if-list (x) (and (listp x) x))
378 (defsubst geiser-syntax--normalize (vars)
379 (mapcar (lambda (i)
380 (let ((i (if (listp i) (car i) i)))
381 (and (symbolp i) (symbol-name i))))
382 vars))
384 (defun geiser-syntax--linearize (form)
385 (cond ((not (listp form)) (list form))
386 ((null form) nil)
387 (t (cons (car form) (geiser-syntax--linearize (cdr form))))))
389 (defun geiser-syntax--scan-locals (bfs sbfs form nesting locals)
390 (if (or (null form) (not (listp form)))
391 (geiser-syntax--normalize locals)
392 (if (not (geiser-syntax--binding-form-p bfs sbfs (car form)))
393 (geiser-syntax--scan-locals bfs sbfs
394 (car (last form))
395 (1- nesting) locals)
396 (let* ((head (car form))
397 (name (geiser-syntax--if-symbol (cadr form)))
398 (names (if name (geiser-syntax--if-list (caddr form))
399 (geiser-syntax--if-list (cadr form))))
400 (bns (and name
401 (geiser-syntax--binding-form-p bfs sbfs (car names))))
402 (rest (if (and name (not bns)) (cdddr form) (cddr form)))
403 (use-names (and (or rest
404 (< nesting 1)
405 (geiser-syntax--binding-form*-p sbfs head))
406 (not bns))))
407 (when name (push name locals))
408 (when (geiser-syntax--symbol-eq head 'case-lambda)
409 (dolist (n (and (> nesting 0) (caar (last form))))
410 (when n (push n locals)))
411 (setq rest (and (> nesting 0) (cdr form)))
412 (setq use-names nil))
413 (when (geiser-syntax--symbol-eq head 'syntax-rules)
414 (dolist (n (and (> nesting 0) (cdaar (last form))))
415 (when n (push n locals)))
416 (setq rest (and (> nesting 0) (cdr form))))
417 (when use-names
418 (dolist (n (geiser-syntax--linearize names))
419 (let ((xs (if (and (listp n) (listp (car n))) (car n) (list n))))
420 (dolist (x xs) (when x (push x locals))))))
421 (dolist (f (butlast rest))
422 (when (and (listp f)
423 (geiser-syntax--symbol-eq (car f) 'define)
424 (cadr f))
425 (push (cadr f) locals)))
426 (geiser-syntax--scan-locals bfs sbfs
427 (car (last (or rest names)))
428 (1- nesting)
429 locals)))))
431 (defun geiser-syntax--locals-around-point (bfs sbfs)
432 (when (eq major-mode 'scheme-mode)
433 (save-excursion
434 (let ((sym (unless (geiser-syntax--skip-comment/string)
435 (thing-at-point 'symbol))))
436 (skip-syntax-forward "->")
437 (let ((boundary (point))
438 (nesting (geiser-syntax--nesting-level)))
439 (geiser-syntax--pop-to-top)
440 (multiple-value-bind (form end)
441 (geiser-syntax--form-after-point boundary)
442 (delete sym
443 (geiser-syntax--scan-locals bfs
444 sbfs
445 form
446 (1- nesting)
447 '()))))))))
450 ;;; Display and fontify strings as Scheme code:
452 (defun geiser-syntax--display (a)
453 (cond ((null a) "()")
454 ((eq a :t) "#t")
455 ((eq a :f) "#f")
456 ((geiser-syntax--keywordp a) (format "#%s" a))
457 ((symbolp a) (format "%s" a))
458 ((equal a "...") "...")
459 ((stringp a) (format "%S" a))
460 ((and (listp a) (symbolp (car a))
461 (equal (symbol-name (car a)) "quote"))
462 (format "'%s" (geiser-syntax--display (cadr a))))
463 ((listp a)
464 (format "(%s)"
465 (geiser-syntax--mapconcat 'geiser-syntax--display a " ")))
466 (t (format "%s" a))))
468 (defun geiser-syntax--font-lock-buffer ()
469 (let ((name " *geiser font lock*"))
470 (or (get-buffer name)
471 (let ((buffer (get-buffer-create name)))
472 (set-buffer buffer)
473 (let ((geiser-default-implementation
474 (or geiser-default-implementation
475 (car geiser-active-implementations))))
476 (scheme-mode))
477 buffer))))
479 (defun geiser-syntax--scheme-str (str)
480 (save-current-buffer
481 (set-buffer (geiser-syntax--font-lock-buffer))
482 (erase-buffer)
483 (insert str)
484 (let ((font-lock-verbose nil))
485 (if (fboundp 'font-lock-ensure)
486 (font-lock-ensure)
487 (with-no-warnings
488 (font-lock-fontify-buffer))))
489 (buffer-string)))
492 (provide 'geiser-syntax)