[
  ["start",
    [["*", ["definition"]]]
  ],
  ["definition",
    ["defProgram"],
    ["defInteractiveProgram"],
    ["defProcedure"],
    ["defFunction"],
    ["defType"]
  ],
  ["defProgram",
    ["PROGRAM", "stmtBlock"]
  ],
  ["defInteractiveProgram",
    ["INTERACTIVE", "PROGRAM", ["*", ["stmtSwitchBranch"]], "RBRACE"]
  ],
  ["defProcedure",
    ["PROCEDURE", "LPAREN", "loweridSeq", "RPAREN", "stmtBlock"]
  ],
  ["defFunction",
    ["FUNCTION", "LPAREN", "loweridSeq", "RPAREN", "stmtBlock"]
  ],
  ["defType",
    ["FUNCTION", "LPAREN", "loweridSeq", "RPAREN", "stmtBlock"],
    ["TYPE", "UPPERID", "IS", "RECORD", "LBRACE", ["*", ["fieldDeclaration"]], "RBRACE"],
    ["TYPE", "UPPERID", "IS", "VARIANT", "LBRACE", ["*", ["constructorDeclaration"]], "RBRACE"]
  ],
  ["constructorDeclaration",
    ["CASE", "UPPERID", "LBRACE", ["*", ["fieldDeclaration"]], "RBRACE"]
  ],
  ["fieldDeclaration",
    ["FIELD", "LOWERID"]
  ],
  ["loweridSeq",
    ["EMPTY"],
    ["nonEmptyLoweridSeq"]
  ],
  ["nonEmptyLoweridSeq",
    ["LOWERID", ["?", ["COMMA", "nonEmptyLoweridSeq"]]]
  ],
  ["statement",
    ["stmtEllipsis"],
    ["stmtBlock"],
    ["stmtReturn"],
    ["stmtIf"],
    ["stmtRepeat"],
    ["stmtForeach"],
    ["stmtWhile"],
    ["stmtSwitch"],
    ["stmtLet"],
    ["stmtVariableAssignment"],
    ["stmtProcedureCall"]
  ],
  ["stmtEllipsis",
    ["ELLIPSIS"]
  ],
  ["stmtBlock",
    ["LBRACE", ["*", ["statement", ["?", ["SEMICOLON"]]]], "RBRACE"]
  ],
  ["stmtReturn",
    ["RETURN", "LPAREN", "nonEmptyExpressionSeq", "RPAREN"]
  ],
  ["stmtIf",
    ["IF", "stmtIfBranch", ["*", ["ELSEIF", "stmtIfBranch"]], ["?", ["ELSE", "stmtBlock"]]]
  ],
  ["stmtIfBranch",
    ["LPAREN", "expression", "RPAREN", ["?", ["THEN"]], "stmtBlock"]
  ],
  ["stmtRepeat",
    ["REPEAT", "LPAREN", "expression", "RPAREN", "stmtBlock"]
  ],
  ["stmtForeach",
    ["FOREACH", "pattern", "IN", "expression", "stmtBlock"]
  ],
  ["stmtWhile",
    ["WHILE", "LPAREN", "expression", "RPAREN", "stmtBlock"]
  ],
  ["stmtSwitch",
    ["SWITCH", "LPAREN", "expression", "RPAREN", ["?", ["TO"]], "LBRACE", ["*", ["stmtSwitchBranch"]], "RBRACE"]
  ],
  ["stmtSwitchBranch",
    ["pattern", "ARROW", "stmtBlock"]
  ],
  ["stmtLet",
    ["LET", "stmtVariableAssignment"],
    ["LET", "stmtTupleAssignment"]
  ],
  ["stmtVariableAssignment",
    ["LOWERID", "ASSIGN", "expression"]
  ],
  ["stmtTupleAssignment",
    ["LPAREN", "RPAREN", "ASSIGN", "expression"],
    ["LPAREN", "LOWERID", "COMMA", "nonEmptyLoweridSeq", "RPAREN", "ASSIGN", "expression"]
  ],
  ["stmtProcedureCall",
    ["UPPERID", "LPAREN", "expressionSeq", "RPAREN"]
  ],
  ["pattern",
    ["patternWildcard"],
    ["patternVariable"],
    ["patternNumber"],
    ["patternStructure"],
    ["patternTuple"],
    ["patternTimeout"]
  ],
  ["patternWildcard",
    ["UNDERSCORE"]
  ],
  ["patternVariable",
    ["LOWERID"]
  ],
  ["patternNumber",
    ["NUM"],
    ["MINUS", "NUM"]
  ],
  ["patternStructure",
    ["UPPERID", ["?", ["LPAREN", "loweridSeq", "RPAREN"]]]
  ],
  ["patternTuple",
    ["LPAREN", "RPAREN"],
    ["LPAREN", "LOWERID", "COMMA", "nonEmptyLoweridSeq", "RPAREN"]
  ],
  ["patternTimeout",
    ["TIMEOUT", "LPAREN", "NUM", "RPAREN"]
  ],
  ["expression",
    ["exprAtom"],
    ["expression", "infixOperator", "expression"],
    ["prefixOperator", "expression"],
    ["LPAREN", "expression", "RPAREN"]
  ],
  ["exprAtom",
    ["exprEllipsis"],
    ["exprVariable"],
    ["exprFunctionCall"],
    ["exprConstantNumber"],
    ["exprConstantString"],
    ["exprChoose"],
    ["exprMatching"],
    ["exprList"],
    ["exprRange"],
    ["exprTuple"],
    ["exprStructure"],
    ["exprStructureUpdate"]
  ],
  ["exprEllipsis",
    ["ELLIPSIS"]
  ],
  ["exprVariable",
    ["LOWERID"]
  ],
  ["exprFunctionCall",
    ["LOWERID", "LPAREN", "expressionSeq", "RPAREN"]
  ],
  ["exprConstantNumber",
    ["NUM"]
  ],
  ["exprConstantString",
    ["STRING"]
  ],
  ["exprChoose",
    ["CHOOSE",
      ["*", ["expression", "WHEN", "LPAREN", "expression", "RPAREN"]],
      "expression", "OTHERWISE"]
  ],
  ["exprMatching",
    ["MATCHING", "expression", "SELECT",
      ["*", ["expression", "ON", "pattern"]],
      "expression", "OTHERWISE"]
  ],
  ["exprList",
    ["LBRACK", "expressionSeq", "RBRACK"]
  ],
  ["exprRange",
    ["LBRACK", "expression", "RANGE", "expression", "RBRACK"],
    ["LBRACK", "expression", "COMMA", "expression", "RANGE", "expression", "RBRACK"]
  ],
  ["exprTuple",
    ["LPAREN", "RPAREN"],
    ["LPAREN", "expression", "COMMA", "nonEmptyExpressionSeq", "RPAREN"]
  ],
  ["exprStructure",
    ["UPPERID", ["?", ["LPAREN", "fieldBindingSeq", "RPAREN"]]]
  ],
  ["exprStructureUpdate",
    ["UPPERID", "LPAREN", "expression", "PIPE", "fieldBindingSeq", "RPAREN"]
  ],
  ["fieldBinding",
    ["LOWERID", "GETS", "expression"]
  ],
  ["fieldBindingSeq",
    ["EMPTY"],
    ["nonEmptyFieldBindingSeq"]
  ],
  ["nonEmptyFieldBindingSeq",
    ["fieldBinding", ["?", ["COMMA", "nonEmptyFieldBindingSeq"]]]
  ],
  ["infixOperator",
    ["OR"],
    ["AND"],
    ["EQ"],
    ["NE"],
    ["LE"],
    ["GE"],
    ["LT"],
    ["GT"],
    ["CONCAT"],
    ["PLUS"],
    ["MINUS"],
    ["TIMES"],
    ["DIV"],
    ["MOD"],
    ["POW"]
  ],
  ["prefixOperator",
    ["MINUS"],
    ["NOT"]
  ],
  ["expressionSeq",
    ["EMPTY"],
    ["nonEmptyExpressionSeq"]
  ],
  ["nonEmptyExpressionSeq",
    ["expression", ["?", ["COMMA", "nonEmptyExpressionSeq"]]]
  ]
]
