3.1 Syntax Objects
The '…' form produces a syntax object. The syntax object holds an unparsed shrubbery, not a parsed Rhombus expression.
> '1'
'1'
> 'hello'
'hello'
> '1 + 2'
'1 + 2'
> 'x:
y'
'x: y'
> '1' + 2
+: value does not satisfy annotation
annotation: Number
value: ’1’
Within the '…', the $ operator unquotes the immediately following term. That is, the term after $ is a Rhombus expression whose value replaces the $ and its argument within the quoted form.
'1 + 5'
A $ only unquotes when it is followed by a term, otherwise the $ itself remains quoted.
'1 + $ 2'
Nesting quotes do not require a corresponding nesting of escaping $ to escape outside the original quotes. For example, '('$(1+2)')' is the same as '('3')' in Rhombus, even though the escape is inside two layers of quotes.
Like $, ... is treated specially within a '…'-quoted term (except, like $, when it’s the only thing in the term). When ... immediately follows a term that includes at least one $, the form after that $ must refer to a repetition. Then, instead of the parenthesized group in place of $, the term before ... is replicated as many times as the repetition has items, and each of those items is used in one replication.
When ... is the only term in a group, and when that group follows another, then ... replicates the preceding group. For example, putting ... after a , in parentheses means that it follows a the group before the ,, which effectively replicates that group with its separating comma:
'(hi 1, hi 2, hi 3)'
Along the same lines, ... just after a | can replicate a preceding | block:
'cond
| 1
| 2
| 3'
In other words, ... in various places within a quoted shrubbery works the way you’d expect it to work.
When '…' is used in a binding position, it constructs a pattern that matches syntax objects, and it binds variables that are escaped in the pattern with $.
> x
'1'
> y
'(2 + 3)'
A ... works the way you would expect in a syntax pattern, matching any ...-replicated pattern variables to form a repetition of matches:
A tail pattern $id ... combined with a tail template $id ... is similar to using . in S-expression patterns and templates, where it allows sharing between the input and output syntax objects. That sharing and an associated expansion-cost difference is all the more important in the Rhombus expansion protocol, which must thread potentially long sequences into and out of macro transformers.
A $-escaped variable in a '…' pattern matches one term among other terms in the group. A block created with : counts as a single term of its enclosing group, and a sequence of | alternatives (not an individual alternative) similarly counts as one term.
> x
'block'
> y
': 1 2 3'
> z
'cond'
> w
'| is_ok: "good"
| ~else: "bad"'
Keep in mind that '…' creates syntax objects containing shrubberies that are not yet parsed, so a variable will not be matched to a multi-term sequence that would be parsed as an expression. For example, a pattern variable y by itself cannot be matched to a sequence 2 + 3:
def: value does not satisfy annotation
value: ’1 + 2 + 3 + 4’
annotation: ’1 + $y + 4’
Having pattern variables always stand for individual terms turns out to be tedious, however. For example, to match a thunk pattern with a block that has any number of groups with any number of terms, you’d have to use two layers of ellipses. Then, to substitute that same body into a fun template, you’d have to use the two layers of ellipses again.
As a shorthand, when an escaped variable is alone in its group in a pattern, it stands for a match to the whole group (at least by default). A pattern variable that is alone in a multi-group context similarly stands for a match to all the groups.
> [group, ...]
['def x = 1', 'x + 1']
> body
'def x = 1
x + 1'
As a further generalization, when an escaped variable is at the end of its group in a pattern, it stands for a match to remaining terms in group.
> y
'2 + 3 + 4'
These multi-term and multi-group syntax objects can be spliced into similar positions in templates, where an escape is by itself within its group or by itself in a multi-group position.
'fun ():
def x = 1
x + 1'
'fun ():
def x = 1
x + 1'
There is no constraint that the original and destination contexts have the same shape, so a match from a block-like context can be put into a brackets context, for example.
> '[$x]'
'[1 + 2 + 3, 4 * 5 * 6]'
A multi-term, single-group syntax object can be spliced in place of any term escape, even if it is not at the end of the group.
'0 + 1 + 2 + 3 + 4'
A multi-group syntax object splices multiple groups in place of a group escape only when the escape is alone in its group. A list of group syntax objects does not splice into group contexts, because that would create ambiguities among group and term contexts. Meanwhile, a single-term syntax object can be used as a group syntax object, a single-group syntax object can be used as a multi-group syntax object, and a single-term syntax object can be used as a multi-group syntax object.
Sometimes, a pattern variable that is at the end of a group is meant to match a single term and not a group of terms. To match a single term in a group context, annotate the pattern variable with the Term syntax class using the :: operator.
def: value does not satisfy annotation
value: ’1 + 2’
annotation: ’$(x :: Term)’
You can similarly use the Group syntax class to match a single group instead of a multi-group sequence. There are several other predefined syntax classes, such as Identifier to match an identifier, String to match a string literal, and Int to match an integer literal.
In practice, you should use Block to match a block, preserving its lexical context for the implicit #%body form (see Implicit Forms).