1 ;;; geiser-syntax.el -- utilities for parsing scheme syntax
3 ;; Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 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
)
20 (eval-when-compile (require 'cl
))
25 (defmacro geiser-syntax--scheme-indent
(&rest pairs
)
26 `(progn ,@(mapcar (lambda (p)
27 `(put ',(car p
) 'scheme-indent-function
',(cadr p
)))
30 (geiser-syntax--scheme-indent
49 (with-error-to-port 1)
53 ;;; Extra syntax keywords
54 (defconst geiser-syntax--keywords
55 `(("\\[\\(else\\)\\>" .
1)
56 ("(\\(parameterize\\)\\>" .
1)
57 (,(rx "(" (group "define-syntax-rule") eow
(* space
)
58 (?
"(") (?
(group (1+ word
))))
59 (1 font-lock-keyword-face
)
60 (2 font-lock-function-name-face nil t
))
61 (,(rx "(" (group "when") eow
) .
1)))
63 (font-lock-add-keywords 'scheme-mode geiser-syntax--keywords
)
65 (geiser-impl--define-caller geiser-syntax--impl-kws keywords
()
66 "A variable (or thunk returning a value) giving additional,
67 implementation-specific entries for font-lock-keywords.")
69 (geiser-impl--define-caller geiser-syntax--case-sensitive case-sensitive
()
70 "A flag saying whether keywords are case sensitive.")
72 (defun geiser-syntax--add-kws ()
73 (when (not (and (boundp 'quack-mode
) quack-mode
))
74 (let ((kw (geiser-syntax--impl-kws geiser-impl--implementation
))
75 (cs (geiser-syntax--case-sensitive geiser-impl--implementation
)))
76 (when kw
(font-lock-add-keywords nil kw
))
77 (setq font-lock-keywords-case-fold-search
(not cs
)))))
80 ;;; A simple scheme reader
82 (defvar geiser-syntax--read
/buffer-limit nil
)
84 (defsubst geiser-syntax--read
/eos
()
86 (and geiser-syntax--read
/buffer-limit
87 (<= geiser-syntax--read
/buffer-limit
(point)))))
89 (defsubst geiser-syntax--read
/next-char
()
90 (unless (geiser-syntax--read/eos
)
94 (defsubst geiser-syntax--read
/token
(token)
95 (geiser-syntax--read/next-char
)
96 (if (listp token
) token
(list token
)))
98 (defsubst geiser-syntax--read
/elisp
()
99 (ignore-errors (read (current-buffer))))
101 (defun geiser-syntax--read/symbol
()
102 (with-syntax-table scheme-mode-syntax-table
103 (when (re-search-forward "\\(\\sw\\|\\s_\\)+" nil t
)
104 (make-symbol (match-string-no-properties 0)))))
106 (defun geiser-syntax--read/matching
(open close
)
109 (while (and (> count
0)
110 (geiser-syntax--read/next-char
))
111 (cond ((looking-at-p open
) (setq count
(1+ count
)))
112 ((looking-at-p close
) (setq count
(1- count
)))))
113 (buffer-substring-no-properties p
(point))))
115 (defsubst geiser-syntax--read
/unprintable
()
116 (geiser-syntax--read/token
117 (cons 'unprintable
(geiser-syntax--read/matching
"<" ">"))))
119 (defun geiser-syntax--read/skip-comment
()
120 (while (and (geiser-syntax--read/next-char
)
121 (nth 8 (syntax-ppss))))
122 (geiser-syntax--read/next-token
))
124 (defun geiser-syntax--read/next-token
()
125 (skip-syntax-forward "->")
126 (if (geiser-syntax--read/eos
) '(eob)
128 (?\
; (geiser-syntax--read/skip-comment))
129 ((?\
( ?\
[) (geiser-syntax--read/token
'lparen
))
130 ((?\
) ?\
]) (geiser-syntax--read/token
'rparen
))
131 (?.
(if (memq (car (syntax-after (1+ (point)))) '(0 11 12))
132 (geiser-syntax--read/token
'dot
)
133 (cons 'atom
(geiser-syntax--read/elisp
))))
134 (?\
# (case (geiser-syntax--read/next-char
)
136 (?|
(geiser-syntax--read/skip-comment
))
137 (?
: (if (geiser-syntax--read/next-char
)
138 (cons 'kwd
(geiser-syntax--read/symbol
))
140 (?
\\ (cons 'char
(geiser-syntax--read/elisp
)))
141 (?\
( (geiser-syntax--read/token
'vectorb
))
142 (?\
< (geiser-syntax--read/unprintable
))
143 ((?
' ?
` ?
,) (geiser-syntax--read/next-token
))
144 (t (let ((tok (geiser-syntax--read/symbol
)))
145 (cond ((equal (symbol-name tok
) "t") '(boolean .
:t
))
146 ((equal (symbol-name tok
) "f") '(boolean .
:f
))
147 (tok (cons 'atom tok
))
148 (t (geiser-syntax--read/next-token
)))))))
149 (?
\' (geiser-syntax--read/token
'(quote . quote
)))
150 (?\
` (geiser-syntax--read/token
151 `(backquote .
,backquote-backquote-symbol
)))
152 (?
, (if (eq (geiser-syntax--read/next-char
) ?
@)
153 (geiser-syntax--read/token
154 `(splice .
,backquote-splice-symbol
))
155 `(unquote .
,backquote-unquote-symbol
)))
156 (?
\" (cons 'string
(geiser-syntax--read/elisp
)))
157 (t (cons 'atom
(geiser-syntax--read/symbol
))))))
159 (defsubst geiser-syntax--read
/match
(&rest tks
)
160 (let ((token (geiser-syntax--read/next-token
)))
161 (if (memq (car token
) tks
) token
162 (error "Unexpected token: %s" token
))))
164 (defsubst geiser-syntax--read
/skip-until
(&rest tks
)
166 (while (and (not (memq (car token
) tks
))
167 (not (eq (car token
) 'eob
)))
168 (setq token
(geiser-syntax--read/next-token
)))
171 (defsubst geiser-syntax--read
/try
(&rest tks
)
173 (tk (ignore-errors (apply 'geiser-syntax--read
/match tks
))))
174 (unless tk
(goto-char p
))
177 (defun geiser-syntax--read/list
()
178 (cond ((geiser-syntax--read/try
'dot
)
179 (let ((tail (geiser-syntax--read)))
180 (geiser-syntax--read/skip-until
'eob
'rparen
)
182 ((geiser-syntax--read/try
'rparen
'eob
) nil
)
183 (t (cons (geiser-syntax--read)
184 (geiser-syntax--read/list
)))))
186 (defun geiser-syntax--read ()
187 (let ((token (geiser-syntax--read/next-token
))
188 (max-lisp-eval-depth (max max-lisp-eval-depth
3000)))
191 (lparen (geiser-syntax--read/list
))
192 (vectorb (apply 'vector
(geiser-syntax--read/list
)))
193 ((quote backquote unquote splice
) (list (cdr token
)
194 (geiser-syntax--read)))
195 (kwd (make-symbol (format ":%s" (cdr token
))))
196 (unprintable (format "#<%s>" (cdr token
)))
197 ((char string atom
) (cdr token
))
198 (boolean (cdr token
))
199 (t (error "Reading scheme syntax: unexpected token: %s" token
)))))
201 (defun geiser-syntax--read-from-string (string &optional start end
)
202 (when (stringp string
)
203 (let* ((start (or start
0))
204 (end (or end
(length string
)))
205 (max-lisp-eval-depth (min 20000
206 (max max-lisp-eval-depth
(- end start
)))))
208 (save-excursion (insert string
))
209 (cons (ignore-errors (geiser-syntax--read)) (point))))))
211 (defun geiser-syntax--form-from-string (s)
212 (car (geiser-syntax--read-from-string s
)))
214 (defsubst geiser-syntax--form-after-point
(&optional boundary
)
215 (let ((geiser-syntax--read/buffer-limit
(and (numberp boundary
) boundary
)))
216 (save-excursion (values (geiser-syntax--read) (point)))))
218 (defun geiser-syntax--mapconcat (fun lst sep
)
219 (cond ((null lst
) "")
220 ((not (listp lst
)) (format ".%s%s" sep
(funcall fun lst
)))
221 ((null (cdr lst
)) (format "%s" (funcall fun
(car lst
))))
223 (funcall fun
(car lst
))
225 (geiser-syntax--mapconcat fun
(cdr lst
) sep
)))))
227 (defun geiser-syntax--display (a)
228 (cond ((null a
) "()")
231 ((geiser-syntax--keywordp a
) (format "#%s" a
))
232 ((symbolp a
) (format "%s" a
))
233 ((equal a
"...") "...")
234 ((stringp a
) (format "%S" a
))
235 ((and (listp a
) (symbolp (car a
))
236 (equal (symbol-name (car a
)) "quote"))
237 (format "'%s" (geiser-syntax--display (cadr a
))))
240 (geiser-syntax--mapconcat 'geiser-syntax--display a
" ")))
241 (t (format "%s" a
))))
246 (defsubst geiser-syntax--symbol-at-point
()
247 (and (not (nth 8 (syntax-ppss)))
248 (car (geiser-syntax--read-from-string (thing-at-point 'symbol
)))))
250 (defsubst geiser-syntax--skip-comment
/string
()
251 (let ((pos (nth 8 (syntax-ppss))))
252 (goto-char (or pos
(point)))
255 (defsubst geiser-syntax--nesting-level
()
256 (or (nth 0 (syntax-ppss)) 0))
258 (defsubst geiser-syntax--in-string-p
()
259 (nth 3 (syntax-ppss)))
261 (defsubst geiser-syntax--pair-length
(p)
262 (if (cdr (last p
)) (1+ (safe-length p
)) (length p
)))
264 (defun geiser-syntax--shallow-form (boundary)
265 (when (looking-at-p "\\s(")
270 (while (< (point) boundary
)
271 (skip-syntax-forward "-<>")
272 (when (<= (point) boundary
)
274 (let ((s (thing-at-point 'symbol
)))
275 (unless (equal "." s
)
276 (push (car (geiser-syntax--read-from-string s
)) elems
))))))
279 (defsubst geiser-syntax--keywordp
(s)
280 (and s
(symbolp s
) (string-match "^:.+" (symbol-name s
))))
282 (defsubst geiser-syntax--symbol-eq
(s0 s1
)
283 (and (symbolp s0
) (symbolp s1
) (equal (symbol-name s0
) (symbol-name s1
))))
285 (defun geiser-syntax--scan-sexps (&optional begin
)
286 (let* ((fst (geiser-syntax--symbol-at-point))
287 (smth (or fst
(not (looking-at-p "[\s \s)\s>\s<\n]"))))
288 (path (and fst
`((,fst
0)))))
290 (while (not (zerop (geiser-syntax--nesting-level)))
291 (let ((boundary (point)))
292 (geiser-syntax--skip-comment/string
)
294 (let ((form (geiser-syntax--shallow-form boundary
)))
295 (when (and (listp form
) (car form
) (symbolp (car form
)))
296 (let* ((len (geiser-syntax--pair-length form
))
297 (pos (if smth
(1- len
) (progn (setq smth t
) len
)))
298 (prev (and (> pos
1) (nth (1- pos
) form
)))
299 (prev (and (geiser-syntax--keywordp prev
)
301 (push `(,(car form
) ,pos
,@prev
) path
)))))))
303 (cons (substring-no-properties (format "%s" (car e
))) (cdr e
)))
306 (defsubst geiser-syntax--binding-form-p
(bfs sbfs f
)
308 (let ((f (symbol-name f
)))
309 (or (member f
'("define" "define*" "define-syntax"
310 "syntax-rules" "lambda" "case-lambda"
311 "let" "let*" "let-values" "let*-values"
312 "letrec" "letrec*" "parameterize"))
316 (defsubst geiser-syntax--binding-form
*-p
(sbfs f
)
318 (let ((f (symbol-name f
)))
319 (or (member f
'("let*" "let*-values" "letrec" "letrec*"))
322 (defsubst geiser-syntax--if-symbol
(x) (and (symbolp x
) x
))
323 (defsubst geiser-syntax--if-list
(x) (and (listp x
) x
))
325 (defsubst geiser-syntax--normalize
(vars)
327 (let ((i (if (listp i
) (car i
) i
)))
328 (and (symbolp i
) (symbol-name i
))))
331 (defun geiser-syntax--linearize (form)
332 (cond ((not (listp form
)) (list form
))
334 (t (cons (car form
) (geiser-syntax--linearize (cdr form
))))))
336 (defun geiser-syntax--scan-locals (bfs sbfs form nesting locals
)
337 (if (or (null form
) (not (listp form
)))
338 (geiser-syntax--normalize locals
)
339 (if (not (geiser-syntax--binding-form-p bfs sbfs
(car form
)))
340 (geiser-syntax--scan-locals bfs sbfs
343 (let* ((head (car form
))
344 (name (geiser-syntax--if-symbol (cadr form
)))
345 (names (if name
(geiser-syntax--if-list (caddr form
))
346 (geiser-syntax--if-list (cadr form
))))
348 (geiser-syntax--binding-form-p bfs sbfs
(car names
))))
349 (rest (if (and name
(not bns
)) (cdddr form
) (cddr form
)))
350 (use-names (and (or rest
352 (geiser-syntax--binding-form*-p sbfs head
))
354 (when name
(push name locals
))
355 (when (geiser-syntax--symbol-eq head
'case-lambda
)
356 (dolist (n (and (> nesting
0) (caar (last form
))))
357 (when n
(push n locals
)))
358 (setq rest
(and (> nesting
0) (cdr form
)))
359 (setq use-names nil
))
360 (when (geiser-syntax--symbol-eq head
'syntax-rules
)
361 (dolist (n (and (> nesting
0) (cdaar (last form
))))
362 (when n
(push n locals
)))
363 (setq rest
(and (> nesting
0) (cdr form
))))
365 (dolist (n (geiser-syntax--linearize names
))
366 (let ((xs (if (and (listp n
) (listp (car n
))) (car n
) (list n
))))
367 (dolist (x xs
) (when x
(push x locals
))))))
368 (dolist (f (butlast rest
))
370 (geiser-syntax--symbol-eq (car f
) 'define
)
372 (push (cadr f
) locals
)))
373 (geiser-syntax--scan-locals bfs sbfs
374 (car (last (or rest names
)))
378 (defun geiser-syntax--locals-around-point (bfs sbfs
)
379 (when (eq major-mode
'scheme-mode
)
381 (let ((sym (unless (geiser-syntax--skip-comment/string
)
382 (thing-at-point 'symbol
))))
383 (skip-syntax-forward "->")
384 (let ((boundary (point))
385 (nesting (geiser-syntax--nesting-level)))
386 (while (not (zerop (geiser-syntax--nesting-level)))
388 (multiple-value-bind (form end
)
389 (geiser-syntax--form-after-point boundary
)
391 (geiser-syntax--scan-locals bfs
398 ;;; Fontify strings as Scheme code:
400 (defun geiser-syntax--font-lock-buffer ()
401 (let ((name " *geiser font lock*"))
402 (or (get-buffer name
)
403 (let ((buffer (get-buffer-create name
)))
405 (let ((geiser-default-implementation
406 (or geiser-default-implementation
407 (car geiser-active-implementations
))))
411 (defun geiser-syntax--scheme-str (str)
413 (set-buffer (geiser-syntax--font-lock-buffer))
416 (let ((font-lock-verbose nil
)) (font-lock-fontify-buffer))
420 (provide 'geiser-syntax
)