/** A dictionary of binary operator token types, their associated precedence and evaluation functions */
Parser.BinaryOperators = {}
Parser.BinaryOperators[Parser.TokenType.LogicalOR] = {
evaluate: function(left, right) { return left || right; },
precedence: 15
}
Parser.BinaryOperators[Parser.TokenType.LogicalAND] = {
evaluate: function(left, right) { return left && right; },
precedence: 14
}
Parser.BinaryOperators[Parser.TokenType.BitwiseOR] = {
evaluate: function(left, right) { return left | right; },
precedence: 13
}
Parser.BinaryOperators[Parser.TokenType.BitwiseXOR] = {
evaluate: function(left, right) { return left ^ right; },
precedence: 12
}
Parser.BinaryOperators[Parser.TokenType.BitwiseAND] = {
evaluate: function(left, right) { return left & right; },
precedence: 11
}
Parser.BinaryOperators[Parser.TokenType.Equals] = {
evaluate: function(left, right) { return left == right; },
precedence: 10
}
Parser.BinaryOperators[Parser.TokenType.NotEquals] = {
evaluate: function(left, right) { return left != right; },
precedence: 10
}
Parser.BinaryOperators[Parser.TokenType.LessThan] = {
evaluate: function(left, right) { return left < right; },
precedence: 9
}
Parser.BinaryOperators[Parser.TokenType.LessEqual] = {
evaluate: function(left, right) { return left <= right; },
precedence: 9
}
Parser.BinaryOperators[Parser.TokenType.GreaterThan] = {
evaluate: function(left, right) { return left > right; },
precedence: 9
}
Parser.BinaryOperators[Parser.TokenType.GreaterEqual] = {
evaluate: function(left, right) { return left >= right; },
precedence: 9
}
Parser.BinaryOperators[Parser.TokenType.LogicalShiftLeft] = {
evaluate: function(left, right) { return left << right; },
precedence: 7
}
Parser.BinaryOperators[Parser.TokenType.LogicalShiftRight] = {
evaluate: function(left, right) { return left >>> right; },
precedence: 7
}
Parser.BinaryOperators[Parser.TokenType.ArithmeticShiftRight] = {
evaluate: function(left, right) { return left >> right; },
precedence: 7
}
Parser.BinaryOperators[Parser.TokenType.Addition] = {
evaluate: function(left, right) { return left + right; },
precedence: 6
}
Parser.BinaryOperators[Parser.TokenType.Subtraction] = {
evaluate: function(left, right) { return left - right; },
precedence: 6
}
Parser.BinaryOperators[Parser.TokenType.Multiplication] = {
evaluate: function(left, right) { return left * right; },
precedence: 5
}
Parser.BinaryOperators[Parser.TokenType.Division] = {
evaluate: function(left, right) { return Math.trunc(left / right); },
precedence: 5
}
Parser.BinaryOperators[Parser.TokenType.Remainder] = {
evaluate: function(left, right) { return left % right; },
precedence: 5
}
/** A dictionary of unary operator token types and their evaluation functions */
Parser.UnaryOperators = {}
Parser.UnaryOperators[Parser.TokenType.Addition] = function(op) { return op; };
Parser.UnaryOperators[Parser.TokenType.Subtraction] = function(op) { return -op; };
Parser.UnaryOperators[Parser.TokenType.BitwiseNOT] = function(op) { return ~op; };
/** A dictionary of builtin function names and their evaluation functions */
Parser.Builtins = {
'lo16': function(op) { return op[0] & 65535; },
'hi16': function(op) { return (op[0] >>> 16) & 65535; }
}
/** Parser for (constant) expressions
* @constructor
* @param {Parser.TokenStream} tokenStream The stream of tokens to parse
* @param {array} symbols An array of pre-defined symbols
*/
Parser.ExprParser = function(tokenStream, symbols) {
this.tokenStream = tokenStream;
this.symbols = symbols || {};
/** Parse a primary expression
* A primary expression is a number literal or a parenthesized expression.
* @returns {number} The value of the expression
*/
this.parsePrimaryExpression = function() {
if (this.tokenStream.checkNext(Parser.TokenType.LParen)) {
this.tokenStream.consume();
let expr = this.parseExpression();
this.tokenStream.consume(Parser.TokenType.RParen);
return expr;
} else if (this.tokenStream.checkNext(Parser.TokenType.Identifier)) {
let symbol = this.tokenStream.consume();
if (symbol.value in this.symbols) {
return this.symbols[symbol.value];
} else {
throw new Parser.UnknownSymbolError(symbol);
}
} else {
let number = this.tokenStream.consume(Parser.TokenType.Number);
return number.value;
}
}
/** Parse a postfix expression
* A postfix expression is a builtin function call or a primary expression.
* @returns {number} The value of the expression
*/
this.parsePostfixExpression = function() {
let token= this.tokenStream.lookahead();
if (token.type == Parser.TokenType.Identifier &&
this.tokenStream.checkNext(Parser.TokenType.LParen, 1)) {
if (token.value in Parser.Builtins) {
let builtinFunction = Parser.Builtins[token.value];
this.tokenStream.consume();
this.tokenStream.consume(Parser.TokenType.LParen);
let params = [this.parseExpression()];
while (this.tokenStream.checkNext(Parser.TokenType.Comma)) {
this.tokenStream.consume();
params.push(this.parseExpression());
}
this.tokenStream.consume(Parser.TokenType.RParen);
return builtinFunction(params);
} else {
throw new Parser.ParseError('Unknown builtin function', token);
}
} else {
return this.parsePrimaryExpression();
}
}
/** Parse an optional unary operator
* @returns {(function|undefined)} The function used to evaluate the operator,
* or undefined if no operator is present.
*/
this.parseUnaryOperator = function() {
let operator = this.tokenStream.lookahead();
if (operator.type in Parser.UnaryOperators) {
this.tokenStream.consume();
return Parser.UnaryOperators[operator.type];
} else {
return undefined;
}
}
/** Parse an unary expression
* An unary expression is either a unary operator followed by an unary expression or a postfix expression.
* @returns {number} The value of the expression
*/
this.parseUnaryExpression = function() {
let operator = this.parseUnaryOperator();
if (operator) {
let operand = this.parseUnaryExpression();
return operator(operand);
} else {
return this.parsePostfixExpression();
}
}
/** Parse an optional binary operator
* @returns {(Object|undefined)} The description of the binary operator,
* or undefined if no operator is present.
*/
this.parseBinaryOperator = function() {
let operator = this.tokenStream.lookahead();
if (operator.type in Parser.BinaryOperators) {
this.tokenStream.consume();
return Parser.BinaryOperators[operator.type];
} else {
return undefined;
}
}
/** Parse a binary expression
* A binary expression is either an unary expression or a binary expression followed by a binary operator and another binary expression.
*
* Precedence of operations is taken into account.
*
* @returns {number} The value of the expression
*/
this.parseBinaryExpression = function() {
let lhs = this.parseUnaryExpression();
let leftOperator = this.parseBinaryOperator();
if (leftOperator) {
let rhs = this.parseUnaryExpression();
let operands = [];
let operators = [];
let rightOperator = this.parseBinaryOperator();
while (rightOperator) {
if (rightOperator.precedence < leftOperator.precedence) {
/* Must evaluate the right side first => push */
operands.push(lhs);
operators.push(leftOperator);
lhs = rhs;
} else {
/* No right side or left side has higher precedence => evaluate left side */
lhs = leftOperator.evaluate(lhs, rhs);
}
leftOperator = rightOperator;
rhs = this.parseUnaryExpression();
rightOperator = this.parseBinaryOperator();
}
/* Perform all the pending operations */
while (leftOperator) {
rhs = leftOperator.evaluate(lhs, rhs);
lhs = operands.pop();
leftOperator = operators.pop();
}
return rhs;
} else {
return lhs;
}
}
this.parseConditionalExpression = function() {
let cond = this.parseBinaryExpression();
if (this.tokenStream.checkNext(Parser.TokenType.QuestionMark)) {
this.tokenStream.consume();
let trueValue = this.parseExpression();
this.tokenStream.consume(Parser.TokenType.Colon);
let falseValue = this.parseConditionalExpression();
return (cond?trueValue:falseValue);
} else {
return cond;
}
}
/** Parse an expression
* An expression is a binary expression.
*
* This function is here to allow extension by other expression types.
* Use this function instead of the more specific functions if you want to
* parse general expressions.
*
* @returns {number} The value of the expression
*/
this.parseExpression = function() {
return this.parseConditionalExpression();
}
}
/** Create an {@link Parser.ExprParser} from a string
* @param {string} input The string to parse
* @param {array} symbols An array of pre-defined symbols
* @returns {Parser.ExprParser} The expression parser for parsing the string
*/
Parser.exprParserFromString = function(input, symbols) {
let tokenStream = Parser.tokenStreamFromString(input);
return new Parser.ExprParser(tokenStream, symbols);
}