Hi Florian, "pelzflorian (Florian Pelz)" <pelzflor...@pelzflorian.de> writes:
> To retain unhygienic references, I am now using datum->syntax instead > of local-eval. It is much better. For example, to make a macro that > increments all numbers in a given program by one: > > (use-modules (ice-9 match)) > (define-syntax one-more > (lambda (x) > (syntax-case x () > ((_ exp) > (datum->syntax > #'exp > (let loop ((y (syntax->datum #'exp))) > (match y > ((? number?) (1+ y)) > ((? list?) (map loop y)) > (else y)))))))) > > (let ((four 4)) > (one-more (* 2 3 four))) > > Yields 48. I hope this is the right approach for rewriting programs. There are some problems above: (1) The first argument to 'datum->syntax' must be an identifier, which is the syntax object corresponding to a symbol. Here, you are passing an entire expression, and in the example usage above, #'exp will be the syntax object corresponding to (* 2 3 hour). Guile should ideally raise an error in this case. (2) The way you are doing things here destroys hygiene within the expression that you are rewriting. You convert the entire expression with 'syntax->datum', process the datum, and then convert the rewritten expression using 'datum->syntax'. The problem here is that 'syntax->datum' discards all of the extra information about lexical environments of identifiers that were kept in the syntax object. This will cause severe problems when 'one-more' is used in combination with other macros, including unintended variable capture. To do this properly, you must do the rewriting on the syntax objects themselves. It's okay to convert a syntax object to a datum to test whether it's a literal number, but the important thing is that all *identifiers* in the rewritten code should be preserved. So, instead of using 'match' on the result of 'syntax->datum', you should instead use 'syntax-case' on the syntax object itself, like this (untested): (let loop ((e #'exp)) (syntax-case e () (num (number? (syntax->datum #'num)) #'(1+ num)) ((x ...) (map loop #'(x ...))) (y #'y))) Finally, I should mention that macro expansion is always done from the outside in, meaning that when 'one-more' is expanded, its operand will not yet have been expanded. In general, this means that it's impossible to comprehend the code within a macro's operands unless the parsing code knows about every macro that might be used within the operands. It's not even possible to know which subparts are expressions and which are other things like variable binding lists. For this reason, I think it's generally a mistake to try to parse code within a macro's operands. It normally only makes sense for macros to inspect the parts of operands that are considered part of the macro's syntax. For example, it makes sense for a 'let' macro to parse its binding list, or for a 'match' macro to parse its patterns and templates, but it does *not* make sense for a macro to try to parse general subexpressions passed to the macro. If you could give me a birds-eye view of what you're trying to do here, I might be able to suggest other approaches. Best, Mark