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-popup
)
15 (require 'geiser-base
)
22 (defmacro geiser-syntax--scheme-indent
(&rest pairs
)
23 `(progn ,@(mapcar (lambda (p)
24 `(put ',(car p
) 'scheme-indent-function
',(cadr p
)))
27 (geiser-syntax--scheme-indent
60 (parameterize-break 1)
81 ;;; A simple scheme reader
83 (defvar geiser-syntax--read
/buffer-limit nil
)
85 (defsubst geiser-syntax--read
/eos
()
87 (and geiser-syntax--read
/buffer-limit
88 (<= geiser-syntax--read
/buffer-limit
(point)))))
90 (defsubst geiser-syntax--read
/next-char
()
91 (unless (geiser-syntax--read/eos
)
95 (defsubst geiser-syntax--read
/token
(token)
96 (geiser-syntax--read/next-char
)
97 (if (listp token
) token
(list token
)))
99 (defsubst geiser-syntax--read
/elisp
()
100 (ignore-errors (read (current-buffer))))
102 (defun geiser-syntax--read/matching
(open close
)
105 (while (and (> count
0)
106 (geiser-syntax--read/next-char
))
107 (cond ((looking-at-p open
) (setq count
(1+ count
)))
108 ((looking-at-p close
) (setq count
(1- count
)))))
109 (buffer-substring-no-properties p
(point))))
111 (defsubst geiser-syntax--read
/unprintable
()
112 (geiser-syntax--read/token
113 (cons 'unprintable
(geiser-syntax--read/matching
"<" ">"))))
115 (defun geiser-syntax--read/skip-comment
()
116 (while (and (geiser-syntax--read/next-char
)
117 (nth 8 (syntax-ppss))))
118 (geiser-syntax--read/next-token
))
120 (defun geiser-syntax--read/next-token
()
121 (skip-syntax-forward "->")
122 (if (geiser-syntax--read/eos
) '(eob)
124 (?\
; (geiser-syntax--read/skip-comment))
125 ((?\
( ?\
[) (geiser-syntax--read/token
'lparen
))
126 ((?\
) ?\
]) (geiser-syntax--read/token
'rparen
))
127 (?.
(if (memq (car (syntax-after (1+ (point)))) '(0 11 12))
128 (geiser-syntax--read/token
'dot
)
129 (cons 'atom
(geiser-syntax--read/elisp
))))
130 (?\
# (case (geiser-syntax--read/next-char
)
132 (?|
(geiser-syntax--read/skip-comment
))
133 (?
: (if (geiser-syntax--read/next-char
)
134 (cons 'kwd
(geiser-syntax--read/elisp
))
136 (?
\\ (cons 'char
(geiser-syntax--read/elisp
)))
137 (?\
( (geiser-syntax--read/token
'vectorb
))
138 (?\
< (geiser-syntax--read/unprintable
))
139 ((?
' ?
` ?
,) (geiser-syntax--read/next-token
))
140 (t (let ((tok (geiser-syntax--read/elisp
)))
141 (if tok
(cons 'atom
(intern (format "#%s" tok
)))
142 (geiser-syntax--read/next-token
))))))
143 (?
\' (geiser-syntax--read/token
'(quote . quote
)))
144 (?\
` (geiser-syntax--read/token
145 `(backquote .
,backquote-backquote-symbol
)))
146 (?
, (if (eq (geiser-syntax--read/next-char
) ?
@)
147 (geiser-syntax--read/token
148 `(splice .
,backquote-splice-symbol
))
149 `(unquote .
,backquote-unquote-symbol
)))
150 (?
\" (cons 'string
(geiser-syntax--read/elisp
)))
151 (t (cons 'atom
(geiser-syntax--read/elisp
))))))
153 (defsubst geiser-syntax--read
/match
(&rest tks
)
154 (let ((token (geiser-syntax--read/next-token
)))
155 (if (memq (car token
) tks
) token
156 (error "Unexpected token: %s" token
))))
158 (defsubst geiser-syntax--read
/skip-until
(&rest tks
)
160 (while (and (not (memq (car token
) tks
))
161 (not (eq (car token
) 'eob
)))
162 (setq token
(geiser-syntax--read/next-token
)))
165 (defsubst geiser-syntax--read
/try
(&rest tks
)
167 (tk (ignore-errors (apply 'geiser-syntax--read
/match tks
))))
168 (unless tk
(goto-char p
))
171 (defun geiser-syntax--read/list
()
172 (cond ((geiser-syntax--read/try
'dot
)
173 (let ((tail (geiser-syntax--read)))
174 (geiser-syntax--read/skip-until
'eob
'rparen
)
176 ((geiser-syntax--read/try
'rparen
'eob
) nil
)
177 (t (cons (geiser-syntax--read)
178 (geiser-syntax--read/list
)))))
180 (defun geiser-syntax--read ()
181 (let ((token (geiser-syntax--read/next-token
))
182 (max-lisp-eval-depth (max max-lisp-eval-depth
3000)))
185 (lparen (geiser-syntax--read/list
))
186 (vectorb (apply 'vector
(geiser-syntax--read/list
)))
187 ((quote backquote unquote splice
) (list (cdr token
)
188 (geiser-syntax--read)))
189 (kwd (intern (format ":%s" (cdr token
))))
190 (unprintable (format "#<%s>" (cdr token
)))
191 ((char string atom
) (cdr token
))
192 (t (error "Reading scheme syntax: unexpected token: %s" token
)))))
194 (defun geiser-syntax--read-from-string (string &optional start end
)
195 (when (stringp string
)
196 (let* ((start (or start
0))
197 (end (or end
(length string
)))
198 (max-lisp-eval-depth (max max-lisp-eval-depth
(- end start
))))
200 (save-excursion (insert string
))
201 (cons (ignore-errors (geiser-syntax--read)) (point))))))
203 (defsubst geiser-syntax--form-after-point
(&optional boundary
)
204 (let ((geiser-syntax--read/buffer-limit
(and (numberp boundary
) boundary
)))
205 (save-excursion (values (geiser-syntax--read) (point)))))
210 (defsubst geiser-syntax--skip-comment
/string
()
211 (goto-char (or (nth 8 (syntax-ppss)) (point))))
213 (defsubst geiser-syntax--nesting-level
()
214 (or (nth 0 (syntax-ppss)) 0))
216 (defsubst geiser-syntax--pair-length
(p)
217 (if (cdr (last p
)) (1+ (safe-length p
)) (length p
)))
219 (defun geiser-syntax--scan-sexps (&optional begin
)
220 (let* ((fst (symbol-at-point))
221 (smth (or fst
(not (looking-at-p "[\\s \\s)\\s>\\s<\n]"))))
222 (path (and fst
`((,fst
0)))))
224 (geiser-syntax--skip-comment/string
)
225 (while (not (zerop (geiser-syntax--nesting-level)))
226 (let ((boundary (1+ (point))))
229 (nth-value 0 (geiser-syntax--form-after-point boundary
))))
230 (when (and (listp form
) (car form
) (symbolp (car form
)))
231 (let* ((len (geiser-syntax--pair-length form
))
232 (pos (if smth
(1- len
) (progn (setq smth t
) len
)))
233 (prev (and (> pos
1) (nth (1- pos
) form
)))
234 (prev (and (keywordp prev
) (list prev
))))
235 (push `(,(car form
) ,pos
,@prev
) path
)))))))
238 (defsubst geiser-syntax--binding-form-p
(bfs sbfs f
)
239 (or (memq f
'(define define
* lambda let let
* letrec
))
243 (defsubst geiser-syntax--binding-form
*-p
(sbfs f
)
244 (or (eq 'let
* f
) (memq f sbfs
)))
246 (defun geiser-syntax--scan-locals (bfs sbfs form partial locals
)
247 (flet ((if-symbol (x) (and (symbolp x
) x
))
248 (if-list (x) (and (listp x
) x
))
249 (normalize (vars) (mapcar (lambda (i) (if (listp i
) (car i
) i
)) vars
)))
250 (let ((form (if (listp form
) (normalize form
) form
)))
251 (cond ((or (null form
) (not (listp form
))) (normalize locals
))
252 ((not (geiser-syntax--binding-form-p bfs sbfs
(car form
)))
253 (geiser-syntax--scan-locals bfs sbfs
254 (car (last form
)) partial locals
))
256 (let* ((head (car form
))
257 (name (if-symbol (cadr form
)))
258 (names (if name
(if-list (caddr form
))
259 (if-list (cadr form
))))
260 (rest (if name
(cdddr form
) (cddr form
)))
263 (geiser-syntax--binding-form*-p sbfs
265 (when name
(push name locals
))
266 (when use-names
(dolist (n names
) (push n locals
)))
267 (dolist (f (butlast rest
))
268 (when (and (listp f
) (eq (car f
) 'define
))
269 (push (cadr f
) locals
)))
270 (geiser-syntax--scan-locals bfs sbfs
271 (car (last (or rest names
)))
275 (defun geiser-syntax--locals-around-point (bfs sbfs
)
276 (when (eq major-mode
'scheme-mode
)
278 (geiser-syntax--skip-comment/string
)
279 (let ((boundary (point)))
280 (while (not (zerop (geiser-syntax--nesting-level)))
282 (multiple-value-bind (form end
)
283 (geiser-syntax--form-after-point boundary
)
284 (geiser-syntax--scan-locals bfs sbfs form
(> end boundary
) '()))))))
287 ;;; Fontify strings as Scheme code:
289 (geiser-popup--define syntax
" *geiser syntax analyst*" scheme-mode
)
291 (defun geiser-syntax--font-lock-buffer ()
292 (let ((name " *geiser font lock*"))
293 (or (get-buffer name
)
294 (let ((buffer (get-buffer-create name
)))
299 (defun geiser-syntax--scheme-str (str)
301 (set-buffer (geiser-syntax--font-lock-buffer))
304 (let ((font-lock-verbose nil
)) (font-lock-fontify-buffer))
308 (provide 'geiser-syntax
)
309 ;;; geiser-syntax.el ends here