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
)
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
64 (parameterize-break 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 (let ((kw (geiser-syntax--impl-kws geiser-impl--implementation
)))
101 (when kw
(font-lock-add-keywords nil kw
))))
104 ;;; A simple scheme reader
106 (defvar geiser-syntax--read
/buffer-limit nil
)
108 (defsubst geiser-syntax--read
/eos
()
110 (and geiser-syntax--read
/buffer-limit
111 (<= geiser-syntax--read
/buffer-limit
(point)))))
113 (defsubst geiser-syntax--read
/next-char
()
114 (unless (geiser-syntax--read/eos
)
118 (defsubst geiser-syntax--read
/token
(token)
119 (geiser-syntax--read/next-char
)
120 (if (listp token
) token
(list token
)))
122 (defsubst geiser-syntax--read
/elisp
()
123 (ignore-errors (read (current-buffer))))
125 (defun geiser-syntax--read/symbol
()
126 (with-syntax-table scheme-mode-syntax-table
127 (when (re-search-forward "\\(\\sw\\|\\s_\\)+" nil t
)
128 (make-symbol (match-string 0)))))
130 (defun geiser-syntax--read/matching
(open close
)
133 (while (and (> count
0)
134 (geiser-syntax--read/next-char
))
135 (cond ((looking-at-p open
) (setq count
(1+ count
)))
136 ((looking-at-p close
) (setq count
(1- count
)))))
137 (buffer-substring-no-properties p
(point))))
139 (defsubst geiser-syntax--read
/unprintable
()
140 (geiser-syntax--read/token
141 (cons 'unprintable
(geiser-syntax--read/matching
"<" ">"))))
143 (defun geiser-syntax--read/skip-comment
()
144 (while (and (geiser-syntax--read/next-char
)
145 (nth 8 (syntax-ppss))))
146 (geiser-syntax--read/next-token
))
148 (defun geiser-syntax--read/next-token
()
149 (skip-syntax-forward "->")
150 (if (geiser-syntax--read/eos
) '(eob)
152 (?\
; (geiser-syntax--read/skip-comment))
153 ((?\
( ?\
[) (geiser-syntax--read/token
'lparen
))
154 ((?\
) ?\
]) (geiser-syntax--read/token
'rparen
))
155 (?.
(if (memq (car (syntax-after (1+ (point)))) '(0 11 12))
156 (geiser-syntax--read/token
'dot
)
157 (cons 'atom
(geiser-syntax--read/elisp
))))
158 (?\
# (case (geiser-syntax--read/next-char
)
160 (?|
(geiser-syntax--read/skip-comment
))
161 (?
: (if (geiser-syntax--read/next-char
)
162 (cons 'kwd
(geiser-syntax--read/elisp
))
164 (?
\\ (cons 'char
(geiser-syntax--read/elisp
)))
165 (?\
( (geiser-syntax--read/token
'vectorb
))
166 (?\
< (geiser-syntax--read/unprintable
))
167 ((?
' ?
` ?
,) (geiser-syntax--read/next-token
))
168 (t (let ((tok (geiser-syntax--read/symbol
)))
169 (if tok
(cons 'atom
(intern (format "#%s" tok
)))
170 (geiser-syntax--read/next-token
))))))
171 (?
\' (geiser-syntax--read/token
'(quote . quote
)))
172 (?\
` (geiser-syntax--read/token
173 `(backquote .
,backquote-backquote-symbol
)))
174 (?
, (if (eq (geiser-syntax--read/next-char
) ?
@)
175 (geiser-syntax--read/token
176 `(splice .
,backquote-splice-symbol
))
177 `(unquote .
,backquote-unquote-symbol
)))
178 (?
\" (cons 'string
(geiser-syntax--read/elisp
)))
179 (t (cons 'atom
(geiser-syntax--read/symbol
))))))
181 (defsubst geiser-syntax--read
/match
(&rest tks
)
182 (let ((token (geiser-syntax--read/next-token
)))
183 (if (memq (car token
) tks
) token
184 (error "Unexpected token: %s" token
))))
186 (defsubst geiser-syntax--read
/skip-until
(&rest tks
)
188 (while (and (not (memq (car token
) tks
))
189 (not (eq (car token
) 'eob
)))
190 (setq token
(geiser-syntax--read/next-token
)))
193 (defsubst geiser-syntax--read
/try
(&rest tks
)
195 (tk (ignore-errors (apply 'geiser-syntax--read
/match tks
))))
196 (unless tk
(goto-char p
))
199 (defun geiser-syntax--read/list
()
200 (cond ((geiser-syntax--read/try
'dot
)
201 (let ((tail (geiser-syntax--read)))
202 (geiser-syntax--read/skip-until
'eob
'rparen
)
204 ((geiser-syntax--read/try
'rparen
'eob
) nil
)
205 (t (cons (geiser-syntax--read)
206 (geiser-syntax--read/list
)))))
208 (defun geiser-syntax--read ()
209 (let ((token (geiser-syntax--read/next-token
))
210 (max-lisp-eval-depth (max max-lisp-eval-depth
3000)))
213 (lparen (geiser-syntax--read/list
))
214 (vectorb (apply 'vector
(geiser-syntax--read/list
)))
215 ((quote backquote unquote splice
) (list (cdr token
)
216 (geiser-syntax--read)))
217 (kwd (intern (format ":%s" (cdr token
))))
218 (unprintable (format "#<%s>" (cdr token
)))
219 ((char string atom
) (cdr token
))
220 (t (error "Reading scheme syntax: unexpected token: %s" token
)))))
222 (defun geiser-syntax--read-from-string (string &optional start end
)
223 (when (stringp string
)
224 (let* ((start (or start
0))
225 (end (or end
(length string
)))
226 (max-lisp-eval-depth (max max-lisp-eval-depth
(- end start
))))
228 (save-excursion (insert string
))
229 (cons (ignore-errors (geiser-syntax--read)) (point))))))
231 (defsubst geiser-syntax--form-after-point
(&optional boundary
)
232 (let ((geiser-syntax--read/buffer-limit
(and (numberp boundary
) boundary
)))
233 (save-excursion (values (geiser-syntax--read) (point)))))
238 (defsubst geiser-syntax--symbol-at-point
()
239 (and (not (nth 8 (syntax-ppss)))
240 (let ((s (thing-at-point 'symbol
)))
243 (make-symbol (substring-no-properties s
))))))
245 (defsubst geiser-syntax--skip-comment
/string
()
246 (let ((pos (nth 8 (syntax-ppss))))
247 (goto-char (or pos
(point)))
250 (defsubst geiser-syntax--nesting-level
()
251 (or (nth 0 (syntax-ppss)) 0))
253 (defsubst geiser-syntax--pair-length
(p)
254 (if (cdr (last p
)) (1+ (safe-length p
)) (length p
)))
256 (defun geiser-syntax--shallow-form (boundary)
257 (when (looking-at-p "\\s(")
262 (while (< (point) boundary
)
263 (skip-syntax-forward "-<>")
264 (when (<= (point) boundary
)
266 (let ((s (thing-at-point 'symbol
)))
267 (cond ((not s
) (push s elems
))
268 ((not (equal "." s
)) (push (make-symbol s
) elems
)))))))
271 (defun geiser-syntax--scan-sexps (&optional begin
)
272 (let* ((fst (geiser-syntax--symbol-at-point))
273 (smth (or fst
(not (looking-at-p "[\s \s)\s>\s<\n]"))))
274 (path (and fst
`((,fst
0)))))
276 (while (not (zerop (geiser-syntax--nesting-level)))
277 (let ((boundary (point)))
278 (geiser-syntax--skip-comment/string
)
280 (let ((form (geiser-syntax--shallow-form boundary
)))
281 (when (and (listp form
) (car form
) (symbolp (car form
)))
282 (let* ((len (geiser-syntax--pair-length form
))
283 (pos (if smth
(1- len
) (progn (setq smth t
) len
)))
284 (prev (and (> pos
1) (nth (1- pos
) form
)))
285 (prev (and (keywordp prev
) (list prev
))))
286 (push `(,(car form
) ,pos
,@prev
) path
)))))))
287 (mapcar (lambda (e) (cons (format "%s" (car e
)) (cdr e
)))
290 (defsubst geiser-syntax--binding-form-p
(bfs sbfs f
)
291 (or (memq f
'(define define
* define-syntax define-syntax-rule
292 lambda let let
* letrec parameterize
))
296 (defsubst geiser-syntax--binding-form
*-p
(sbfs f
)
297 (or (eq 'let
* f
) (memq f sbfs
)))
299 (defsubst geiser-syntax--if-symbol
(x) (and (symbolp x
) x
))
300 (defsubst geiser-syntax--if-list
(x) (and (listp x
) x
))
301 (defsubst geiser-syntax--normalize
(vars)
302 (mapcar (lambda (i) (if (listp i
) (car i
) i
)) vars
))
304 (defun geiser-syntax--linearize (form)
305 (cond ((not (listp form
)) (list form
))
307 (t (cons (car form
) (geiser-syntax--linearize (cdr form
))))))
309 (defun geiser-syntax--scan-locals (bfs sbfs form partial locals
)
310 (if (or (null form
) (not (listp form
)))
311 (geiser-syntax--normalize locals
)
312 (if (not (geiser-syntax--binding-form-p bfs sbfs
(car form
)))
313 (geiser-syntax--scan-locals bfs sbfs
314 (car (last form
)) partial locals
)
315 (let* ((head (car form
))
316 (name (geiser-syntax--if-symbol (cadr form
)))
317 (names (if name
(geiser-syntax--if-list (caddr form
))
318 (geiser-syntax--if-list (cadr form
))))
319 (rest (if name
(cdddr form
) (cddr form
)))
322 (geiser-syntax--binding-form*-p sbfs
324 (when name
(push name locals
))
326 (dolist (n (geiser-syntax--linearize names
))
328 (dolist (f (butlast rest
))
329 (when (and (listp f
) (eq (car f
) 'define
))
330 (push (cadr f
) locals
)))
331 (geiser-syntax--scan-locals bfs sbfs
332 (car (last (or rest names
)))
336 (defun geiser-syntax--locals-around-point (bfs sbfs
)
337 (when (eq major-mode
'scheme-mode
)
339 (let* ((sym (unless (geiser-syntax--skip-comment/string
)
342 (while (not (zerop (geiser-syntax--nesting-level)))
344 (multiple-value-bind (form end
)
345 (geiser-syntax--form-after-point boundary
)
347 (geiser-syntax--scan-locals bfs sbfs form
348 (> end boundary
) '())))))))
351 ;;; Fontify strings as Scheme code:
353 (defun geiser-syntax--font-lock-buffer ()
354 (let ((name " *geiser font lock*"))
355 (or (get-buffer name
)
356 (let ((buffer (get-buffer-create name
)))
358 (let ((geiser-default-implementation
359 (or geiser-default-implementation
360 (car geiser-active-implementations
))))
364 (defun geiser-syntax--scheme-str (str)
366 (set-buffer (geiser-syntax--font-lock-buffer))
369 (let ((font-lock-verbose nil
)) (font-lock-fontify-buffer))
373 (provide 'geiser-syntax
)
374 ;;; geiser-syntax.el ends here