<?xml version="1.0"?>
<paste-with-annotations>
  <paste>
    <number>
      <integer>53681</integer>
    </number>
    <user>
      <string>|Agent</string>
    </user>
    <title>
      <string>macro</string>
    </title>
    <contents>
      <string>Here are the macro definitions:

---------------------------
define macro tokenizer-definer
   {
      define tokenizer ?token-name:name
         rule ?rule =&gt; ?product-name:name;
         ?slots
      end
   } =&gt; {
      define class &quot;&lt;&quot; ## ?token-name ## &quot;-token&gt;&quot; (&lt;token&gt;)
         token-class-slots(?slots)
      end class;
      
      define method initialize (token :: &quot;&lt;&quot; ## ?token-name ## &quot;-token&gt;&quot;,
            #key ?product-name)
         slot-initializers(?slots)
      end method;
      
      define method &quot;parse-&quot; ## ?token-name
         (stream :: &lt;positionable-stream&gt;, context)
      =&gt; (token :: &quot;&lt;&quot; ## ?token-name ## &quot;-token&gt;&quot;)
         let production =
               block()
                  apply(?rule, stream, context)
               exception (err :: &lt;parse-failure&gt;)
                  err.expected := concatenate!(err.expected, &quot; in &quot; ## ?&quot;token-name&quot;);
                  error(err)
               end;
         make(&quot;&lt;&quot; ## ?token-name ## &quot;-token&gt;&quot;, 
              position: stream.position, ?product-name: production)
      end method;
      
      $rule-names[&quot;parse-&quot; ## ?token-name] := &quot;?token-name&quot;;
   }
rule:
   { ?:name(...) } =&gt; { ?name(...) }
   { ?:name, ...  } =&gt; { &quot;parse-&quot; ## ?name, ... }
   { } =&gt; { }
slots:
   { slot ?:variable = ?:expression; ... } =&gt; { ?variable = ?expression, ... }
   { slot ?:variable; ... } =&gt; { ?variable, ... }
   { } =&gt; { }
end macro;

define macro token-class-slots
   { token-class-slots (?:variable = ?:expression, ?more:*) }
      =&gt; { slot ?variable; token-class-slots(?more) }
   { token-class-slots (?:variable, ?more:*) }
      =&gt; { slot ?variable; token-class-slots(?more) }
   { token-class-slots () }
      =&gt; { }
end macro;

define macro slot-initializers
   { slot-initializers (?:variable = ?:expression, ?more:*) }
      =&gt; { ?variable := ?expression; slot-initializers(?more) }
   { slot-initializers (?:variable, ?more:*) }
      =&gt; { slot-initializers(?more) }
   { slot-initializers () }
      =&gt; { }
end macro;
---------------------------


Here is an example of use:

------------------------
define tokenizer topic
   rule seq(choice(topic-directive, topic-title), opt(topic-content)) =&gt; tokens;
   slot topic-spec = tokens[0];
   slot content = tokens[1];
end;
------------------------


That should result in:

------------------------
define class &lt;topic-token&gt; (&lt;token&gt;)
   slot topic-spec;
   slot content;
end class;
      
define method initialize (token :: &lt;topic-token&gt;, #key tokens)
   content := tokens[0];
   topic-spec := tokens[1];
end method;

define method parse-topic
   (stream :: &lt;positionable-stream&gt;, context)
=&gt; (token :: &lt;topic-token&gt;)
   let production =
         block()
            apply(seq(choice(parse-topic-directive, parse-topic-title), opt(parse-topic-content)),
                  stream, context);
         exception (err :: &lt;parse-failure&gt;)
            err.expected := concatenate!(err.expected, &quot; in topic&quot;);
            error(err)
         end;
   make(&lt;topic-token&gt;, position: stream.position, tokens: production)
end method;
      
$rule-names[parse-topic] := &quot;topic&quot;;
-----------------------


Instead, compilation fails with the following:

Serious warning at tokenizers.dylan:9: Invalid syntax for rule in tokenizer-definer macro call. 

tokenizers.dylan:9:             --------------------------------------------------------
tokenizers.dylan:9:    rule seq(choice(topic-directive, topic-title), opt(topic-content)) =&gt; tokens;
tokenizers.dylan:9:             --------------------------------------------------------

Serious warning at tokenizers.dylan:8: 
  Skipping tokenizer-definer macro call due to previous syntax error. 

tokenizers.dylan:8: ----------------------
tokenizers.dylan:8: define tokenizer topic
tokenizers.dylan:9:    rule seq(choice(topic-directive, topic-title), opt(topic-content)) =&gt; tokens;
tokenizers.dylan:11: [...]
tokenizers.dylan:12: end;
tokenizers.dylan:13: ---

</string>
    </contents>
    <universal-time>
      <integer>3408594423</integer>
    </universal-time>
    <channel>
      <string>#dylan</string>
    </channel>
    <colorization-mode>
      <string></string>
    </colorization-mode>
    <maybe-spam>
      <null/>
    </maybe-spam>
    <is-unicode>
      <null/>
    </is-unicode>
  </paste>
  <annotation>
    <number>
      <integer>2</integer>
    </number>
    <user>
      <string>|Agent</string>
    </user>
    <title>
      <string>fixed macro</string>
    </title>
    <contents>
      <string>Here is the replacement for the original rule: aux pattern

----
rule:
   { seq(?nested-rule) ... } =&gt; { seq(?nested-rule) ... }
   { choice(?nested-rule) ... } =&gt; { choice(?nested-rule) ... }
   { many(?nested-rule) ... } =&gt; { many(?nested-rule) ... }
   { opt(?nested-rule) ... } =&gt; { opt(?nested-rule) ... }
   { opt-seq(?nested-rule) ... } =&gt; { opt-seq(?nested-rule) ... }
   { opt-choice(?nested-rule) ... } =&gt; { opt-choice(?nested-rule) ... }
   { opt-many(?nested-rule) ... } =&gt; { opt-many(?nested-rule) ... }
   { req-next(?nested-rule) ... } =&gt; { req-next(?nested-rule) ... }
   { not-next(?nested-rule) ... } =&gt; { not-next(?nested-rule) ... }
   { ?:name ... } =&gt; { &quot;parse-&quot; ## ?name ... }
   { ?:token ... } =&gt; { ?token ... }
   { } =&gt; { }
nested-rule:
   { ?rule } =&gt; { ?rule }
----

Aux patterns must match everything, and recursion into an expression can be done via nested-rule as I show here.

</string>
    </contents>
    <universal-time>
      <integer>3408746174</integer>
    </universal-time>
    <channel>
      <string>#dylan</string>
    </channel>
    <colorization-mode>
      <string></string>
    </colorization-mode>
    <maybe-spam>
      <null/>
    </maybe-spam>
    <is-unicode>
      <null/>
    </is-unicode>
  </annotation>
  <annotation>
    <number>
      <integer>1</integer>
    </number>
    <user>
      <string>|Agent</string>
    </user>
    <title>
      <string>slight improvement</string>
    </title>
    <contents>
      <string>I replaced the rule: aux-pattern with these three patterns:

-------
rule:
   { ?rule-op, ... } =&gt; { ?rule-op, ... }
   { ?rule-parser, ...  } =&gt; { ?rule-parser, ... }
   { } =&gt; { }
rule-op:
   { ?:name(?rule) } =&gt; { ?name(?rule) }
rule-parser:
   { ?:name } =&gt; { &quot;parse-&quot; ## ?name }
-------

This is slightly better. Now it recurses within the seq(...) calls, but I still get the following error:

-------
Serious warning at tokenizers.dylan:9: Invalid syntax for rule-op in tokenizer-definer macro call. 

tokenizers.dylan:9:                    ---------------
tokenizers.dylan:9:    rule seq(choice(topic-directive, topic-title), opt(topic-content)) =&gt; tokens;
tokenizers.dylan:9:                    ---------------

Serious warning at tokenizers.dylan:8: 
  Skipping tokenizer-definer macro call due to previous syntax error. 

tokenizers.dylan:8: ----------------------
tokenizers.dylan:8: define tokenizer topic
tokenizers.dylan:9:    rule seq(choice(topic-directive, topic-title), opt(topic-content)) =&gt; tokens;
tokenizers.dylan:11: [...]
tokenizers.dylan:12: end;
tokenizers.dylan:13: ---
-------

It looks like it tries to match &quot;topic-directive&quot; against rule-op, but if that fails, it doesn't try to match it
against rule-parser:?

</string>
    </contents>
    <universal-time>
      <integer>3408597695</integer>
    </universal-time>
    <channel>
      <string>#dylan</string>
    </channel>
    <colorization-mode>
      <string></string>
    </colorization-mode>
    <maybe-spam>
      <null/>
    </maybe-spam>
    <is-unicode>
      <null/>
    </is-unicode>
  </annotation>
</paste-with-annotations>