Pattern Variables¶
A macro pattern variables pulls out and transforms part of a code fragment. This partial code fragment is then substituted into the macro’s expansion. The substitution can be altered in some ways, or intercepted and more extensively transformed using auxiliary rules.
Every pattern variable has a name and a constraint. The constraint forces
the pattern variable to only match certain code fragments. If the pattern
variable cannot match, the pattern containing the variable will not match.
Unless the pattern variable has the wildcard (or *
) constraint, it can only
match a code fragment that is part of the core language or a macro call; a
pattern variable cannot match a code fragment that is only legal with respect to
a given inner macro. An example of this is given in the discussion of the
?:body
constraint below.
The scope of a pattern variable is the rule that uses it. Other rules or auxiliary rule sets cannot use the pattern variable.
Simple pattern variables¶
?name:constraint
This is the basic pattern variable.
?:constraint
This is a pattern variable where its constraint is also its name. For example,
?:expression
is equivalent to?expression:expression
, that is, a pattern variable namedexpression
with a constraint ofexpression
.?name:name
This matches a name.
?name:token
This matches a name, operator, or simple literal such as a string, character constant, or number. It does not match vector literals or function calls.
?name:expression
This matches any expression, including vector literals, function calls, and begin…end blocks.
?name:variable
This matches a variable name and optional specialization, for example,
color
orcolor :: <color>
.?name:name :: ?specialization:expression
This matches a variable name and optional specialization, like
?:variable
, but lets you extract each part separately. If the code fragment just has the name part, the substitution for?specialization
will be<object>
. Note that?specialization
will not match every expression; it will only match an expression that happens to also be a valid type specialization.
Property list pattern variables¶
#rest ?name:constraint
This matches a property list where every value part meets the constraint. If the constraint is
*
, any value part will match. The substitution for?name
is the entire property list code fragment, including both the symbol and value parts of each property.#key ?prop-1:constraint, ?prop-2:constraint
This matches a property list that only includes the
prop-1:
andprop-2:
properties. If the property list includes any other property such asalpha:
or if eitherprop-1:
orprop-2:
are missing, this pattern variable will not match. Additionally, the properties’ value parts have to meet the constraints given. If the constraint is*
, any value part will match.The substitution for
?prop-1
is the value part of theprop-1:
property.#key ??prop-1:constraint, ??prop-2:constraint
This matches a property list that has several properties with a symbol part of
prop-1:
orprop-2:
. The substitution for??prop-1
is several code fragments, each being the value part of aprop-1:
property. The substitution may use one of the separators listed in Final items between each code fragment.For example, consider this pattern:
{ #key ??my-key:name }
It will match the following code fragment:
my-key: alpha, my-key: beta
The substitution will be the following code fragment:
alpha beta
If the property list did not include a
my-key:
property, the substitution for??my-key
would have been empty.#key ?prop:constraint, #all-keys
This matches a property list that contains
prop:
, but also matches if the property list contains other properties in addition toprop:
.For example, consider this code fragment:
my-key: alpha, another-key: beta
This pattern would not match:
{ #key ?my-key:name }
However, this pattern would:
{ #key ?my-key:name, #all-keys }
#key ?prop:constraint = default-value
This matches a property list that contains
prop:
, but also matches a property list that is missing that property. If the property is missing, the substitution will be the default value given.The default value is not evaluated during macro expansion. Instead, it is simply treated as a code fragment and substituted for
?prop
in the template. The default value code fragment does not have to abide by the pattern variable’s constraint. For example, the following pattern is valid even though#f
is not a name:{ #key ?name:name = #f }
#key ??prop:constraint = default-value
This matches a property list containing zero or more
prop:
properties. Ifprop:
properties are present, the substitution for??prop
will be a sequence of value parts as it is for the#key ??prop:constraint
pattern. However, if the property list does not have anyprop:
properties, the substitution will be a sequence of only one code fragment — the default value code fragment.#rest …, #key …
With these two syntaxes are combined, both match separately against the same property list.
Body and macro pattern variables¶
?name:body
This matches a series of semicolon-separated statement and expressions. If the code fragment does not have any statements or expressions, the substitution will be
#f
. The substitution will wrap the code fragment inbegin
andend
to make an expression.A
?:body
pattern variable matches statements and expressions in a code fragment until it reaches some word, called an intermediate word. You must ensure that all your?:body
pattern variables are either followed by a word, or followed by a pattern variable referring to an auxiliary rule set whose rules all start with a word. Those word will become the intermediate words that tells the parser to stop matching the pattern variable.In this example, the
?:body
variable matches all code fragments up toendif
:{ if (?:expression) ?:body endif }
In this example with auxiliary rules, the
?:body
variable matches all code fragments up toendif
orelse
:{ if (?:expression) ?:body ?else-or-end } else-or-end: { endif } { else ?:body endif }
In this example, the macro will not work because the pattern does not include an intermediate word following the
?:body
variable:{ when (?:expression) ?:body }
A
?:body
pattern variable matches semicolons. It cannot be used in a series of comma- or semicolon-separated sub-patterns, and cannot itself be followed by a comma or semicolon in the pattern. The following will not work:{ if (?:expression) ?:body; ?else-or-end }
A
?:body
pattern variable does not match things that are not statements or expressions. For example, the following pattern is designed to be used with the aboveif
macro:{ if-into (?:expression) ?rest:body => ?:name } => { let ?name = if (?expression) ?rest }
You might expect that you can use this macro on the following code:
if-into (x = #f) format-out("false") else x + 1 endif => x
However, the
?rest:body
variable will not match the wordselse
orendif
because they are not part of the core Dylan language. They are not statements or expressions. Those words are actually an extension to the language allowed by theif
macro, but theif
macro will never see them because the?rest:body
variable does not match or pass them on to theif
macro. To match arbitrary fragments for theif
macro, theif-into
macro must use the wildcard constraint on the variable instead, like?rest:*
.?name:case-body
This matches a list of cases separated by semicolons, where each case consists of: a list of expressions, an arrow, and a body. For example, this pattern variable would match the following:
"red" => "stop"; "green", "blue" => "go"; otherwise => error("I don't know what this means.")
Since a case includes a body, a
?:case-body
pattern variable must be followed with an intermediate word just like a?:body
pattern variable and cannot be followed by a comma or semicolon.?name:macro
This matches any macro call. The substitution will be the expanded macro, without the begin…end block that normally surrounds macro expansions.
While you can use
?:expression
and?:body
pattern variables to match macro calls, their substitutions will include a called macro’s begin…end wrapper, and?:expression
can only match function macro calls.
Wildcard pattern variables¶
?name:*
Wildcard pattern variables match as many code fragments as can be matched before the next comma, semicolon, or other pattern fragment in the pattern. For example, consider the following pattern:
{ ?many-things:* ?:name }
?many-things
will match everything up to but not including a name. The substitution for?many-things
will be everything except that name. If the code fragment only has a name, the substitution for?many-things
will be empty.There can only be one wildcard pattern variable in a comma- or semicolon-separated sub-pattern. Each must be separated from other wildcards by a semicolon or comma. For example, this is not a legal pattern:
{ ?first:* ?second:* }
However, this is:
{ ?first:*, ?second:* }
As a special case, main rules of definition macros can have wildcards in both the
MODIFIERS
part and theLIST-PATTERN
orBODY-PATTERN
part without an intervening comma or semicolon. This allows patterns like the following that would normally not be allowed:{ define ?modifiers:* collection ?:name ?contents:* end }
Finally, consider this pattern:
{ ?first:*, ?second:* }
As described in Patterns, it will match any of the following:
alpha, beta alpha, beta, gamma alpha, alpha
In all cases, the wildcard constraint on
?first
will match up to the first comma in the code fragment.?first
will containalpha
.?second
will contain nothing,beta
, orbeta, gamma
.
Auxiliary rule set pattern variables¶
?aux-rules
This syntax can only be used when there is an auxiliary rule set named the same as the pattern variable. It is equivalent to
?aux-rules:*
. See Auxiliary Rules and Expansions....
This syntax can only be used within an auxiliary rule set. If the rule set is named
my-aux-rules
,...
is equivalent to?my-aux-rules:*
.