You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
4.7 KiB
JavaScript

{
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"} }