6.8.11.3 Advanced Syntax Patterns

To match a function with a variable number of arguments, you could write

foo ( # ) := myfunc(#1)
foo ( # , # ) := myfunc(#1,#2)
foo ( # , # , # ) := myfunc(#1,#2,#3)

but this isn’t very elegant. To match variable numbers of items, Calc uses some notations inspired regular expressions and the “extended BNF” style used by some language designers.

foo ( { # }*, ) := apply(myfunc,#1)

The token ‘{’ introduces a repeated or optional portion. One of the three tokens ‘}*’, ‘}+’, or ‘}?’ ends the portion. These will match zero or more, one or more, or zero or one copies of the enclosed pattern, respectively. In addition, ‘}*’ and ‘}+’ can be followed by a separator token (with no space in between, as shown above). Thus ‘{ # }*,’ matches nothing, or one expression, or several expressions separated by commas.

A complete ‘{ ... }’ item matches as a vector of the items that matched inside it. For example, the above rule will match ‘foo(1,2,3)’ to get ‘apply(myfunc,[1,2,3])’. The Calc apply function takes a function name and a vector of arguments and builds a call to the function with those arguments, so the net result is the formula ‘myfunc(1,2,3)’.

If the body of a ‘{ ... }’ contains several ‘#’s (or nested ‘{ ... }’ constructs), then the items will be strung together into the resulting vector. If the body does not contain anything but literal tokens, the result will always be an empty vector.

foo ( { # , # }+, ) := bar(#1)
foo ( { { # }*, }*; ) := matrix(#1)

will parse ‘foo(1, 2, 3, 4)’ as ‘bar([1, 2, 3, 4])’, and ‘foo(1, 2; 3, 4)’ as ‘matrix([[1, 2], [3, 4]])’. Also, after some thought it’s easy to see how this pair of rules will parse ‘foo(1, 2, 3)’ as ‘matrix([[1, 2, 3]])’, since the first rule will only match an even number of arguments. The rule

foo ( # { , # , # }? ) := bar(#1,#2)

will parse ‘foo(2,3,4)’ as ‘bar(2,[3,4])’, and ‘foo(2)’ as ‘bar(2,[])’.

The notation ‘{ ... }?.’ (note the trailing period) works just the same as regular ‘{ ... }?’, except that it does not count as an argument; the following two rules are equivalent:

foo ( # , { also }? # ) := bar(#1,#3)
foo ( # , { also }?. # ) := bar(#1,#2)

Note that in the first case the optional text counts as ‘#2’, which will always be an empty vector, but in the second case no empty vector is produced.

Another variant is ‘{ ... }?$’, which means the body is optional only at the end of the input formula. All built-in syntax rules in Calc use this for closing delimiters, so that during algebraic entry you can type [sqrt(2), sqrt(3 RET, omitting the closing parenthesis and bracket. Calc does this automatically for trailing ‘)’, ‘]’, and ‘>’ tokens in syntax rules, but you can use ‘{ ... }?$’ explicitly to get this effect with any token (such as ‘"}"’ or ‘end’). Like ‘{ ... }?.’, this notation does not count as an argument. Conversely, you can use quotes, as in ‘")"’, to prevent a closing-delimiter token from being automatically treated as optional.

Calc’s parser does not have full backtracking, which means some patterns will not work as you might expect:

foo ( { # , }? # , # ) := bar(#1,#2,#3)

Here we are trying to make the first argument optional, so that ‘foo(2,3)’ parses as ‘bar([],2,3)’. Unfortunately, Calc first tries to match ‘2,’ against the optional part of the pattern, finds a match, and so goes ahead to match the rest of the pattern. Later on it will fail to match the second comma, but it doesn’t know how to go back and try the other alternative at that point. One way to get around this would be to use two rules:

foo ( # , # , # ) := bar([#1],#2,#3)
foo ( # , # ) := bar([],#1,#2)

More precisely, when Calc wants to match an optional or repeated part of a pattern, it scans forward attempting to match that part. If it reaches the end of the optional part without failing, it “finalizes” its choice and proceeds. If it fails, though, it backs up and tries the other alternative. Thus Calc has “partial” backtracking. A fully backtracking parser would go on to make sure the rest of the pattern matched before finalizing the choice.