Tulip Formal Syntax
This provides a formal syntax for Tulip written in an extended BNF.
1 Datums and Operators
| ‹identifier› | ::= | ‹identifier start› ‹identifier body›* |
| ‹identifier start› | ::= | ‹letter› |
| ‹identifier body› | ::= | ‹letter› | ‹digit› | - |
| ‹tag word› | ::= | . ‹identifier› |
| ‹flag word› | ::= | - ‹identifier› |
| ‹number› | ::= | ‹digit›+ [.] |
|
| | | ‹digit›* . ‹digit›+ |
| ‹letter› | ::= | a | b | c | ... | z |
|
| | | A | B | C | ... | Z |
| ‹digit› | ::= | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| ‹sequence delimiter› | ::= | ; | newline |
2 Expressions
| ‹expression› | ::= | ‹group› |
|
| | | ‹block› |
|
| | | ‹chain› |
|
| | | ‹application› |
|
| | | ‹identifier› |
|
| | | ‹tag word› |
|
| | | ‹flag pair› |
|
| | | ‹lambda› |
|
| | | ‹block lambda› |
|
| | | $ |
| ‹group› | ::= | ( ‹expression› ) |
| ‹block› | ::= | { ‹expression› {‹sequence delimiter› ‹expression›}* } |
| ‹chain› | ::= | ‹expression› > ‹expression› |
| ‹application› | ::= | ‹expression›+ |
|
| | | ‹expression› ! |
| ‹flag pair› | ::= | ‹flag word› : ‹expression› |
Note that $ is only allowed as an ‹expression› while parsing an ‹auto lambda›.
2.1 Lambdas
| ‹lambda› | ::= | [ ‹lambda body› ] |
| ‹block lambda› | ::= | { ‹lambda body› } |
| ‹lambda body› | ::= | ‹full lambda› | ‹auto lambda› |
| ‹full lambda› | ::= | ‹lambda clause› {‹sequence delimiter› ‹lambda clause›}* |
| ‹lambda clause› | ::= | ‹lambda formals› => ‹expression› |
| ‹lambda formals› | ::= | ‹pattern›+ |
|
| | | ! |
| ‹auto lambda› | ::= | ‹expression› |
| ‹pattern› | ::= | ‹grouped pattern› |
|
| | | ‹conjunct pattern› |
|
| | | ‹condition pattern› |
|
| | | ‹tag pattern› |
|
| | | ‹identifier› |
|
| | | ‹number› |
|
| | | _ |
| ‹grouped pattern› | ::= | ( ‹pattern› ) |
| ‹conjunct pattern› | ::= | ‹pattern› : ‹pattern› |
| ‹condition pattern› | ::= | ‹pattern› ? ‹expression› |
| ‹tag pattern› | ::= | ‹tag word› ‹pattern›* |
The above grammar is slightly ambiguous when applied to the following syntax:
[ .foo bar baz => ... ]
Specifically, either of the following forms would be reasonable interpretations:
[ (.foo) bar baz => ... ]
[ (.foo bar baz) => ... ]
To disambiguate, a special rule is considered when parsing the pattern for the first argument of a lambda. If the pattern is a ‹tag pattern›, then the lambda is parsed as accepting a single argument, the tag. If the pattern is anything else, then all patterns at root level of the lambda formals are treated as patterns for individual arguments.
[ .foo bar baz => ... ] # parses as [ (.foo bar baz) => ... ]
[ (.foo) bar baz => ... ] # parses as [ (.foo) bar baz => ... ]
[ foo .bar baz => ... ] # parses as [ foo (.bar) baz => ... ]
3 Definitions
| ‹definition› | ::= | ‹declaration› = ‹expression› |
| ‹declaration› | ::= | ‹binding declaration› |
|
| | | ‹function declaration› |
| ‹binding declaration› | ::= | ‹identifier› |
| ‹function declaration› | ::= | ‹identifier› ‹lambda formals› |
Function definitions do not use the custom pattern parsing rule described in Lambdas, so patterns are always interpreted as separate arguments when possible.
foo .bar baz = ... # parses like [ (.bar) baz => ... ]
4 Whole Programs
| ‹program› | ::= | ‹top-level form› {‹sequence delimiter› ‹top-level form›}* |
| ‹top-level form› | ::= | ‹expression› |
|
| | | ‹definition› |