Program = exprs:(ContinuationExpression / _)* { return exprs.filter(x => !Array.isArray(x)); } ContinuationExpression = RecordExpression / SelectExpression / OffsetExpression / ApplicationExpression / FixExpression / SwitchExpression / PrimitiveOperationExpression SelectExpression = SELECT _? LPAREN _? record:Integer _? COMMA _? val:Value _? COMMA _? bind:Identifier _? COMMA _? continuation:ContinuationExpression _? RPAREN { return { select: { record, val, bind, continuation } }; } OffsetExpression = OFFSET _? LPAREN _? index:Integer _? COMMA _? val:Value _? COMMA _? bind:Identifier _? continuation:ContinuationExpression _? RPAREN { return { offset: { index, val, bind, continuation } }; } IdentifierList = LBRACKET _? identifiers:(Identifier _? COMMA _?)* _? lastIdent:Identifier? _? RBRACKET { return identifiers.length || lastIdent ? [...identifiers.map(x => x[0]), lastIdent] : []; } ValueList = LBRACKET _? values:(Value _? COMMA _?)* _? lastValue:Value? _? RBRACKET { return values.length || lastValue ? [...values.map(x => x[0]), lastValue] : []; } SwitchExpression = SWITCH _? LPAREN _? switchIndex:Value _? COMMA _? continuations:ContinuationList _? RPAREN { return { switch: { switchIndex, continuations } }; } ApplicationExpression = APP _? LPAREN _? fn:Value _? COMMA _? args:ValueList _? RPAREN { return { application: { fn, args } }; } FixBinding = LPAREN _? fn:Identifier _? COMMA _? args:IdentifierList _? COMMA _? continuation:ContinuationExpression _? RPAREN FixBindingList = LBRACKET _? bindings:(FixBinding _? COMMA _?)* _? lastBinding:FixBinding? _? RBRACKET { return bindings.length || lastBinding ? [...bindings.map(x => x[0]), lastBinding] : []; } FixExpression = FIX _? LPAREN _? fixBindings:FixBindingList _? COMMA _? continuation:ContinuationExpression _? RPAREN { return { fix: { fixBindings, continuation } }; } ContinuationList = LBRACKET _? continuations:(ContinuationExpression _? COMMA _?)* _? lastContinuation:ContinuationExpression? _? RBRACKET { return lastContinuation || continuations.length ? [...continuations.map(x => x[0]), lastContinuation] : []; } PrimitiveOperationExpression = PRIMOP _? LPAREN _? opr:PrimitiveOperation _? COMMA _? operands:ValueList _? COMMA _? resultBindings:IdentifierList _? COMMA _? continuations:ContinuationList _? RPAREN { return { primitiveOperation: { opr, operands, resultBindings, continuations }, }; } RecordExpressionTuple = LPAREN _? variable:VarStatement _? COMMA _? offset:OffsetStatement _? RPAREN { return { variable, offset }; } RecordExpressionTupleList = LBRACKET _? records:(RecordExpressionTuple _? COMMA _?)* _? lastRecord:RecordExpressionTuple? _? RBRACKET { return records.length || lastRecord ? [...records.map(x => x[0]), lastRecord] : []; } RecordExpression = RECORD _? LPAREN _? records:RecordExpressionTupleList _? COMMA _? address:Literal _? COMMA _? body:ContinuationExpression _? RPAREN { return { record: { records, address, body, }, }; } Value = VarStatement / LabelStatement / IntStatement / RealStatement / StringStatement VarStatement = VAR _ ident:Identifier { return ident; } LabelStatement = LABEL _ ident:Identifier { return ident; } IntStatement = INT _ int:Integer { return int; } RealStatement = REAL _ real:Real { return real; } StringStatement = STRING _ string:QuotedString { return string; } AccessStatement = OffsetStatement / SelectStatement OffsetStatement = OFFP _ offset:Integer { return offset; } SelectStatement = SELP _ offset:Integer { return offset; } Identifier = name:([A-Za-z] (LETTER / DIGIT / SAFE_SYMBOL)*) { return { name: name[0] + name[1].join('') }; } PrimitiveOperation = ArithmeticOperation / ComparisonOperation / BitOperation / StoreOperation StoreOperation = STORE / UPDATE / MAKEREF / MAKEREFUNBOXED / UNBOXED_UPDATE / BOXED / SUBSCRIPT ArithmeticOperation = "+" / "-" / "/" / "*" / "**" / "%" BitOperation = ">>" / "<<" / "~" / "^" ComparisonOperation = "==" / "<=" / ">=" / "!=" / "!" / ">" / "<" / "||" Integer = digits:("-"? [0-9]+) !"." { return { int: parseInt(digits.join(''), 10) }; } QuotedString = "'" content:[^']* "'" { return content.join(''); } / "\"" content:[^"]* "\"" { return content.join(''); } Real = value:("-"? [0-9]+ ("." [0-9]+)?) { return { real: parseFloat( value.map(x => (Array.isArray(x) ? x.join('') : x)).join(''), ) }; } Literal = Real / QuotedString / Integer OFFSET = "OFFSET" OFFP = "OFFp" SELP = "SELp" VAR = "VAR" INT = "INT" REAL = "REAL" STRING = "STRING" APP = "APP" RECORD = "RECORD" SELECT = "SELECT" FIX = "FIX" SWITCH = "SWITCH" PRIMOP = "PRIMOP" LABEL = "LABEL" STORE = "store" UPDATE = "update" MAKEREF = "makeref" MAKEREFUNBOXED = "makerefunboxed" UNBOXED_UPDATE = "unboxedupdate" SUBSCRIPT = "subscript" BOXED = "boxed" LETTER = [A-Za-z] SAFE_SYMBOL = "_" DIGIT = [0-9] LBRACKET = "[" RBRACKET = "]" COMMA = "," EQUALS = "=" LPAREN = "(" RPAREN = ")" _ = (" " / "\n" / "\t" / "\r\n")+