prog       =  { SOI ~ PROGRAM ~ id ~ ";" ~ block ~ EOI }
block      =  { ws* ~ const_decl? ~ var_decl? ~ proc? ~ body ~ ws* }
const_decl =  { ws* ~ CONST ~ const ~ ("," ~ const)* ~ ";" ~ ws* }
const      =  { ws* ~ id ~ ":=" ~ integer ~ ws* }
var_decl   =  { ws* ~ VAR ~ id ~ ("," ~ id)* ~ ";" ~ ws* }
proc       =  { ws* ~ PROCEDURE ~ id ~ "(" ~ (id ~ ("," ~ id)*)? ~ ")" ~ ";" ~ block ~ (";" ~ proc)* ~ ws* }
body       =  { ws* ~ BEGIN ~ statement ~ (";" ~ statement)* ~ END ~ ws* }
statement  =  { ws* ~ stmt ~ ws* }
stmt       =  {
    (id ~ ":=" ~ exp)
  | (IF ~ l_exp ~ THEN ~ statement ~ (ELSE ~ statement)?)
  | (WHILE ~ l_exp ~ DO ~ statement)
  | (CALL ~ id ~ "(" ~ (exp ~ ("," ~ exp)*)? ~ ")")
  | body
  | (READ ~ "(" ~ id ~ ("," ~ id)* ~ ")")
  | (WRITE ~ "(" ~ exp ~ ("," ~ exp)* ~ ")")
}
l_exp      =  { ws* ~ (exp ~ lop ~ exp) | (ODD ~ exp) ~ ws* }
exp        =  { ws* ~ aop? ~ term ~ (aop ~ term)* ~ ws* }
term       =  { ws* ~ factor ~ (mop ~ factor)* ~ ws* }
factor     =  { ws* ~ ("(" ~ exp ~ ")" | integer | id) ~ ws* }
lop        =  { ws* ~ ("=" | "<>" | "<" | "<=" | ">" | ">=") ~ ws* }
aop        =  { ws* ~ ("+" | "-") ~ ws* }
mop        =  { ws* ~ ("*" | "/") ~ ws* }
id         =  { ws* ~ letter ~ (letter | digit)* ~ ws* }
integer    =  { ws* ~ digit ~ (digit)* ~ ws* }
letter     = @{ 'a'..'z' | 'A'..'Z' }
digit      = @{ '0'..'9' }
CONST      = _{ "const" ~ ws+ }
PROGRAM    = _{ "program" ~ ws+ }
VAR        = _{ "var" ~ ws+ }
PROCEDURE  = _{ "procedure" ~ ws+ }
BEGIN      = _{ "begin" ~ ws+ }
END        = _{ "end" ~ ws* }
IF         = _{ "if" ~ ws+ }
THEN       = _{ "then" ~ ws+ }
ELSE       = _{ "else" ~ ws+ }
WHILE      = _{ "while" ~ ws+ }
DO         = _{ "do" ~ ws+ }
CALL       = _{ "call" ~ ws+ }
READ       = _{ "read" ~ ws* }
WRITE      = _{ "write" ~ ws* }
ODD        = _{ "odd" ~ ws+ }
ws         = _{ WHITE_SPACE }
