The language
Grammar
The complete syntax of Coach on one page, with the order in which operators apply.
This page is a reference. It describes the syntax of Coach as the compiler sees it, which is handy when you want to know exactly what is allowed where. If you are learning the language, the pages on values, the core elements and the commands explain the same things with a lot more care.
Notation
The grammar below uses a compact notation:
| Notation | Meaning |
|---|---|
'if' |
the literal text if |
a b |
a followed by b |
| `a | b` |
a? |
a, optionally |
a* |
zero or more a |
( … ) |
grouping |
IDENT is a name, STRING a string literal, INT a whole number and
FLOAT a fractional one.
Lexical structure
IDENT = [a-zA-Z_] [a-zA-Z_0-9?]*
INT = '0' | [1-9] [0-9]*
FLOAT = INT '.' [0-9]+ EXPONENT? | INT EXPONENT
EXPONENT = ('e' | 'E') ('+' | '-')? [0-9]+
STRING = '"' (any character but '"' or '\', or an escape)* '"'
escape = '\b' | '\t' | '\n' | '\f' | '\r' | '\"' | '\'' | '\\' | '\u' HEX HEX HEX HEX | '\' [0-7]{1,3}
A double quote inside a string is written \", as in "say \"hi\"".
- Whitespace, including line breaks, only separates tokens. There are no semicolons; one statement simply follows another.
- Comments are either
// to the end of the lineor/* anything */.
These words are reserved and cannot be used as names:
import as action input output exec group let
if else repeat while break continue
specify measure ask true false nil
boolean string number range fetch from
Programs
program = import* element*
import = 'import' STRING ('as' IDENT)?
element = action | input | output | group | exec
| let | specify | measure | assign | metaAssign | statement
statement = 'if' expression statement ('else' statement)?
| 'repeat' (IDENT ':')? expression statement
| 'while' expression statement
| 'break'
| 'continue'
| expression
| block
block = '{' element* '}'
The body of an if, repeat or while is a single statement, usually a
block. Conditions do not need parentheses, but most programs use them for
readability: if (reps < 5) { … }.
Declarations
action = 'action' IDENT ('<' qualifiedName)? keyValueList
input = 'input' ':' typeHint IDENT keyValueList
| 'input' IDENT '<' qualifiedName keyValueList
output = 'output' ':' typeHint IDENT keyValueList
| 'output' IDENT '<' qualifiedName keyValueList
typeHint = 'boolean' | 'string' | 'number' | unit
group = 'group' IDENT '(' params? ')' block
exec = 'exec' IDENT '(' params? ')' block
params = IDENT (',' IDENT)*
let = 'let' IDENT ('=' expression)? (',' IDENT ('=' expression)?)*
assign = IDENT ('.' position)* '=' expression
metaAssign = '@' position ('.' position)* '=' expression
specify = 'specify' (':' qualifiedName)? qualifiedName expression
(',' qualifiedName expression)*
measure = 'measure' qualifiedName (',' qualifiedName)*
Expressions
expression = IDENT
| literal
| measurement
| array
| keyValueList
| '(' expression ')'
| expression '(' (expression (',' expression)*)? ')'
| '@' position ('.' position)*
| 'ask' '?'? qualifiedName
| expression '..' '.'? expression
| '..' '.'? expression
| expression '..'
| '&' expression
| expression '.' position
| expression '.' '(' expression ')'
| ('-' | '!') expression
| expression ('^' | '*' | '/' | '%' | '+' | '-') expression
| expression ('<>' | '<' | '<=' | '>' | '>=' | '==' | '!=') expression
| expression ('&&' | '||') expression
| expression '?' expression ':' expression
literal = INT | FLOAT | STRING | 'true' | 'false' | 'nil'
array = '[' (expression (',' expression)*)? ']'
keyValueList = '[' ((IDENT | STRING) ':' expression
(',' (IDENT | STRING) ':' expression)*)? ']'
position = IDENT | STRING | '-'? INT
qualifiedName = IDENT | IDENT '.' IDENT
Notice that [] is both an empty array and an empty map, and that the
properties of an action, an input or an output are written as a map.
Operator precedence
When an expression mixes operators, they apply in the order below, from the
one that binds tightest to the one that binds loosest. Operators on the same
row apply from left to right, except ^ and ? :, which apply from right to
left.
| Operators | Kind |
|---|---|
f(…) |
call |
.. ... |
range |
& |
specified value |
. |
access |
- ! |
negation, not |
^ |
power |
* / % |
multiplication |
+ - |
addition |
<> |
containment |
< <= > >= |
comparison |
== != |
equality |
&& |
and |
| ` | |
? : |
conditional |
Most of it works the way you would expect from school arithmetic, but a few consequences are worth knowing:
| You write | Coach reads | Write instead |
|---|---|---|
-2.5..8 |
-(2.5..8) |
(-2.5)..8 |
-2^2 |
(-2)^2, which is 4 |
-(2^2) |
load.min..load.max |
(load.min..load).max |
(load.min)..(load.max) |
&fdb.Squat.reps |
(&fdb).Squat.reps |
— this is what you want |
Line breaks
Since line breaks do not end a statement, a line that starts with ( is read
as a call of whatever ends the previous line:
let x = y
(a + b) * 2 // read as: let x = y(a + b) * 2
Start such a line with something else, for example by assigning the result to a name.
Measurements and units
measurement = (INT | FLOAT) unit
unit = '|' unitExpr '|'
unitExpr = IDENT
| unitExpr '^' exponent
| unitExpr ('*' | '/') unitExpr
| '(' unitExpr ')'
exponent = '-'? INT ('/' INT)? | '(' exponent ')'
The names a unit is made of, and the prefixes they accept, are listed under measurements.