unofficial mirror of guile-devel@gnu.org 
 help / color / mirror / Atom feed
From: Frank Terbeck <ft@bewatermyfriend.org>
To: guile-devel@gnu.org
Subject: Recursive Macros generating Definitions
Date: Mon, 03 Oct 2022 13:32:59 +0200	[thread overview]
Message-ID: <87zgedkuqc.fsf@ft.bewatermyfriend.org> (raw)

Good day, good people!

There might  be a bug  in recursive macro  expansion, at least  when the
definition of parameters, using (define …) and similar is involved. Here
is a slightly simplified example.

The purpose  of this macro  is to define a  couple of short-hands  for a
generic encoder/decoder pair  of functions. The intention is  to call it
like this:

  (generate-shorthands (unsigned-integer twos-complement zig-zag)
                       (32 64 128 256 512))

…to generate 5*3*2 = 30 functions,  that call the generic functions with
the proper concrete  arguments. The macro is  implemented recursively to
generate all desired combinations.

If called like that, this implementation generates names like:

  varint:sint32-decode-ea351ae5fca3566

This seems to be connected to the recursiveness of the macro. If calling
the base case manually (see example at the end to reproduce), the inten-
ded name is generated:

  varint:sint32-decode

This happens  with guile 3.0.5,  3.0.8 as well  as the current  git main
branch HEAD. It does not seem to happen in 2.0.0.

I've bisected this down to:

    commit de41e56492666801078e73860a358e1c63cbc8c2
    Author: Andy Wingo <wingo@pobox.com>
    Date:   Fri Nov 4 19:34:22 2011 +0100

    hygienically rename macro-introduced bindings, reproducibly

    * module/ice-9/psyntax.scm (chi-top-sequence): Detect bindings to
    identifiers introduced by macros.  In that case, in order to preserve
    hygiene, uniquify the variable's name, but in a way that is
    reproduceable (i.e., yields the same uniquified name after a
    recompile).

    module/ice-9/psyntax.scm | 22 ++++++++++++++++++++--
    1 file changed, 20 insertions(+), 2 deletions(-)


When looking at  this, I also saw the following,  which might be related
if ‘syntax-rules’ is implemented using  ‘syntax-case’ (I didn't check if
this is the case):

    (define-syntax-rule (foobar n) (define quux n))
    ,exp (foobar 23)
  → (define quux-ea7bdcf8675f4a4 23)

Here's the code, that  can be loaded into a REPL  and example REPL macro
expansion calls to reproduce the issue:


(use-modules (ice-9 match))

(define-syntax generate-shorthands
  (lambda (x)
    ;; This is a helper that makes a name depending on semantics and width. It is
    ;; completely inconsequential to the issue and can be ignored.
    (define (make-base-name s w)
      (symbol-append 'varint:
                     (match (syntax->datum s)
                       ('unsigned-integer 'uint)
                       ('twos-complement  'int)
                       ('zig-zag          'sint))
                     (string->symbol (number->string (syntax->datum w)))))

    ;; The first two cases of this syntax-case recur on generate-shorthands, to
    ;; iterate on the list input to generate all desired combinations.
    (syntax-case x ()
      ;; (_ LIST-OF-SEMANTICS-SYMBOLS LIST-OF-WIDTH-LITERALS)
      ((_ (sems ...) (widths ...))
       (format #t "# Outer~%")  ;; (format #t …) returns #t, so it can be
                                ;; called in guard position to get a trace.
       #'(begin (generate-shorthands sems (widths ...)) ...))

      ;; (_ SEMANTICS-SYMBOL LIST-OF-WIDTH-LITERALS)
      ((_ sem (widths ...))
       (and (format #t "# Middle~%")
            (identifier? #'sem))
       #'(begin (generate-shorthands sem widths) ...))

      ;; Base case:
      ;; (_ SEMANTICS-SYMBOL WIDTH-LITERAL)
      ((_ s w)
       (and (format #t "# Inner~%")
            (identifier? #'s)
            (integer? (syntax->datum #'w)))
       (let ((base (make-base-name #'s #'w)))
         (with-syntax ((enc (datum->syntax x (symbol-append base '-encode)))
                       (dec (datum->syntax x (symbol-append base '-decode))))
           #'(begin (define (dec bv) (varint-decode bv w s))
                    (define (enc  n) (varint-encode  n w s)))))))))


;; Example expansions:

;; ,exp (generate-shorthands (zig-zag) (32))
;; # Outer
;; # Middle
;; # Inner
;; (begin (define (varint:sint32-decode-ea351ae5fca3566 bv) (varint-decode bv 32 zig-zag))
;;        (define (varint:sint32-encode-e47ba11af8c0627  n) (varint-encode  n 32 zig-zag)))

;; ,exp (generate-shorthands zig-zag (32))
;; # Middle
;; # Inner
;; (begin (define (varint:sint32-decode-ea351ae5fca3566 bv) (varint-decode bv 32 zig-zag))
;;        (define (varint:sint32-encode-e47ba11af8c0627  n) (varint-encode  n 32 zig-zag)))


;; ,exp (generate-shorthands zig-zag 32)
;; # Inner
;; (begin (define (varint:sint32-decode bv) (varint-decode bv 32 zig-zag))
;;        (define (varint:sint32-encode  n) (varint-encode  n 32 zig-zag)))



             reply	other threads:[~2022-10-03 11:32 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-10-03 11:32 Frank Terbeck [this message]
2022-10-03 12:48 ` Recursive Macros generating Definitions Maxime Devos
2022-10-03 13:41   ` Frank Terbeck
2022-10-03 18:42     ` Jean Abou Samra
2022-10-03 20:29       ` Frank Terbeck

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/guile/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87zgedkuqc.fsf@ft.bewatermyfriend.org \
    --to=ft@bewatermyfriend.org \
    --cc=guile-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).