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