cisco / ChezScheme

Chez Scheme
Apache License 2.0
6.91k stars 982 forks source link

No macro expansion in define-ftype form #726

Closed mbakhterev closed 9 months ago

mbakhterev commented 9 months ago

Greetings. I am writing some low-level code for some device's configuration manipulation. There are many bit fields and I would rather not to repeat always the (xxx unsigned 1) expression in the bits form and not to compute the padding manually. So I made the macro

  (define-syntax u32-bits
    (lambda (x)
      (syntax-case x ()
        ((k fields ...)           
         (let ((n (length (syntax->datum #'(fields ...)))))
           (cond ((< n 32) #`(bits (fields unsigned 1) ...
                                   (_padding_ unsigned #,(- 32 n))))
                 ((= n 32) #`(bits (fields unsigned 1) ...))
                 (else (syntax-error x "no more 32 fields are allowed"))))))))

which works when being used standalone

> (expand '(u32-bits a b c d))
(bits
  [a unsigned 1]
  [b unsigned 1]
  [c unsigned 1]
  [d unsigned 1]
  [_padding_ unsigned 28])

But it does not expand inside define-ftype form.

> (define-ftype T (struct (field (u32-bits a b c d))))
Exception: invalid ftype (u32-bits a b c d)

Is that a feature or an issue? And if that is a feature, how can the generation of the parts of the ftype description be automated?

-- With thanks in advance, Mike.

jltaylor-us commented 9 months ago

Syntax expansion is an iterative "outside-in" process. A syntax transformer may reproduce some of its input in its output, in which case the next iteration of syntax expansion will then work on that result (expanding any syntax it contains). But, it can also deconstruct that input, perform arbitrary computation, and then output something completely new. In that case, there is nothing that will have performed any syntax expansion on the pieces of the input. Since define-ftype is itself syntax, no syntax expansion is performed on its parameters before they are deconstructed by the define-ftype implementation.

In order to do what you are trying to do, you have to replace define-ftype with your own syntax that processes the parameters to perform the substitution(s) you want and emits a call to the build-in define-ftype with the expanded parameters. Ideally you'd deconstruct the parameters in the same way as define-ftype and perform the extra bit of work only at the point(s) in the grammar where it makes sense for your new u32-bits term to appear, but in practice you can get away with just recursively processing the list structure and expanding u32-bits wherever you run into it.

mbakhterev commented 9 months ago

Syntax expansion is an iterative "outside-in" process. A syntax transformer may reproduce some of its input in its output, in which case the next iteration of syntax expansion will then work on that result (expanding any syntax it contains). But, it can also deconstruct that input, perform arbitrary computation, and then output something completely new. In that case, there is nothing that will have performed any syntax expansion on the pieces of the input. Since define-ftype is itself syntax, no syntax expansion is performed on its parameters before they are deconstructed by the define-ftype implementation.

Oh! I see now. Thank you for elaboration and making it crystal clear!