Grammar
This is the whole grammar. Anything outside it is a parse error. History indexing with square brackets is valid only directly on a series name, so write (high + low) into a series first if you need its history.
program = separators, { statement, separators }, EOF ;
separators = { NEWLINE | ";" } ;
statement = variable-declaration
| assignment
| if-statement
| for-statement
| while-statement
| "break" | "continue"
| function-declaration
| return-statement
| call-expression ;
variable-declaration
= ("let" | "var"), identifier, [":", type], "=", expression ;
assignment = identifier, (":=" | "+=" | "-=" | "*=" | "/="), expression ;
if-statement = "if", expression, block,
[separators, "else", (block | if-statement)] ;
for-statement = "for", identifier, "=", expression, "to", expression,
["by", expression], block ;
while-statement = "while", expression, block ;
function-declaration
= "fn", identifier, "(", [parameters], ")",
["->", type], block ;
parameters = parameter, {",", parameter} ;
parameter = identifier, ":", type ;
return-statement = "return", [expression] ;
block = "{", separators, {statement, separators}, "}" ;
expression = conditional ;
conditional = logical-or, ["?", expression, ":", expression] ;
logical-or = logical-and, {"||", logical-and} ;
logical-and = equality, {"&&", equality} ;
equality = comparison, {("==" | "!="), comparison} ;
comparison = additive, {("<" | "<=" | ">" | ">="), additive} ;
additive = multiplicative, {("+" | "-"), multiplicative} ;
multiplicative = unary, {("*" | "/" | "%"), unary} ;
unary = ["+" | "-" | "!"], unary | postfix ;
postfix = primary, ["[", expression, "]"] ;
primary = number | string | "true" | "false"
| identifier | constant
| call-expression
| "(", expression, ")" ;
constant = "math.pi" | "math.e" | "color.red" | "color.green" | ... ;
call-expression = dotted-identifier, "(", [arguments], ")" ;
arguments = expression, {",", expression} ;
type = "float" | "int" | "bool" | "string" | "color" | "side" | "void" ;Operator precedence
From tightest to loosest: unary + - !, then * / %, then + -, then < <= > >=, then == !=, then &&, then ||, and last the conditional ? :. All binary operators associate to the left. The conditional associates to the right, so a ? b : c ? d : e reads as a ? b : (c ? d : e).
- Arithmetic operands must be numbers. The + operator never joins strings; use str.concat.
- Division or remainder by zero returns nan rather than infinity.
- && and || require booleans and short circuit. ! requires a boolean. An if condition must be a boolean too.
- An ordered comparison against nan is false. Both == and != against nan are false, even when both sides are nan. Test with is_nan or replace with nz.
- Equality is strict. Numbers, booleans and strings are never coerced.
- condition ? a : b needs a bool condition and evaluates only the branch it takes, exactly like an if. Both branches should have one type; int and float mix freely.
- x += e, x -= e, x *= e and x /= e are short for x := x + e and so on, on a var or a local.
- A ta.* call inside one arm of ? : runs only on the bars that take that arm, exactly as inside an if branch. Compute it into a series first and pick between the results.
let tone: color = close >= open ? color.green : color.red
let average_volume: float = ta.sma(volume, 20)
let size_class: string = volume > average_volume * 2 ? "HEAVY" : volume > average_volume ? "ABOVE" : "QUIET"
plot.candle_color(true, tone, 0.6)
plot.label(size_class, "#E5E7EB")