1.11 Syntax Templates and Patterns🔗ℹ

In a '' expression for a syntax object, $ acts as an escape back to an espression that is evaluated as a Shplait expression. That is, instead of quoting the escaped expression, the value of the expression is substituted into the syntax object. The $ escapes only the immediately following term, so a complicated expression needs parentheses.

> '1 + $(integer_to_syntax(2 + 3)) + 4'

- Syntax

'1 + 5 + 4'

> let x = integer_to_syntax(2 + 3):

    '1 + $x + 4'

- Syntax

'1 + 5 + 4'

This kind of '' form is called a template, because it is instantiated to a synatx object each time it is evaluated. If the template is put inside a function, it can generate a different syntax object each time.

fun make_mult(left, right):

  '$left * $right'

> make_mult('1', '2')

- Syntax

'1 * 2'

> make_mult('x', 'y')

- Syntax

'x * y'

> make_mult('3 + 4', '5 + 6')

- Syntax

'3 + 4 * 5 + 6'

As the last example illustrates, the syntax object substituted into a template can have multiple terms inside, although it must have a single shrubbery group. The last example also illustrates that template construction is oblivious to precedence rules, which are not part of the specification of shrubbery notation (and are instead defined for specific languages like Shplait).

Going the other way, suppose you want to check whether a syntax object is anything with '*' in the middle. Any such term is one that could have been generated with the template '$left * $right' for some left and right. In other words, we want to turn the template around and use it as a pattern.

When match uses a '-quoted form instead of a variant constructor, then it performs syntax matching instead of datatype matching. Within the quoted term, $ escapes, and a variable after $ is bound to a matching part of the input syntax.

> match '3 * 4'

  | '$left * $right':

      [left, right]

- Listof(Syntax)

['3', '4']

In this example, left was matched up with 3 in the input pattern, and right was matched up with 4, and that assignment of variables allows '3 * 4' to match '$left * $right'. The two matched syntax objects were then returned in a list with [left, right], but left and right could be used in other ways. Another way to use them, for example, is to put them back into a new synatx object through a template.

fun commute(exp):      

  match exp

  | '$left * $right':

      '$right * $left'

> commute('1 * 2')

- Syntax

'2 * 1'

> commute('1 + 2 * 3 + 4')

- Syntax

'3 + 4 * 1 + 2'

As the last call to commute illustrates, again, matching and template construction manipulate terms sequences with no regard to meaning or precedence beyond the rules that shrubbery notation defines.

Escapes in a syntax pattern can be nested so that they stand for more deeply nested parts of a matching syntax object.

fun check_call(exp):

  match exp

  | 'f(1 + $a, 2 + $b)':

      "matches with " +& a +& " and " +& b

  | ~else:

      "doesn't match"

> check_call('f(1 + 3, 2 + 4 + 5)')

- String

"matches with 3 and 4 + 5"

> check_call('3')

- String

"doesn't match"

> check_call('f(1, 2)')

- String

"doesn't match"

What if we want to match a function-call shape with any number of arguments, instead of just two? A pattern variable can match any number of terms in a row, but , in shrubbery notation separates groups, so 'f($args)' cannot match 'f(1, 2, 3)', because the 1, 2, and 3 are in different groups. To support more general sequence matching, patterns and templates recognize ... to mean zero or more repetitions of the group or term before the .... So, the pattern 'f($arg, ...)' does match 'f(1, 2, 3)'.

fun check_any_call(exp):

  match exp

  | 'f($arg, ...)':

      "matches"

  | ~else:

      "doesn't match"

> check_any_call('f(1)')

- String

"matches"

> check_any_call('f(1, 2, 3)')

- String

"matches"

> check_any_call('f()')

- String

"matches"

> check_any_call('f')

- String

"doesn't match"

When 'f($arg, ...)' matches 'f(1, 2, 3)', the variable arg doesn’t just stand for one piece of the input syntax object. It needs to stand for all of the matches, and the only way to get at those matches is to use the variable in a template that also uses ....

fun show_call_args(exp):

  match exp

  | 'f($arg, ...)':

      "matches with " +& '[$arg, ...]'

> show_call_args('f(1, 2, 3)')

- String

"matches with [1, 2, 3]"

The template '[$arg, ...]' creates a syntax object that isn’t the same as a list, but it corresponds to a list. Use the function syntax_to_list to convert it to a list of syntax objects.

fun get_call_args(exp):

  match exp

  | 'f($arg, ...)':

      syntax_to_list('[$arg, ...]')

> get_call_args('f(1, 2, 3 + 4)')

- Listof(Syntax)

['1', '2', '3 + 4']

> get_call_args('f()')

- Listof(Syntax)

[]