Oh, and note that I changed define-dsl-syntax to _not_ use define-syntax-rule. That's important, if you're going to use the value of variables used in macro expansion. The define-syntax-rule form protects its output; you can't disassemble it and then run parts of the result. You can print them, quote them as s-expressions, and do other things that are sort of "superficial" from a macro standpoint, but you can't do anything that runs them or inspects their bindings. If you use regular define-syntax and never call the "syntax-protect" function, you won't run into this problem.
Carl Eastlund On Sun, Jan 26, 2014 at 6:34 PM, Carl Eastlund <carl.eastl...@gmail.com>wrote: > You can do it like this: > > https://gist.github.com/carl-eastlund/8640925 > > Basically, you quasiquote the result of the expansion, and then you > unquote variables when you hit them so that they are evaluated normally. > Anything with an unbound variable will fail at expansion time. > > Carl Eastlund > > > On Sun, Jan 26, 2014 at 6:02 PM, Scott Klarenbach <sc...@pointyhat.ca>wrote: > >> Sorry, I was combining all the examples into one for brevity. x has 2 >> possible bindings (one in the module and one in the macro), y is bound in >> the module and closes over the macro, and z is unbound and therefore should >> result in an error since nothing can be done with it. >> >> ;; example 1 (as per our earlier discussion) >> (define-dsl-syntax (pred? x) (or (= 2 x) (> x 3))) >> (recursive-expand (pred? 1)) >> '(or (= 2 1) (> 1 3)) >> >> ;; example 2 (capturing a reference bound by the enclosing environment) >> (define y 3) >> (define-dsl-syntax (pred? x) (or (= 2 x) (> x y))) >> (recursive-expand (pred? 1)) >> '(or (= 2 1) (> 1 3)) >> >> ;; example 3 (ensuring some degree of hygiene, note the two bindings for >> x) >> (define x 3) >> (define y 3) >> (define-dsl-syntax (pred? x) (or (= 2 x) (> x y))) >> (recursive-expand (pred? 1)) >> '(or (= 2 1) (> 1 3)) >> >> ;; example 4, (I'll throw in this case, or replace the quoted output with >> ??? for each unbound id >> (define-dsl-syntax (pred? x) (or (= 2 x) (> x z))) >> (recursive-expand (pred? 1)) >> >> unbound identifier: z at pred? ;; or maybe >> '(or (= 2 1) (> 1 ???)) >> >> Just to rephrase, my "expansion" is essentially just a >> "procedure->string" operation, or a custom-write, that I will use to target >> sql or something else. Any use of pred? at runtime within racket will run >> fine as is. >> >> Thanks, >> Scott. >> >> >> On Sun, Jan 26, 2014 at 2:47 PM, Carl Eastlund >> <carl.eastl...@gmail.com>wrote: >> >>> Scott -- I don't understand exactly what you're asking, at least not >>> based on the example you wrote. You defined x and y, but the error is >>> about z. Is this just a typo? Or are you expecting a value for z to come >>> from somewhere? >>> >>> Carl Eastlund >>> >>> >>> On Sun, Jan 26, 2014 at 1:01 PM, Scott Klarenbach <sc...@pointyhat.ca>wrote: >>> >>>> Thanks a lot Carl...this is very enlightening. If I could impose one >>>> last question before I go off and digest everything: >>>> >>>> What is the "correct" approach to capturing the runtime values of any >>>> references that may be bound by the enclosing environment, for splicing >>>> into the final recursively-expanded expression? >>>> >>>> ;; example 1 >>>> >>>> (define y 3) >>>> (define x 3) >>>> (define-dsl-syntax (pred? x) (or (= 2 x) (> x y))) >>>> (define-dsl-syntax (bad-pred? x) (or (= 2 x) (> x z))) >>>> >>>> (recursive-expand (pred? 1)) >>>> '(or (= 2 1) (> 1 3)) >>>> >>>> (recursive-expand (bad-pred? 1)) >>>> >> unbound identifier: z >>>> >>>> My naive approach was to collect all the identifiers in the >>>> expression body that had bindings, compare them to the argument list of the >>>> macro with bound-identifier=? to see which ones were explicitly introduced >>>> by the user, and then eval the remaining ones at runtime in a second step >>>> in order to splice them in. >>>> >>>> I haven't tried this, and am sure people are cringing just by reading >>>> it, lol. I know there are tons of features like marking and syntax >>>> properties and origins, etc which I don't yet understand, and which may >>>> provide a more durable solution. >>>> >>>> If needbe, I could explicitly provide to the macro the bindings I wish >>>> to capture, like postgresql does with query params...ie, >>>> >>>> (define-dsl-syntax (pred? x) (or (= 2 x) (> x $1)) #:capture (y)) ;; or >>>> something >>>> >>>> but for obvious reasons it is much better if these expressions just >>>> expanded and automatically captured any referenced values in the same way >>>> as would happen at runtime. >>>> >>>> Thanks a lot. >>>> >>>> Scott. >>>> >>>> >>>> >>>> >>>> On Sat, Jan 25, 2014 at 6:00 PM, Carl Eastlund <carl.eastl...@gmail.com >>>> > wrote: >>>> >>>>> Scott, >>>>> >>>>> I see what you're doing now. You're not actually trying to use macro >>>>> expansion at all; you're just using local-expand to substitute the >>>>> definition of pred? where it occurs, so that you can make its macro >>>>> definition also serve as its DSL definition. That's sensible, but >>>>> local-expand is still doing more than you want it to. That's why I put in >>>>> all the expansion caveats -- not because you necessarily meant to do full >>>>> expansion, but because local-expand is pretty explicitly built for full >>>>> expansion, and always tries to push as far as it can. Any time the >>>>> caveats >>>>> about expansion don't apply, local-expand is probably a bigger gun than >>>>> you >>>>> need. >>>>> >>>>> Where local-expand is going to bite you is when the definition of >>>>> pred? uses a macro at its top level. For instance: >>>>> >>>>> (define-syntax-rule (pred? x) (or (< x 3) (> x 7))) >>>>> >>>>> Here, local-expand is going to expand the use of (or ...), and any >>>>> macro that (or ...) produces at its top level, until you reach a core form >>>>> as the main expression, or something you've put in an explicit stop list. >>>>> That's not what you want, as I understand it -- you only want to expand >>>>> pred?. >>>>> >>>>> So what to do when you want to apply one macro, but not perform >>>>> general expansion? Extract its transformer using syntax-local-value, and >>>>> apply it to the expression. You probably also want to apply a syntax mark >>>>> before and after transformation, just to simulate the base level of >>>>> hygiene >>>>> the macro may be relying on. It might not be necessary for simple >>>>> definitions, but it can't hurt. >>>>> >>>>> I wrote up some code that does this, along with a test showing that it >>>>> won't expand "or" too far. It's also reasonably hygienic -- it won't be >>>>> confused if someone defines a different macro named "pred?", for example. >>>>> I don't know if that's a concern, but again, it can't hurt. Anyway, you >>>>> can find what I wrote here: >>>>> https://gist.github.com/carl-eastlund/8626893 >>>>> >>>>> Carl Eastlund >>>>> >>>>> On Fri, Jan 24, 2014 at 1:30 PM, Scott Klarenbach >>>>> <sc...@pointyhat.ca>wrote: >>>>> >>>>>> Just an update, I was able to make this work. >>>>>> >>>>>> #lang racket >>>>>> (require (for-syntax racket/syntax syntax/stx)) >>>>>> >>>>>> (define-syntax-rule (pred? x) (> 3 x)) >>>>>> >>>>>> (define-for-syntax (recursive-expand stx) >>>>>> (let loop ([l (syntax->list stx)]) >>>>>> (cond [(stx-null? l) l] >>>>>> [(stx-pair? (stx-car l)) >>>>>> (cons (loop (stx-car l)) (loop (stx-cdr l)))] >>>>>> [(equal? 'pred? (syntax->datum (stx-car l))) >>>>>> (local-expand (cons (stx-car l) (loop (stx-cdr l))) 'expression >>>>>> #f)] ;; this works >>>>>> [else >>>>>> (cons (stx-car l) (loop (stx-cdr l)))]))) >>>>>> >>>>>> (define-syntax (test stx) >>>>>> (syntax-case stx () >>>>>> [(_ x) >>>>>> (with-syntax ([expanded (recursive-expand #'x)]) >>>>>> #''expanded)])) >>>>>> >>>>>> (module+ test >>>>>> (require rackunit) >>>>>> (check-equal? (test (or (< 10 x) (pred? y))) >>>>>> '(or (< 10 x) (> 3 y)))) >>>>>> >>>>>> The code I couldn't figure out last night was: >>>>>> (local-expand (cons (stx-car l) (loop (stx-cdr l))) 'expression #f)] >>>>>> >>>>>> Thanks. >>>>>> >>>>>> -- >>>>>> Talk to you soon, >>>>>> >>>>>> Scott Klarenbach >>>>>> >>>>>> PointyHat Software Corp. >>>>>> www.pointyhat.ca >>>>>> p 604-568-4280 >>>>>> e sc...@pointyhat.ca >>>>>> 200-1575 W. Georgia >>>>>> Vancouver, BC V6G2V3 >>>>>> >>>>>> _______________________________________ >>>>>> To iterate is human; to recur, divine >>>>>> >>>>> >>>>> >>>> >>>> >>>> -- >>>> Talk to you soon, >>>> >>>> Scott Klarenbach >>>> >>>> PointyHat Software Corp. >>>> www.pointyhat.ca >>>> p 604-568-4280 >>>> e sc...@pointyhat.ca >>>> 200-1575 W. Georgia >>>> Vancouver, BC V6G2V3 >>>> >>>> _______________________________________ >>>> To iterate is human; to recur, divine >>>> >>> >>> >> >> >> -- >> Talk to you soon, >> >> Scott Klarenbach >> >> PointyHat Software Corp. >> www.pointyhat.ca >> p 604-568-4280 >> e sc...@pointyhat.ca >> 200-1575 W. Georgia >> Vancouver, BC V6G2V3 >> >> _______________________________________ >> To iterate is human; to recur, divine >> > >
____________________ Racket Users list: http://lists.racket-lang.org/users