On 2013-06-02, at 11:06 AM, Nathan Sorenson takeoutweight@hotmail.com wrote:
There are a few solutions I can think of. But let me ask this first, can't you simply avoid wrapping the head of special forms and macros? That location information will be (mostly) ignored anyway. What matters is the location of the complete expression.
Yes, avoiding wrapping macro symbols is definitely one option. I would like to preserve the ability to do the compilation on the Java VM Clojure side, so I would have to maintain a blacklist of scheme macro symbols and explicitly avoid adding source info to them. I would not be able to introspect gambit's compile-time environment and see which symbols named macros.
One complication is that I provide an "in-line scheme" macro, scm*, for Clojure code, (similar to c-lambda for in-line C). This would enable users to define and use arbitrary scheme macros, so my macro blacklist would not work here; users might define and use new scheme macros that aren't on the blacklist. I could avoid attaching source macros to any head-position symbols generated via scm*, I suppose... Maybe I will do this for now.
It would be nice if clojure-scheme could indiscriminately tag every form without foreknowledge of what the symbol names. That's why my first impulse was reader evaluation of some sort -- it simplifies the code generation process.
In that case the simplest solution I see is for the source-at macro to recursively traverse its subform. That way subforms that are not expressions will also get processed correctly. See the attached code.
As you say the reader could also be doing this job and I think this would be cleaner. However it would be necessary to invent a new syntactic form for this and I don't yet have a bright idea. Alternatively, you could enable the #. reader macro, that allows read-time evaluation, and generate a #. that explicitly tags the expression.
Marc
;;; File: source-at.scm
(##define-syntax source-at (lambda (source-at-form)
(define (unwrap x) (if (##source? x) (##source-code x) x))
(define (wrap path line col code) (##make-source code (##make-locat (##path->container path) (##make-filepos (- line 1) (- col 1) 0))))
(define (rewrap-list code) (cond ((null? code) code) ((pair? code) (cons (rewrap (car code)) (rewrap-list (cdr code)))) (else (rewrap code))))
(define (rewrap form)
(define (return code) (if (##source? form) (##make-source code (##source-locat form)) code))
(let ((code (unwrap form))) (cond ((pair? code) (if (eq? (unwrap (car code)) 'source-at)
(rewrap (apply (lambda (_ path line col subform) (wrap (unwrap path) (unwrap line) (unwrap col) (unwrap (rewrap subform)))) code))
(return (rewrap-list code))))
((vector? code) (return (list->vector (map rewrap (vector->list code)))))
(else form))))
(rewrap source-at-form)))
;; test it:
(define (f x y) (cons 'hello (source-at "foo.scm" 10 1 ;; file line column ((source-at "foo.scm" 11 2 if) (source-at "foo.scm" 12 3 (< x 0)) (source-at "foo.scm" 13 4 ((source-at "foo.scm" 14 5 quote) (source-at "foo.scm" 15 6 #((source-at "foo.scm" 16 7 111) (source-at "foo.scm" 17 8 222))))) (source-at "foo.scm" 18 9 (/ x y))))))
(pp (f -1 2)) (pp (f 100 0))
;; Output: ;; ;; % gsi source-at.scm ;; (hello . #(111 222)) ;; *** ERROR IN f, "foo.scm"@18.9 -- Divide by zero ;; (/ 100 0)