{ let operators = [{ associativity: "left", operators: ["call"] }, { associativity: "none", unary: true, operators: ["numericalNegate"] }, { associativity: "none", operators: ["hasAttribute"] }, { associativity: "right", operators: ["++"] }, { associativity: "left", operators: ["*", "/"] }, { associativity: "left", operators: ["+", "-"] }, { associativity: "none", unary: true, operators: ["booleanNegate"] }, { associativity: "right", operators: ["//"] }, { associativity: "left", operators: ["<", "<=", ">", ">="] }, { // NOTE: Special case, can only occur once in sequence! associativity: "none", operators: ["==", "!="] }, { associativity: "left", operators: ["&&"] }, { associativity: "left", operators: ["||"] }, { // NOTE: Special case, can only occur once in sequence! associativity: "none", operators: ["->"] }]; function concatRepeat(first, rest, restIndex) { return [first].concat(rest.map(function(item) { return item[restIndex]; })); } function maybeReorderOperatorExpressions(node) { if (node.type === "operatorExpression") { return reorderOperatorExpressions(node, operators); } else { return node; } } } start = _ expression:reorderedExpression _ { return expression; } // Character classes whitespace = "\t" / "\n" / "\r" / space space = " " stringLiteralCharacter = !('"' / "\\") char:. { return char; } / "\\" escapableCharacter numberLiteralCharacter = [0-9.] escapableCharacter = '"' / "n" / "t" / "r" commentCharacter = [^\n\r] // Literals stringLiteral = '"' chars:stringLiteralCharacter+ '"' { return {type: "stringLiteral", value: chars.join("")} } numberLiteral = chars:numberLiteralCharacter+ { return {type: "numberLiteral", value: chars.join("")} } // Utilities _ = (whitespace / comment)* {} // Language constructs reorderedExpression = expression:expression { return maybeReorderOperatorExpressions(expression); } expression = numericallyNegatedOperatorExpression / booleanNegatedOperatorExpression / binaryOperatorExpression / functionCallOperatorExpression / nonOperatorExpression nonOperatorExpression = group / letBlock / stringLiteral / numberLiteral / functionDefinition / recursiveSet / set / identifier comment = "#" chars:commentCharacter* _ { return {type: "comment", text: chars.join("")} } functionCallOperatorExpression = left:nonOperatorExpression space right:expression { return {type: "operatorExpression", operator: "call", left: left, right: right} } hasAttributeOperatorExpression = left:nonOperatorExpression _ "?" _ right:identifier { return {type: "operatorExpression", operator: "?", left: left, right: right} } // FIXME: attribute path binaryOperatorExpression = left:nonOperatorExpression _ operator:("++" / "+" / "-" / "*" / "/" / "//" / "<" / "<=" / ">" / ">=" / "&&" / "||" / "==" / "!=" / "->") _ right:expression { return {type: "operatorExpression", operator: operator, left: left, right: right} } numericallyNegatedOperatorExpression = "-" _ right:expression { return {type: "operatorExpression", operator: "numericalNegate", right: right} } booleanNegatedOperatorExpression = "!" _ right:expression { return {type: "operatorExpression", operator: "booleanNegate", right: right} } identifier = literal:stringLiteral { return {type: "identifier", identifier: literal.value} } / chars:[a-z.]i+ { return {type: "identifier", identifier: text()} } group = "(" _ expression:reorderedExpression _ ")" { return {type: "group", expression: expression} } functionDefinition = argument:functionDefinitionArgument _ ":" _ body:reorderedExpression { return {type: "functionDefinition", argument: argument, body: body} } functionDefinitionArgument = identifier / setPattern assignment = _ identifier:identifier _ "=" _ expression:reorderedExpression _ ";" { return {type: "assignment", identifier: identifier, expression: expression} } assignmentList = items:assignment* { return items; } bindingList = items:assignment* { return {type: "bindings", assignments: items} } set = "{" _ items:assignmentList _ "}" { return {type: "set", assignments: items} } recursiveSet = "rec" _ setData:set { return {type: "recursiveSet", assignments: setData.assignments} } letBlock = "let" _ bindingList:bindingList _ "in" _ expression:reorderedExpression { return {type: "letBlock", bindings: bindingList.assignments, expression: expression} } setPattern = "{" _ args:setPatternVariableList _ "}" { return {type: "setPattern", variables: args} } setPatternVariableList = firstItem:setPatternVariable otherItems:(_ "," _ setPatternVariable)* { return concatRepeat(firstItem, otherItems, 3); } setPatternVariable = restParameter / identifier restParameter = "..." { return {type: "restParameter"} }