From 31ea87c070120de6868eaff9bb917b94aa5d433b Mon Sep 17 00:00:00 2001 From: Ben March Date: Thu, 25 Jun 2015 18:26:12 -0400 Subject: [PATCH] switched to ES2015 modules + babelify instead of checking for environment, and linting --- .gitignore | 1 + .jshintrc | 2 +- bower.json | 2 +- dist/spel2js.js | 5428 ++++++++++----------- dist/spel2js.min.js | 4 +- gruntfile.js | 47 +- package.json | 5 +- src/SpelExpressionEvaluator.js | 69 +- src/SpelExpressionParser.js | 1724 ++++--- src/StandardContext.js | 63 +- src/Token.js | 110 +- src/TokenKind.js | 166 +- src/Tokenizer.js | 991 ++-- src/ast/Assign.js | 46 +- src/ast/BooleanLiteral.js | 40 +- src/ast/CompoundExpression.js | 98 +- src/ast/Elvis.js | 32 +- src/ast/FunctionReference.js | 58 +- src/ast/Indexer.js | 79 +- src/ast/InlineList.js | 39 +- src/ast/InlineMap.js | 68 +- src/ast/MethodReference.js | 112 +- src/ast/NullLiteral.js | 33 +- src/ast/NumberLiteral.js | 40 +- src/ast/OpAnd.js | 34 +- src/ast/OpDec.js | 42 +- src/ast/OpDivide.js | 32 +- src/ast/OpEQ.js | 32 +- src/ast/OpGE.js | 32 +- src/ast/OpGT.js | 32 +- src/ast/OpInc.js | 42 +- src/ast/OpLE.js | 32 +- src/ast/OpLT.js | 32 +- src/ast/OpMinus.js | 32 +- src/ast/OpModulus.js | 32 +- src/ast/OpMultiply.js | 58 +- src/ast/OpNE.js | 33 +- src/ast/OpNot.js | 32 +- src/ast/OpOr.js | 34 +- src/ast/OpPlus.js | 34 +- src/ast/OpPower.js | 32 +- src/ast/Projection.js | 82 +- src/ast/PropertyReference.js | 98 +- src/ast/RootNode.js | 38 +- src/ast/Selection.js | 172 +- src/ast/SpelNode.js | 146 +- src/ast/StringLiteral.js | 56 +- src/ast/Ternary.js | 32 +- src/ast/VariableReference.js | 86 +- src/lib/Stack.js | 2 +- src/main.js | 4 + test/karma.conf.js | 24 +- test/spec/SpelExpressionEvaluator.spec.js | 370 +- test/spec/Tokenizer.spec.js | 119 +- 54 files changed, 5215 insertions(+), 5868 deletions(-) create mode 100644 src/main.js diff --git a/.gitignore b/.gitignore index 4f4f716..1af479e 100755 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ bower_components/ # generated files # ###################### coverage/ +complexity/ .idea/ .DS_Store .DS_Store? diff --git a/.jshintrc b/.jshintrc index 05c57ca..31ac331 100755 --- a/.jshintrc +++ b/.jshintrc @@ -2,7 +2,7 @@ "node": true, "browser": true, "esnext": true, - "bitwise": true, + "bitwise": false, "camelcase": true, "curly": true, "eqeqeq": true, diff --git a/bower.json b/bower.json index 26a6453..2988d43 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "spel2js", - "version": "0.1.0", + "version": "0.2.0", "description": "Parse Spring Expression Language in JavaScript", "main": "dist/spel2js.js", "author": { diff --git a/dist/spel2js.js b/dist/spel2js.js index f64fe0a..cabf264 100644 --- a/dist/spel2js.js +++ b/dist/spel2js.js @@ -1,3418 +1,3260 @@ -function Stack(startingElements) { - this.elements = startingElements || []; +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.spel2js = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0; -}; +},{"./SpelExpressionParser":2,"./lib/Stack":43}],2:[function(require,module,exports){ +'use strict'; -Stack.prototype.search = function (el) { - return this.elements.length - this.elements.indexOf(el); -}; +Object.defineProperty(exports, '__esModule', { + value: true +}); + +var _TokenKind = require('./TokenKind'); -(function (exports) { - 'use strict'; +var _Tokenizer = require('./Tokenizer'); - // ordered by priority - operands first - var types = { +var _astRootNode = require('./ast/RootNode'); - LITERAL_INT: 1, //tested +var _astBooleanLiteral = require('./ast/BooleanLiteral'); - LITERAL_LONG: 2, //tested +var _astNumberLiteral = require('./ast/NumberLiteral'); - LITERAL_HEXINT: 3, //tested +var _astStringLiteral = require('./ast/StringLiteral'); - LITERAL_HEXLONG: 4, //tested +var _astNullLiteral = require('./ast/NullLiteral'); - LITERAL_STRING: 5, //tested +var _astFunctionReference = require('./ast/FunctionReference'); - LITERAL_REAL: 6, //tested +var _astMethodReference = require('./ast/MethodReference'); - LITERAL_REAL_FLOAT: 7, //tested +var _astPropertyReference = require('./ast/PropertyReference'); - LPAREN: "(", //tested +var _astVariableReference = require('./ast/VariableReference'); - RPAREN: ")", //tested +var _astCompoundExpression = require('./ast/CompoundExpression'); - COMMA: ",", //tested +var _astIndexer = require('./ast/Indexer'); - IDENTIFIER: 0, //tested +var _astAssign = require('./ast/Assign'); - COLON: ":", //tested +var _astOpEQ = require('./ast/OpEQ'); - HASH: "#", //tested +var _astOpNE = require('./ast/OpNE'); - RSQUARE: "]", //tested +var _astOpGE = require('./ast/OpGE'); - LSQUARE: "[", //tested +var _astOpGT = require('./ast/OpGT'); - LCURLY: "{", //tested +var _astOpLE = require('./ast/OpLE'); - RCURLY: "}", //tested +var _astOpLT = require('./ast/OpLT'); - DOT: ".", //tested +var _astOpPlus = require('./ast/OpPlus'); - PLUS: "+", //tested +var _astOpMinus = require('./ast/OpMinus'); - STAR: "*", //tested +var _astOpMultiply = require('./ast/OpMultiply'); - MINUS: "-", //tested +var _astOpDivide = require('./ast/OpDivide'); - SELECT_FIRST: "^[", //tested +var _astOpModulus = require('./ast/OpModulus'); - SELECT_LAST: "$[", //tested +var _astOpPower = require('./ast/OpPower'); - QMARK: "?", //tested +var _astOpInc = require('./ast/OpInc'); - PROJECT: "![", //tested +var _astOpDec = require('./ast/OpDec'); - DIV: "/", //tested +var _astOpNot = require('./ast/OpNot'); - GE: ">=", //tested +var _astOpAnd = require('./ast/OpAnd'); - GT: ">", //tested +var _astOpOr = require('./ast/OpOr'); - LE: "<=", //tested +var _astTernary = require('./ast/Ternary'); - LT: "<", //tested +var _astElvis = require('./ast/Elvis'); - EQ: "==", //tested +var _astInlineList = require('./ast/InlineList'); - NE: "!=", //tested +var _astInlineMap = require('./ast/InlineMap'); - MOD: "%", //tested +var _astSelection = require('./ast/Selection'); - NOT: "!", //tested +var _astProjection = require('./ast/Projection'); - ASSIGN: "=", //tested +//not yet implemented +var OperatorInstanceof, OperatorMatches, OperatorBetween, BeanReference, TypeReference, QualifiedIdentifier, Identifier, ConstructorReference; - INSTANCEOF: "instanceof", //test fails +var SpelExpressionParser = function SpelExpressionParser() { - MATCHES: "matches", //test fails + var VALID_QUALIFIED_ID_PATTERN = new RegExp('[\\p{L}\\p{N}_$]+'); - BETWEEN: "between", //test fails + var configuration; - SELECT: "?[", //tested + // For rules that build nodes, they are stacked here for return + var constructedNodes = []; - POWER: "^", //tested + // The expression being parsed + var expressionString; - ELVIS: "?:", //tested + // The token stream constructed from that expression string + var tokenStream; - SAFE_NAVI: "?.", //tested + // length of a populated token stream + var tokenStreamLength; - BEAN_REF: "@", //tested + // Current location in the token stream when processing tokens + var tokenStreamPointer; - SYMBOLIC_OR: "||", //tested + /** + * Create a parser with some configured behavior. + * @param config custom configuration options + */ + function setConfiguration(config) { + configuration = config; + } + + function parse(expression, context) { + try { + expressionString = expression; + tokenStream = _Tokenizer.Tokenizer.tokenize(expression); + tokenStreamLength = tokenStream.length; + tokenStreamPointer = 0; + constructedNodes = []; + var ast = eatExpression(); + if (moreTokens()) { + raiseInternalException(peekToken().startPos, 'MORE_INPUT', nextToken().toString()); + } + //Assert.isTrue(this.constructedNodes.isEmpty()); + return ast; + } catch (e) { + throw e.message; + } + } - SYMBOLIC_AND: "&&", //tested + // expression + // : logicalOrExpression + // ( (ASSIGN^ logicalOrExpression) + // | (DEFAULT^ logicalOrExpression) + // | (QMARK^ expression COLON! expression) + // | (ELVIS^ expression))?; + function eatExpression() { + var expr = eatLogicalOrExpression(); + if (moreTokens()) { + var token = peekToken(); + if (token.getKind() === _TokenKind.TokenKind.ASSIGN) { + // a=b + if (expr === null) { + expr = _astNullLiteral.NullLiteral.create(toPosBounds(token.startPos - 1, token.endPos - 1)); + } + nextToken(); + var assignedValue = eatLogicalOrExpression(); + return _astAssign.Assign.create(toPosToken(token), expr, assignedValue); + } - INC: "++", //tested + if (token.getKind() === _TokenKind.TokenKind.ELVIS) { + // a?:b (a if it isn't null, otherwise b) + if (expr === null) { + expr = _astNullLiteral.NullLiteral.create(toPosBounds(token.startPos - 1, token.endPos - 2)); + } + nextToken(); // elvis has left the building + var valueIfNull = eatExpression(); + if (valueIfNull === null) { + valueIfNull = _astNullLiteral.NullLiteral.create(toPosBounds(token.startPos + 1, token.endPos + 1)); + } + return _astElvis.Elvis.create(toPosToken(token), expr, valueIfNull); + } - DEC: "--" //tested - }; + if (token.getKind() === _TokenKind.TokenKind.QMARK) { + // a?b:c + if (expr === null) { + expr = _astNullLiteral.NullLiteral.create(toPosBounds(token.startPos - 1, token.endPos - 1)); + } + nextToken(); + var ifTrueExprValue = eatExpression(); + eatToken(_TokenKind.TokenKind.COLON); + var ifFalseExprValue = eatExpression(); + return _astTernary.Ternary.create(toPosToken(token), expr, ifTrueExprValue, ifFalseExprValue); + } + } + return expr; + } - function TokenKind(type) { - this.type = type; - this.tokenChars = types[type]; - this._hasPayload = typeof types[type] !== 'string'; - if (typeof types[type] === 'number') { - this._ordinal = types[type]; + //logicalOrExpression : logicalAndExpression (OR^ logicalAndExpression)*; + function eatLogicalOrExpression() { + var expr = eatLogicalAndExpression(); + while (peekIdentifierToken('or') || peekTokenOne(_TokenKind.TokenKind.SYMBOLIC_OR)) { + var token = nextToken(); //consume OR + var rhExpr = eatLogicalAndExpression(); + checkOperands(token, expr, rhExpr); + expr = _astOpOr.OpOr.create(toPosToken(token), expr, rhExpr); } + return expr; } - //create enum - for (var t in types) { - if (types.hasOwnProperty(t)) { - TokenKind[t] = new TokenKind(t); + // logicalAndExpression : relationalExpression (AND^ relationalExpression)*; + function eatLogicalAndExpression() { + var expr = eatRelationalExpression(); + while (peekIdentifierToken('and') || peekTokenOne(_TokenKind.TokenKind.SYMBOLIC_AND)) { + var token = nextToken(); // consume 'AND' + var rhExpr = eatRelationalExpression(); + checkOperands(token, expr, rhExpr); + expr = _astOpAnd.OpAnd.create(toPosToken(token), expr, rhExpr); } + return expr; } - TokenKind.prototype.toString = function () { - return this.type + (this.tokenChars.length != 0 ? "(" + this.tokenChars + ")" : "") - }; + // relationalExpression : sumExpression (relationalOperator^ sumExpression)?; + function eatRelationalExpression() { + var expr = eatSumExpression(); + var relationalOperatorToken = maybeEatRelationalOperator(); + if (relationalOperatorToken !== null) { + var token = nextToken(); // consume relational operator token + var rhExpr = eatSumExpression(); + checkOperands(token, expr, rhExpr); + var tk = relationalOperatorToken.kind; - TokenKind.prototype.getLength = function () { - return this.tokenChars.length; - }; + if (relationalOperatorToken.isNumericRelationalOperator()) { + var pos = toPosToken(token); + if (tk === _TokenKind.TokenKind.GT) { + return _astOpGT.OpGT.create(pos, expr, rhExpr); + } + if (tk === _TokenKind.TokenKind.LT) { + return _astOpLT.OpLT.create(pos, expr, rhExpr); + } + if (tk === _TokenKind.TokenKind.LE) { + return _astOpLE.OpLE.create(pos, expr, rhExpr); + } + if (tk === _TokenKind.TokenKind.GE) { + return _astOpGE.OpGE.create(pos, expr, rhExpr); + } + if (tk === _TokenKind.TokenKind.EQ) { + return _astOpEQ.OpEQ.create(pos, expr, rhExpr); + } + //Assert.isTrue(tk === TokenKind.NE); + return _astOpNE.OpNE.create(pos, expr, rhExpr); + } - TokenKind.prototype.hasPayload = function () { - return this._hasPayload; - }; + if (tk === _TokenKind.TokenKind.INSTANCEOF) { + return new OperatorInstanceof(toPosToken(token), expr, rhExpr); + } - TokenKind.prototype.valueOf = function (id) { - for (var t in types) { - if (types.hasOwnProperty(t) && types[t] === id) { - return TokenKind[t]; + if (tk === _TokenKind.TokenKind.MATCHES) { + return new OperatorMatches(toPosToken(token), expr, rhExpr); } + + //Assert.isTrue(tk === TokenKind.BETWEEN); + return new OperatorBetween(toPosToken(token), expr, rhExpr); } - }; + return expr; + } - TokenKind.prototype.ordinal = function () { - return this._ordinal; - }; + //sumExpression: productExpression ( (PLUS^ | MINUS^) productExpression)*; + function eatSumExpression() { + var expr = eatProductExpression(); + while (peekTokenAny(_TokenKind.TokenKind.PLUS, _TokenKind.TokenKind.MINUS, _TokenKind.TokenKind.INC)) { + var token = nextToken(); //consume PLUS or MINUS or INC + var rhExpr = eatProductExpression(); + checkRightOperand(token, rhExpr); + if (token.getKind() === _TokenKind.TokenKind.PLUS) { + expr = _astOpPlus.OpPlus.create(toPosToken(token), expr, rhExpr); + } else if (token.getKind() === _TokenKind.TokenKind.MINUS) { + expr = _astOpMinus.OpMinus.create(toPosToken(token), expr, rhExpr); + } + } + return expr; + } - exports.TokenKind = TokenKind; + // productExpression: powerExpr ((STAR^ | DIV^| MOD^) powerExpr)* ; + function eatProductExpression() { + var expr = eatPowerIncDecExpression(); + while (peekTokenAny(_TokenKind.TokenKind.STAR, _TokenKind.TokenKind.DIV, _TokenKind.TokenKind.MOD)) { + var token = nextToken(); // consume STAR/DIV/MOD + var rhExpr = eatPowerIncDecExpression(); + checkOperands(token, expr, rhExpr); + if (token.getKind() === _TokenKind.TokenKind.STAR) { + expr = _astOpMultiply.OpMultiply.create(toPosToken(token), expr, rhExpr); + } else if (token.getKind() === _TokenKind.TokenKind.DIV) { + expr = _astOpDivide.OpDivide.create(toPosToken(token), expr, rhExpr); + } else { + //Assert.isTrue(token.getKind() === TokenKind.MOD); + expr = _astOpModulus.OpModulus.create(toPosToken(token), expr, rhExpr); + } + } + return expr; + } -}(window || exports)); + // powerExpr : unaryExpression (POWER^ unaryExpression)? (INC || DEC) ; + function eatPowerIncDecExpression() { + var expr = eatUnaryExpression(), + token; -(function (exports) { - 'use strict'; + if (peekTokenOne(_TokenKind.TokenKind.POWER)) { + token = nextToken(); //consume POWER + var rhExpr = eatUnaryExpression(); + checkRightOperand(token, rhExpr); + return _astOpPower.OpPower.create(toPosToken(token), expr, rhExpr); + } - var TokenKind; + if (expr !== null && peekTokenAny(_TokenKind.TokenKind.INC, _TokenKind.TokenKind.DEC)) { + token = nextToken(); //consume INC/DEC + if (token.getKind() === _TokenKind.TokenKind.INC) { + return _astOpInc.OpInc.create(toPosToken(token), true, expr); + } + return _astOpDec.OpDec.create(toPosToken(token), true, expr); + } - try { - TokenKind = require('./TokenKind').TokenKind; - } catch(e) { - TokenKind = exports.TokenKind; + return expr; } - function Token(tokenKind, tokenData, startPos, endPos) { - this.kind = tokenKind; - this.startPos = startPos; - this.endPos = endPos; - if (tokenData) { - this.data = tokenData; + // unaryExpression: (PLUS^ | MINUS^ | BANG^ | INC^ | DEC^) unaryExpression | primaryExpression ; + function eatUnaryExpression() { + var token, expr; + + if (peekTokenAny(_TokenKind.TokenKind.PLUS, _TokenKind.TokenKind.MINUS, _TokenKind.TokenKind.NOT)) { + token = nextToken(); + expr = eatUnaryExpression(); + if (token.getKind() === _TokenKind.TokenKind.NOT) { + return _astOpNot.OpNot.create(toPosToken(token), expr); + } + + if (token.getKind() === _TokenKind.TokenKind.PLUS) { + return _astOpPlus.OpPlus.create(toPosToken(token), expr); + } + //Assert.isTrue(token.getKind() === TokenKind.MINUS); + return _astOpMinus.OpMinus.create(toPosToken(token), expr); } - if (!endPos) { - debugger; + if (peekTokenAny(_TokenKind.TokenKind.INC, _TokenKind.TokenKind.DEC)) { + token = nextToken(); + expr = eatUnaryExpression(); + if (token.getKind() === _TokenKind.TokenKind.INC) { + return _astOpInc.OpInc.create(toPosToken(token), false, expr); + } + return _astOpDec.OpDec.create(toPosToken(token), false, expr); } + + return eatPrimaryExpression(); } - Token.prototype.getKind = function () { - return this.kind; - }; + // primaryExpression : startNode (node)? -> ^(EXPRESSION startNode (node)?); + function eatPrimaryExpression() { + var nodes = []; + var start = eatStartNode(); // always a start node + nodes.push(start); + while (maybeEatNode()) { + nodes.push(pop()); + } + if (nodes.length === 1) { + return nodes[0]; + } + return _astCompoundExpression.CompoundExpression.create(toPosBounds(start.getStartPosition(), nodes[nodes.length - 1].getEndPosition()), nodes); + } - Token.prototype.toString = function () { - var s = '['; - s += this.kind.toString(); - if (this.kind.hasPayload()) { - s += ':' + this.data; + // node : ((DOT dottedNode) | (SAFE_NAVI dottedNode) | nonDottedNode)+; + function maybeEatNode() { + var expr = null; + if (peekTokenAny(_TokenKind.TokenKind.DOT, _TokenKind.TokenKind.SAFE_NAVI)) { + expr = eatDottedNode(); + } else { + expr = maybeEatNonDottedNode(); } - s += ']'; - s += '(' + this.startPos + ',' + this.endPos + ')'; - return s; - }; - Token.prototype.isIdentifier = function () { - return (this.kind == TokenKind.IDENTIFIER); - }; + if (expr === null) { + return false; + } else { + push(expr); + return true; + } + } - Token.prototype.isNumericRelationalOperator = function () { - return (this.kind == TokenKind.GT || this.kind == TokenKind.GE || this.kind == TokenKind.LT || - this.kind == TokenKind.LE || this.kind==TokenKind.EQ || this.kind==TokenKind.NE); - }; + // nonDottedNode: indexer; + function maybeEatNonDottedNode() { + if (peekTokenOne(_TokenKind.TokenKind.LSQUARE)) { + if (maybeEatIndexer()) { + return pop(); + } + } + return null; + } + + //dottedNode + // : ((methodOrProperty + // | functionOrVar + // | projection + // | selection + // | firstSelection + // | lastSelection + // )) + // ; + function eatDottedNode() { + var token = nextToken(); // it was a '.' or a '?.' + var nullSafeNavigation = token.getKind() === _TokenKind.TokenKind.SAFE_NAVI; + if (maybeEatMethodOrProperty(nullSafeNavigation) || maybeEatFunctionOrVar() || maybeEatProjection(nullSafeNavigation) || maybeEatSelection(nullSafeNavigation)) { + return pop(); + } + if (peekToken() === null) { + // unexpectedly ran out of data + raiseInternalException(token.startPos, 'OOD'); + } else { + raiseInternalException(token.startPos, 'UNEXPECTED_DATA_AFTER_DOT', toString(peekToken())); + } + return null; + } + + // functionOrVar + // : (POUND ID LPAREN) => function + // | var + // + // function : POUND id=ID methodArgs -> ^(FUNCTIONREF[$id] methodArgs); + // var : POUND id=ID -> ^(VARIABLEREF[$id]); + function maybeEatFunctionOrVar() { + if (!peekTokenOne(_TokenKind.TokenKind.HASH)) { + return false; + } + var token = nextToken(); + var functionOrVariableName = eatToken(_TokenKind.TokenKind.IDENTIFIER); + var args = maybeEatMethodArgs(); + if (args === null) { + push(_astVariableReference.VariableReference.create(functionOrVariableName.data, toPosBounds(token.startPos, functionOrVariableName.endPos))); + return true; + } - Token.prototype.stringValue = function () { - return this.data; - }; + push(_astFunctionReference.FunctionReference.create(functionOrVariableName.data, toPosBounds(token.startPos, functionOrVariableName.endPos), args)); + return true; + } - Token.prototype.asInstanceOfToken = function () { - return new Token(TokenKind.INSTANCEOF, this.startPos, this.endPos); - }; + // methodArgs : LPAREN! (argument (COMMA! argument)* (COMMA!)?)? RPAREN!; + function maybeEatMethodArgs() { + if (!peekTokenOne(_TokenKind.TokenKind.LPAREN)) { + return null; + } + var args = []; + consumeArguments(args); + eatToken(_TokenKind.TokenKind.RPAREN); + return args; + } - Token.prototype.asMatchesToken = function () { - return new Token(TokenKind.MATCHES, this.startPos, this.endPos); - }; + function eatConstructorArgs(accumulatedArguments) { + if (!peekTokenOne(_TokenKind.TokenKind.LPAREN)) { + raiseInternalException(toPosToken(peekToken()), 'MISSING_CONSTRUCTOR_ARGS'); + } + consumeArguments(accumulatedArguments); + eatToken(_TokenKind.TokenKind.RPAREN); + } - Token.prototype.asBetweenToken = function () { - return new Token(TokenKind.BETWEEN, this.startPos, this.endPos); - }; + /** + * Used for consuming arguments for either a method or a constructor call + */ + function consumeArguments(accumulatedArguments) { + var pos = peekToken().startPos; + var next; + do { + nextToken(); // consume ( (first time through) or comma (subsequent times) + var token = peekToken(); + if (token === null) { + raiseInternalException(pos, 'RUN_OUT_OF_ARGUMENTS'); + } + if (token.getKind() !== _TokenKind.TokenKind.RPAREN) { + accumulatedArguments.push(eatExpression()); + } + next = peekToken(); + } while (next !== null && next.kind === _TokenKind.TokenKind.COMMA); + + if (next === null) { + raiseInternalException(pos, 'RUN_OUT_OF_ARGUMENTS'); + } + } + + function positionOf(token) { + if (token === null) { + // if null assume the problem is because the right token was + // not found at the end of the expression + return expressionString.length; + } + return token.startPos; + } + + //startNode + // : parenExpr | literal + // | type + // | methodOrProperty + // | functionOrVar + // | projection + // | selection + // | firstSelection + // | lastSelection + // | indexer + // | constructor + function eatStartNode() { + if (maybeEatLiteral()) { + return pop(); + } else if (maybeEatParenExpression()) { + return pop(); + } else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstructorReference() || maybeEatMethodOrProperty(false) || maybeEatFunctionOrVar()) { + return pop(); + } else if (maybeEatBeanReference()) { + return pop(); + } else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) { + return pop(); + } else if (maybeEatInlineListOrMap()) { + return pop(); + } else { + return null; + } + } - Token.prototype.getStartPosition = function () { - return this.startPos; - }; + // parse: @beanname @'bean.name' + // quoted if dotted + function maybeEatBeanReference() { + if (peekTokenOne(_TokenKind.TokenKind.BEAN_REF)) { + var beanRefToken = nextToken(); + var beanNameToken = null; + var beanName = null; + if (peekTokenOne(_TokenKind.TokenKind.IDENTIFIER)) { + beanNameToken = eatToken(_TokenKind.TokenKind.IDENTIFIER); + beanName = beanNameToken.data; + } else if (peekTokenOne(_TokenKind.TokenKind.LITERAL_STRING)) { + beanNameToken = eatToken(_TokenKind.TokenKind.LITERAL_STRING); + beanName = beanNameToken.stringValue(); + beanName = beanName.substring(1, beanName.length() - 1); + } else { + raiseInternalException(beanRefToken.startPos, 'INVALID_BEAN_REFERENCE'); + } - Token.prototype.getEndPosition = function () { - return this.endPos; - }; + var beanReference = new BeanReference(toPosToken(beanNameToken), beanName); + push(beanReference); + return true; + } + return false; + } - exports.Token = Token; + function maybeEatTypeReference() { + if (peekTokenOne(_TokenKind.TokenKind.IDENTIFIER)) { + var typeName = peekToken(); + if (typeName.stringValue() !== 'T') { + return false; + } + // It looks like a type reference but is T being used as a map key? + var token = nextToken(); + if (peekTokenOne(_TokenKind.TokenKind.RSQUARE)) { + // looks like 'T]' (T is map key) + push(_astPropertyReference.PropertyReference.create(token.stringValue(), toPosToken(token))); + return true; + } + eatToken(_TokenKind.TokenKind.LPAREN); + var node = eatPossiblyQualifiedId(); + // dotted qualified id + // Are there array dimensions? + var dims = 0; + while (peekTokenConsumeIfMatched(_TokenKind.TokenKind.LSQUARE, true)) { + eatToken(_TokenKind.TokenKind.RSQUARE); + dims++; + } + eatToken(_TokenKind.TokenKind.RPAREN); + push(new TypeReference(toPosToken(typeName), node, dims)); + return true; + } + return false; + } -}(window || exports)); + function maybeEatNullReference() { + if (peekTokenOne(_TokenKind.TokenKind.IDENTIFIER)) { + var nullToken = peekToken(); + if (nullToken.stringValue().toLowerCase() !== 'null') { + return false; + } + nextToken(); + push(_astNullLiteral.NullLiteral.create(toPosToken(nullToken))); + return true; + } + return false; + } -(function (exports) { - 'use strict'; + //projection: PROJECT^ expression RCURLY!; + function maybeEatProjection(nullSafeNavigation) { + var token = peekToken(); + if (!peekTokenConsumeIfMatched(_TokenKind.TokenKind.PROJECT, true)) { + return false; + } + var expr = eatExpression(); + eatToken(_TokenKind.TokenKind.RSQUARE); + push(_astProjection.Projection.create(nullSafeNavigation, toPosToken(token), expr)); + return true; + } - var ALTERNATIVE_OPERATOR_NAMES = ["DIV", "EQ", "GE", "GT", "LE", "LT", "MOD", "NE", "NOT"], - FLAGS = [], - IS_DIGIT = 1, - IS_HEXDIGIT = 2, - IS_ALPHA = 4, + // list = LCURLY (element (COMMA element)*) RCURLY + // map = LCURLY (key ':' value (COMMA key ':' value)*) RCURLY + function maybeEatInlineListOrMap() { + var token = peekToken(), + listElements = []; - Token, - TokenKind; + if (!peekTokenConsumeIfMatched(_TokenKind.TokenKind.LCURLY, true)) { + return false; + } + var expr = null; + var closingCurly = peekToken(); + if (peekTokenConsumeIfMatched(_TokenKind.TokenKind.RCURLY, true)) { + // empty list '{}' + expr = _astInlineList.InlineList.create(toPosBounds(token.startPos, closingCurly.endPos)); + } else if (peekTokenConsumeIfMatched(_TokenKind.TokenKind.COLON, true)) { + closingCurly = eatToken(_TokenKind.TokenKind.RCURLY); + // empty map '{:}' + expr = _astInlineMap.InlineMap.create(toPosBounds(token.startPos, closingCurly.endPos)); + } else { + var firstExpression = eatExpression(); + // Next is either: + // '}' - end of list + // ',' - more expressions in this list + // ':' - this is a map! + + if (peekTokenOne(_TokenKind.TokenKind.RCURLY)) { + // list with one item in it + listElements.push(firstExpression); + closingCurly = eatToken(_TokenKind.TokenKind.RCURLY); + expr = _astInlineList.InlineList.create(toPosBounds(token.startPos, closingCurly.endPos), listElements); + } else if (peekTokenConsumeIfMatched(_TokenKind.TokenKind.COMMA, true)) { + // multi item list + listElements.push(firstExpression); + do { + listElements.push(eatExpression()); + } while (peekTokenConsumeIfMatched(_TokenKind.TokenKind.COMMA, true)); + closingCurly = eatToken(_TokenKind.TokenKind.RCURLY); + expr = _astInlineList.InlineList.create(toPosToken(token.startPos, closingCurly.endPos), listElements); + } else if (peekTokenConsumeIfMatched(_TokenKind.TokenKind.COLON, true)) { + // map! + var mapElements = []; + mapElements.push(firstExpression); + mapElements.push(eatExpression()); + while (peekTokenConsumeIfMatched(_TokenKind.TokenKind.COMMA, true)) { + mapElements.push(eatExpression()); + eatToken(_TokenKind.TokenKind.COLON); + mapElements.push(eatExpression()); + } + closingCurly = eatToken(_TokenKind.TokenKind.RCURLY); + expr = _astInlineMap.InlineMap.create(toPosBounds(token.startPos, closingCurly.endPos), mapElements); + } else { + raiseInternalException(token.startPos, 'OOD'); + } + } + push(expr); + return true; + } - try { - Token = require('./Token').Token; - TokenKind = require('./TokenKind').TokenKind; - } catch(e) { - Token = exports.Token; - TokenKind = exports.TokenKind; + function maybeEatIndexer() { + var token = peekToken(); + if (!peekTokenConsumeIfMatched(_TokenKind.TokenKind.LSQUARE, true)) { + return false; + } + var expr = eatExpression(); + eatToken(_TokenKind.TokenKind.RSQUARE); + push(_astIndexer.Indexer.create(toPosToken(token), expr)); + return true; } - function init() { - var ch; + function maybeEatSelection(nullSafeNavigation) { + var token = peekToken(); + if (!peekSelectToken()) { + return false; + } + nextToken(); + var expr = eatExpression(); + if (expr === null) { + raiseInternalException(toPosToken(token), 'MISSING_SELECTION_EXPRESSION'); + } + eatToken(_TokenKind.TokenKind.RSQUARE); + if (token.getKind() === _TokenKind.TokenKind.SELECT_FIRST) { + push(_astSelection.Selection.create(nullSafeNavigation, _astSelection.Selection.FIRST, toPosToken(token), expr)); + } else if (token.getKind() === _TokenKind.TokenKind.SELECT_LAST) { + push(_astSelection.Selection.create(nullSafeNavigation, _astSelection.Selection.LAST, toPosToken(token), expr)); + } else { + push(_astSelection.Selection.create(nullSafeNavigation, _astSelection.Selection.ALL, toPosToken(token), expr)); + } + return true; + } + + /** + * Eat an identifier, possibly qualified (meaning that it is dotted). + * TODO AndyC Could create complete identifiers (a.b.c) here rather than a sequence of them? (a, b, c) + */ + function eatPossiblyQualifiedId() { + var qualifiedIdPieces = []; + var node = peekToken(); + while (isValidQualifiedId(node)) { + nextToken(); + if (node.kind !== _TokenKind.TokenKind.DOT) { + qualifiedIdPieces.push(new Identifier(node.stringValue(), toPosToken(node))); + } + node = peekToken(); + } + if (!qualifiedIdPieces.length) { + if (node === null) { + raiseInternalException(expressionString.length(), 'OOD'); + } + raiseInternalException(node.startPos, 'NOT_EXPECTED_TOKEN', 'qualified ID', node.getKind().toString().toLowerCase()); + } + var pos = toPosBounds(qualifiedIdPieces[0].getStartPosition(), qualifiedIdPieces[qualifiedIdPieces.length - 1].getEndPosition()); + return new QualifiedIdentifier(pos, qualifiedIdPieces); + } - for (ch = '0'.charCodeAt(0); ch <= '9'.charCodeAt(0); ch += 1) { - FLAGS[ch] |= IS_DIGIT | IS_HEXDIGIT; + function isValidQualifiedId(node) { + if (node === null || node.kind === _TokenKind.TokenKind.LITERAL_STRING) { + return false; + } + if (node.kind === _TokenKind.TokenKind.DOT || node.kind === _TokenKind.TokenKind.IDENTIFIER) { + return true; } - for (ch = 'A'.charCodeAt(0); ch <= 'F'.charCodeAt(0); ch += 1) { - FLAGS[ch] |= IS_HEXDIGIT; + var value = node.stringValue(); + return value.length && VALID_QUALIFIED_ID_PATTERN.test(value); + } + + // This is complicated due to the support for dollars in identifiers. Dollars are normally separate tokens but + // there we want to combine a series of identifiers and dollars into a single identifier + function maybeEatMethodOrProperty(nullSafeNavigation) { + if (peekTokenOne(_TokenKind.TokenKind.IDENTIFIER)) { + var methodOrPropertyName = nextToken(); + var args = maybeEatMethodArgs(); + if (args === null) { + // property + push(_astPropertyReference.PropertyReference.create(nullSafeNavigation, methodOrPropertyName.stringValue(), toPosToken(methodOrPropertyName))); + return true; + } + // methodreference + push(_astMethodReference.MethodReference.create(nullSafeNavigation, methodOrPropertyName.stringValue(), toPosToken(methodOrPropertyName), args)); + // TODO what is the end position for a method reference? the name or the last arg? + return true; } - for (ch = 'a'.charCodeAt(0); ch <= 'f'.charCodeAt(0); ch += 1) { - FLAGS[ch] |= IS_HEXDIGIT; + return false; + } + + //constructor + //: ('new' qualifiedId LPAREN) => 'new' qualifiedId ctorArgs -> ^(CONSTRUCTOR qualifiedId ctorArgs) + function maybeEatConstructorReference() { + if (peekIdentifierToken('new')) { + var newToken = nextToken(); + // It looks like a constructor reference but is NEW being used as a map key? + if (peekTokenOne(_TokenKind.TokenKind.RSQUARE)) { + // looks like 'NEW]' (so NEW used as map key) + push(_astPropertyReference.PropertyReference.create(newToken.stringValue(), toPosToken(newToken))); + return true; + } + var possiblyQualifiedConstructorName = eatPossiblyQualifiedId(); + var nodes = []; + nodes.push(possiblyQualifiedConstructorName); + if (peekTokenOne(_TokenKind.TokenKind.LSQUARE)) { + // array initializer + var dimensions = []; + while (peekTokenConsumeIfMatched(_TokenKind.TokenKind.LSQUARE, true)) { + if (!peekTokenOne(_TokenKind.TokenKind.RSQUARE)) { + dimensions.push(eatExpression()); + } else { + dimensions.push(null); + } + eatToken(_TokenKind.TokenKind.RSQUARE); + } + if (maybeEatInlineListOrMap()) { + nodes.push(pop()); + } + push(new ConstructorReference(toPosToken(newToken), dimensions, nodes)); + } else { + // regular constructor invocation + eatConstructorArgs(nodes); + // TODO correct end position? + push(new ConstructorReference(toPosToken(newToken), nodes)); + } + return true; } - for (ch = 'A'.charCodeAt(0); ch <= 'Z'.charCodeAt(0); ch += 1) { - FLAGS[ch] |= IS_ALPHA; + return false; + } + + function push(newNode) { + constructedNodes.push(newNode); + } + + function pop() { + return constructedNodes.pop(); + } + + // literal + // : INTEGER_LITERAL + // | boolLiteral + // | STRING_LITERAL + // | HEXADECIMAL_INTEGER_LITERAL + // | REAL_LITERAL + // | DQ_STRING_LITERAL + // | NULL_LITERAL + function maybeEatLiteral() { + var token = peekToken(); + if (token === null) { + return false; } - for (ch = 'a'.charCodeAt(0); ch <= 'z'.charCodeAt(0); ch += 1) { - FLAGS[ch] |= IS_ALPHA; + if (token.getKind() === _TokenKind.TokenKind.LITERAL_INT || token.getKind() === _TokenKind.TokenKind.LITERAL_LONG) { + push(_astNumberLiteral.NumberLiteral.create(parseInt(token.stringValue(), 10), toPosToken(token))); + } else if (token.getKind() === _TokenKind.TokenKind.LITERAL_REAL || token.getKind() === _TokenKind.TokenKind.LITERAL_REAL_FLOAT) { + push(_astNumberLiteral.NumberLiteral.create(parseFloat(token.stringValue()), toPosToken(token))); + } else if (token.getKind() === _TokenKind.TokenKind.LITERAL_HEXINT || token.getKind() === _TokenKind.TokenKind.LITERAL_HEXLONG) { + push(_astNumberLiteral.NumberLiteral.create(parseInt(token.stringValue(), 16), toPosToken(token))); + } else if (peekIdentifierToken('true')) { + push(_astBooleanLiteral.BooleanLiteral.create(true, toPosToken(token))); + } else if (peekIdentifierToken('false')) { + push(_astBooleanLiteral.BooleanLiteral.create(false, toPosToken(token))); + } else if (token.getKind() === _TokenKind.TokenKind.LITERAL_STRING) { + push(_astStringLiteral.StringLiteral.create(token.stringValue(), toPosToken(token))); + } else { + return false; } + nextToken(); + return true; } - init(); + //parenExpr : LPAREN! expression RPAREN!; + function maybeEatParenExpression() { + if (peekTokenOne(_TokenKind.TokenKind.LPAREN)) { + nextToken(); + var expr = eatExpression(); + eatToken(_TokenKind.TokenKind.RPAREN); + push(expr); + return true; + } else { + return false; + } + } - function tokenize(inputData) { - var expressionString = inputData, - toProcess = inputData + '\0', - max = toProcess.length, - pos = 0, - tokens = []; + // relationalOperator + // : EQUAL | NOT_EQUAL | LESS_THAN | LESS_THAN_OR_EQUAL | GREATER_THAN + // | GREATER_THAN_OR_EQUAL | INSTANCEOF | BETWEEN | MATCHES + function maybeEatRelationalOperator() { + var token = peekToken(); + if (token === null) { + return null; + } + if (token.isNumericRelationalOperator()) { + return token; + } + if (token.isIdentifier()) { + var idString = token.stringValue(); + if (idString.toLowerCase() === 'instanceof') { + return token.asInstanceOfToken(); + } + if (idString.toLowerCase() === 'matches') { + return token.asMatchesToken(); + } + if (idString.toLowerCase() === 'between') { + return token.asBetweenToken(); + } + } + return null; + } - function process() { - var ch; + function eatToken(expectedKind) { + var token = nextToken(); + if (token === null) { + raiseInternalException(expressionString.length, 'OOD'); + } + if (token.getKind() !== expectedKind) { + raiseInternalException(token.startPos, 'NOT_EXPECTED_TOKEN', expectedKind.toString().toLowerCase(), token.getKind().toString().toLowerCase()); + } + return token; + } - while (pos < max) { - ch = toProcess[pos]; - if (isAlphabetic(ch)) { - lexIdentifier(); - } - else { - switch (ch) { - case '+': - if (isTwoCharToken(TokenKind.INC)) { - pushPairToken(TokenKind.INC); - } - else { - pushCharToken(TokenKind.PLUS); - } - break; - case '_': // the other way to start an identifier - lexIdentifier(); - break; - case '-': - if (isTwoCharToken(TokenKind.DEC)) { - pushPairToken(TokenKind.DEC); - } - else { - pushCharToken(TokenKind.MINUS); - } - break; - case ':': - pushCharToken(TokenKind.COLON); - break; - case '.': - pushCharToken(TokenKind.DOT); - break; - case ',': - pushCharToken(TokenKind.COMMA); - break; - case '*': - pushCharToken(TokenKind.STAR); - break; - case '/': - pushCharToken(TokenKind.DIV); - break; - case '%': - pushCharToken(TokenKind.MOD); - break; - case '(': - pushCharToken(TokenKind.LPAREN); - break; - case ')': - pushCharToken(TokenKind.RPAREN); - break; - case '[': - pushCharToken(TokenKind.LSQUARE); - break; - case '#': - pushCharToken(TokenKind.HASH); - break; - case ']': - pushCharToken(TokenKind.RSQUARE); - break; - case '{': - pushCharToken(TokenKind.LCURLY); - break; - case '}': - pushCharToken(TokenKind.RCURLY); - break; - case '@': - pushCharToken(TokenKind.BEAN_REF); - break; - case '^': - if (isTwoCharToken(TokenKind.SELECT_FIRST)) { - pushPairToken(TokenKind.SELECT_FIRST); - } - else { - pushCharToken(TokenKind.POWER); - } - break; - case '!': - if (isTwoCharToken(TokenKind.NE)) { - pushPairToken(TokenKind.NE); - } - else if (isTwoCharToken(TokenKind.PROJECT)) { - pushPairToken(TokenKind.PROJECT); - } - else { - pushCharToken(TokenKind.NOT); - } - break; - case '=': - if (isTwoCharToken(TokenKind.EQ)) { - pushPairToken(TokenKind.EQ); - } - else { - pushCharToken(TokenKind.ASSIGN); - } - break; - case '&': - if (!isTwoCharToken(TokenKind.SYMBOLIC_AND)) { - throw { - name: 'SpelParseException', - message: 'Missing character \'&\' in expression (' + expressionString + ') at position ' + pos - }; - } - pushPairToken(TokenKind.SYMBOLIC_AND); - break; - case '|': - if (!isTwoCharToken(TokenKind.SYMBOLIC_OR)) { - throw { - name: 'SpelParseException', - message: 'Missing character \'|\' in expression (' + expressionString + ') at position ' + pos - }; - } - pushPairToken(TokenKind.SYMBOLIC_OR); - break; - case '?': - if (isTwoCharToken(TokenKind.SELECT)) { - pushPairToken(TokenKind.SELECT); - } - else if (isTwoCharToken(TokenKind.ELVIS)) { - pushPairToken(TokenKind.ELVIS); - } - else if (isTwoCharToken(TokenKind.SAFE_NAVI)) { - pushPairToken(TokenKind.SAFE_NAVI); - } - else { - pushCharToken(TokenKind.QMARK); - } - break; - case '$': - if (isTwoCharToken(TokenKind.SELECT_LAST)) { - pushPairToken(TokenKind.SELECT_LAST); - } - else { - lexIdentifier(); - } - break; - case '>': - if (isTwoCharToken(TokenKind.GE)) { - pushPairToken(TokenKind.GE); - } - else { - pushCharToken(TokenKind.GT); - } - break; - case '<': - if (isTwoCharToken(TokenKind.LE)) { - pushPairToken(TokenKind.LE); - } - else { - pushCharToken(TokenKind.LT); - } - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - lexNumericLiteral(ch === '0'); - break; - case ' ': - case '\t': - case '\r': - case '\n': - // drift over white space - pos += 1; - break; - case '\'': - lexQuotedStringLiteral(); - break; - case '"': - lexDoubleQuotedStringLiteral(); - break; - case '\0': - // hit sentinel at end of value - pos += 1; // will take us to the end - break; - case '\\': - throw { - name: 'SpelParseException', - message: 'Unexpected escape character in expression (' + expressionString + ') at position ' + pos - }; - default: - throw { - name: 'SpelParseException', - message: 'Cannot handle character \'' + ch + '\' in expression (' + expressionString + ') at position ' + pos - }; - } - } - } - } - - function lexQuotedStringLiteral() { - var start = pos, - terminated = false, - ch; + function peekTokenOne(desiredTokenKind) { + return peekTokenConsumeIfMatched(desiredTokenKind, false); + } - while (!terminated) { - pos += 1; - ch = toProcess[pos]; - if (ch == '\'') { - // may not be the end if the char after is also a ' - if (toProcess[pos + 1] == '\'') { - pos += 1; // skip over that too, and continue - } - else { - terminated = true; - } - } - if (ch.charCodeAt(0) === 0) { - throw { - name: 'SpelParseException', - message: 'Non-terminating quoted string in expression (' + expressionString + ') at position ' + pos - }; - } - } - pos += 1; - tokens.push(new Token(TokenKind.LITERAL_STRING, subarray(start, pos), start, pos)); + function peekTokenConsumeIfMatched(desiredTokenKind, consumeIfMatched) { + if (!moreTokens()) { + return false; } - function lexDoubleQuotedStringLiteral() { - var start = pos, - terminated = false, - ch; - - while (!terminated) { - pos += 1; - ch = toProcess[pos]; - if (ch == '"') { - // may not be the end if the char after is also a ' - if (toProcess[pos + 1] == '"') { - pos += 1; // skip over that too, and continue - } - else { - terminated = true; - } - } - if (ch.charCodeAt(0) === 0) { - throw { - name: 'SpelParseException', - message: 'Non-terminating double-quoted string in expression (' + expressionString + ') at position ' + pos - }; - } - } - pos += 1; - tokens.push(new Token(TokenKind.LITERAL_STRING, subarray(start, pos), start, pos)); - } - - // REAL_LITERAL : - // ('.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) | - // ((DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) | - // ((DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)?) | - // ((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX)); - // fragment INTEGER_TYPE_SUFFIX : ( 'L' | 'l' ); - // fragment HEX_DIGIT : - // '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'A'|'B'|'C'|'D'|'E'|'F'|'a'|'b'|'c'|'d'|'e'|'f'; - // - // fragment EXPONENT_PART : 'e' (SIGN)* (DECIMAL_DIGIT)+ | 'E' (SIGN)* - // (DECIMAL_DIGIT)+ ; - // fragment SIGN : '+' | '-' ; - // fragment REAL_TYPE_SUFFIX : 'F' | 'f' | 'D' | 'd'; - // INTEGER_LITERAL - // : (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)?; - - function lexNumericLiteral(firstCharIsZero) { - var isReal = false, - start = pos, - ch = toProcess[pos + 1], - isHex = ch == 'x' || ch == 'X', - dotpos, - endOfNumber, - possibleSign, - isFloat; - - // deal with hexadecimal - if (firstCharIsZero && isHex) { - pos = pos + 1; - do { - pos += 1; - } - while (isHexadecimalDigit(toProcess[pos])); - if (isChar('L', 'l')) { - pushHexIntToken(subarray(start + 2, pos), true, start, pos); - pos += 1; - } - else { - pushHexIntToken(subarray(start + 2, pos), false, start, pos); - } - return; + var token = peekToken(); + if (token.getKind() === desiredTokenKind) { + if (consumeIfMatched) { + tokenStreamPointer++; } + return true; + } - // real numbers must have leading digits - - // Consume first part of number - do { - pos += 1; + if (desiredTokenKind === _TokenKind.TokenKind.IDENTIFIER) { + // might be one of the textual forms of the operators (e.g. NE for !== ) - in which case we can treat it as an identifier + // The list is represented here: Tokenizer.alternativeOperatorNames and those ones are in order in the TokenKind enum + if (token.getKind().ordinal() >= _TokenKind.TokenKind.DIV.ordinal() && token.getKind().ordinal() <= _TokenKind.TokenKind.NOT.ordinal() && token.data !== null) { + // if token.data were null, we'd know it wasn'token the textual form, it was the symbol form + return true; } - while (isDigit(toProcess[pos])); + } + return false; + } - // a '.' indicates this number is a real - ch = toProcess[pos]; - if (ch == '.') { - isReal = true; - dotpos = pos; - // carry on consuming digits - do { - pos += 1; - } - while (isDigit(toProcess[pos])); - if (pos === dotpos + 1) { - // the number is something like '3.'. It is really an int but may be - // part of something like '3.toString()'. In this case process it as - // an int and leave the dot as a separate token. - pos = dotpos; - pushIntToken(subarray(start, pos), false, start, pos); - return; - } + function peekTokenAny() { + if (!moreTokens()) { + return false; + } + var token = peekToken(); + var args = Array.prototype.slice.call(arguments); + for (var i = 0, l = args.length; i < l; i += 1) { + if (token.getKind() === args[i]) { + return true; } + } + return false; + } - endOfNumber = pos; + function peekIdentifierToken(identifierString) { + if (!moreTokens()) { + return false; + } + var token = peekToken(); + return token.getKind() === _TokenKind.TokenKind.IDENTIFIER && token.stringValue().toLowerCase() === identifierString.toLowerCase(); + } - // Now there may or may not be an exponent + function peekSelectToken() { + if (!moreTokens()) { + return false; + } + var token = peekToken(); + return token.getKind() === _TokenKind.TokenKind.SELECT || token.getKind() === _TokenKind.TokenKind.SELECT_FIRST || token.getKind() === _TokenKind.TokenKind.SELECT_LAST; + } - // is it a long ? - if (isChar('L', 'l')) { - if (isReal) { // 3.4L - not allowed - throw { - name: 'SpelParseException', - message: 'Real cannot be long in expression (' + expressionString + ') at position ' + pos - }; - } - pushIntToken(subarray(start, endOfNumber), true, start, endOfNumber); - pos += 1; - } - else if (isExponentChar(toProcess[pos])) { - isReal = true; // if it wasn't before, it is now - pos += 1; - possibleSign = toProcess[pos]; - if (isSign(possibleSign)) { - pos += 1; - } + function moreTokens() { + return tokenStreamPointer < tokenStream.length; + } - // exponent digits - do { - pos += 1; - } - while (isDigit(toProcess[pos])); - isFloat = false; - if (isFloatSuffix(toProcess[pos])) { - isFloat = true; - pos += 1; - endOfNumber = pos; - } - else if (isDoubleSuffix(toProcess[pos])) { - pos += 1; - endOfNumber = pos; - } - pushRealToken(subarray(start, pos), isFloat, start, pos); - } - else { - ch = toProcess[pos]; - isFloat = false; - if (isFloatSuffix(ch)) { - isReal = true; - isFloat = true; - pos += 1; - endOfNumber = pos; - } - else if (isDoubleSuffix(ch)) { - isReal = true; - pos += 1; - endOfNumber = pos; - } - if (isReal) { - pushRealToken(subarray(start, endOfNumber), isFloat, start, endOfNumber); - } - else { - pushIntToken(subarray(start, endOfNumber), false, start, endOfNumber); - } - } + function nextToken() { + if (tokenStreamPointer >= tokenStreamLength) { + return null; } + return tokenStream[tokenStreamPointer++]; + } - function lexIdentifier() { - var start = pos, - substring, - asString, - idx; - do { - pos += 1; - } - while (isIdentifier(toProcess[pos])); - substring = subarray(start, pos); - - // Check if this is the alternative (textual) representation of an operator (see - // alternativeOperatorNames) - if ((pos - start) === 2 || (pos - start) === 3) { - asString = substring.toUpperCase(); - idx = ALTERNATIVE_OPERATOR_NAMES.indexOf(asString); - if (idx >= 0) { - pushOneCharOrTwoCharToken(TokenKind.valueOf(asString), start, substring); - return; - } - } - tokens.push(new Token(TokenKind.IDENTIFIER, substring.replace('\0', ''), start, pos)); + function peekToken() { + if (tokenStreamPointer >= tokenStreamLength) { + return null; } + return tokenStream[tokenStreamPointer]; + } - function pushIntToken(data, isLong, start, end) { - if (isLong) { - tokens.push(new Token(TokenKind.LITERAL_LONG, data, start, end)); - } - else { - tokens.push(new Token(TokenKind.LITERAL_INT, data, start, end)); - } + function raiseInternalException(pos, message, expected, actual) { + if (expected) { + message += '\nExpected: ' + expected; } - - function pushHexIntToken(data, isLong, start, end) { - if (data.length === 0) { - if (isLong) { - throw { - name: 'SpelParseException', - message: 'Not a long in expression (' + expressionString + ') at position ' + pos - }; - } - else { - throw { - name: 'SpelParseException', - message: 'Not an int in expression (' + expressionString + ') at position ' + pos - }; - } - } - if (isLong) { - tokens.push(new Token(TokenKind.LITERAL_HEXLONG, data, start, end)); - } - else { - tokens.push(new Token(TokenKind.LITERAL_HEXINT, data, start, end)); - } + if (actual) { + message += '\nActual: ' + actual; } + throw { + name: 'InternalParseException', + message: 'Error occurred while attempting to parse expression \'' + expressionString + '\' at position ' + pos + '. Message: ' + message + }; + } - function pushRealToken(data, isFloat, start, end) { - if (isFloat) { - tokens.push(new Token(TokenKind.LITERAL_REAL_FLOAT, data, start, end)); - } - else { - tokens.push(new Token(TokenKind.LITERAL_REAL, data, start, end)); - } + function toString(token) { + if (token.getKind().hasPayload()) { + return token.stringValue(); } + return token.getKind().toString().toLowerCase(); + } - function subarray(start, end) { - return toProcess.substring(start, end); - } + function checkOperands(token, left, right) { + checkLeftOperand(token, left); + checkRightOperand(token, right); + } - /** - * Check if this might be a two character token. - */ - function isTwoCharToken(kind) { - if (kind.tokenChars.length === 2 && toProcess[pos] == kind.tokenChars[0]) { - return toProcess[pos + 1] === kind.tokenChars[1]; - } - return false; + function checkLeftOperand(token, operandExpression) { + if (operandExpression === null) { + raiseInternalException(token.startPos, 'LEFT_OPERAND_PROBLEM'); } + } - /** - * Push a token of just one character in length. - */ - function pushCharToken(kind) { - tokens.push(new Token(kind, null, pos, pos + 1)); - pos += 1; + function checkRightOperand(token, operandExpression) { + if (operandExpression === null) { + raiseInternalException(token.startPos, 'RIGHT_OPERAND_PROBLEM'); } + } - /** - * Push a token of two characters in length. - */ - function pushPairToken(kind) { - tokens.push(new Token(kind, null, pos, pos + 2)); - pos += 2; - } + /** + * Compress the start and end of a token into a single int. + */ + function toPosToken(token) { + return (token.startPos << 16) + token.endPos; + } - function pushOneCharOrTwoCharToken(kind, pos, data) { - tokens.push(new Token(kind, data, pos, pos + kind.getLength())); - } + function toPosBounds(start, end) { + return (start << 16) + end; + } - // ID: ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9'|DOT_ESCAPED)*; - function isIdentifier(ch) { - return isAlphabetic(ch) || isDigit(ch) || ch === '_' || ch === '$'; - } + return { + setConfiguration: setConfiguration, + parse: parse + }; +}; +exports.SpelExpressionParser = SpelExpressionParser; - function isChar(a, b) { - var ch = toProcess[pos]; - return ch === a || ch === b; - } +},{"./TokenKind":5,"./Tokenizer":6,"./ast/Assign":7,"./ast/BooleanLiteral":8,"./ast/CompoundExpression":9,"./ast/Elvis":10,"./ast/FunctionReference":11,"./ast/Indexer":12,"./ast/InlineList":13,"./ast/InlineMap":14,"./ast/MethodReference":15,"./ast/NullLiteral":16,"./ast/NumberLiteral":17,"./ast/OpAnd":18,"./ast/OpDec":19,"./ast/OpDivide":20,"./ast/OpEQ":21,"./ast/OpGE":22,"./ast/OpGT":23,"./ast/OpInc":24,"./ast/OpLE":25,"./ast/OpLT":26,"./ast/OpMinus":27,"./ast/OpModulus":28,"./ast/OpMultiply":29,"./ast/OpNE":30,"./ast/OpNot":31,"./ast/OpOr":32,"./ast/OpPlus":33,"./ast/OpPower":34,"./ast/Projection":35,"./ast/PropertyReference":36,"./ast/RootNode":37,"./ast/Selection":38,"./ast/StringLiteral":40,"./ast/Ternary":41,"./ast/VariableReference":42}],3:[function(require,module,exports){ +"use strict"; - function isExponentChar(ch) { - return ch === 'e' || ch === 'E'; - } +Object.defineProperty(exports, "__esModule", { + value: true +}); +function create(authentication, principal) { + var context = {}; - function isFloatSuffix(ch) { - return ch === 'f' || ch === 'F'; - } + context.authentication = authentication || {}; + context.principal = principal || {}; - function isDoubleSuffix(ch) { - return ch === 'd' || ch === 'D'; - } + context.hasRole = function (role) { + var hasRole = false; - function isSign(ch) { - return ch === '+' || ch === '-'; + if (!role) { + return false; } - - function isDigit(ch) { - if (ch.charCodeAt(0) > 255) { - return false; - } - return (FLAGS[ch.charCodeAt(0)] & IS_DIGIT) !== 0; + if (!context.authentication && !Array.isArray(context.authentication.authorities)) { + return false; } - function isAlphabetic(ch) { - if (ch.charCodeAt(0) > 255) { - return false; + context.authentication.authorities.forEach(function (grantedAuthority) { + if (grantedAuthority.authority.toLowerCase() === role.toLowerCase()) { + hasRole = true; } - return (FLAGS[ch.charCodeAt(0)] & IS_ALPHA) !== 0; - } + }); - function isHexadecimalDigit(ch) { - if (ch.charCodeAt(0) > 255) { - return false; - } - return (FLAGS[ch.charCodeAt(0)] & IS_HEXDIGIT) !== 0; + return hasRole; + }; + + context.hasPermission = function () { + var args = Array.prototype.slice.call(arguments); + + if (args.length === 1) { + return context.hasRole(args[0]); } + }; - process(); + return context; +} - return tokens; +var StandardContext = { + create: create +}; +exports.StandardContext = StandardContext; +/*variable arguments*/ - } +},{}],4:[function(require,module,exports){ +'use strict'; - exports.Tokenizer = { - tokenize: tokenize - }; +Object.defineProperty(exports, '__esModule', { + value: true +}); -}(window || exports)); +var _TokenKind = require('./TokenKind'); -(function (exports) { - 'use strict'; +function Token(tokenKind, tokenData, startPos, endPos) { + this.kind = tokenKind; + this.startPos = startPos; + this.endPos = endPos; + if (tokenData) { + this.data = tokenData; + } +} - function createSpelNode(nodeType, position /*, operands */) { - var node = {}, - type = nodeType || 'Abstract', - children = [], - parent = null, - args = Array.prototype.slice.call(arguments), - operands = args.length > 2 ? args.slice(2) : null, - activeContext; +Token.prototype.getKind = function () { + return this.kind; +}; - node._type = type; +Token.prototype.toString = function () { + var s = '['; + s += this.kind.toString(); + if (this.kind.hasPayload()) { + s += ':' + this.data; + } + s += ']'; + s += '(' + this.startPos + ',' + this.endPos + ')'; + return s; +}; - node.getType = function () { - return type; - }; - node.setType = function (nodeType) { - type = nodeType; - }; +Token.prototype.isIdentifier = function () { + return this.kind === _TokenKind.TokenKind.IDENTIFIER; +}; - node.getChildren = function () { - return children; - }; - node.addChild = function (childNode) { - childNode.setParent(node); - children.push(childNode); - }; +Token.prototype.isNumericRelationalOperator = function () { + return this.kind === _TokenKind.TokenKind.GT || this.kind === _TokenKind.TokenKind.GE || this.kind === _TokenKind.TokenKind.LT || this.kind === _TokenKind.TokenKind.LE || this.kind === _TokenKind.TokenKind.EQ || this.kind === _TokenKind.TokenKind.NE; +}; - node.getParent = function () { - return parent; - }; - node.setParent = function (parentNode) { - parent = parentNode; - }; +Token.prototype.stringValue = function () { + return this.data; +}; - node.getContext = function (state) { - return activeContext || state.activeContext.peek(); - }; - node.setContext = function (nodeContext) { - activeContext = nodeContext; - }; +Token.prototype.asInstanceOfToken = function () { + return new Token(_TokenKind.TokenKind.INSTANCEOF, this.startPos, this.endPos); +}; - node.getStartPosition = function () { - return (position >> 16); - }; +Token.prototype.asMatchesToken = function () { + return new Token(_TokenKind.TokenKind.MATCHES, this.startPos, this.endPos); +}; - node.getEndPosition = function () { - return (position & 0xffff); - }; +Token.prototype.asBetweenToken = function () { + return new Token(_TokenKind.TokenKind.BETWEEN, this.startPos, this.endPos); +}; - //must override - node.getValue = function () { - throw { - name: 'MethodNotImplementedException', - message: 'SpelNode#getValue() must be overridden.' - } - }; +Token.prototype.getStartPosition = function () { + return this.startPos; +}; - node.toString = function () { - var s = 'Kind: ' + node.getType(); - //s += ', Value: ' + node.getValue(); - s += ', Children: ['; - for (var i = 0, l = node.getChildren().length; i < l; i += 1) { - s += '{' + node.getChildren()[i] + '}, '; - } - s += ']'; - return s; - }; +Token.prototype.getEndPosition = function () { + return this.endPos; +}; - //constructor - if (position === 0) { - throw { - name: 'Error', - message: 'Position cannot be 0' - }; - } +exports.Token = Token; - if (operands) { - operands.forEach(function (operand) { - node.addChild(operand); - }); - } +},{"./TokenKind":5}],5:[function(require,module,exports){ +'use strict'; +Object.defineProperty(exports, '__esModule', { + value: true +}); +var types = { - return node; - } + LITERAL_INT: 1, //tested - exports.SpelNode = { - create: createSpelNode - }; + LITERAL_LONG: 2, //tested -}(window || exports)); + LITERAL_HEXINT: 3, //tested -(function (exports, undefined) { - 'use strict'; + LITERAL_HEXLONG: 4, //tested - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } + LITERAL_STRING: 5, //tested - function createNode(position, property, assignedValue) { - var node = SpelNode.create('assign', position, property, assignedValue); + LITERAL_REAL: 6, //tested - node.getValue = function (state) { - var context = state.activeContext.peek(); + LITERAL_REAL_FLOAT: 7, //tested - if (!context) { - throw { - name: 'ContextDoesNotExistException', - message: 'Attempting to assign property \''+ property.getValue(state) +'\' for an undefined context.' - } - } + LPAREN: '(', //tested - return property.setValue(assignedValue.getValue(state), state); - }; + RPAREN: ')', //tested - return node; - } + COMMA: ',', //tested - exports.Assign = { - create: createNode - }; + IDENTIFIER: 0, //tested -}(window || exports)); + COLON: ':', //tested -(function (exports) { - 'use strict'; + HASH: '#', //tested - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } + RSQUARE: ']', //tested - function createNode(value, position) { - var node = SpelNode.create('boolean', position); + LSQUARE: '[', //tested - node.getValue = function () { - return value; - }; + LCURLY: '{', //tested - node.setValue = function (newValue) { - value = newValue; - }; + RCURLY: '}', //tested - return node; - } + DOT: '.', //tested - exports.BooleanLiteral = { - create: createNode - }; + PLUS: '+', //tested -}(window || exports)); + STAR: '*', //tested -(function (exports) { - 'use strict'; + MINUS: '-', //tested - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } + SELECT_FIRST: '^[', //tested - function createNode(position, expressionComponents) { - var node = SpelNode.create.apply(null, ['compound', position].concat(expressionComponents)); + SELECT_LAST: '$[', //tested - function buildContextStack(state) { - var childrenCount = node.getChildren().length, - i; + QMARK: '?', //tested - for (i = 0; i < childrenCount; i += 1) { - if (node.getChildren()[i].getType() === 'indexer') { - state.activeContext.push(state.activeContext.peek()[node.getChildren()[i].getValue(state)]); - } else { - state.activeContext.push(node.getChildren()[i].getValue(state)); - } - } + PROJECT: '![', //tested - return function unbuildContextStack() { - for (i = 0; i < childrenCount; i += 1) { - state.activeContext.pop(); - } - } - } + DIV: '/', //tested - node.getValue = function (state) { - var context = state.activeContext.peek(), - value; + GE: '>=', //tested - if (!context) { - throw { - name: 'ContextDoesNotExistException', - message: 'Attempting to evaluate compound expression with an undefined context.' - }; - } + GT: '>', //tested - var unbuildContextStack = buildContextStack(state); + LE: '<=', //tested - value = state.activeContext.peek(); + LT: '<', //tested - unbuildContextStack(); + EQ: '==', //tested - return value; - }; + NE: '!=', //tested - node.setValue = function (value, state) { - var unbuildContextStack = buildContextStack(state), - childCount = node.getChildren().length; + MOD: '%', //tested - state.activeContext.pop(); + NOT: '!', //tested - value = node.getChildren()[childCount - 1].setValue(value, state); + ASSIGN: '=', //tested - state.activeContext.push(null); + INSTANCEOF: 'instanceof', //test fails - unbuildContextStack(); + MATCHES: 'matches', //test fails - return value; + BETWEEN: 'between', //test fails - }; + SELECT: '?[', //tested - return node; - } + POWER: '^', //tested - exports.CompoundExpression = { - create: createNode - }; + ELVIS: '?:', //tested -}(window || exports)); + SAFE_NAVI: '?.', //tested -(function (exports) { - 'use strict'; + BEAN_REF: '@', //tested - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } + SYMBOLIC_OR: '||', //tested - function createNode(position, expression, ifFalse) { - var node = SpelNode.create('elvis', position, expression, ifFalse); + SYMBOLIC_AND: '&&', //tested - node.getValue = function (state) { - return expression.getValue(state) !== null ? expression.getValue(state) : ifFalse.getValue(state); - }; + INC: '++', //tested + + DEC: '--' //tested +}; - return node; +function TokenKind(type) { + this.type = type; + this.tokenChars = types[type]; + this._hasPayload = typeof types[type] !== 'string'; + if (typeof types[type] === 'number') { + this._ordinal = types[type]; } +} - exports.Elvis = { - create: createNode - }; +//create enum +for (var t in types) { + if (types.hasOwnProperty(t)) { + TokenKind[t] = new TokenKind(t); + } +} -}(window || exports)); +TokenKind.prototype.toString = function () { + return this.type + (this.tokenChars.length !== 0 ? '(' + this.tokenChars + ')' : ''); +}; + +TokenKind.prototype.getLength = function () { + return this.tokenChars.length; +}; -(function (exports) { - 'use strict'; +TokenKind.prototype.hasPayload = function () { + return this._hasPayload; +}; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; +TokenKind.prototype.valueOf = function (id) { + for (var t in types) { + if (types.hasOwnProperty(t) && types[t] === id) { + return TokenKind[t]; + } } +}; - function createNode(parent, functionName) { - var node = SpelNode.create('method', parent); +TokenKind.prototype.ordinal = function () { + return this._ordinal; +}; - node.getValue = function () { - var refNode = node, - context = null; - do { - if (refNode.getParent()) { - refNode = refNode.getParent(); - } else { - context = refNode.getContext(); - } - } while (refNode); - if (context[functionName]) { - return context[functionName].call(context); - } - throw { - name: 'FunctionDoesNotExistException', - message: 'Function \'' + functionName + '\' does not exist.' - } - }; +exports.TokenKind = TokenKind; - return node; - } +},{}],6:[function(require,module,exports){ +'use strict'; - exports.FunctionReference = { - create: createNode - }; +Object.defineProperty(exports, '__esModule', { + value: true +}); + +var _Token = require('./Token'); -}(window || exports)); +var _TokenKind = require('./TokenKind'); -(function (exports) { - 'use strict'; +var ALTERNATIVE_OPERATOR_NAMES = ['DIV', 'EQ', 'GE', 'GT', 'LE', 'LT', 'MOD', 'NE', 'NOT'], + FLAGS = [], + IS_DIGIT = 1, + IS_HEXDIGIT = 2, + IS_ALPHA = 4; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; +function init() { + var ch; + + for (ch = '0'.charCodeAt(0); ch <= '9'.charCodeAt(0); ch += 1) { + FLAGS[ch] |= IS_DIGIT | IS_HEXDIGIT; + } + for (ch = 'A'.charCodeAt(0); ch <= 'F'.charCodeAt(0); ch += 1) { + FLAGS[ch] |= IS_HEXDIGIT; } + for (ch = 'a'.charCodeAt(0); ch <= 'f'.charCodeAt(0); ch += 1) { + FLAGS[ch] |= IS_HEXDIGIT; + } + for (ch = 'A'.charCodeAt(0); ch <= 'Z'.charCodeAt(0); ch += 1) { + FLAGS[ch] |= IS_ALPHA; + } + for (ch = 'a'.charCodeAt(0); ch <= 'z'.charCodeAt(0); ch += 1) { + FLAGS[ch] |= IS_ALPHA; + } +} - function createNode(position, expressionComponents) { - var node = SpelNode.create.apply(null, ['indexer', position].concat(expressionComponents)); +init(); - node.getValue = function (state) { - var activeContext = state.activeContext, - context, - childrenCount = node.getChildren().length, - i, - value; +function tokenize(inputData) { + var expressionString = inputData, + toProcess = inputData + '\u0000', + max = toProcess.length, + pos = 0, + tokens = []; - state.activeContext = new Stack(); - state.activeContext.push(state.rootContext); + function process() { + var ch; + + while (pos < max) { + ch = toProcess[pos]; + if (isAlphabetic(ch)) { + lexIdentifier(); + } else { + switch (ch) { + case '+': + if (isTwoCharToken(_TokenKind.TokenKind.INC)) { + pushPairToken(_TokenKind.TokenKind.INC); + } else { + pushCharToken(_TokenKind.TokenKind.PLUS); + } + break; + case '_': + // the other way to start an identifier + lexIdentifier(); + break; + case '-': + if (isTwoCharToken(_TokenKind.TokenKind.DEC)) { + pushPairToken(_TokenKind.TokenKind.DEC); + } else { + pushCharToken(_TokenKind.TokenKind.MINUS); + } + break; + case ':': + pushCharToken(_TokenKind.TokenKind.COLON); + break; + case '.': + pushCharToken(_TokenKind.TokenKind.DOT); + break; + case ',': + pushCharToken(_TokenKind.TokenKind.COMMA); + break; + case '*': + pushCharToken(_TokenKind.TokenKind.STAR); + break; + case '/': + pushCharToken(_TokenKind.TokenKind.DIV); + break; + case '%': + pushCharToken(_TokenKind.TokenKind.MOD); + break; + case '(': + pushCharToken(_TokenKind.TokenKind.LPAREN); + break; + case ')': + pushCharToken(_TokenKind.TokenKind.RPAREN); + break; + case '[': + pushCharToken(_TokenKind.TokenKind.LSQUARE); + break; + case '#': + pushCharToken(_TokenKind.TokenKind.HASH); + break; + case ']': + pushCharToken(_TokenKind.TokenKind.RSQUARE); + break; + case '{': + pushCharToken(_TokenKind.TokenKind.LCURLY); + break; + case '}': + pushCharToken(_TokenKind.TokenKind.RCURLY); + break; + case '@': + pushCharToken(_TokenKind.TokenKind.BEAN_REF); + break; + case '^': + if (isTwoCharToken(_TokenKind.TokenKind.SELECT_FIRST)) { + pushPairToken(_TokenKind.TokenKind.SELECT_FIRST); + } else { + pushCharToken(_TokenKind.TokenKind.POWER); + } + break; + case '!': + if (isTwoCharToken(_TokenKind.TokenKind.NE)) { + pushPairToken(_TokenKind.TokenKind.NE); + } else if (isTwoCharToken(_TokenKind.TokenKind.PROJECT)) { + pushPairToken(_TokenKind.TokenKind.PROJECT); + } else { + pushCharToken(_TokenKind.TokenKind.NOT); + } + break; + case '=': + if (isTwoCharToken(_TokenKind.TokenKind.EQ)) { + pushPairToken(_TokenKind.TokenKind.EQ); + } else { + pushCharToken(_TokenKind.TokenKind.ASSIGN); + } + break; + case '&': + if (!isTwoCharToken(_TokenKind.TokenKind.SYMBOLIC_AND)) { + throw { + name: 'SpelParseException', + message: 'Missing character \'&\' in expression (' + expressionString + ') at position ' + pos + }; + } + pushPairToken(_TokenKind.TokenKind.SYMBOLIC_AND); + break; + case '|': + if (!isTwoCharToken(_TokenKind.TokenKind.SYMBOLIC_OR)) { + throw { + name: 'SpelParseException', + message: 'Missing character \'|\' in expression (' + expressionString + ') at position ' + pos + }; + } + pushPairToken(_TokenKind.TokenKind.SYMBOLIC_OR); + break; + case '?': + if (isTwoCharToken(_TokenKind.TokenKind.SELECT)) { + pushPairToken(_TokenKind.TokenKind.SELECT); + } else if (isTwoCharToken(_TokenKind.TokenKind.ELVIS)) { + pushPairToken(_TokenKind.TokenKind.ELVIS); + } else if (isTwoCharToken(_TokenKind.TokenKind.SAFE_NAVI)) { + pushPairToken(_TokenKind.TokenKind.SAFE_NAVI); + } else { + pushCharToken(_TokenKind.TokenKind.QMARK); + } + break; + case '$': + if (isTwoCharToken(_TokenKind.TokenKind.SELECT_LAST)) { + pushPairToken(_TokenKind.TokenKind.SELECT_LAST); + } else { + lexIdentifier(); + } + break; + case '>': + if (isTwoCharToken(_TokenKind.TokenKind.GE)) { + pushPairToken(_TokenKind.TokenKind.GE); + } else { + pushCharToken(_TokenKind.TokenKind.GT); + } + break; + case '<': + if (isTwoCharToken(_TokenKind.TokenKind.LE)) { + pushPairToken(_TokenKind.TokenKind.LE); + } else { + pushCharToken(_TokenKind.TokenKind.LT); + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + lexNumericLiteral(ch === '0'); + break; + case ' ': + case '\t': + case '\r': + case '\n': + // drift over white space + pos += 1; + break; + case '\'': + lexQuotedStringLiteral(); + break; + case '"': + lexDoubleQuotedStringLiteral(); + break; + case '\u0000': + // hit sentinel at end of value + pos += 1; // will take us to the end + break; + case '\\': + throw { + name: 'SpelParseException', + message: 'Unexpected escape character in expression (' + expressionString + ') at position ' + pos + }; + default: + throw { + name: 'SpelParseException', + message: 'Cannot handle character \'' + ch + '\' in expression (' + expressionString + ') at position ' + pos + }; + } + } + } + } - context = state.activeContext.peek(); + function lexQuotedStringLiteral() { + var start = pos, + terminated = false, + ch; - if (!context) { + while (!terminated) { + pos += 1; + ch = toProcess[pos]; + if (ch === '\'') { + // may not be the end if the char after is also a ' + if (toProcess[pos + 1] === '\'') { + pos += 1; // skip over that too, and continue + } else { + terminated = true; + } + } + if (ch.charCodeAt(0) === 0) { throw { - name: 'ContextDoesNotExistException', - message: 'Attempting to evaluate compound expression with an undefined context.' + name: 'SpelParseException', + message: 'Non-terminating quoted string in expression (' + expressionString + ') at position ' + pos }; } + } + pos += 1; + tokens.push(new _Token.Token(_TokenKind.TokenKind.LITERAL_STRING, subarray(start, pos), start, pos)); + } + function lexDoubleQuotedStringLiteral() { + var start = pos, + terminated = false, + ch; - for (i = 0; i < childrenCount; i += 1) { - state.activeContext.push(node.getChildren()[i].getValue(state)); + while (!terminated) { + pos += 1; + ch = toProcess[pos]; + if (ch === '"') { + // may not be the end if the char after is also a ' + if (toProcess[pos + 1] === '"') { + pos += 1; // skip over that too, and continue + } else { + terminated = true; + } + } + if (ch.charCodeAt(0) === 0) { + throw { + name: 'SpelParseException', + message: 'Non-terminating double-quoted string in expression (' + expressionString + ') at position ' + pos + }; + } + } + pos += 1; + tokens.push(new _Token.Token(_TokenKind.TokenKind.LITERAL_STRING, subarray(start, pos), start, pos)); + } + + // REAL_LITERAL : + // ('.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) | + // ((DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) | + // ((DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)?) | + // ((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX)); + // fragment INTEGER_TYPE_SUFFIX : ( 'L' | 'l' ); + // fragment HEX_DIGIT : + // '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'A'|'B'|'C'|'D'|'E'|'F'|'a'|'b'|'c'|'d'|'e'|'f'; + // + // fragment EXPONENT_PART : 'e' (SIGN)* (DECIMAL_DIGIT)+ | 'E' (SIGN)* + // (DECIMAL_DIGIT)+ ; + // fragment SIGN : '+' | '-' ; + // fragment REAL_TYPE_SUFFIX : 'F' | 'f' | 'D' | 'd'; + // INTEGER_LITERAL + // : (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)?; + + function lexNumericLiteral(firstCharIsZero) { + var isReal = false, + start = pos, + ch = toProcess[pos + 1], + isHex = ch === 'x' || ch === 'X', + dotpos, + endOfNumber, + possibleSign, + isFloat; + + // deal with hexadecimal + if (firstCharIsZero && isHex) { + pos = pos + 1; + do { + pos += 1; + } while (isHexadecimalDigit(toProcess[pos])); + if (isChar('L', 'l')) { + pushHexIntToken(subarray(start + 2, pos), true, start, pos); + pos += 1; + } else { + pushHexIntToken(subarray(start + 2, pos), false, start, pos); } + return; + } - value = state.activeContext.peek(); + // real numbers must have leading digits - for (i = 0; i < childrenCount; i += 1) { - state.activeContext.pop(); + // Consume first part of number + do { + pos += 1; + } while (isDigit(toProcess[pos])); + + // a '.' indicates this number is a real + ch = toProcess[pos]; + if (ch === '.') { + isReal = true; + dotpos = pos; + // carry on consuming digits + do { + pos += 1; + } while (isDigit(toProcess[pos])); + if (pos === dotpos + 1) { + // the number is something like '3.'. It is really an int but may be + // part of something like '3.toString()'. In this case process it as + // an int and leave the dot as a separate token. + pos = dotpos; + pushIntToken(subarray(start, pos), false, start, pos); + return; } + } - state.activeContext = activeContext; + endOfNumber = pos; - return value; - }; + // Now there may or may not be an exponent - //node.setContext(node.getValue()); + // is it a long ? + if (isChar('L', 'l')) { + if (isReal) { + // 3.4L - not allowed + throw { + name: 'SpelParseException', + message: 'Real cannot be long in expression (' + expressionString + ') at position ' + pos + }; + } + pushIntToken(subarray(start, endOfNumber), true, start, endOfNumber); + pos += 1; + } else if (isExponentChar(toProcess[pos])) { + isReal = true; // if it wasn't before, it is now + pos += 1; + possibleSign = toProcess[pos]; + if (isSign(possibleSign)) { + pos += 1; + } - return node; + // exponent digits + do { + pos += 1; + } while (isDigit(toProcess[pos])); + isFloat = false; + if (isFloatSuffix(toProcess[pos])) { + isFloat = true; + pos += 1; + endOfNumber = pos; + } else if (isDoubleSuffix(toProcess[pos])) { + pos += 1; + endOfNumber = pos; + } + pushRealToken(subarray(start, pos), isFloat, start, pos); + } else { + ch = toProcess[pos]; + isFloat = false; + if (isFloatSuffix(ch)) { + isReal = true; + isFloat = true; + pos += 1; + endOfNumber = pos; + } else if (isDoubleSuffix(ch)) { + isReal = true; + pos += 1; + endOfNumber = pos; + } + if (isReal) { + pushRealToken(subarray(start, endOfNumber), isFloat, start, endOfNumber); + } else { + pushIntToken(subarray(start, endOfNumber), false, start, endOfNumber); + } + } } - exports.Indexer = { - create: createNode - }; - -}(window || exports)); - -(function (exports) { - 'use strict'; + function lexIdentifier() { + var start = pos, + substring, + asString, + idx; + do { + pos += 1; + } while (isIdentifier(toProcess[pos])); + substring = subarray(start, pos); + + // Check if this is the alternative (textual) representation of an operator (see + // alternativeOperatorNames) + if (pos - start === 2 || pos - start === 3) { + asString = substring.toUpperCase(); + idx = ALTERNATIVE_OPERATOR_NAMES.indexOf(asString); + if (idx >= 0) { + pushOneCharOrTwoCharToken(_TokenKind.TokenKind.valueOf(asString), start, substring); + return; + } + } + tokens.push(new _Token.Token(_TokenKind.TokenKind.IDENTIFIER, substring.replace('\u0000', ''), start, pos)); + } - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; + function pushIntToken(data, isLong, start, end) { + if (isLong) { + tokens.push(new _Token.Token(_TokenKind.TokenKind.LITERAL_LONG, data, start, end)); + } else { + tokens.push(new _Token.Token(_TokenKind.TokenKind.LITERAL_INT, data, start, end)); + } } - function createNode(position, elements) { - var node = SpelNode.create('list', position), - list = [].concat(elements || []); + function pushHexIntToken(data, isLong, start, end) { + if (data.length === 0) { + if (isLong) { + throw { + name: 'SpelParseException', + message: 'Not a long in expression (' + expressionString + ') at position ' + pos + }; + } else { + throw { + name: 'SpelParseException', + message: 'Not an int in expression (' + expressionString + ') at position ' + pos + }; + } + } + if (isLong) { + tokens.push(new _Token.Token(_TokenKind.TokenKind.LITERAL_HEXLONG, data, start, end)); + } else { + tokens.push(new _Token.Token(_TokenKind.TokenKind.LITERAL_HEXINT, data, start, end)); + } + } - node.getValue = function (state) { - return list.map(function (element) { - return element.getValue(state); - }); - }; + function pushRealToken(data, isFloat, start, end) { + if (isFloat) { + tokens.push(new _Token.Token(_TokenKind.TokenKind.LITERAL_REAL_FLOAT, data, start, end)); + } else { + tokens.push(new _Token.Token(_TokenKind.TokenKind.LITERAL_REAL, data, start, end)); + } + } - return node; + function subarray(start, end) { + return toProcess.substring(start, end); } - exports.InlineList = { - create: createNode - }; + /** + * Check if this might be a two character token. + */ + function isTwoCharToken(kind) { + if (kind.tokenChars.length === 2 && toProcess[pos] === kind.tokenChars[0]) { + return toProcess[pos + 1] === kind.tokenChars[1]; + } + return false; + } -}(window || exports)); + /** + * Push a token of just one character in length. + */ + function pushCharToken(kind) { + tokens.push(new _Token.Token(kind, null, pos, pos + 1)); + pos += 1; + } -(function (exports) { - 'use strict'; + /** + * Push a token of two characters in length. + */ + function pushPairToken(kind) { + tokens.push(new _Token.Token(kind, null, pos, pos + 2)); + pos += 2; + } - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; + function pushOneCharOrTwoCharToken(kind, pos, data) { + tokens.push(new _Token.Token(kind, data, pos, pos + kind.getLength())); } - function createNode(position, elements) { - var node = SpelNode.create('map', position), - mapPieces = [].concat(elements || []); + // ID: ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9'|DOT_ESCAPED)*; + function isIdentifier(ch) { + return isAlphabetic(ch) || isDigit(ch) || ch === '_' || ch === '$'; + } - node.getValue = function (state) { - var key = true, - keyValue = null, - map = {}; + function isChar(a, b) { + var ch = toProcess[pos]; + return ch === a || ch === b; + } - mapPieces.forEach(function (piece) { - if (key) { - //unquoted property names come as type "property" but should be treated as strings - if (piece.getType() === 'property') { - keyValue = piece.getName(); - } else { - keyValue = piece.getValue(state); - } - } else { - map[keyValue] = piece.getValue(state); - } - key = !key; - }); + function isExponentChar(ch) { + return ch === 'e' || ch === 'E'; + } - return map; - }; + function isFloatSuffix(ch) { + return ch === 'f' || ch === 'F'; + } - return node; + function isDoubleSuffix(ch) { + return ch === 'd' || ch === 'D'; } - exports.InlineMap = { - create: createNode - }; + function isSign(ch) { + return ch === '+' || ch === '-'; + } -}(window || exports)); + function isDigit(ch) { + if (ch.charCodeAt(0) > 255) { + return false; + } + return (FLAGS[ch.charCodeAt(0)] & IS_DIGIT) !== 0; + } -(function (exports, undefined) { - 'use strict'; + function isAlphabetic(ch) { + if (ch.charCodeAt(0) > 255) { + return false; + } + return (FLAGS[ch.charCodeAt(0)] & IS_ALPHA) !== 0; + } - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; + function isHexadecimalDigit(ch) { + if (ch.charCodeAt(0) > 255) { + return false; + } + return (FLAGS[ch.charCodeAt(0)] & IS_HEXDIGIT) !== 0; } - function createNode(nullSafeNavigation, methodName, position, args) { - var node = SpelNode.create('method', position); + process(); - node.getValue = function (state) { - var context = state.activeContext.peek(), - compiledArgs = [], - method; + return tokens; +} - if (!context) { - throw { - name: 'ContextDoesNotExistException', - message: 'Attempting to look up property \''+ methodName +'\' for an undefined context.' - }; - } +var Tokenizer = { + tokenize: tokenize +}; +exports.Tokenizer = Tokenizer; - //handle safe navigation - function maybeHandleNullSafeNavigation(member) { - if (member === undefined) { - if (nullSafeNavigation) { - return null; - } +},{"./Token":4,"./TokenKind":5}],7:[function(require,module,exports){ +'use strict'; - throw { - name: 'NullPointerException', - message: 'Method ' + methodName + ' does not exist.' - }; - } +Object.defineProperty(exports, '__esModule', { + value: true +}); - return member; - } +var _SpelNode = require('./SpelNode'); - //populate arguments - args.forEach(function (arg) { - compiledArgs.push(arg.getValue(state)); - }); +function createNode(position, property, assignedValue) { + var node = _SpelNode.SpelNode.create('assign', position, property, assignedValue); - //accessors might not be available - if (methodName.substr(0, 3) === 'get' && !context[methodName]) { - return maybeHandleNullSafeNavigation(context[methodName.charAt(3).toLowerCase() + methodName.substring(4)]); - } - if (methodName.substr(0, 3) === 'set' && !context[methodName]) { - return context[methodName.charAt(3).toLowerCase() + methodName.substring(4)] = compiledArgs[0]; - } + node.getValue = function (state) { + var context = state.activeContext.peek(); - //size() -> length - if (methodName === 'size' && Array.isArray(context)) { - return context.length; - } + if (!context) { + throw { + name: 'ContextDoesNotExistException', + message: 'Attempting to assign property \'' + property.getValue(state) + '\' for an undefined context.' + }; + } - method = maybeHandleNullSafeNavigation(context[methodName]); - if (method) { - return method.apply(context, compiledArgs); - } - return null; - }; + return property.setValue(assignedValue.getValue(state), state); + }; - return node; - } + return node; +} + +var Assign = { + create: createNode +}; +exports.Assign = Assign; + +},{"./SpelNode":39}],8:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +var _SpelNode = require('./SpelNode'); - exports.MethodReference = { - create: createNode +function createNode(value, position) { + var node = _SpelNode.SpelNode.create('boolean', position); + + node.getValue = function () { + return value; }; -}(window || exports)); + node.setValue = function (newValue) { + /*jshint -W093 */ + return value = newValue; + /*jshint +W093 */ + }; -(function (exports) { - 'use strict'; + return node; +} - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +var BooleanLiteral = { + create: createNode +}; +exports.BooleanLiteral = BooleanLiteral; - function createNode(value, position) { - var node = SpelNode.create('null', position); +},{"./SpelNode":39}],9:[function(require,module,exports){ +'use strict'; - node.getValue = function () { - return null; - }; +Object.defineProperty(exports, '__esModule', { + value: true +}); - return node; - } +var _SpelNode = require('./SpelNode'); - exports.NullLiteral = { - create: createNode - }; +function createNode(position, expressionComponents) { + var node = _SpelNode.SpelNode.create.apply(null, ['compound', position].concat(expressionComponents)); -}(window || exports)); + function buildContextStack(state) { + var childrenCount = node.getChildren().length, + i; -(function (exports) { - 'use strict'; + for (i = 0; i < childrenCount; i += 1) { + if (node.getChildren()[i].getType() === 'indexer') { + state.activeContext.push(state.activeContext.peek()[node.getChildren()[i].getValue(state)]); + } else { + state.activeContext.push(node.getChildren()[i].getValue(state)); + } + } - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; + return function unbuildContextStack() { + for (i = 0; i < childrenCount; i += 1) { + state.activeContext.pop(); + } + }; } - function createNode(value, position) { - var node = SpelNode.create('number', position); + node.getValue = function (state) { + var context = state.activeContext.peek(), + value; - node.getValue = function () { - return value; - }; + if (!context) { + throw { + name: 'ContextDoesNotExistException', + message: 'Attempting to evaluate compound expression with an undefined context.' + }; + } - node.setValue = function (newValue) { - value = newValue; - }; + var unbuildContextStack = buildContextStack(state); - return node; - } + value = state.activeContext.peek(); + + unbuildContextStack(); - exports.NumberLiteral = { - create: createNode + return value; }; -}(window || exports)); + node.setValue = function (value, state) { + var unbuildContextStack = buildContextStack(state), + childCount = node.getChildren().length; -(function (exports) { - 'use strict'; + state.activeContext.pop(); - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } + value = node.getChildren()[childCount - 1].setValue(value, state); - function createNode(position, left, right) { - var node = SpelNode.create('op-and', position, left, right); + state.activeContext.push(null); - node.getValue = function (state) { - //double bang for javascript - return !!left.getValue(state) && !!right.getValue(state); - }; + unbuildContextStack(); - return node; - } + return value; + }; + + return node; +} + +var CompoundExpression = { + create: createNode +}; +exports.CompoundExpression = CompoundExpression; + +},{"./SpelNode":39}],10:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +var _SpelNode = require('./SpelNode'); - exports.OpAnd = { - create: createNode +function createNode(position, expression, ifFalse) { + var node = _SpelNode.SpelNode.create('elvis', position, expression, ifFalse); + + node.getValue = function (state) { + return expression.getValue(state) !== null ? expression.getValue(state) : ifFalse.getValue(state); }; -}(window || exports)); + return node; +} -(function (exports) { - 'use strict'; +var Elvis = { + create: createNode +}; +exports.Elvis = Elvis; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +},{"./SpelNode":39}],11:[function(require,module,exports){ +'use strict'; - function createNode(position, postfix, int) { - var node = SpelNode.create('op-dec', position, int); +Object.defineProperty(exports, '__esModule', { + value: true +}); - node.getValue = function (state) { - var cur = int.getValue(state); - int.setValue(cur - 1, state); - if (postfix) { - return cur; +var _SpelNode = require('./SpelNode'); + +function createNode(parent, functionName) { + var node = _SpelNode.SpelNode.create('method', parent); + + node.getValue = function () { + var refNode = node, + context = null; + do { + if (refNode.getParent()) { + refNode = refNode.getParent(); + } else { + context = refNode.getContext(); } - return cur - 1; + } while (refNode); + if (context[functionName]) { + return context[functionName].call(context); + } + throw { + name: 'FunctionDoesNotExistException', + message: 'Function \'' + functionName + '\' does not exist.' }; + }; - return node; - } + return node; +} - exports.OpDec = { - create: createNode - }; +var FunctionReference = { + create: createNode +}; +exports.FunctionReference = FunctionReference; -}(window || exports)); +},{"./SpelNode":39}],12:[function(require,module,exports){ +'use strict'; -(function (exports) { - 'use strict'; +Object.defineProperty(exports, '__esModule', { + value: true +}); - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +var _SpelNode = require('./SpelNode'); - function createNode(position, left, right) { - var node = SpelNode.create('op-divide', position, left, right); +var _libStack = require('../lib/Stack'); - node.getValue = function (state) { - return left.getValue(state) / right.getValue(state); - }; +function createNode(position, expressionComponents) { + var node = _SpelNode.SpelNode.create.apply(null, ['indexer', position].concat(expressionComponents)); - return node; - } + node.getValue = function (state) { + var activeContext = state.activeContext, + context, + childrenCount = node.getChildren().length, + i, + value; - exports.OpDivide = { - create: createNode - }; + state.activeContext = new _libStack.Stack(); + state.activeContext.push(state.rootContext); -}(window || exports)); + context = state.activeContext.peek(); -(function (exports) { - 'use strict'; + if (!context) { + throw { + name: 'ContextDoesNotExistException', + message: 'Attempting to evaluate compound expression with an undefined context.' + }; + } - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } + for (i = 0; i < childrenCount; i += 1) { + state.activeContext.push(node.getChildren()[i].getValue(state)); + } - function createNode(position, left, right) { - var node = SpelNode.create('op-eq', position, left, right); + value = state.activeContext.peek(); - node.getValue = function (state) { - return left.getValue(state) === right.getValue(state); - }; + for (i = 0; i < childrenCount; i += 1) { + state.activeContext.pop(); + } - return node; - } + state.activeContext = activeContext; - exports.OpEQ = { - create: createNode + return value; }; -}(window || exports)); + //node.setContext(node.getValue()); -(function (exports) { - 'use strict'; + return node; +} - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +var Indexer = { + create: createNode +}; +exports.Indexer = Indexer; - function createNode(position, left, right) { - var node = SpelNode.create('op-ge', position, left, right); +},{"../lib/Stack":43,"./SpelNode":39}],13:[function(require,module,exports){ +'use strict'; - node.getValue = function (state) { - return left.getValue(state) >= right.getValue(state); - }; +Object.defineProperty(exports, '__esModule', { + value: true +}); - return node; - } +var _SpelNode = require('./SpelNode'); + +function createNode(position, elements) { + var node = _SpelNode.SpelNode.create('list', position), + list = [].concat(elements || []); - exports.OpGE = { - create: createNode + node.getValue = function (state) { + return list.map(function (element) { + return element.getValue(state); + }); }; -}(window || exports)); + return node; +} -(function (exports) { - 'use strict'; +var InlineList = { + create: createNode +}; +exports.InlineList = InlineList; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +},{"./SpelNode":39}],14:[function(require,module,exports){ +'use strict'; - function createNode(position, left, right) { - var node = SpelNode.create('op-gt', position, left, right); +Object.defineProperty(exports, '__esModule', { + value: true +}); - node.getValue = function (state) { - return left.getValue(state) > right.getValue(state); - }; +var _SpelNode = require('./SpelNode'); - return node; - } +function createNode(position, elements) { + var node = _SpelNode.SpelNode.create('map', position), + mapPieces = [].concat(elements || []); + + node.getValue = function (state) { + var key = true, + keyValue = null, + map = {}; + + mapPieces.forEach(function (piece) { + if (key) { + //unquoted property names come as type "property" but should be treated as strings + if (piece.getType() === 'property') { + keyValue = piece.getName(); + } else { + keyValue = piece.getValue(state); + } + } else { + map[keyValue] = piece.getValue(state); + } + key = !key; + }); - exports.OpGT = { - create: createNode + return map; }; -}(window || exports)); + return node; +} -(function (exports) { - 'use strict'; +var InlineMap = { + create: createNode +}; +exports.InlineMap = InlineMap; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +},{"./SpelNode":39}],15:[function(require,module,exports){ +'use strict'; - function createNode(position, postfix, int) { - var node = SpelNode.create('op-inc', position, int); +Object.defineProperty(exports, '__esModule', { + value: true +}); - node.getValue = function (state) { - var cur = int.getValue(state); - int.setValue(cur + 1, state); - if (postfix) { - return cur; - } - return cur + 1; - }; +var _SpelNode = require('./SpelNode'); - return node; - } +function createNode(nullSafeNavigation, methodName, position, args) { + var node = _SpelNode.SpelNode.create('method', position); - exports.OpInc = { - create: createNode - }; + node.getValue = function (state) { + var context = state.activeContext.peek(), + compiledArgs = [], + method; + + if (!context) { + throw { + name: 'ContextDoesNotExistException', + message: 'Attempting to look up property \'' + methodName + '\' for an undefined context.' + }; + } -}(window || exports)); + //handle safe navigation + function maybeHandleNullSafeNavigation(member) { + if (member === undefined) { + if (nullSafeNavigation) { + return null; + } -(function (exports) { - 'use strict'; + throw { + name: 'NullPointerException', + message: 'Method ' + methodName + ' does not exist.' + }; + } - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } + return member; + } - function createNode(position, left, right) { - var node = SpelNode.create('op-le', position, left, right); + //populate arguments + args.forEach(function (arg) { + compiledArgs.push(arg.getValue(state)); + }); - node.getValue = function (state) { - return left.getValue(state) <= right.getValue(state); - }; + //accessors might not be available + if (methodName.substr(0, 3) === 'get' && !context[methodName]) { + return maybeHandleNullSafeNavigation(context[methodName.charAt(3).toLowerCase() + methodName.substring(4)]); + } + if (methodName.substr(0, 3) === 'set' && !context[methodName]) { + /*jshint -W093 */ + return context[methodName.charAt(3).toLowerCase() + methodName.substring(4)] = compiledArgs[0]; + /*jshint +W093 */ + } - return node; - } + //size() -> length + if (methodName === 'size' && Array.isArray(context)) { + return context.length; + } - exports.OpLE = { - create: createNode + method = maybeHandleNullSafeNavigation(context[methodName]); + if (method) { + return method.apply(context, compiledArgs); + } + return null; }; -}(window || exports)); + return node; +} -(function (exports) { - 'use strict'; +var MethodReference = { + create: createNode +}; +exports.MethodReference = MethodReference; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +},{"./SpelNode":39}],16:[function(require,module,exports){ +'use strict'; - function createNode(position, left, right) { - var node = SpelNode.create('op-lt', position, left, right); +Object.defineProperty(exports, '__esModule', { + value: true +}); - node.getValue = function (state) { - return left.getValue(state) < right.getValue(state); - }; +var _SpelNode = require('./SpelNode'); - return node; - } +function createNode(value, position) { + var node = _SpelNode.SpelNode.create('null', position); - exports.OpLT = { - create: createNode + node.getValue = function () { + return null; }; -}(window || exports)); + return node; +} -(function (exports) { - 'use strict'; +var NullLiteral = { + create: createNode +}; +exports.NullLiteral = NullLiteral; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +},{"./SpelNode":39}],17:[function(require,module,exports){ +'use strict'; - function createNode(position, left, right) { - var node = SpelNode.create('op-minus', position, left, right); +Object.defineProperty(exports, '__esModule', { + value: true +}); - node.getValue = function (state) { - return left.getValue(state) - right.getValue(state); - }; +var _SpelNode = require('./SpelNode'); - return node; - } +function createNode(value, position) { + var node = _SpelNode.SpelNode.create('number', position); - exports.OpMinus = { - create: createNode + node.getValue = function () { + return value; }; -}(window || exports)); + node.setValue = function (newValue) { + /*jshint -W093 */ + return value = newValue; + /*jshint +W093 */ + }; -(function (exports) { - 'use strict'; + return node; +} - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +var NumberLiteral = { + create: createNode +}; +exports.NumberLiteral = NumberLiteral; - function createNode(position, left, right) { - var node = SpelNode.create('op-modulus', position, left, right); +},{"./SpelNode":39}],18:[function(require,module,exports){ +'use strict'; - node.getValue = function (state) { - return left.getValue(state) % right.getValue(state); - }; +Object.defineProperty(exports, '__esModule', { + value: true +}); - return node; - } +var _SpelNode = require('./SpelNode'); - exports.OpModulus = { - create: createNode +function createNode(position, left, right) { + var node = _SpelNode.SpelNode.create('op-and', position, left, right); + + node.getValue = function (state) { + //double bang for javascript + return !!left.getValue(state) && !!right.getValue(state); }; -}(window || exports)); + return node; +} -(function (exports) { - 'use strict'; +var OpAnd = { + create: createNode +}; +exports.OpAnd = OpAnd; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +},{"./SpelNode":39}],19:[function(require,module,exports){ +'use strict'; - function createNode(position, left, right) { - var node = SpelNode.create('op-multiply', position, left, right); +Object.defineProperty(exports, '__esModule', { + value: true +}); - node.getValue = function (state) { - var leftValue = left.getValue(state), - rightValue = right.getValue(state); +var _SpelNode = require('./SpelNode'); - if (typeof leftValue === 'number' && typeof rightValue === 'number') { - return leftValue * rightValue; - } +function createNode(position, postfix, int) { + var node = _SpelNode.SpelNode.create('op-dec', position, int); - //repeats (ex. 'abc' * 2 = 'abcabc') - if (typeof leftValue === 'string' && typeof rightValue === 'number') { - var s = '', - i = 0; - for (; i < rightValue; i += 1) { - s += leftValue; - } - return s; - } + node.getValue = function (state) { + var cur = int.getValue(state); + int.setValue(cur - 1, state); + if (postfix) { + return cur; + } + return cur - 1; + }; - return null; - }; + return node; +} - return node; - } +var OpDec = { + create: createNode +}; +exports.OpDec = OpDec; + +},{"./SpelNode":39}],20:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); - exports.OpMultiply = { - create: createNode +var _SpelNode = require('./SpelNode'); + +function createNode(position, left, right) { + var node = _SpelNode.SpelNode.create('op-divide', position, left, right); + + node.getValue = function (state) { + return left.getValue(state) / right.getValue(state); }; -}(window || exports)); + return node; +} -(function (exports) { - 'use strict'; +var OpDivide = { + create: createNode +}; +exports.OpDivide = OpDivide; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +},{"./SpelNode":39}],21:[function(require,module,exports){ +'use strict'; - function createNode(position, left, right) { - var node = SpelNode.create('op-ne', position, left, right); +Object.defineProperty(exports, '__esModule', { + value: true +}); - node.getValue = function (state) { - return left.getValue(state) !== right.getValue(state); - }; +var _SpelNode = require('./SpelNode'); - return node; - } +function createNode(position, left, right) { + var node = _SpelNode.SpelNode.create('op-eq', position, left, right); - exports.OpNE = { - create: createNode + node.getValue = function (state) { + return left.getValue(state) === right.getValue(state); }; -}(window || exports)); + return node; +} -(function (exports) { - 'use strict'; +var OpEQ = { + create: createNode +}; +exports.OpEQ = OpEQ; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +},{"./SpelNode":39}],22:[function(require,module,exports){ +'use strict'; - function createNode(position, expr) { - var node = SpelNode.create('op-not', position, expr); +Object.defineProperty(exports, '__esModule', { + value: true +}); - node.getValue = function (state) { - return !expr.getValue(state); - }; +var _SpelNode = require('./SpelNode'); - return node; - } +function createNode(position, left, right) { + var node = _SpelNode.SpelNode.create('op-ge', position, left, right); - exports.OpNot = { - create: createNode + node.getValue = function (state) { + return left.getValue(state) >= right.getValue(state); }; -}(window || exports)); + return node; +} -(function (exports) { - 'use strict'; +var OpGE = { + create: createNode +}; +exports.OpGE = OpGE; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +},{"./SpelNode":39}],23:[function(require,module,exports){ +'use strict'; - function createNode(position, left, right) { - var node = SpelNode.create('op-or', position, left, right); +Object.defineProperty(exports, '__esModule', { + value: true +}); - node.getValue = function (state) { - //double bang for javascript - return !!left.getValue(state) || !!right.getValue(state); - }; +var _SpelNode = require('./SpelNode'); - return node; - } +function createNode(position, left, right) { + var node = _SpelNode.SpelNode.create('op-gt', position, left, right); - exports.OpOr = { - create: createNode + node.getValue = function (state) { + return left.getValue(state) > right.getValue(state); }; -}(window || exports)); + return node; +} -(function (exports) { - 'use strict'; +var OpGT = { + create: createNode +}; +exports.OpGT = OpGT; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +},{"./SpelNode":39}],24:[function(require,module,exports){ +'use strict'; - function createNode(position, left, right) { - var node = SpelNode.create('op-plus', position, left, right); +Object.defineProperty(exports, '__esModule', { + value: true +}); - node.getValue = function (state) { - //javascript will handle string concatenation or addition depending on types - return left.getValue(state) + right.getValue(state); - }; +var _SpelNode = require('./SpelNode'); - return node; - } +function createNode(position, postfix, int) { + var node = _SpelNode.SpelNode.create('op-inc', position, int); - exports.OpPlus = { - create: createNode + node.getValue = function (state) { + var cur = int.getValue(state); + int.setValue(cur + 1, state); + if (postfix) { + return cur; + } + return cur + 1; }; -}(window || exports)); + return node; +} -(function (exports) { - 'use strict'; +var OpInc = { + create: createNode +}; +exports.OpInc = OpInc; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +},{"./SpelNode":39}],25:[function(require,module,exports){ +'use strict'; - function createNode(position, base, exp) { - var node = SpelNode.create('op-power', position, base, exp); +Object.defineProperty(exports, '__esModule', { + value: true +}); - node.getValue = function (state) { - return Math.pow(base.getValue(state), exp.getValue(state)); - }; +var _SpelNode = require('./SpelNode'); - return node; - } +function createNode(position, left, right) { + var node = _SpelNode.SpelNode.create('op-le', position, left, right); - exports.OpPower = { - create: createNode + node.getValue = function (state) { + return left.getValue(state) <= right.getValue(state); }; -}(window || exports)); + return node; +} -(function (exports) { - 'use strict'; +var OpLE = { + create: createNode +}; +exports.OpLE = OpLE; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +},{"./SpelNode":39}],26:[function(require,module,exports){ +'use strict'; - function projectCollection(collection, expr, state) { - return collection.map(function (element) { - var matches; - state.activeContext.push(element); - matches = expr.getValue(state); - state.activeContext.pop(); - return matches; - }); - } +Object.defineProperty(exports, '__esModule', { + value: true +}); - function createNode(nullSafeNavigation, position, expr) { - var node = SpelNode.create('projection', position, expr); +var _SpelNode = require('./SpelNode'); - node.getValue = function (state) { - var collection = state.activeContext.peek(), - entries = [], - key; +function createNode(position, left, right) { + var node = _SpelNode.SpelNode.create('op-lt', position, left, right); - if (Array.isArray(collection)) { - return projectCollection(collection, expr, state); - } - else if (typeof collection === 'object') { - for (key in collection) { - if (collection.hasOwnProperty(key)) { - entries.push(collection[key]); - } - } - return projectCollection(entries, expr, state); - } + node.getValue = function (state) { + return left.getValue(state) < right.getValue(state); + }; - return null; - }; + return node; +} - return node; - } +var OpLT = { + create: createNode +}; +exports.OpLT = OpLT; + +},{"./SpelNode":39}],27:[function(require,module,exports){ +'use strict'; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +var _SpelNode = require('./SpelNode'); - exports.Projection = { - create: createNode +function createNode(position, left, right) { + var node = _SpelNode.SpelNode.create('op-minus', position, left, right); + + node.getValue = function (state) { + return left.getValue(state) - right.getValue(state); }; -}(window || exports)); + return node; +} -(function (exports, undefined) { - 'use strict'; +var OpMinus = { + create: createNode +}; +exports.OpMinus = OpMinus; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +},{"./SpelNode":39}],28:[function(require,module,exports){ +'use strict'; - function createNode(nullSafeNavigation, propertyName, position) { - var node = SpelNode.create('property', position); +Object.defineProperty(exports, '__esModule', { + value: true +}); - node.getValue = function (state) { - var context = state.activeContext.peek(); +var _SpelNode = require('./SpelNode'); - if (!context) { - throw { - name: 'ContextDoesNotExistException', - message: 'Attempting to look up property \''+ propertyName +'\' for an undefined context.' - } - } +function createNode(position, left, right) { + var node = _SpelNode.SpelNode.create('op-modulus', position, left, right); - if (context[propertyName] === undefined) { - //handle safe navigation - if (nullSafeNavigation) { - return null; - } + node.getValue = function (state) { + return left.getValue(state) % right.getValue(state); + }; - //handle conversion of Java properties to JavaScript properties - //this might cause problems, I'll look into alternatives - if (propertyName === 'size' && Array.isArray(context)) { - return context.length; - } + return node; +} - throw { - name: 'NullPointerException', - message: 'Property \'' + propertyName + '\' does not exist.' - }; - } +var OpModulus = { + create: createNode +}; +exports.OpModulus = OpModulus; - return context[propertyName]; - }; +},{"./SpelNode":39}],29:[function(require,module,exports){ +'use strict'; - node.setValue = function (value, state) { - var context = state.activeContext.peek(); +Object.defineProperty(exports, '__esModule', { + value: true +}); - if (!context) { - throw { - name: 'ContextDoesNotExistException', - message: 'Attempting to assign property \''+ propertyName +'\' for an undefined context.' - } - } +var _SpelNode = require('./SpelNode'); - return context[propertyName] = value; - }; +function createNode(position, left, right) { + var node = _SpelNode.SpelNode.create('op-multiply', position, left, right); - node.getName = function () { - return propertyName; - }; + node.getValue = function (state) { + var leftValue = left.getValue(state), + rightValue = right.getValue(state); - return node; - } + if (typeof leftValue === 'number' && typeof rightValue === 'number') { + return leftValue * rightValue; + } + + //repeats (ex. 'abc' * 2 = 'abcabc') + if (typeof leftValue === 'string' && typeof rightValue === 'number') { + var s = '', + i = 0; + for (; i < rightValue; i += 1) { + s += leftValue; + } + return s; + } - exports.PropertyReference = { - create: createNode + return null; }; -}(window || exports)); + return node; +} -(function (exports) { - 'use strict'; +var OpMultiply = { + create: createNode +}; +exports.OpMultiply = OpMultiply; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +},{"./SpelNode":39}],30:[function(require,module,exports){ +'use strict'; - function createNode(context) { - var node = SpelNode.create('root', null, context); +Object.defineProperty(exports, '__esModule', { + value: true +}); - node.getValue = function () { - if (node.getChildren()[0]) { - return node.getChildren()[0].getValue(); - } - return null; - }; +var _SpelNode = require('./SpelNode'); - return node; - } +function createNode(position, left, right) { + var node = _SpelNode.SpelNode.create('op-ne', position, left, right); - exports.RootNode = { - create: createNode + node.getValue = function (state) { + return left.getValue(state) !== right.getValue(state); }; -}(window || exports)); + return node; +} -(function (exports) { - 'use strict'; +var OpNE = { + create: createNode +}; +exports.OpNE = OpNE; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +},{"./SpelNode":39}],31:[function(require,module,exports){ +'use strict'; - function matches(element, expr, state) { - var matches = false; - state.activeContext.push(element); - matches = expr.getValue(state); - state.activeContext.pop(); - return matches; - } +Object.defineProperty(exports, '__esModule', { + value: true +}); - function selectFromArray(collection, whichElement, expr, state) { - var newCollection = collection.filter(function (element) { - return matches(element, expr, state); - }); +var _SpelNode = require('./SpelNode'); - switch (whichElement) { - case 'ALL': - return newCollection; - break; - case 'FIRST': - return newCollection[0] || null; - break; - case 'LAST': - if (newCollection.length) { - return newCollection[newCollection.length - 1]; - break; - } - return null; - } - } - - function selectFromMap(collection, whichElement, expr, state) { - var newCollection = {}, - entry, - key, - entries = [], - returnValue = {}; - - for (key in collection) { - if (collection.hasOwnProperty(key)) { - entry = { - key: key, - value: collection[key] - }; - if (matches(entry, expr, state)) { - entries.push(entry); - } - } - } +function createNode(position, expr) { + var node = _SpelNode.SpelNode.create('op-not', position, expr); - switch (whichElement) { - case 'ALL': - entries.forEach(function (entry) { - newCollection[entry.key] = entry.value; - }); - return newCollection; - break; - case 'FIRST': - if (entries.length) { - returnValue[entries[0].key] = entries[0].value; - return returnValue; - } - return null; - break; - case 'LAST': - if (entries.length) { - returnValue[entries[entries.length - 1].key] = entries[entries.length - 1].value; - return returnValue; - } - return null; - break; - } + node.getValue = function (state) { + return !expr.getValue(state); + }; - entries.forEach(function (entry) { - newCollection[entry.key] = entry.value; - }); - } + return node; +} - function createNode(nullSafeNavigation, whichElement, position, expr) { - var node = SpelNode.create('selection', position, expr); +var OpNot = { + create: createNode +}; +exports.OpNot = OpNot; - node.getValue = function (state) { - var collection = state.activeContext.peek(); +},{"./SpelNode":39}],32:[function(require,module,exports){ +'use strict'; - if (collection) { - if (Array.isArray(collection)) { - return selectFromArray(collection, whichElement, expr, state); - } - else if (typeof collection === 'object') { - return selectFromMap(collection, whichElement, expr, state); - } - } +Object.defineProperty(exports, '__esModule', { + value: true +}); - return null; - }; +var _SpelNode = require('./SpelNode'); - return node; - } +function createNode(position, left, right) { + var node = _SpelNode.SpelNode.create('op-or', position, left, right); - exports.Selection = { - create: createNode, - FIRST: 'FIRST', - LAST: 'LAST', - ALL: 'ALL' + node.getValue = function (state) { + //double bang for javascript + return !!left.getValue(state) || !!right.getValue(state); }; -}(window || exports)); + return node; +} -(function (exports) { - 'use strict'; +var OpOr = { + create: createNode +}; +exports.OpOr = OpOr; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +},{"./SpelNode":39}],33:[function(require,module,exports){ +'use strict'; - function createNode(value, position) { - var node = SpelNode.create('string', position); +Object.defineProperty(exports, '__esModule', { + value: true +}); - function stripQuotes(value) { - if ((value[0] === '\'' && value[value.length - 1] === '\'') || - (value[0] === '"' && value[value.length - 1] === '"')) { - return value.substring(1, value.length - 1); - } - return value; - } +var _SpelNode = require('./SpelNode'); - //value cannot be null so no check - value = stripQuotes(value); +function createNode(position, left, right) { + var node = _SpelNode.SpelNode.create('op-plus', position, left, right); - node.getValue = function () { - return value; - }; + node.getValue = function (state) { + //javascript will handle string concatenation or addition depending on types + return left.getValue(state) + right.getValue(state); + }; - node.setValue = function (newValue) { - value = newValue; - }; + return node; +} - return node; - } +var OpPlus = { + create: createNode +}; +exports.OpPlus = OpPlus; - exports.StringLiteral = { - create: createNode - }; +},{"./SpelNode":39}],34:[function(require,module,exports){ +'use strict'; -}(window || exports)); +Object.defineProperty(exports, '__esModule', { + value: true +}); -(function (exports) { - 'use strict'; +var _SpelNode = require('./SpelNode'); - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, base, exp) { + var node = _SpelNode.SpelNode.create('op-power', position, base, exp); - function createNode(position, expression, ifTrue, ifFalse) { - var node = SpelNode.create('ternary', position, expression, ifTrue, ifFalse); + node.getValue = function (state) { + return Math.pow(base.getValue(state), exp.getValue(state)); + }; - node.getValue = function (state) { - return expression.getValue(state) ? ifTrue.getValue(state) : ifFalse.getValue(state); - }; + return node; +} - return node; - } +var OpPower = { + create: createNode +}; +exports.OpPower = OpPower; - exports.Ternary = { - create: createNode - }; +},{"./SpelNode":39}],35:[function(require,module,exports){ +'use strict'; -}(window || exports)); +Object.defineProperty(exports, '__esModule', { + value: true +}); -(function (exports) { - 'use strict'; +var _SpelNode = require('./SpelNode'); - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function projectCollection(collection, expr, state) { + return collection.map(function (element) { + var matches; + state.activeContext.push(element); + matches = expr.getValue(state); + state.activeContext.pop(); + return matches; + }); +} - function createNode(variableName, position) { - var node = SpelNode.create('variable', position); +function createNode(nullSafeNavigation, position, expr) { + var node = _SpelNode.SpelNode.create('projection', position, expr); - node.getValue = function (state) { - var context = state.activeContext.peek(), - locals = state.locals; + node.getValue = function (state) { + var collection = state.activeContext.peek(), + entries = [], + key; - if (!context) { - throw { - name: 'ContextDoesNotExistException', - message: 'Attempting to look up variable \''+ variableName +'\' for an undefined context.' + if (Array.isArray(collection)) { + return projectCollection(collection, expr, state); + } else if (typeof collection === 'object') { + for (key in collection) { + if (collection.hasOwnProperty(key)) { + entries.push(collection[key]); } } + return projectCollection(entries, expr, state); + } - //there are 2 keywords (root, this) that need to be dealt with - if (variableName === 'this') { - return context; - } - if (variableName === 'root') { - return state.rootContext; - } - - return locals[variableName]; - }; + return null; + }; - node.setValue = function (value, state) { - var locals = state.locals; + return node; +} - return locals[variableName] = value; - }; +var Projection = { + create: createNode +}; +exports.Projection = Projection; - return node; - } +},{"./SpelNode":39}],36:[function(require,module,exports){ +'use strict'; - exports.VariableReference = { - create: createNode - }; +Object.defineProperty(exports, '__esModule', { + value: true +}); -}(window || exports)); - -(function (exports) { - - var TokenKind, - Tokenizer, - RootNode, - BooleanLiteral, - NumberLiteral, - StringLiteral, - NullLiteral, - FunctionReference, - MethodReference, - PropertyReference, - VariableReference, - CompoundExpression, - Indexer, - Assign, - OpEQ, - OpNE, - OpGE, - OpGT, - OpLE, - OpLT, - OpPlus, - OpMinus, - OpMultiply, - OpDivide, - OpModulus, - OpPower, - OpInc, - OpDec, - OpNot, - OpAnd, - OpOr, - Ternary, - Elvis, - InlineList, - InlineMap, - Selection, - Projection; - - try { - TokenKind = require('./TokenKind'); - Tokenizer = require('./Tokenizer'); - RootNode = require('./ast/RootNode'); - BooleanLiteral = require('./ast/BooleanLiteral'); - NumberLiteral = require('./ast/NumberLiteral'); - StringLiteral = require('./ast/StringLiteral'); - NullLiteral = require('./ast/NullLiteral'); - FunctionReference = require('./ast/FunctionReference'); - MethodReference = require('./ast/MethodReference'); - PropertyReference = require('./ast/PropertyReference'); - VariableReference = require('./ast/VariableReference'); - CompoundExpression = require('./ast/CompoundExpression'); - Indexer = require('./ast/Indexer'); - Assign = require('./ast/Assign'); - OpEQ = require('./ast/OpEQ'); - OpNE = require('./ast/OpNE'); - OpGE = require('./ast/OpGE'); - OpGT = require('./ast/OpGT'); - OpLE = require('./ast/OpLE'); - OpLT = require('./ast/OpLT'); - OpPlus = require('./ast/OpPlus'); - OpMinus = require('./ast/OpMinus'); - OpMultiply = require('./ast/OpMultiply'); - OpDivide = require('./ast/OpDivide'); - OpModulus = require('./ast/OpModulus'); - OpPower = require('./ast/OpPower'); - OpInc = require('./ast/OpInc'); - OpDec = require('./ast/OpDec'); - OpNot = require('./ast/OpNot'); - OpAnd = require('./ast/OpAnd'); - OpOr = require('./ast/OpOr'); - Ternary = require('./ast/Ternary'); - Elvis = require('./ast/Elvis'); - InlineList = require('./ast/InlineList'); - InlineMap = require('./ast/InlineMap'); - Selection = require('./ast/Selection'); - Projection = require('./ast/Projection'); - } catch (e) { - TokenKind = exports.TokenKind; - Tokenizer = exports.Tokenizer; - RootNode = exports.RootNode; - BooleanLiteral = exports.BooleanLiteral; - NumberLiteral = exports.NumberLiteral; - StringLiteral = exports.StringLiteral; - NullLiteral = exports.NullLiteral; - FunctionReference = exports.FunctionReference; - MethodReference = exports.MethodReference; - PropertyReference = exports.PropertyReference; - VariableReference = exports.VariableReference; - CompoundExpression = exports.CompoundExpression; - Indexer = exports.Indexer; - Assign = exports.Assign; - OpEQ = exports.OpEQ; - OpNE = exports.OpNE; - OpGE = exports.OpGE; - OpGT = exports.OpGT; - OpLE = exports.OpLE; - OpLT = exports.OpLT; - OpPlus = exports.OpPlus; - OpMinus = exports.OpMinus; - OpMultiply = exports.OpMultiply; - OpDivide = exports.OpDivide; - OpModulus = exports.OpModulus; - OpPower = exports.OpPower; - OpInc = exports.OpInc; - OpDec = exports.OpDec; - OpNot = exports.OpNot; - OpAnd = exports.OpAnd; - OpOr = exports.OpOr; - Ternary = exports.Ternary; - Elvis = exports.Elvis; - InlineList = exports.InlineList; - InlineMap = exports.InlineMap; - Selection = exports.Selection; - Projection = exports.Projection; - } - - - var SpelExpressionParser = function () { - - - var VALID_QUALIFIED_ID_PATTERN = new RegExp("[\\p{L}\\p{N}_$]+"); - - - var configuration; - - // For rules that build nodes, they are stacked here for return - var constructedNodes = []; - - // The expression being parsed - var expressionString; - - // The token stream constructed from that expression string - var tokenStream; - - // length of a populated token stream - var tokenStreamLength; - - // Current location in the token stream when processing tokens - var tokenStreamPointer; - - - /** - * Create a parser with some configured behavior. - * @param config custom configuration options - */ - function setConfiguration(config) { - configuration = config; - } - - - function parse(expression, context) { - try { - expressionString = expression; - tokenStream = Tokenizer.tokenize(expression); - tokenStreamLength = tokenStream.length; - tokenStreamPointer = 0; - constructedNodes = []; - var ast = eatExpression(); - if (moreTokens()) { - raiseInternalException(peekToken().startPos, 'MORE_INPUT', nextToken().toString()); - } - //Assert.isTrue(this.constructedNodes.isEmpty()); - return ast; - } - catch (e) { - throw e.message; - } - } +var _SpelNode = require('./SpelNode'); - // expression - // : logicalOrExpression - // ( (ASSIGN^ logicalOrExpression) - // | (DEFAULT^ logicalOrExpression) - // | (QMARK^ expression COLON! expression) - // | (ELVIS^ expression))?; - function eatExpression() { - var expr = eatLogicalOrExpression(); - if (moreTokens()) { - var token = peekToken(); - if (token.getKind() == TokenKind.ASSIGN) { // a=b - if (expr == null) { - expr = NullLiteral.create(toPosBounds(token.startPos - 1, token.endPos - 1)); - } - nextToken(); - var assignedValue = eatLogicalOrExpression(); - return Assign.create(toPosToken(token), expr, assignedValue); - } +function createNode(nullSafeNavigation, propertyName, position) { + var node = _SpelNode.SpelNode.create('property', position); - if (token.getKind() == TokenKind.ELVIS) { // a?:b (a if it isn't null, otherwise b) - if (expr == null) { - expr = NullLiteral.create(toPosBounds(token.startPos - 1, token.endPos - 2)); - } - nextToken(); // elvis has left the building - var valueIfNull = eatExpression(); - if (valueIfNull == null) { - valueIfNull = NullLiteral.create(toPosBounds(token.startPos + 1, token.endPos + 1)); - } - return Elvis.create(toPosToken(token), expr, valueIfNull); - } + node.getValue = function (state) { + var context = state.activeContext.peek(); - if (token.getKind() == TokenKind.QMARK) { // a?b:c - if (expr == null) { - expr = NullLiteral.create(toPosBounds(token.startPos - 1, token.endPos - 1)); - } - nextToken(); - var ifTrueExprValue = eatExpression(); - eatToken(TokenKind.COLON); - var ifFalseExprValue = eatExpression(); - return Ternary.create(toPosToken(token), expr, ifTrueExprValue, ifFalseExprValue); - } - } - return expr; + if (!context) { + throw { + name: 'ContextDoesNotExistException', + message: 'Attempting to look up property \'' + propertyName + '\' for an undefined context.' + }; } - //logicalOrExpression : logicalAndExpression (OR^ logicalAndExpression)*; - function eatLogicalOrExpression() { - var expr = eatLogicalAndExpression(); - while (peekIdentifierToken("or") || peekTokenOne(TokenKind.SYMBOLIC_OR)) { - var token = nextToken(); //consume OR - var rhExpr = eatLogicalAndExpression(); - checkOperands(token, expr, rhExpr); - expr = OpOr.create(toPosToken(token), expr, rhExpr); + if (context[propertyName] === undefined) { + //handle safe navigation + if (nullSafeNavigation) { + return null; } - return expr; - } - // logicalAndExpression : relationalExpression (AND^ relationalExpression)*; - function eatLogicalAndExpression() { - var expr = eatRelationalExpression(); - while (peekIdentifierToken("and") || peekTokenOne(TokenKind.SYMBOLIC_AND)) { - var token = nextToken(); // consume 'AND' - var rhExpr = eatRelationalExpression(); - checkOperands(token, expr, rhExpr); - expr = OpAnd.create(toPosToken(token), expr, rhExpr); + //handle conversion of Java properties to JavaScript properties + if (propertyName === 'size' && Array.isArray(context)) { + return context.length; } - return expr; - } - - // relationalExpression : sumExpression (relationalOperator^ sumExpression)?; - function eatRelationalExpression() { - var expr = eatSumExpression(); - var relationalOperatorToken = maybeEatRelationalOperator(); - if (relationalOperatorToken != null) { - var token = nextToken(); // consume relational operator token - var rhExpr = eatSumExpression(); - checkOperands(token, expr, rhExpr); - var tk = relationalOperatorToken.kind; - - if (relationalOperatorToken.isNumericRelationalOperator()) { - var pos = toPosToken(token); - if (tk == TokenKind.GT) { - return OpGT.create(pos, expr, rhExpr); - } - if (tk == TokenKind.LT) { - return OpLT.create(pos, expr, rhExpr); - } - if (tk == TokenKind.LE) { - return OpLE.create(pos, expr, rhExpr); - } - if (tk == TokenKind.GE) { - return OpGE.create(pos, expr, rhExpr); - } - if (tk == TokenKind.EQ) { - return OpEQ.create(pos, expr, rhExpr); - } - //Assert.isTrue(tk == TokenKind.NE); - return OpNE.create(pos, expr, rhExpr); - } - if (tk == TokenKind.INSTANCEOF) { - return new OperatorInstanceof(toPosToken(token), expr, rhExpr); - } + throw { + name: 'NullPointerException', + message: 'Property \'' + propertyName + '\' does not exist.' + }; + } - if (tk == TokenKind.MATCHES) { - return new OperatorMatches(toPosToken(token), expr, rhExpr); - } + return context[propertyName]; + }; - //Assert.isTrue(tk == TokenKind.BETWEEN); - return new OperatorBetween(toPosToken(token), expr, rhExpr); - } - return expr; - } - - //sumExpression: productExpression ( (PLUS^ | MINUS^) productExpression)*; - function eatSumExpression() { - var expr = eatProductExpression(); - while (peekTokenAny(TokenKind.PLUS, TokenKind.MINUS, TokenKind.INC)) { - var token = nextToken();//consume PLUS or MINUS or INC - var rhExpr = eatProductExpression(); - checkRightOperand(token, rhExpr); - if (token.getKind() == TokenKind.PLUS) { - expr = OpPlus.create(toPosToken(token), expr, rhExpr); - } - else if (token.getKind() == TokenKind.MINUS) { - expr = OpMinus.create(toPosToken(token), expr, rhExpr); - } - } - return expr; - } - - // productExpression: powerExpr ((STAR^ | DIV^| MOD^) powerExpr)* ; - function eatProductExpression() { - var expr = eatPowerIncDecExpression(); - while (peekTokenAny(TokenKind.STAR, TokenKind.DIV, TokenKind.MOD)) { - var token = nextToken(); // consume STAR/DIV/MOD - var rhExpr = eatPowerIncDecExpression(); - checkOperands(token, expr, rhExpr); - if (token.getKind() == TokenKind.STAR) { - expr = OpMultiply.create(toPosToken(token), expr, rhExpr); - } - else if (token.getKind() == TokenKind.DIV) { - expr = OpDivide.create(toPosToken(token), expr, rhExpr); - } - else { - //Assert.isTrue(token.getKind() == TokenKind.MOD); - expr = OpModulus.create(toPosToken(token), expr, rhExpr); - } - } - return expr; + node.setValue = function (value, state) { + var context = state.activeContext.peek(); + + if (!context) { + throw { + name: 'ContextDoesNotExistException', + message: 'Attempting to assign property \'' + propertyName + '\' for an undefined context.' + }; } - // powerExpr : unaryExpression (POWER^ unaryExpression)? (INC || DEC) ; - function eatPowerIncDecExpression() { - var expr = eatUnaryExpression(), - token; + /*jshint -W093 */ + return context[propertyName] = value; + /*jshint +W093 */ + }; - if (peekTokenOne(TokenKind.POWER)) { - token = nextToken(); //consume POWER - var rhExpr = eatUnaryExpression(); - checkRightOperand(token, rhExpr); - return OpPower.create(toPosToken(token), expr, rhExpr); - } + node.getName = function () { + return propertyName; + }; - if (expr != null && peekTokenAny(TokenKind.INC, TokenKind.DEC)) { - token = nextToken(); //consume INC/DEC - if (token.getKind() == TokenKind.INC) { - return OpInc.create(toPosToken(token), true, expr); - } - return OpDec.create(toPosToken(token), true, expr); - } + return node; +} - return expr; - } +var PropertyReference = { + create: createNode +}; +exports.PropertyReference = PropertyReference; - // unaryExpression: (PLUS^ | MINUS^ | BANG^ | INC^ | DEC^) unaryExpression | primaryExpression ; - function eatUnaryExpression() { - var token, - expr; +},{"./SpelNode":39}],37:[function(require,module,exports){ +'use strict'; - if (peekTokenAny(TokenKind.PLUS, TokenKind.MINUS, TokenKind.NOT)) { - token = nextToken(); - expr = eatUnaryExpression(); - if (token.getKind() == TokenKind.NOT) { - return OpNot.create(toPosToken(token), expr); - } +Object.defineProperty(exports, '__esModule', { + value: true +}); - if (token.getKind() == TokenKind.PLUS) { - return OpPlus.create(toPosToken(token), expr); - } - //Assert.isTrue(token.getKind() == TokenKind.MINUS); - return OpMinus.create(toPosToken(token), expr); +var _SpelNode = require('./SpelNode'); - } - if (peekTokenAny(TokenKind.INC, TokenKind.DEC)) { - token = nextToken(); - expr = eatUnaryExpression(); - if (token.getKind() == TokenKind.INC) { - return OpInc.create(toPosToken(token), false, expr); - } - return OpDec.create(toPosToken(token), false, expr); - } +function createNode(context) { + var node = _SpelNode.SpelNode.create('root', null, context); - return eatPrimaryExpression(); + node.getValue = function () { + if (node.getChildren()[0]) { + return node.getChildren()[0].getValue(); } + return null; + }; - // primaryExpression : startNode (node)? -> ^(EXPRESSION startNode (node)?); - function eatPrimaryExpression() { - var nodes = []; - var start = eatStartNode(); // always a start node - nodes.push(start); - while (maybeEatNode()) { - nodes.push(pop()); - } - if (nodes.length == 1) { - return nodes[0]; - } - return CompoundExpression.create(toPosBounds(start.getStartPosition(), nodes[nodes.length - 1].getEndPosition()), nodes); - } + return node; +} - // node : ((DOT dottedNode) | (SAFE_NAVI dottedNode) | nonDottedNode)+; - function maybeEatNode() { - var expr = null; - if (peekTokenAny(TokenKind.DOT, TokenKind.SAFE_NAVI)) { - expr = eatDottedNode(); - } - else { - expr = maybeEatNonDottedNode(); - } +var RootNode = { + create: createNode +}; +exports.RootNode = RootNode; - if (expr == null) { - return false; - } - else { - push(expr); - return true; - } - } +},{"./SpelNode":39}],38:[function(require,module,exports){ +'use strict'; - // nonDottedNode: indexer; - function maybeEatNonDottedNode() { - if (peekTokenOne(TokenKind.LSQUARE)) { - if (maybeEatIndexer()) { - return pop(); - } - } - return null; - } +Object.defineProperty(exports, '__esModule', { + value: true +}); - //dottedNode - // : ((methodOrProperty - // | functionOrVar - // | projection - // | selection - // | firstSelection - // | lastSelection - // )) - // ; - function eatDottedNode() { - var token = nextToken();// it was a '.' or a '?.' - var nullSafeNavigation = token.getKind() == TokenKind.SAFE_NAVI; - if (maybeEatMethodOrProperty(nullSafeNavigation) || maybeEatFunctionOrVar() - || maybeEatProjection(nullSafeNavigation) - || maybeEatSelection(nullSafeNavigation)) { - return pop(); - } - if (peekToken() == null) { - // unexpectedly ran out of data - raiseInternalException(token.startPos, 'OOD'); - } - else { - raiseInternalException(token.startPos, 'UNEXPECTED_DATA_AFTER_DOT', toString(peekToken())); - } - return null; - } +var _SpelNode = require('./SpelNode'); - // functionOrVar - // : (POUND ID LPAREN) => function - // | var - // - // function : POUND id=ID methodArgs -> ^(FUNCTIONREF[$id] methodArgs); - // var : POUND id=ID -> ^(VARIABLEREF[$id]); - function maybeEatFunctionOrVar() { - if (!peekTokenOne(TokenKind.HASH)) { - return false; - } - var token = nextToken(); - var functionOrVariableName = eatToken(TokenKind.IDENTIFIER); - var args = maybeEatMethodArgs(); - if (args == null) { - push(VariableReference.create(functionOrVariableName.data, toPosBounds(token.startPos, functionOrVariableName.endPos))); - return true; - } +function matches(element, expr, state) { + var doesMatch = false; + state.activeContext.push(element); + doesMatch = expr.getValue(state); + state.activeContext.pop(); + return doesMatch; +} - push(FunctionReference.create(functionOrVariableName.data, toPosBounds(token.startPos, functionOrVariableName.endPos), args)); - return true; - } +function selectFromArray(collection, whichElement, expr, state) { + var newCollection = collection.filter(function (element) { + return matches(element, expr, state); + }); - // methodArgs : LPAREN! (argument (COMMA! argument)* (COMMA!)?)? RPAREN!; - function maybeEatMethodArgs() { - if (!peekTokenOne(TokenKind.LPAREN)) { - return null; + switch (whichElement) { + case 'ALL': + return newCollection; + case 'FIRST': + return newCollection[0] || null; + case 'LAST': + if (newCollection.length) { + return newCollection[newCollection.length - 1]; } - var args = []; - consumeArguments(args); - eatToken(TokenKind.RPAREN); - return args; - } + return null; + } +} - function eatConstructorArgs(accumulatedArguments) { - if (!peekTokenOne(TokenKind.LPAREN)) { - raiseInternalException(toPosToken(peekToken()), 'MISSING_CONSTRUCTOR_ARGS'); +function selectFromMap(collection, whichElement, expr, state) { + var newCollection = {}, + entry, + key, + entries = [], + returnValue = {}; + + for (key in collection) { + if (collection.hasOwnProperty(key)) { + entry = { + key: key, + value: collection[key] + }; + if (matches(entry, expr, state)) { + entries.push(entry); } - consumeArguments(accumulatedArguments); - eatToken(TokenKind.RPAREN); } + } - /** - * Used for consuming arguments for either a method or a constructor call - */ - function consumeArguments(accumulatedArguments) { - var pos = peekToken().startPos; - var next; - do { - nextToken(); // consume ( (first time through) or comma (subsequent times) - var token = peekToken(); - if (token == null) { - raiseInternalException(pos, 'RUN_OUT_OF_ARGUMENTS'); - } - if (token.getKind() != TokenKind.RPAREN) { - accumulatedArguments.push(eatExpression()); - } - next = peekToken(); + switch (whichElement) { + case 'ALL': + entries.forEach(function (entry) { + newCollection[entry.key] = entry.value; + }); + return newCollection; + case 'FIRST': + if (entries.length) { + returnValue[entries[0].key] = entries[0].value; + return returnValue; } - while (next != null && next.kind == TokenKind.COMMA); - - if (next == null) { - raiseInternalException(pos, 'RUN_OUT_OF_ARGUMENTS'); + return null; + case 'LAST': + if (entries.length) { + returnValue[entries[entries.length - 1].key] = entries[entries.length - 1].value; + return returnValue; } - } + return null; + } - function positionOf(token) { - if (token == null) { - // if null assume the problem is because the right token was - // not found at the end of the expression - return expressionString.length; - } - return token.startPos; - } - - //startNode - // : parenExpr | literal - // | type - // | methodOrProperty - // | functionOrVar - // | projection - // | selection - // | firstSelection - // | lastSelection - // | indexer - // | constructor - function eatStartNode() { - if (maybeEatLiteral()) { - return pop(); - } - else if (maybeEatParenExpression()) { - return pop(); - } - else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstructorReference() || - maybeEatMethodOrProperty(false) || maybeEatFunctionOrVar()) { - return pop(); - } - else if (maybeEatBeanReference()) { - return pop(); - } - else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) { - return pop(); - } - else if (maybeEatInlineListOrMap()) { - return pop(); - } - else { - return null; - } - } + entries.forEach(function (entry) { + newCollection[entry.key] = entry.value; + }); +} - // parse: @beanname @'bean.name' - // quoted if dotted - function maybeEatBeanReference() { - if (peekTokenOne(TokenKind.BEAN_REF)) { - var beanRefToken = nextToken(); - var beanNameToken = null; - var beanName = null; - if (peekTokenOne(TokenKind.IDENTIFIER)) { - beanNameToken = eatToken(TokenKind.IDENTIFIER); - beanName = beanNameToken.data; - } - else if (peekTokenOne(TokenKind.LITERAL_STRING)) { - beanNameToken = eatToken(TokenKind.LITERAL_STRING); - beanName = beanNameToken.stringValue(); - beanName = beanName.substring(1, beanName.length() - 1); - } - else { - raiseInternalException(beanRefToken.startPos, 'INVALID_BEAN_REFERENCE'); - } +function createNode(nullSafeNavigation, whichElement, position, expr) { + var node = _SpelNode.SpelNode.create('selection', position, expr); - var beanReference = new BeanReference(toPosToken(beanNameToken), beanName); - push(beanReference); - return true; - } - return false; - } + node.getValue = function (state) { + var collection = state.activeContext.peek(); - function maybeEatTypeReference() { - if (peekTokenOne(TokenKind.IDENTIFIER)) { - var typeName = peekToken(); - if (typeName.stringValue() !== "T") { - return false; - } - // It looks like a type reference but is T being used as a map key? - var token = nextToken(); - if (peekTokenOne(TokenKind.RSQUARE)) { - // looks like 'T]' (T is map key) - push(PropertyReference.create(token.stringValue(), toPosToken(token))); - return true; - } - eatToken(TokenKind.LPAREN); - var node = eatPossiblyQualifiedId(); - // dotted qualified id - // Are there array dimensions? - var dims = 0; - while (peekTokenConsumeIfMatched(TokenKind.LSQUARE, true)) { - eatToken(TokenKind.RSQUARE); - dims++; - } - eatToken(TokenKind.RPAREN); - push(new TypeReference(toPosToken(typeName), node, dims)); - return true; + if (collection) { + if (Array.isArray(collection)) { + return selectFromArray(collection, whichElement, expr, state); + } else if (typeof collection === 'object') { + return selectFromMap(collection, whichElement, expr, state); } - return false; } - function maybeEatNullReference() { - if (peekTokenOne(TokenKind.IDENTIFIER)) { - var nullToken = peekToken(); - if (nullToken.stringValue().toLowerCase() !== "null") { - return false; - } - nextToken(); - push(NullLiteral.create(toPosToken(nullToken))); - return true; - } - return false; - } + return null; + }; - //projection: PROJECT^ expression RCURLY!; - function maybeEatProjection(nullSafeNavigation) { - var token = peekToken(); - if (!peekTokenConsumeIfMatched(TokenKind.PROJECT, true)) { - return false; - } - var expr = eatExpression(); - eatToken(TokenKind.RSQUARE); - push(Projection.create(nullSafeNavigation, toPosToken(token), expr)); - return true; - } + return node; +} - // list = LCURLY (element (COMMA element)*) RCURLY - // map = LCURLY (key ':' value (COMMA key ':' value)*) RCURLY - function maybeEatInlineListOrMap() { - var token = peekToken(), - listElements = []; +var Selection = { + create: createNode, + FIRST: 'FIRST', + LAST: 'LAST', + ALL: 'ALL' +}; +exports.Selection = Selection; - if (!peekTokenConsumeIfMatched(TokenKind.LCURLY, true)) { - return false; - } - var expr = null; - var closingCurly = peekToken(); - if (peekTokenConsumeIfMatched(TokenKind.RCURLY, true)) { - // empty list '{}' - expr = InlineList.create(toPosBounds(token.startPos, closingCurly.endPos)); - } - else if (peekTokenConsumeIfMatched(TokenKind.COLON, true)) { - closingCurly = eatToken(TokenKind.RCURLY); - // empty map '{:}' - expr = InlineMap.create(toPosBounds(token.startPos, closingCurly.endPos)); - } - else { - var firstExpression = eatExpression(); - // Next is either: - // '}' - end of list - // ',' - more expressions in this list - // ':' - this is a map! - - if (peekTokenOne(TokenKind.RCURLY)) { // list with one item in it - listElements.push(firstExpression); - closingCurly = eatToken(TokenKind.RCURLY); - expr = InlineList.create(toPosBounds(token.startPos, closingCurly.endPos), listElements); - } - else if (peekTokenConsumeIfMatched(TokenKind.COMMA, true)) { // multi item list - listElements.push(firstExpression); - do { - listElements.push(eatExpression()); - } - while (peekTokenConsumeIfMatched(TokenKind.COMMA, true)); - closingCurly = eatToken(TokenKind.RCURLY); - expr = InlineList.create(toPosToken(token.startPos, closingCurly.endPos), listElements); +},{"./SpelNode":39}],39:[function(require,module,exports){ +'use strict'; - } - else if (peekTokenConsumeIfMatched(TokenKind.COLON, true)) { // map! - var mapElements = []; - mapElements.push(firstExpression); - mapElements.push(eatExpression()); - while (peekTokenConsumeIfMatched(TokenKind.COMMA, true)) { - mapElements.push(eatExpression()); - eatToken(TokenKind.COLON); - mapElements.push(eatExpression()); - } - closingCurly = eatToken(TokenKind.RCURLY); - expr = InlineMap.create(toPosBounds(token.startPos, closingCurly.endPos), mapElements); - } - else { - raiseInternalException(token.startPos, 'OOD'); - } - } - push(expr); - return true; - } +Object.defineProperty(exports, '__esModule', { + value: true +}); +function createSpelNode(nodeType, position) { + var node = {}, + type = nodeType || 'Abstract', + children = [], + parent = null, + activeContext; - function maybeEatIndexer() { - var token = peekToken(); - if (!peekTokenConsumeIfMatched(TokenKind.LSQUARE, true)) { - return false; - } - var expr = eatExpression(); - eatToken(TokenKind.RSQUARE); - push(Indexer.create(toPosToken(token), expr)); - return true; - } + node._type = type; - function maybeEatSelection(nullSafeNavigation) { - var token = peekToken(); - if (!peekSelectToken()) { - return false; - } - nextToken(); - var expr = eatExpression(); - if (expr == null) { - raiseInternalException(toPosToken(token), 'MISSING_SELECTION_EXPRESSION'); - } - eatToken(TokenKind.RSQUARE); - if (token.getKind() == TokenKind.SELECT_FIRST) { - push(Selection.create(nullSafeNavigation, Selection.FIRST, toPosToken(token), expr)); - } - else if (token.getKind() == TokenKind.SELECT_LAST) { - push(Selection.create(nullSafeNavigation, Selection.LAST, toPosToken(token), expr)); - } - else { - push(Selection.create(nullSafeNavigation, Selection.ALL, toPosToken(token), expr)); - } - return true; - } + node.getType = function () { + return type; + }; + node.setType = function (nodeType) { + type = nodeType; + }; - /** - * Eat an identifier, possibly qualified (meaning that it is dotted). - * TODO AndyC Could create complete identifiers (a.b.c) here rather than a sequence of them? (a, b, c) - */ - function eatPossiblyQualifiedId() { - var qualifiedIdPieces = []; - var node = peekToken(); - while (isValidQualifiedId(node)) { - nextToken(); - if (node.kind != TokenKind.DOT) { - qualifiedIdPieces.push(new Identifier(node.stringValue(), toPosToken(node))); - } - node = peekToken(); - } - if (!qualifiedIdPieces.length) { - if (node == null) { - raiseInternalException(expressionString.length(), 'OOD'); - } - raiseInternalException(node.startPos, 'NOT_EXPECTED_TOKEN', "qualified ID", node.getKind().toString().toLowerCase()); - } - var pos = toPosBounds(qualifiedIdPieces[0].getStartPosition(), qualifiedIdPieces[qualifiedIdPieces.length - 1].getEndPosition()); - return new QualifiedIdentifier(pos, qualifiedIdPieces); - } + node.getChildren = function () { + return children; + }; + node.addChild = function (childNode) { + childNode.setParent(node); + children.push(childNode); + }; - function isValidQualifiedId(node) { - if (node == null || node.kind == TokenKind.LITERAL_STRING) { - return false; - } - if (node.kind == TokenKind.DOT || node.kind == TokenKind.IDENTIFIER) { - return true; - } - var value = node.stringValue(); - return (value.length && VALID_QUALIFIED_ID_PATTERN.test(value)); - } - - // This is complicated due to the support for dollars in identifiers. Dollars are normally separate tokens but - // there we want to combine a series of identifiers and dollars into a single identifier - function maybeEatMethodOrProperty(nullSafeNavigation) { - if (peekTokenOne(TokenKind.IDENTIFIER)) { - var methodOrPropertyName = nextToken(); - var args = maybeEatMethodArgs(); - if (args == null) { - // property - push(PropertyReference.create(nullSafeNavigation, methodOrPropertyName.stringValue(), toPosToken(methodOrPropertyName))); - return true; - } - // methodreference - push(MethodReference.create(nullSafeNavigation, methodOrPropertyName.stringValue(), toPosToken(methodOrPropertyName), args)); - // TODO what is the end position for a method reference? the name or the last arg? - return true; - } - return false; - } + node.getParent = function () { + return parent; + }; + node.setParent = function (parentNode) { + parent = parentNode; + }; - //constructor - //: ('new' qualifiedId LPAREN) => 'new' qualifiedId ctorArgs -> ^(CONSTRUCTOR qualifiedId ctorArgs) - function maybeEatConstructorReference() { - if (peekIdentifierToken("new")) { - var newToken = nextToken(); - // It looks like a constructor reference but is NEW being used as a map key? - if (peekTokenOne(TokenKind.RSQUARE)) { - // looks like 'NEW]' (so NEW used as map key) - push(PropertyReference.create(newToken.stringValue(), toPosToken(newToken))); - return true; - } - var possiblyQualifiedConstructorName = eatPossiblyQualifiedId(); - var nodes = []; - nodes.push(possiblyQualifiedConstructorName); - if (peekTokenOne(TokenKind.LSQUARE)) { - // array initializer - var dimensions = []; - while (peekTokenConsumeIfMatched(TokenKind.LSQUARE, true)) { - if (!peekTokenOne(TokenKind.RSQUARE)) { - dimensions.push(eatExpression()); - } - else { - dimensions.push(null); - } - eatToken(TokenKind.RSQUARE); - } - if (maybeEatInlineListOrMap()) { - nodes.push(pop()); - } - push(new ConstructorReference(toPosToken(newToken), dimensions, nodes)); - } - else { - // regular constructor invocation - eatConstructorArgs(nodes); - // TODO correct end position? - push(new ConstructorReference(toPosToken(newToken), nodes)); - } - return true; - } - return false; - } + node.getContext = function (state) { + return activeContext || state.activeContext.peek(); + }; + node.setContext = function (nodeContext) { + activeContext = nodeContext; + }; - function push(newNode) { - constructedNodes.push(newNode); - } + node.getStartPosition = function () { + return position >> 16; + }; - function pop() { - return constructedNodes.pop(); - } + node.getEndPosition = function () { + return position & 0xffff; + }; - // literal - // : INTEGER_LITERAL - // | boolLiteral - // | STRING_LITERAL - // | HEXADECIMAL_INTEGER_LITERAL - // | REAL_LITERAL - // | DQ_STRING_LITERAL - // | NULL_LITERAL - function maybeEatLiteral() { - var token = peekToken(); - if (token == null) { - return false; - } - if (token.getKind() === TokenKind.LITERAL_INT || - token.getKind() === TokenKind.LITERAL_LONG) { - push(NumberLiteral.create(parseInt(token.stringValue(), 10), toPosToken(token))); - } - else if ( token.getKind() === TokenKind.LITERAL_REAL || - token.getKind() === TokenKind.LITERAL_REAL_FLOAT) { - push(NumberLiteral.create(parseFloat(token.stringValue()), toPosToken(token))); - } - else if ( token.getKind() === TokenKind.LITERAL_HEXINT || - token.getKind() === TokenKind.LITERAL_HEXLONG) { - push(NumberLiteral.create(parseInt(token.stringValue(), 16), toPosToken(token))); - } - else if (peekIdentifierToken("true")) { - push(BooleanLiteral.create(true, toPosToken(token))); - } - else if (peekIdentifierToken("false")) { - push(BooleanLiteral.create(false, toPosToken(token))); - } - else if (token.getKind() === TokenKind.LITERAL_STRING) { - push(StringLiteral.create(token.stringValue(), toPosToken(token))); - } - else { - return false; - } - nextToken(); - return true; - } + //must override + node.getValue = function () { + throw { + name: 'MethodNotImplementedException', + message: 'SpelNode#getValue() must be overridden.' + }; + }; - //parenExpr : LPAREN! expression RPAREN!; - function maybeEatParenExpression() { - if (peekTokenOne(TokenKind.LPAREN)) { - nextToken(); - var expr = eatExpression(); - eatToken(TokenKind.RPAREN); - push(expr); - return true; - } - else { - return false; - } + node.toString = function () { + var s = 'Kind: ' + node.getType(); + //s += ', Value: ' + node.getValue(); + s += ', Children: ['; + for (var i = 0, l = node.getChildren().length; i < l; i += 1) { + s += '{' + node.getChildren()[i] + '}, '; } + s += ']'; + return s; + }; - // relationalOperator - // : EQUAL | NOT_EQUAL | LESS_THAN | LESS_THAN_OR_EQUAL | GREATER_THAN - // | GREATER_THAN_OR_EQUAL | INSTANCEOF | BETWEEN | MATCHES - function maybeEatRelationalOperator() { - var token = peekToken(); - if (token == null) { - return null; - } - if (token.isNumericRelationalOperator()) { - return token; - } - if (token.isIdentifier()) { - var idString = token.stringValue(); - if (idString.toLowerCase() === "instanceof") { - return token.asInstanceOfToken(); - } - if (idString.toLowerCase() === "matches") { - return token.asMatchesToken(); - } - if (idString.toLowerCase() === "between") { - return token.asBetweenToken(); - } - } - return null; - } + //constructor + if (position === 0) { + throw { + name: 'Error', + message: 'Position cannot be 0' + }; + } - function eatToken(expectedKind) { - var token = nextToken(); - if (token == null) { - raiseInternalException(expressionString.length, 'OOD'); - } - if (token.getKind() != expectedKind) { - raiseInternalException(token.startPos, 'NOT_EXPECTED_TOKEN', - expectedKind.toString().toLowerCase(), token.getKind().toString().toLowerCase()); - } - return token; - } + for (var _len = arguments.length, operands = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + operands[_key - 2] = arguments[_key]; + } - function peekTokenOne(desiredTokenKind) { - return peekTokenConsumeIfMatched(desiredTokenKind, false); - } + if (operands) { + operands.forEach(function (operand) { + node.addChild(operand); + }); + } - function peekTokenConsumeIfMatched(desiredTokenKind, consumeIfMatched) { - if (!moreTokens()) { - return false; - } - var token = peekToken(); - if (token.getKind() == desiredTokenKind) { - if (consumeIfMatched) { - tokenStreamPointer++; - } - return true; - } + return node; +} - if (desiredTokenKind == TokenKind.IDENTIFIER) { - // might be one of the textual forms of the operators (e.g. NE for != ) - in which case we can treat it as an identifier - // The list is represented here: Tokenizer.alternativeOperatorNames and those ones are in order in the TokenKind enum - if (token.getKind().ordinal() >= TokenKind.DIV.ordinal() && token.getKind().ordinal() <= TokenKind.NOT.ordinal() && token.data != null) { - // if token.data were null, we'd know it wasn'token the textual form, it was the symbol form - return true; - } - } - return false; - } +var SpelNode = { + create: createSpelNode +}; +exports.SpelNode = SpelNode; - function peekTokenAny() { - if (!moreTokens()) { - return false; - } - var token = peekToken(); - var args = Array.prototype.slice.call(arguments); - for (var i = 0, l = args.length; i < l; i += 1) { - if (token.getKind() === args[i]) { - return true; - } - } - return false; - } +},{}],40:[function(require,module,exports){ +'use strict'; - function peekIdentifierToken(identifierString) { - if (!moreTokens()) { - return false; - } - var token = peekToken(); - return token.getKind() === TokenKind.IDENTIFIER && token.stringValue().toLowerCase() === identifierString.toLowerCase(); - } +Object.defineProperty(exports, '__esModule', { + value: true +}); - function peekSelectToken() { - if (!moreTokens()) { - return false; - } - var token = peekToken(); - return token.getKind() === TokenKind.SELECT || token.getKind() === TokenKind.SELECT_FIRST - || token.getKind() === TokenKind.SELECT_LAST; - } +var _SpelNode = require('./SpelNode'); - function moreTokens() { - return tokenStreamPointer < tokenStream.length; - } +function createNode(value, position) { + var node = _SpelNode.SpelNode.create('string', position); - function nextToken() { - if (tokenStreamPointer >= tokenStreamLength) { - return null; - } - return tokenStream[tokenStreamPointer++]; + function stripQuotes(value) { + if (value[0] === '\'' && value[value.length - 1] === '\'' || value[0] === '"' && value[value.length - 1] === '"') { + return value.substring(1, value.length - 1); } + return value; + } - function peekToken() { - if (tokenStreamPointer >= tokenStreamLength) { - return null; - } - return tokenStream[tokenStreamPointer]; - } + //value cannot be null so no check + value = stripQuotes(value); - function raiseInternalException(pos, message, expected, actual) { - if (expected) { - message += '\nExpected: ' + expected; - } - if (actual) { - message += '\nActual: ' + actual; - } - throw { - name: 'InternalParseException', - message: 'Error occurred while attempting to parse expression \'' + expressionString + '\' at position ' + pos + '. Message: ' + message - }; - } + node.getValue = function () { + return value; + }; - function toString(token) { - if (token.getKind().hasPayload()) { - return token.stringValue(); - } - return token.getKind().toString().toLowerCase(); - } + node.setValue = function (newValue) { + /*jshint -W093 */ + return value = newValue; + /*jshint +W093 */ + }; - function checkOperands(token, left, right) { - checkLeftOperand(token, left); - checkRightOperand(token, right); - } + return node; +} - function checkLeftOperand(token, operandExpression) { - if (operandExpression == null) { - raiseInternalException(token.startPos, 'LEFT_OPERAND_PROBLEM'); - } - } +var StringLiteral = { + create: createNode +}; +exports.StringLiteral = StringLiteral; - function checkRightOperand(token, operandExpression) { - if (operandExpression == null) { - raiseInternalException(token.startPos, 'RIGHT_OPERAND_PROBLEM'); - } - } +},{"./SpelNode":39}],41:[function(require,module,exports){ +'use strict'; - /** - * Compress the start and end of a token into a single int. - */ - function toPosToken(token) { - return (token.startPos << 16) + token.endPos; - } +Object.defineProperty(exports, '__esModule', { + value: true +}); - function toPosBounds(start, end) { - return (start << 16) + end; - } +var _SpelNode = require('./SpelNode'); - return { - setConfiguration: setConfiguration, - parse: parse - } +function createNode(position, expression, ifTrue, ifFalse) { + var node = _SpelNode.SpelNode.create('ternary', position, expression, ifTrue, ifFalse); + + node.getValue = function (state) { + return expression.getValue(state) ? ifTrue.getValue(state) : ifFalse.getValue(state); }; - exports.SpelExpressionParser = SpelExpressionParser; + return node; +} -}(window || exports)); +var Ternary = { + create: createNode +}; +exports.Ternary = Ternary; -(function (exports) { - 'use strict'; +},{"./SpelNode":39}],42:[function(require,module,exports){ +'use strict'; - var spelExpressionEvaluator = {}, - SpelExpressionParser; +Object.defineProperty(exports, '__esModule', { + value: true +}); - try { - SpelExpressionParser = require('./SpelExpressionParser').SpelExpressionParser; - } catch(e) { - SpelExpressionParser = exports.SpelExpressionParser; - } +var _SpelNode = require('./SpelNode'); + +function createNode(variableName, position) { + var node = _SpelNode.SpelNode.create('variable', position); - spelExpressionEvaluator.compile = function (expression) { - var compiledExpression = SpelExpressionParser().parse(expression); - return { - eval: function (context, locals) { - return evalCompiled(compiledExpression, context, locals); - }, - _compiledExpression: compiledExpression + node.getValue = function (state) { + var context = state.activeContext.peek(), + locals = state.locals; + + if (!context) { + throw { + name: 'ContextDoesNotExistException', + message: 'Attempting to look up variable \'' + variableName + '\' for an undefined context.' + }; + } + + //there are 2 keywords (root, this) that need to be dealt with + if (variableName === 'this') { + return context; + } + if (variableName === 'root') { + return state.rootContext; } - }; - spelExpressionEvaluator.eval = function (expression, context, locals) { - return spelExpressionEvaluator.compile(expression).eval(context, locals); + return locals[variableName]; }; - function evalCompiled(compiledExpression, context, locals) { - var activeContext = new Stack(), - state; + node.setValue = function (value, state) { + var locals = state.locals; - if (!context) { - context = {}; - } + /*jshint -W093 */ + return locals[variableName] = value; + /*jshint +W093 */ + }; - activeContext.push(context); + return node; +} - state = { - rootContext: context, - activeContext: activeContext, - locals: locals - }; - return compiledExpression.getValue(state); - } +var VariableReference = { + create: createNode +}; +exports.VariableReference = VariableReference; - exports.SpelExpressionEvaluator = spelExpressionEvaluator; +},{"./SpelNode":39}],43:[function(require,module,exports){ +"use strict"; -}(window || exports)); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Stack = Stack; -(function (exports) { - 'use strict'; +function Stack(startingElements) { + this.elements = startingElements || []; +} - function create(authentication, principal) { - var context = {}; +Stack.prototype.push = function (el) { + this.elements.push(el); + return el; +}; - context.authentication = authentication || {}; - context.principal = principal || {}; +Stack.prototype.pop = function () { + return this.elements.pop(); +}; - context.hasRole = function (role) { - var hasRole = false; +Stack.prototype.peek = function () { + return this.elements[this.elements.length - 1]; +}; - if (!role) { - return false; - } - if (!context.authentication && !Array.isArray(context.authentication.authorities)) { - return false; - } +Stack.prototype.empty = function () { + return this.elements.length > 0; +}; - context.authentication.authorities.forEach(function (grantedAuthority) { - if (grantedAuthority.authority.toLowerCase() === role.toLowerCase()) { - hasRole = true; - } - }); +Stack.prototype.search = function (el) { + return this.elements.length - this.elements.indexOf(el); +}; - return hasRole; - }; +},{}],44:[function(require,module,exports){ +'use strict'; - context.hasPermission = function (/*variable arguments*/) { - var args = Array.prototype.slice.call(arguments); +Object.defineProperty(exports, '__esModule', { + value: true +}); - if (args.length === 1) { - return context.hasRole(args[0]); - } - }; +var _SpelExpressionEvaluator = require('./SpelExpressionEvaluator'); - return context; - } +var _StandardContext = require('./StandardContext'); - exports.StandardContext = { - create: create - }; +exports.SpelExpressionEvaluator = _SpelExpressionEvaluator.SpelExpressionEvaluator; +exports.StandardContext = _StandardContext.StandardContext; -}(window || exports)); +},{"./SpelExpressionEvaluator":1,"./StandardContext":3}]},{},[44])(44) +}); \ No newline at end of file diff --git a/dist/spel2js.min.js b/dist/spel2js.min.js index 1455dbc..7e5a53d 100644 --- a/dist/spel2js.min.js +++ b/dist/spel2js.min.js @@ -1,2 +1,2 @@ -function Stack(a){this.elements=a||[]}Stack.prototype.push=function(a){return this.elements.push(a),a},Stack.prototype.pop=function(){return this.elements.pop()},Stack.prototype.peek=function(){return this.elements[this.elements.length-1]},Stack.prototype.empty=function(){return this.elements.length>0},Stack.prototype.search=function(a){return this.elements.length-this.elements.indexOf(a)},function(a){"use strict";function b(a){this.type=a,this.tokenChars=c[a],this._hasPayload="string"!=typeof c[a],"number"==typeof c[a]&&(this._ordinal=c[a])}var c={LITERAL_INT:1,LITERAL_LONG:2,LITERAL_HEXINT:3,LITERAL_HEXLONG:4,LITERAL_STRING:5,LITERAL_REAL:6,LITERAL_REAL_FLOAT:7,LPAREN:"(",RPAREN:")",COMMA:",",IDENTIFIER:0,COLON:":",HASH:"#",RSQUARE:"]",LSQUARE:"[",LCURLY:"{",RCURLY:"}",DOT:".",PLUS:"+",STAR:"*",MINUS:"-",SELECT_FIRST:"^[",SELECT_LAST:"$[",QMARK:"?",PROJECT:"![",DIV:"/",GE:">=",GT:">",LE:"<=",LT:"<",EQ:"==",NE:"!=",MOD:"%",NOT:"!",ASSIGN:"=",INSTANCEOF:"instanceof",MATCHES:"matches",BETWEEN:"between",SELECT:"?[",POWER:"^",ELVIS:"?:",SAFE_NAVI:"?.",BEAN_REF:"@",SYMBOLIC_OR:"||",SYMBOLIC_AND:"&&",INC:"++",DEC:"--"};for(var d in c)c.hasOwnProperty(d)&&(b[d]=new b(d));b.prototype.toString=function(){return this.type+(0!=this.tokenChars.length?"("+this.tokenChars+")":"")},b.prototype.getLength=function(){return this.tokenChars.length},b.prototype.hasPayload=function(){return this._hasPayload},b.prototype.valueOf=function(a){for(var d in c)if(c.hasOwnProperty(d)&&c[d]===a)return b[d]},b.prototype.ordinal=function(){return this._ordinal},a.TokenKind=b}(window||exports),function(a){"use strict";function b(a,b,c,d){this.kind=a,this.startPos=c,this.endPos=d,b&&(this.data=b)}var c;try{c=require("./TokenKind").TokenKind}catch(d){c=a.TokenKind}b.prototype.getKind=function(){return this.kind},b.prototype.toString=function(){var a="[";return a+=this.kind.toString(),this.kind.hasPayload()&&(a+=":"+this.data),a+="]",a+="("+this.startPos+","+this.endPos+")"},b.prototype.isIdentifier=function(){return this.kind==c.IDENTIFIER},b.prototype.isNumericRelationalOperator=function(){return this.kind==c.GT||this.kind==c.GE||this.kind==c.LT||this.kind==c.LE||this.kind==c.EQ||this.kind==c.NE},b.prototype.stringValue=function(){return this.data},b.prototype.asInstanceOfToken=function(){return new b(c.INSTANCEOF,this.startPos,this.endPos)},b.prototype.asMatchesToken=function(){return new b(c.MATCHES,this.startPos,this.endPos)},b.prototype.asBetweenToken=function(){return new b(c.BETWEEN,this.startPos,this.endPos)},b.prototype.getStartPosition=function(){return this.startPos},b.prototype.getEndPosition=function(){return this.endPos},a.Token=b}(window||exports),function(a){"use strict";function b(){var a;for(a="0".charCodeAt(0);a<="9".charCodeAt(0);a+=1)g[a]|=h|i;for(a="A".charCodeAt(0);a<="F".charCodeAt(0);a+=1)g[a]|=i;for(a="a".charCodeAt(0);a<="f".charCodeAt(0);a+=1)g[a]|=i;for(a="A".charCodeAt(0);a<="Z".charCodeAt(0);a+=1)g[a]|=j;for(a="a".charCodeAt(0);a<="z".charCodeAt(0);a+=1)g[a]|=j}function c(a){function b(){for(var a;G>H;)if(a=F[H],C(a))m();else switch(a){case"+":r(e.INC)?t(e.INC):s(e.PLUS);break;case"_":m();break;case"-":r(e.DEC)?t(e.DEC):s(e.MINUS);break;case":":s(e.COLON);break;case".":s(e.DOT);break;case",":s(e.COMMA);break;case"*":s(e.STAR);break;case"/":s(e.DIV);break;case"%":s(e.MOD);break;case"(":s(e.LPAREN);break;case")":s(e.RPAREN);break;case"[":s(e.LSQUARE);break;case"#":s(e.HASH);break;case"]":s(e.RSQUARE);break;case"{":s(e.LCURLY);break;case"}":s(e.RCURLY);break;case"@":s(e.BEAN_REF);break;case"^":r(e.SELECT_FIRST)?t(e.SELECT_FIRST):s(e.POWER);break;case"!":r(e.NE)?t(e.NE):r(e.PROJECT)?t(e.PROJECT):s(e.NOT);break;case"=":r(e.EQ)?t(e.EQ):s(e.ASSIGN);break;case"&":if(!r(e.SYMBOLIC_AND))throw{name:"SpelParseException",message:"Missing character '&' in expression ("+E+") at position "+H};t(e.SYMBOLIC_AND);break;case"|":if(!r(e.SYMBOLIC_OR))throw{name:"SpelParseException",message:"Missing character '|' in expression ("+E+") at position "+H};t(e.SYMBOLIC_OR);break;case"?":r(e.SELECT)?t(e.SELECT):r(e.ELVIS)?t(e.ELVIS):r(e.SAFE_NAVI)?t(e.SAFE_NAVI):s(e.QMARK);break;case"$":r(e.SELECT_LAST)?t(e.SELECT_LAST):m();break;case">":r(e.GE)?t(e.GE):s(e.GT);break;case"<":r(e.LE)?t(e.LE):s(e.LT);break;case"0":case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":l("0"===a);break;case" ":case" ":case"\r":case"\n":H+=1;break;case"'":c();break;case'"':k();break;case"\x00":H+=1;break;case"\\":throw{name:"SpelParseException",message:"Unexpected escape character in expression ("+E+") at position "+H};default:throw{name:"SpelParseException",message:"Cannot handle character '"+a+"' in expression ("+E+") at position "+H}}}function c(){for(var a,b=H,c=!1;!c;)if(H+=1,a=F[H],"'"==a&&("'"==F[H+1]?H+=1:c=!0),0===a.charCodeAt(0))throw{name:"SpelParseException",message:"Non-terminating quoted string in expression ("+E+") at position "+H};H+=1,I.push(new d(e.LITERAL_STRING,q(b,H),b,H))}function k(){for(var a,b=H,c=!1;!c;)if(H+=1,a=F[H],'"'==a&&('"'==F[H+1]?H+=1:c=!0),0===a.charCodeAt(0))throw{name:"SpelParseException",message:"Non-terminating double-quoted string in expression ("+E+") at position "+H};H+=1,I.push(new d(e.LITERAL_STRING,q(b,H),b,H))}function l(a){var b,c,d,e,f=!1,g=H,h=F[H+1],i="x"==h||"X"==h;if(a&&i){H+=1;do H+=1;while(D(F[H]));return void(w("L","l")?(o(q(g+2,H),!0,g,H),H+=1):o(q(g+2,H),!1,g,H))}do H+=1;while(B(F[H]));if(h=F[H],"."==h){f=!0,b=H;do H+=1;while(B(F[H]));if(H===b+1)return H=b,void n(q(g,H),!1,g,H)}if(c=H,w("L","l")){if(f)throw{name:"SpelParseException",message:"Real cannot be long in expression ("+E+") at position "+H};n(q(g,c),!0,g,c),H+=1}else if(x(F[H])){f=!0,H+=1,d=F[H],A(d)&&(H+=1);do H+=1;while(B(F[H]));e=!1,y(F[H])?(e=!0,H+=1,c=H):z(F[H])&&(H+=1,c=H),p(q(g,H),e,g,H)}else h=F[H],e=!1,y(h)?(f=!0,e=!0,H+=1,c=H):z(h)&&(f=!0,H+=1,c=H),f?p(q(g,c),e,g,c):n(q(g,c),!1,g,c)}function m(){var a,b,c,g=H;do H+=1;while(v(F[H]));return a=q(g,H),(H-g===2||H-g===3)&&(b=a.toUpperCase(),c=f.indexOf(b),c>=0)?void u(e.valueOf(b),g,a):void I.push(new d(e.IDENTIFIER,a.replace("\x00",""),g,H))}function n(a,b,c,f){b?I.push(new d(e.LITERAL_LONG,a,c,f)):I.push(new d(e.LITERAL_INT,a,c,f))}function o(a,b,c,f){if(0===a.length)throw b?{name:"SpelParseException",message:"Not a long in expression ("+E+") at position "+H}:{name:"SpelParseException",message:"Not an int in expression ("+E+") at position "+H};b?I.push(new d(e.LITERAL_HEXLONG,a,c,f)):I.push(new d(e.LITERAL_HEXINT,a,c,f))}function p(a,b,c,f){b?I.push(new d(e.LITERAL_REAL_FLOAT,a,c,f)):I.push(new d(e.LITERAL_REAL,a,c,f))}function q(a,b){return F.substring(a,b)}function r(a){return 2===a.tokenChars.length&&F[H]==a.tokenChars[0]?F[H+1]===a.tokenChars[1]:!1}function s(a){I.push(new d(a,null,H,H+1)),H+=1}function t(a){I.push(new d(a,null,H,H+2)),H+=2}function u(a,b,c){I.push(new d(a,c,b,b+a.getLength()))}function v(a){return C(a)||B(a)||"_"===a||"$"===a}function w(a,b){var c=F[H];return c===a||c===b}function x(a){return"e"===a||"E"===a}function y(a){return"f"===a||"F"===a}function z(a){return"d"===a||"D"===a}function A(a){return"+"===a||"-"===a}function B(a){return a.charCodeAt(0)>255?!1:0!==(g[a.charCodeAt(0)]&h)}function C(a){return a.charCodeAt(0)>255?!1:0!==(g[a.charCodeAt(0)]&j)}function D(a){return a.charCodeAt(0)>255?!1:0!==(g[a.charCodeAt(0)]&i)}var E=a,F=a+"\x00",G=F.length,H=0,I=[];return b(),I}var d,e,f=["DIV","EQ","GE","GT","LE","LT","MOD","NE","NOT"],g=[],h=1,i=2,j=4;try{d=require("./Token").Token,e=require("./TokenKind").TokenKind}catch(k){d=a.Token,e=a.TokenKind}b(),a.Tokenizer={tokenize:c}}(window||exports),function(a){"use strict";function b(a,b){var c,d={},e=a||"Abstract",f=[],g=null,h=Array.prototype.slice.call(arguments),i=h.length>2?h.slice(2):null;if(d._type=e,d.getType=function(){return e},d.setType=function(a){e=a},d.getChildren=function(){return f},d.addChild=function(a){a.setParent(d),f.push(a)},d.getParent=function(){return g},d.setParent=function(a){g=a},d.getContext=function(a){return c||a.activeContext.peek()},d.setContext=function(a){c=a},d.getStartPosition=function(){return b>>16},d.getEndPosition=function(){return 65535&b},d.getValue=function(){throw{name:"MethodNotImplementedException",message:"SpelNode#getValue() must be overridden."}},d.toString=function(){var a="Kind: "+d.getType();a+=", Children: [";for(var b=0,c=d.getChildren().length;c>b;b+=1)a+="{"+d.getChildren()[b]+"}, ";return a+="]"},0===b)throw{name:"Error",message:"Position cannot be 0"};return i&&i.forEach(function(a){d.addChild(a)}),d}a.SpelNode={create:b}}(window||exports),function(a,b){"use strict";function c(a,b,c){var e=d.create("assign",a,b,c);return e.getValue=function(a){var d=a.activeContext.peek();if(!d)throw{name:"ContextDoesNotExistException",message:"Attempting to assign property '"+b.getValue(a)+"' for an undefined context."};return b.setValue(c.getValue(a),a)},e}var d;try{d=require("./SpelNode").SpelNode}catch(e){d=a.SpelNode}a.Assign={create:c}}(window||exports),function(a){"use strict";function b(a,b){var d=c.create("boolean",b);return d.getValue=function(){return a},d.setValue=function(b){a=b},d}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.BooleanLiteral={create:b}}(window||exports),function(a){"use strict";function b(a,b){function d(a){var b,c=e.getChildren().length;for(b=0;c>b;b+=1)"indexer"===e.getChildren()[b].getType()?a.activeContext.push(a.activeContext.peek()[e.getChildren()[b].getValue(a)]):a.activeContext.push(e.getChildren()[b].getValue(a));return function(){for(b=0;c>b;b+=1)a.activeContext.pop()}}var e=c.create.apply(null,["compound",a].concat(b));return e.getValue=function(a){var b,c=a.activeContext.peek();if(!c)throw{name:"ContextDoesNotExistException",message:"Attempting to evaluate compound expression with an undefined context."};var e=d(a);return b=a.activeContext.peek(),e(),b},e.setValue=function(a,b){var c=d(b),f=e.getChildren().length;return b.activeContext.pop(),a=e.getChildren()[f-1].setValue(a,b),b.activeContext.push(null),c(),a},e}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.CompoundExpression={create:b}}(window||exports),function(a){"use strict";function b(a,b,d){var e=c.create("elvis",a,b,d);return e.getValue=function(a){return null!==b.getValue(a)?b.getValue(a):d.getValue(a)},e}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.Elvis={create:b}}(window||exports),function(a){"use strict";function b(a,b){var d=c.create("method",a);return d.getValue=function(){var a=d,c=null;do a.getParent()?a=a.getParent():c=a.getContext();while(a);if(c[b])return c[b].call(c);throw{name:"FunctionDoesNotExistException",message:"Function '"+b+"' does not exist."}},d}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.FunctionReference={create:b}}(window||exports),function(a){"use strict";function b(a,b){var d=c.create.apply(null,["indexer",a].concat(b));return d.getValue=function(a){var b,c,e,f=a.activeContext,g=d.getChildren().length;if(a.activeContext=new Stack,a.activeContext.push(a.rootContext),b=a.activeContext.peek(),!b)throw{name:"ContextDoesNotExistException",message:"Attempting to evaluate compound expression with an undefined context."};for(c=0;g>c;c+=1)a.activeContext.push(d.getChildren()[c].getValue(a));for(e=a.activeContext.peek(),c=0;g>c;c+=1)a.activeContext.pop();return a.activeContext=f,e},d}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.Indexer={create:b}}(window||exports),function(a){"use strict";function b(a,b){var d=c.create("list",a),e=[].concat(b||[]);return d.getValue=function(a){return e.map(function(b){return b.getValue(a)})},d}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.InlineList={create:b}}(window||exports),function(a){"use strict";function b(a,b){var d=c.create("map",a),e=[].concat(b||[]);return d.getValue=function(a){var b=!0,c=null,d={};return e.forEach(function(e){b?c="property"===e.getType()?e.getName():e.getValue(a):d[c]=e.getValue(a),b=!b}),d},d}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.InlineMap={create:b}}(window||exports),function(a,b){"use strict";function c(a,c,e,f){var g=d.create("method",e);return g.getValue=function(d){function e(d){if(d===b){if(a)return null;throw{name:"NullPointerException",message:"Method "+c+" does not exist."}}return d}var g,h=d.activeContext.peek(),i=[];if(!h)throw{name:"ContextDoesNotExistException",message:"Attempting to look up property '"+c+"' for an undefined context."};return f.forEach(function(a){i.push(a.getValue(d))}),"get"!==c.substr(0,3)||h[c]?"set"!==c.substr(0,3)||h[c]?"size"===c&&Array.isArray(h)?h.length:(g=e(h[c]),g?g.apply(h,i):null):h[c.charAt(3).toLowerCase()+c.substring(4)]=i[0]:e(h[c.charAt(3).toLowerCase()+c.substring(4)])},g}var d;try{d=require("./SpelNode").SpelNode}catch(e){d=a.SpelNode}a.MethodReference={create:c}}(window||exports),function(a){"use strict";function b(a,b){var d=c.create("null",b);return d.getValue=function(){return null},d}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.NullLiteral={create:b}}(window||exports),function(a){"use strict";function b(a,b){var d=c.create("number",b);return d.getValue=function(){return a},d.setValue=function(b){a=b},d}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.NumberLiteral={create:b}}(window||exports),function(a){"use strict";function b(a,b,d){var e=c.create("op-and",a,b,d);return e.getValue=function(a){return!!b.getValue(a)&&!!d.getValue(a)},e}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.OpAnd={create:b}}(window||exports),function(a){"use strict";function b(a,b,d){var e=c.create("op-dec",a,d);return e.getValue=function(a){var c=d.getValue(a);return d.setValue(c-1,a),b?c:c-1},e}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.OpDec={create:b}}(window||exports),function(a){"use strict";function b(a,b,d){var e=c.create("op-divide",a,b,d);return e.getValue=function(a){return b.getValue(a)/d.getValue(a)},e}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.OpDivide={create:b}}(window||exports),function(a){"use strict";function b(a,b,d){var e=c.create("op-eq",a,b,d);return e.getValue=function(a){return b.getValue(a)===d.getValue(a)},e}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.OpEQ={create:b}}(window||exports),function(a){"use strict";function b(a,b,d){var e=c.create("op-ge",a,b,d);return e.getValue=function(a){return b.getValue(a)>=d.getValue(a)},e}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.OpGE={create:b}}(window||exports),function(a){"use strict";function b(a,b,d){var e=c.create("op-gt",a,b,d);return e.getValue=function(a){return b.getValue(a)>d.getValue(a)},e}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.OpGT={create:b}}(window||exports),function(a){"use strict";function b(a,b,d){var e=c.create("op-inc",a,d);return e.getValue=function(a){var c=d.getValue(a);return d.setValue(c+1,a),b?c:c+1},e}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.OpInc={create:b}}(window||exports),function(a){"use strict";function b(a,b,d){var e=c.create("op-le",a,b,d);return e.getValue=function(a){return b.getValue(a)<=d.getValue(a)},e}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.OpLE={create:b}}(window||exports),function(a){"use strict";function b(a,b,d){var e=c.create("op-lt",a,b,d);return e.getValue=function(a){return b.getValue(a)g;g+=1)f+=c;return f}return null},e}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.OpMultiply={create:b}}(window||exports),function(a){"use strict";function b(a,b,d){var e=c.create("op-ne",a,b,d);return e.getValue=function(a){return b.getValue(a)!==d.getValue(a)},e}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.OpNE={create:b}}(window||exports),function(a){"use strict";function b(a,b){var d=c.create("op-not",a,b);return d.getValue=function(a){return!b.getValue(a)},d}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.OpNot={create:b}}(window||exports),function(a){"use strict";function b(a,b,d){var e=c.create("op-or",a,b,d);return e.getValue=function(a){return!!b.getValue(a)||!!d.getValue(a)},e}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.OpOr={create:b}}(window||exports),function(a){"use strict";function b(a,b,d){var e=c.create("op-plus",a,b,d);return e.getValue=function(a){return b.getValue(a)+d.getValue(a)},e}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.OpPlus={create:b}}(window||exports),function(a){"use strict";function b(a,b,d){var e=c.create("op-power",a,b,d);return e.getValue=function(a){return Math.pow(b.getValue(a),d.getValue(a))},e}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.OpPower={create:b}}(window||exports),function(a){"use strict";function b(a,b,c){return a.map(function(a){var d;return c.activeContext.push(a),d=b.getValue(c),c.activeContext.pop(),d})}function c(a,c,e){var f=d.create("projection",c,e);return f.getValue=function(a){var c,d=a.activeContext.peek(),f=[];if(Array.isArray(d))return b(d,e,a);if("object"==typeof d){for(c in d)d.hasOwnProperty(c)&&f.push(d[c]);return b(f,e,a)}return null},f}var d;try{d=require("./SpelNode").SpelNode}catch(e){d=a.SpelNode}a.Projection={create:c}}(window||exports),function(a,b){"use strict";function c(a,c,e){var f=d.create("property",e);return f.getValue=function(d){var e=d.activeContext.peek();if(!e)throw{name:"ContextDoesNotExistException",message:"Attempting to look up property '"+c+"' for an undefined context."};if(e[c]===b){if(a)return null;if("size"===c&&Array.isArray(e))return e.length;throw{name:"NullPointerException",message:"Property '"+c+"' does not exist."}}return e[c]},f.setValue=function(a,b){var d=b.activeContext.peek();if(!d)throw{name:"ContextDoesNotExistException",message:"Attempting to assign property '"+c+"' for an undefined context."};return d[c]=a},f.getName=function(){return c},f}var d;try{d=require("./SpelNode").SpelNode}catch(e){d=a.SpelNode}a.PropertyReference={create:c}}(window||exports),function(a){"use strict";function b(a){var b=c.create("root",null,a);return b.getValue=function(){return b.getChildren()[0]?b.getChildren()[0].getValue():null},b}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.RootNode={create:b}}(window||exports),function(a){"use strict";function b(a,b,c){var d=!1;return c.activeContext.push(a),d=b.getValue(c),c.activeContext.pop(),d}function c(a,c,d,e){var f=a.filter(function(a){return b(a,d,e)});switch(c){case"ALL":return f;case"FIRST":return f[0]||null;case"LAST":return f.length?f[f.length-1]:null}}function d(a,c,d,e){var f,g,h={},i=[],j={};for(g in a)a.hasOwnProperty(g)&&(f={key:g,value:a[g]},b(f,d,e)&&i.push(f));switch(c){case"ALL":return i.forEach(function(a){h[a.key]=a.value}),h;case"FIRST":return i.length?(j[i[0].key]=i[0].value,j):null;case"LAST":return i.length?(j[i[i.length-1].key]=i[i.length-1].value,j):null}i.forEach(function(a){h[a.key]=a.value})}function e(a,b,e,g){var h=f.create("selection",e,g);return h.getValue=function(a){var e=a.activeContext.peek();if(e){if(Array.isArray(e))return c(e,b,g,a);if("object"==typeof e)return d(e,b,g,a)}return null},h}var f;try{f=require("./SpelNode").SpelNode}catch(g){f=a.SpelNode}a.Selection={create:e,FIRST:"FIRST",LAST:"LAST",ALL:"ALL"}}(window||exports),function(a){"use strict";function b(a,b){function d(a){return"'"===a[0]&&"'"===a[a.length-1]||'"'===a[0]&&'"'===a[a.length-1]?a.substring(1,a.length-1):a}var e=c.create("string",b);return a=d(a),e.getValue=function(){return a},e.setValue=function(b){a=b},e}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.StringLiteral={create:b}}(window||exports),function(a){"use strict";function b(a,b,d,e){var f=c.create("ternary",a,b,d,e);return f.getValue=function(a){return b.getValue(a)?d.getValue(a):e.getValue(a)},f}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.Ternary={create:b}}(window||exports),function(a){"use strict";function b(a,b){var d=c.create("variable",b);return d.getValue=function(b){var c=b.activeContext.peek(),d=b.locals;if(!c)throw{name:"ContextDoesNotExistException",message:"Attempting to look up variable '"+a+"' for an undefined context."};return"this"===a?c:"root"===a?b.rootContext:d[a]},d.setValue=function(b,c){var d=c.locals;return d[a]=b},d}var c;try{c=require("./SpelNode").SpelNode}catch(d){c=a.SpelNode}a.VariableReference={create:b}}(window||exports),function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L;try{b=require("./TokenKind"),c=require("./Tokenizer"),d=require("./ast/RootNode"),e=require("./ast/BooleanLiteral"),f=require("./ast/NumberLiteral"),g=require("./ast/StringLiteral"),h=require("./ast/NullLiteral"),i=require("./ast/FunctionReference"),j=require("./ast/MethodReference"),k=require("./ast/PropertyReference"),l=require("./ast/VariableReference"),m=require("./ast/CompoundExpression"),n=require("./ast/Indexer"),o=require("./ast/Assign"),p=require("./ast/OpEQ"),q=require("./ast/OpNE"),r=require("./ast/OpGE"),s=require("./ast/OpGT"),t=require("./ast/OpLE"),u=require("./ast/OpLT"),v=require("./ast/OpPlus"),w=require("./ast/OpMinus"),x=require("./ast/OpMultiply"),y=require("./ast/OpDivide"),z=require("./ast/OpModulus"),A=require("./ast/OpPower"),B=require("./ast/OpInc"),C=require("./ast/OpDec"),D=require("./ast/OpNot"),E=require("./ast/OpAnd"),F=require("./ast/OpOr"),G=require("./ast/Ternary"),H=require("./ast/Elvis"),I=require("./ast/InlineList"),J=require("./ast/InlineMap"),K=require("./ast/Selection"),L=require("./ast/Projection")}catch(M){b=a.TokenKind,c=a.Tokenizer,d=a.RootNode,e=a.BooleanLiteral,f=a.NumberLiteral,g=a.StringLiteral,h=a.NullLiteral,i=a.FunctionReference,j=a.MethodReference,k=a.PropertyReference,l=a.VariableReference,m=a.CompoundExpression,n=a.Indexer,o=a.Assign,p=a.OpEQ,q=a.OpNE,r=a.OpGE,s=a.OpGT,t=a.OpLE,u=a.OpLT,v=a.OpPlus,w=a.OpMinus,x=a.OpMultiply,y=a.OpDivide,z=a.OpModulus,A=a.OpPower,B=a.OpInc,C=a.OpDec,D=a.OpNot,E=a.OpAnd,F=a.OpOr,G=a.Ternary,H=a.Elvis,I=a.InlineList,J=a.InlineMap,K=a.Selection,L=a.Projection}var N=function(){function a(a){Ha=a}function d(a,b){try{Ia=a,Ja=c.tokenize(a),Ka=Ja.length,La=0,Na=[];var d=M();return xa()&&Aa(za().startPos,"MORE_INPUT",ya().toString()),d}catch(e){throw e.message}}function M(){var a=N();if(xa()){var c=za();if(c.getKind()==b.ASSIGN){null==a&&(a=h.create(Ga(c.startPos-1,c.endPos-1))),ya();var d=N();return o.create(Fa(c),a,d)}if(c.getKind()==b.ELVIS){null==a&&(a=h.create(Ga(c.startPos-1,c.endPos-2))),ya();var e=M();return null==e&&(e=h.create(Ga(c.startPos+1,c.endPos+1))),H.create(Fa(c),a,e)}if(c.getKind()==b.QMARK){null==a&&(a=h.create(Ga(c.startPos-1,c.endPos-1))),ya();var f=M();ra(b.COLON);var g=M();return G.create(Fa(c),a,f,g)}}return a}function N(){for(var a=O();va("or")||sa(b.SYMBOLIC_OR);){var c=ya(),d=O();Ca(c,a,d),a=F.create(Fa(c),a,d)}return a}function O(){for(var a=P();va("and")||sa(b.SYMBOLIC_AND);){var c=ya(),d=P();Ca(c,a,d),a=E.create(Fa(c),a,d)}return a}function P(){var a=Q(),c=qa();if(null!=c){var d=ya(),e=Q();Ca(d,a,e);var f=c.kind;if(c.isNumericRelationalOperator()){var g=Fa(d);return f==b.GT?s.create(g,a,e):f==b.LT?u.create(g,a,e):f==b.LE?t.create(g,a,e):f==b.GE?r.create(g,a,e):f==b.EQ?p.create(g,a,e):q.create(g,a,e)}return f==b.INSTANCEOF?new OperatorInstanceof(Fa(d),a,e):f==b.MATCHES?new OperatorMatches(Fa(d),a,e):new OperatorBetween(Fa(d),a,e)}return a}function Q(){for(var a=R();ua(b.PLUS,b.MINUS,b.INC);){var c=ya(),d=R();Ea(c,d),c.getKind()==b.PLUS?a=v.create(Fa(c),a,d):c.getKind()==b.MINUS&&(a=w.create(Fa(c),a,d))}return a}function R(){for(var a=S();ua(b.STAR,b.DIV,b.MOD);){var c=ya(),d=S();Ca(c,a,d),a=c.getKind()==b.STAR?x.create(Fa(c),a,d):c.getKind()==b.DIV?y.create(Fa(c),a,d):z.create(Fa(c),a,d)}return a}function S(){var a,c=T();if(sa(b.POWER)){a=ya();var d=T();return Ea(a,d),A.create(Fa(a),c,d)}return null!=c&&ua(b.INC,b.DEC)?(a=ya(),a.getKind()==b.INC?B.create(Fa(a),!0,c):C.create(Fa(a),!0,c)):c}function T(){var a,c;return ua(b.PLUS,b.MINUS,b.NOT)?(a=ya(),c=T(),a.getKind()==b.NOT?D.create(Fa(a),c):a.getKind()==b.PLUS?v.create(Fa(a),c):w.create(Fa(a),c)):ua(b.INC,b.DEC)?(a=ya(),c=T(),a.getKind()==b.INC?B.create(Fa(a),!1,c):C.create(Fa(a),!1,c)):U()}function U(){var a=[],b=aa();for(a.push(b);V();)a.push(na());return 1==a.length?a[0]:m.create(Ga(b.getStartPosition(),a[a.length-1].getEndPosition()),a)}function V(){var a=null;return a=ua(b.DOT,b.SAFE_NAVI)?X():W(),null==a?!1:(ma(a),!0)}function W(){return sa(b.LSQUARE)&&ga()?na():null}function X(){var a=ya(),c=a.getKind()==b.SAFE_NAVI;return ka(c)||Y()||ea(c)||ha(c)?na():(null==za()?Aa(a.startPos,"OOD"):Aa(a.startPos,"UNEXPECTED_DATA_AFTER_DOT",Ba(za())),null)}function Y(){if(!sa(b.HASH))return!1;var a=ya(),c=ra(b.IDENTIFIER),d=Z();return null==d?(ma(l.create(c.data,Ga(a.startPos,c.endPos))),!0):(ma(i.create(c.data,Ga(a.startPos,c.endPos),d)),!0)}function Z(){if(!sa(b.LPAREN))return null;var a=[];return _(a),ra(b.RPAREN),a}function $(a){sa(b.LPAREN)||Aa(Fa(za()),"MISSING_CONSTRUCTOR_ARGS"),_(a),ra(b.RPAREN)}function _(a){var c,d=za().startPos;do{ya();var e=za();null==e&&Aa(d,"RUN_OUT_OF_ARGUMENTS"),e.getKind()!=b.RPAREN&&a.push(M()),c=za()}while(null!=c&&c.kind==b.COMMA);null==c&&Aa(d,"RUN_OUT_OF_ARGUMENTS")}function aa(){return oa()?na():pa()?na():ca()||da()||la()||ka(!1)||Y()?na():ba()?na():ea(!1)||ha(!1)||ga()?na():fa()?na():null}function ba(){if(sa(b.BEAN_REF)){var a=ya(),c=null,d=null;sa(b.IDENTIFIER)?(c=ra(b.IDENTIFIER),d=c.data):sa(b.LITERAL_STRING)?(c=ra(b.LITERAL_STRING),d=c.stringValue(),d=d.substring(1,d.length()-1)):Aa(a.startPos,"INVALID_BEAN_REFERENCE");var e=new BeanReference(Fa(c),d);return ma(e),!0}return!1}function ca(){if(sa(b.IDENTIFIER)){var a=za();if("T"!==a.stringValue())return!1;var c=ya();if(sa(b.RSQUARE))return ma(k.create(c.stringValue(),Fa(c))),!0;ra(b.LPAREN);for(var d=ia(),e=0;ta(b.LSQUARE,!0);)ra(b.RSQUARE),e++;return ra(b.RPAREN),ma(new TypeReference(Fa(a),d,e)),!0}return!1}function da(){if(sa(b.IDENTIFIER)){var a=za();return"null"!==a.stringValue().toLowerCase()?!1:(ya(),ma(h.create(Fa(a))),!0)}return!1}function ea(a){var c=za();if(!ta(b.PROJECT,!0))return!1;var d=M();return ra(b.RSQUARE),ma(L.create(a,Fa(c),d)),!0}function fa(){var a=za(),c=[];if(!ta(b.LCURLY,!0))return!1;var d=null,e=za();if(ta(b.RCURLY,!0))d=I.create(Ga(a.startPos,e.endPos));else if(ta(b.COLON,!0))e=ra(b.RCURLY),d=J.create(Ga(a.startPos,e.endPos));else{var f=M();if(sa(b.RCURLY))c.push(f),e=ra(b.RCURLY),d=I.create(Ga(a.startPos,e.endPos),c);else if(ta(b.COMMA,!0)){c.push(f);do c.push(M());while(ta(b.COMMA,!0));e=ra(b.RCURLY),d=I.create(Fa(a.startPos,e.endPos),c)}else if(ta(b.COLON,!0)){var g=[];for(g.push(f),g.push(M());ta(b.COMMA,!0);)g.push(M()),ra(b.COLON),g.push(M());e=ra(b.RCURLY),d=J.create(Ga(a.startPos,e.endPos),g)}else Aa(a.startPos,"OOD")}return ma(d),!0}function ga(){var a=za();if(!ta(b.LSQUARE,!0))return!1;var c=M();return ra(b.RSQUARE),ma(n.create(Fa(a),c)),!0}function ha(a){var c=za();if(!wa())return!1;ya();var d=M();return null==d&&Aa(Fa(c),"MISSING_SELECTION_EXPRESSION"),ra(b.RSQUARE),ma(c.getKind()==b.SELECT_FIRST?K.create(a,K.FIRST,Fa(c),d):c.getKind()==b.SELECT_LAST?K.create(a,K.LAST,Fa(c),d):K.create(a,K.ALL,Fa(c),d)),!0}function ia(){for(var a=[],c=za();ja(c);)ya(),c.kind!=b.DOT&&a.push(new Identifier(c.stringValue(),Fa(c))),c=za();a.length||(null==c&&Aa(Ia.length(),"OOD"),Aa(c.startPos,"NOT_EXPECTED_TOKEN","qualified ID",c.getKind().toString().toLowerCase()));var d=Ga(a[0].getStartPosition(),a[a.length-1].getEndPosition());return new QualifiedIdentifier(d,a)}function ja(a){if(null==a||a.kind==b.LITERAL_STRING)return!1;if(a.kind==b.DOT||a.kind==b.IDENTIFIER)return!0;var c=a.stringValue();return c.length&&Ma.test(c)}function ka(a){if(sa(b.IDENTIFIER)){var c=ya(),d=Z();return null==d?(ma(k.create(a,c.stringValue(),Fa(c))),!0):(ma(j.create(a,c.stringValue(),Fa(c),d)),!0)}return!1}function la(){if(va("new")){var a=ya();if(sa(b.RSQUARE))return ma(k.create(a.stringValue(),Fa(a))),!0;var c=ia(),d=[];if(d.push(c),sa(b.LSQUARE)){for(var e=[];ta(b.LSQUARE,!0);)sa(b.RSQUARE)?e.push(null):e.push(M()),ra(b.RSQUARE);fa()&&d.push(na()),ma(new ConstructorReference(Fa(a),e,d))}else $(d),ma(new ConstructorReference(Fa(a),d));return!0}return!1}function ma(a){Na.push(a)}function na(){return Na.pop()}function oa(){var a=za();if(null==a)return!1;if(a.getKind()===b.LITERAL_INT||a.getKind()===b.LITERAL_LONG)ma(f.create(parseInt(a.stringValue(),10),Fa(a)));else if(a.getKind()===b.LITERAL_REAL||a.getKind()===b.LITERAL_REAL_FLOAT)ma(f.create(parseFloat(a.stringValue()),Fa(a)));else if(a.getKind()===b.LITERAL_HEXINT||a.getKind()===b.LITERAL_HEXLONG)ma(f.create(parseInt(a.stringValue(),16),Fa(a)));else if(va("true"))ma(e.create(!0,Fa(a)));else if(va("false"))ma(e.create(!1,Fa(a)));else{if(a.getKind()!==b.LITERAL_STRING)return!1;ma(g.create(a.stringValue(),Fa(a)))}return ya(),!0}function pa(){if(sa(b.LPAREN)){ya();var a=M();return ra(b.RPAREN),ma(a),!0}return!1}function qa(){var a=za();if(null==a)return null;if(a.isNumericRelationalOperator())return a;if(a.isIdentifier()){var b=a.stringValue();if("instanceof"===b.toLowerCase())return a.asInstanceOfToken();if("matches"===b.toLowerCase())return a.asMatchesToken();if("between"===b.toLowerCase())return a.asBetweenToken()}return null}function ra(a){var b=ya();return null==b&&Aa(Ia.length,"OOD"),b.getKind()!=a&&Aa(b.startPos,"NOT_EXPECTED_TOKEN",a.toString().toLowerCase(),b.getKind().toString().toLowerCase()),b}function sa(a){return ta(a,!1)}function ta(a,c){if(!xa())return!1;var d=za();return d.getKind()==a?(c&&La++,!0):a==b.IDENTIFIER&&d.getKind().ordinal()>=b.DIV.ordinal()&&d.getKind().ordinal()<=b.NOT.ordinal()&&null!=d.data?!0:!1}function ua(){if(!xa())return!1;for(var a=za(),b=Array.prototype.slice.call(arguments),c=0,d=b.length;d>c;c+=1)if(a.getKind()===b[c])return!0;return!1}function va(a){if(!xa())return!1;var c=za();return c.getKind()===b.IDENTIFIER&&c.stringValue().toLowerCase()===a.toLowerCase()}function wa(){if(!xa())return!1;var a=za();return a.getKind()===b.SELECT||a.getKind()===b.SELECT_FIRST||a.getKind()===b.SELECT_LAST}function xa(){return La=Ka?null:Ja[La++]}function za(){return La>=Ka?null:Ja[La]}function Aa(a,b,c,d){throw c&&(b+="\nExpected: "+c),d&&(b+="\nActual: "+d),{name:"InternalParseException",message:"Error occurred while attempting to parse expression '"+Ia+"' at position "+a+". Message: "+b}}function Ba(a){return a.getKind().hasPayload()?a.stringValue():a.getKind().toString().toLowerCase()}function Ca(a,b,c){Da(a,b),Ea(a,c)}function Da(a,b){null==b&&Aa(a.startPos,"LEFT_OPERAND_PROBLEM")}function Ea(a,b){null==b&&Aa(a.startPos,"RIGHT_OPERAND_PROBLEM")}function Fa(a){return(a.startPos<<16)+a.endPos}function Ga(a,b){return(a<<16)+b}var Ha,Ia,Ja,Ka,La,Ma=new RegExp("[\\p{L}\\p{N}_$]+"),Na=[];return{setConfiguration:a,parse:d}};a.SpelExpressionParser=N}(window||exports),function(a){"use strict";function b(a,b,c){var d,e=new Stack;return b||(b={}),e.push(b),d={rootContext:b,activeContext:e,locals:c},a.getValue(d)}var c,d={}; -try{c=require("./SpelExpressionParser").SpelExpressionParser}catch(e){c=a.SpelExpressionParser}d.compile=function(a){var d=c().parse(a);return{eval:function(a,c){return b(d,a,c)},_compiledExpression:d}},d.eval=function(a,b,c){return d.compile(a).eval(b,c)},a.SpelExpressionEvaluator=d}(window||exports),function(a){"use strict";function b(a,b){var c={};return c.authentication=a||{},c.principal=b||{},c.hasRole=function(a){var b=!1;return a&&(c.authentication||Array.isArray(c.authentication.authorities))?(c.authentication.authorities.forEach(function(c){c.authority.toLowerCase()===a.toLowerCase()&&(b=!0)}),b):!1},c.hasPermission=function(){var a=Array.prototype.slice.call(arguments);return 1===a.length?c.hasRole(a[0]):void 0},c}a.StandardContext={create:b}}(window||exports); \ No newline at end of file +!function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,b.spel2js=a()}}(function(){return function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g=l.TokenKind.DIV.ordinal()&&c.getKind().ordinal()<=l.TokenKind.NOT.ordinal()&&null!==c.data?!0:!1}function Ca(){if(!Fa())return!1;for(var a=Ha(),b=Array.prototype.slice.call(arguments),c=0,d=b.length;d>c;c+=1)if(a.getKind()===b[c])return!0;return!1}function Da(a){if(!Fa())return!1;var b=Ha();return b.getKind()===l.TokenKind.IDENTIFIER&&b.stringValue().toLowerCase()===a.toLowerCase()}function Ea(){if(!Fa())return!1;var a=Ha();return a.getKind()===l.TokenKind.SELECT||a.getKind()===l.TokenKind.SELECT_FIRST||a.getKind()===l.TokenKind.SELECT_LAST}function Fa(){return Ta=Sa?null:Ra[Ta++]}function Ha(){return Ta>=Sa?null:Ra[Ta]}function Ia(a,b,c,d){throw c&&(b+="\nExpected: "+c),d&&(b+="\nActual: "+d),{name:"InternalParseException",message:"Error occurred while attempting to parse expression '"+Qa+"' at position "+a+". Message: "+b}}function Ja(a){return a.getKind().hasPayload()?a.stringValue():a.getKind().toString().toLowerCase()}function Ka(a,b,c){La(a,b),Ma(a,c)}function La(a,b){null===b&&Ia(a.startPos,"LEFT_OPERAND_PROBLEM")}function Ma(a,b){null===b&&Ia(a.startPos,"RIGHT_OPERAND_PROBLEM")}function Na(a){return(a.startPos<<16)+a.endPos}function Oa(a,b){return(a<<16)+b}var Pa,Qa,Ra,Sa,Ta,Ua=new RegExp("[\\p{L}\\p{N}_$]+"),Va=[];return{setConfiguration:a,parse:b}};c.SpelExpressionParser=V},{"./TokenKind":5,"./Tokenizer":6,"./ast/Assign":7,"./ast/BooleanLiteral":8,"./ast/CompoundExpression":9,"./ast/Elvis":10,"./ast/FunctionReference":11,"./ast/Indexer":12,"./ast/InlineList":13,"./ast/InlineMap":14,"./ast/MethodReference":15,"./ast/NullLiteral":16,"./ast/NumberLiteral":17,"./ast/OpAnd":18,"./ast/OpDec":19,"./ast/OpDivide":20,"./ast/OpEQ":21,"./ast/OpGE":22,"./ast/OpGT":23,"./ast/OpInc":24,"./ast/OpLE":25,"./ast/OpLT":26,"./ast/OpMinus":27,"./ast/OpModulus":28,"./ast/OpMultiply":29,"./ast/OpNE":30,"./ast/OpNot":31,"./ast/OpOr":32,"./ast/OpPlus":33,"./ast/OpPower":34,"./ast/Projection":35,"./ast/PropertyReference":36,"./ast/RootNode":37,"./ast/Selection":38,"./ast/StringLiteral":40,"./ast/Ternary":41,"./ast/VariableReference":42}],3:[function(a,b,c){"use strict";function d(a,b){var c={};return c.authentication=a||{},c.principal=b||{},c.hasRole=function(a){var b=!1;return a&&(c.authentication||Array.isArray(c.authentication.authorities))?(c.authentication.authorities.forEach(function(c){c.authority.toLowerCase()===a.toLowerCase()&&(b=!0)}),b):!1},c.hasPermission=function(){var a=Array.prototype.slice.call(arguments);return 1===a.length?c.hasRole(a[0]):void 0},c}Object.defineProperty(c,"__esModule",{value:!0});var e={create:d};c.StandardContext=e},{}],4:[function(a,b,c){"use strict";function d(a,b,c,d){this.kind=a,this.startPos=c,this.endPos=d,b&&(this.data=b)}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./TokenKind");d.prototype.getKind=function(){return this.kind},d.prototype.toString=function(){var a="[";return a+=this.kind.toString(),this.kind.hasPayload()&&(a+=":"+this.data),a+="]",a+="("+this.startPos+","+this.endPos+")"},d.prototype.isIdentifier=function(){return this.kind===e.TokenKind.IDENTIFIER},d.prototype.isNumericRelationalOperator=function(){return this.kind===e.TokenKind.GT||this.kind===e.TokenKind.GE||this.kind===e.TokenKind.LT||this.kind===e.TokenKind.LE||this.kind===e.TokenKind.EQ||this.kind===e.TokenKind.NE},d.prototype.stringValue=function(){return this.data},d.prototype.asInstanceOfToken=function(){return new d(e.TokenKind.INSTANCEOF,this.startPos,this.endPos)},d.prototype.asMatchesToken=function(){return new d(e.TokenKind.MATCHES,this.startPos,this.endPos)},d.prototype.asBetweenToken=function(){return new d(e.TokenKind.BETWEEN,this.startPos,this.endPos)},d.prototype.getStartPosition=function(){return this.startPos},d.prototype.getEndPosition=function(){return this.endPos},c.Token=d},{"./TokenKind":5}],5:[function(a,b,c){"use strict";function d(a){this.type=a,this.tokenChars=e[a],this._hasPayload="string"!=typeof e[a],"number"==typeof e[a]&&(this._ordinal=e[a])}Object.defineProperty(c,"__esModule",{value:!0});var e={LITERAL_INT:1,LITERAL_LONG:2,LITERAL_HEXINT:3,LITERAL_HEXLONG:4,LITERAL_STRING:5,LITERAL_REAL:6,LITERAL_REAL_FLOAT:7,LPAREN:"(",RPAREN:")",COMMA:",",IDENTIFIER:0,COLON:":",HASH:"#",RSQUARE:"]",LSQUARE:"[",LCURLY:"{",RCURLY:"}",DOT:".",PLUS:"+",STAR:"*",MINUS:"-",SELECT_FIRST:"^[",SELECT_LAST:"$[",QMARK:"?",PROJECT:"![",DIV:"/",GE:">=",GT:">",LE:"<=",LT:"<",EQ:"==",NE:"!=",MOD:"%",NOT:"!",ASSIGN:"=",INSTANCEOF:"instanceof",MATCHES:"matches",BETWEEN:"between",SELECT:"?[",POWER:"^",ELVIS:"?:",SAFE_NAVI:"?.",BEAN_REF:"@",SYMBOLIC_OR:"||",SYMBOLIC_AND:"&&",INC:"++",DEC:"--"};for(var f in e)e.hasOwnProperty(f)&&(d[f]=new d(f));d.prototype.toString=function(){return this.type+(0!==this.tokenChars.length?"("+this.tokenChars+")":"")},d.prototype.getLength=function(){return this.tokenChars.length},d.prototype.hasPayload=function(){return this._hasPayload},d.prototype.valueOf=function(a){for(var b in e)if(e.hasOwnProperty(b)&&e[b]===a)return d[b]},d.prototype.ordinal=function(){return this._ordinal},c.TokenKind=d},{}],6:[function(a,b,c){"use strict";function d(){var a;for(a="0".charCodeAt(0);a<="9".charCodeAt(0);a+=1)i[a]|=j|k;for(a="A".charCodeAt(0);a<="F".charCodeAt(0);a+=1)i[a]|=k;for(a="a".charCodeAt(0);a<="f".charCodeAt(0);a+=1)i[a]|=k;for(a="A".charCodeAt(0);a<="Z".charCodeAt(0);a+=1)i[a]|=l;for(a="a".charCodeAt(0);a<="z".charCodeAt(0);a+=1)i[a]|=l}function e(a){function b(){for(var a;G>H;)if(a=F[H],C(a))m();else switch(a){case"+":r(g.TokenKind.INC)?t(g.TokenKind.INC):s(g.TokenKind.PLUS);break;case"_":m();break;case"-":r(g.TokenKind.DEC)?t(g.TokenKind.DEC):s(g.TokenKind.MINUS);break;case":":s(g.TokenKind.COLON);break;case".":s(g.TokenKind.DOT);break;case",":s(g.TokenKind.COMMA);break;case"*":s(g.TokenKind.STAR);break;case"/":s(g.TokenKind.DIV);break;case"%":s(g.TokenKind.MOD);break;case"(":s(g.TokenKind.LPAREN);break;case")":s(g.TokenKind.RPAREN);break;case"[":s(g.TokenKind.LSQUARE);break;case"#":s(g.TokenKind.HASH);break;case"]":s(g.TokenKind.RSQUARE);break;case"{":s(g.TokenKind.LCURLY);break;case"}":s(g.TokenKind.RCURLY);break;case"@":s(g.TokenKind.BEAN_REF);break;case"^":r(g.TokenKind.SELECT_FIRST)?t(g.TokenKind.SELECT_FIRST):s(g.TokenKind.POWER);break;case"!":r(g.TokenKind.NE)?t(g.TokenKind.NE):r(g.TokenKind.PROJECT)?t(g.TokenKind.PROJECT):s(g.TokenKind.NOT);break;case"=":r(g.TokenKind.EQ)?t(g.TokenKind.EQ):s(g.TokenKind.ASSIGN);break;case"&":if(!r(g.TokenKind.SYMBOLIC_AND))throw{name:"SpelParseException",message:"Missing character '&' in expression ("+E+") at position "+H};t(g.TokenKind.SYMBOLIC_AND);break;case"|":if(!r(g.TokenKind.SYMBOLIC_OR))throw{name:"SpelParseException",message:"Missing character '|' in expression ("+E+") at position "+H};t(g.TokenKind.SYMBOLIC_OR);break;case"?":r(g.TokenKind.SELECT)?t(g.TokenKind.SELECT):r(g.TokenKind.ELVIS)?t(g.TokenKind.ELVIS):r(g.TokenKind.SAFE_NAVI)?t(g.TokenKind.SAFE_NAVI):s(g.TokenKind.QMARK);break;case"$":r(g.TokenKind.SELECT_LAST)?t(g.TokenKind.SELECT_LAST):m();break;case">":r(g.TokenKind.GE)?t(g.TokenKind.GE):s(g.TokenKind.GT);break;case"<":r(g.TokenKind.LE)?t(g.TokenKind.LE):s(g.TokenKind.LT);break;case"0":case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":e("0"===a);break;case" ":case" ":case"\r":case"\n":H+=1;break;case"'":c();break;case'"':d();break;case"\x00":H+=1;break;case"\\":throw{name:"SpelParseException",message:"Unexpected escape character in expression ("+E+") at position "+H};default:throw{name:"SpelParseException",message:"Cannot handle character '"+a+"' in expression ("+E+") at position "+H}}}function c(){for(var a,b=H,c=!1;!c;)if(H+=1,a=F[H],"'"===a&&("'"===F[H+1]?H+=1:c=!0),0===a.charCodeAt(0))throw{name:"SpelParseException",message:"Non-terminating quoted string in expression ("+E+") at position "+H};H+=1,I.push(new f.Token(g.TokenKind.LITERAL_STRING,q(b,H),b,H))}function d(){for(var a,b=H,c=!1;!c;)if(H+=1,a=F[H],'"'===a&&('"'===F[H+1]?H+=1:c=!0),0===a.charCodeAt(0))throw{name:"SpelParseException",message:"Non-terminating double-quoted string in expression ("+E+") at position "+H};H+=1,I.push(new f.Token(g.TokenKind.LITERAL_STRING,q(b,H),b,H))}function e(a){var b,c,d,e,f=!1,g=H,h=F[H+1],i="x"===h||"X"===h;if(a&&i){H+=1;do H+=1;while(D(F[H]));return void(w("L","l")?(o(q(g+2,H),!0,g,H),H+=1):o(q(g+2,H),!1,g,H))}do H+=1;while(B(F[H]));if(h=F[H],"."===h){f=!0,b=H;do H+=1;while(B(F[H]));if(H===b+1)return H=b,void n(q(g,H),!1,g,H)}if(c=H,w("L","l")){if(f)throw{name:"SpelParseException",message:"Real cannot be long in expression ("+E+") at position "+H};n(q(g,c),!0,g,c),H+=1}else if(x(F[H])){f=!0,H+=1,d=F[H],A(d)&&(H+=1);do H+=1;while(B(F[H]));e=!1,y(F[H])?(e=!0,H+=1,c=H):z(F[H])&&(H+=1,c=H),p(q(g,H),e,g,H)}else h=F[H],e=!1,y(h)?(f=!0,e=!0,H+=1,c=H):z(h)&&(f=!0,H+=1,c=H),f?p(q(g,c),e,g,c):n(q(g,c),!1,g,c)}function m(){var a,b,c,d=H;do H+=1;while(v(F[H]));return a=q(d,H),(H-d===2||H-d===3)&&(b=a.toUpperCase(),c=h.indexOf(b),c>=0)?void u(g.TokenKind.valueOf(b),d,a):void I.push(new f.Token(g.TokenKind.IDENTIFIER,a.replace("\x00",""),d,H))}function n(a,b,c,d){b?I.push(new f.Token(g.TokenKind.LITERAL_LONG,a,c,d)):I.push(new f.Token(g.TokenKind.LITERAL_INT,a,c,d))}function o(a,b,c,d){if(0===a.length)throw b?{name:"SpelParseException",message:"Not a long in expression ("+E+") at position "+H}:{name:"SpelParseException",message:"Not an int in expression ("+E+") at position "+H};b?I.push(new f.Token(g.TokenKind.LITERAL_HEXLONG,a,c,d)):I.push(new f.Token(g.TokenKind.LITERAL_HEXINT,a,c,d))}function p(a,b,c,d){b?I.push(new f.Token(g.TokenKind.LITERAL_REAL_FLOAT,a,c,d)):I.push(new f.Token(g.TokenKind.LITERAL_REAL,a,c,d))}function q(a,b){return F.substring(a,b)}function r(a){return 2===a.tokenChars.length&&F[H]===a.tokenChars[0]?F[H+1]===a.tokenChars[1]:!1}function s(a){I.push(new f.Token(a,null,H,H+1)),H+=1}function t(a){I.push(new f.Token(a,null,H,H+2)),H+=2}function u(a,b,c){I.push(new f.Token(a,c,b,b+a.getLength()))}function v(a){return C(a)||B(a)||"_"===a||"$"===a}function w(a,b){var c=F[H];return c===a||c===b}function x(a){return"e"===a||"E"===a}function y(a){return"f"===a||"F"===a}function z(a){return"d"===a||"D"===a}function A(a){return"+"===a||"-"===a}function B(a){return a.charCodeAt(0)>255?!1:0!==(i[a.charCodeAt(0)]&j)}function C(a){return a.charCodeAt(0)>255?!1:0!==(i[a.charCodeAt(0)]&l)}function D(a){return a.charCodeAt(0)>255?!1:0!==(i[a.charCodeAt(0)]&k)}var E=a,F=a+"\x00",G=F.length,H=0,I=[];return b(),I}Object.defineProperty(c,"__esModule",{value:!0});var f=a("./Token"),g=a("./TokenKind"),h=["DIV","EQ","GE","GT","LE","LT","MOD","NE","NOT"],i=[],j=1,k=2,l=4;d();var m={tokenize:e};c.Tokenizer=m},{"./Token":4,"./TokenKind":5}],7:[function(a,b,c){"use strict";function d(a,b,c){var d=e.SpelNode.create("assign",a,b,c);return d.getValue=function(a){var d=a.activeContext.peek();if(!d)throw{name:"ContextDoesNotExistException",message:"Attempting to assign property '"+b.getValue(a)+"' for an undefined context."};return b.setValue(c.getValue(a),a)},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.Assign=f},{"./SpelNode":39}],8:[function(a,b,c){"use strict";function d(a,b){var c=e.SpelNode.create("boolean",b);return c.getValue=function(){return a},c.setValue=function(b){return a=b},c}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.BooleanLiteral=f},{"./SpelNode":39}],9:[function(a,b,c){"use strict";function d(a,b){function c(a){var b,c=d.getChildren().length;for(b=0;c>b;b+=1)"indexer"===d.getChildren()[b].getType()?a.activeContext.push(a.activeContext.peek()[d.getChildren()[b].getValue(a)]):a.activeContext.push(d.getChildren()[b].getValue(a));return function(){for(b=0;c>b;b+=1)a.activeContext.pop()}}var d=e.SpelNode.create.apply(null,["compound",a].concat(b));return d.getValue=function(a){var b,d=a.activeContext.peek();if(!d)throw{name:"ContextDoesNotExistException",message:"Attempting to evaluate compound expression with an undefined context."};var e=c(a);return b=a.activeContext.peek(),e(),b},d.setValue=function(a,b){var e=c(b),f=d.getChildren().length;return b.activeContext.pop(),a=d.getChildren()[f-1].setValue(a,b),b.activeContext.push(null),e(),a},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.CompoundExpression=f},{"./SpelNode":39}],10:[function(a,b,c){"use strict";function d(a,b,c){var d=e.SpelNode.create("elvis",a,b,c);return d.getValue=function(a){return null!==b.getValue(a)?b.getValue(a):c.getValue(a)},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.Elvis=f},{"./SpelNode":39}],11:[function(a,b,c){"use strict";function d(a,b){var c=e.SpelNode.create("method",a);return c.getValue=function(){var a=c,d=null;do a.getParent()?a=a.getParent():d=a.getContext();while(a);if(d[b])return d[b].call(d);throw{name:"FunctionDoesNotExistException",message:"Function '"+b+"' does not exist."}},c}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.FunctionReference=f},{"./SpelNode":39}],12:[function(a,b,c){"use strict";function d(a,b){var c=e.SpelNode.create.apply(null,["indexer",a].concat(b));return c.getValue=function(a){var b,d,e,g=a.activeContext,h=c.getChildren().length;if(a.activeContext=new f.Stack,a.activeContext.push(a.rootContext),b=a.activeContext.peek(),!b)throw{name:"ContextDoesNotExistException",message:"Attempting to evaluate compound expression with an undefined context."};for(d=0;h>d;d+=1)a.activeContext.push(c.getChildren()[d].getValue(a));for(e=a.activeContext.peek(),d=0;h>d;d+=1)a.activeContext.pop();return a.activeContext=g,e},c}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f=a("../lib/Stack"),g={create:d};c.Indexer=g},{"../lib/Stack":43,"./SpelNode":39}],13:[function(a,b,c){"use strict";function d(a,b){var c=e.SpelNode.create("list",a),d=[].concat(b||[]);return c.getValue=function(a){return d.map(function(b){return b.getValue(a)})},c}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.InlineList=f},{"./SpelNode":39}],14:[function(a,b,c){"use strict";function d(a,b){var c=e.SpelNode.create("map",a),d=[].concat(b||[]);return c.getValue=function(a){var b=!0,c=null,e={};return d.forEach(function(d){b?c="property"===d.getType()?d.getName():d.getValue(a):e[c]=d.getValue(a),b=!b}),e},c}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.InlineMap=f},{"./SpelNode":39}],15:[function(a,b,c){"use strict";function d(a,b,c,d){var f=e.SpelNode.create("method",c);return f.getValue=function(c){function e(c){if(void 0===c){if(a)return null;throw{name:"NullPointerException",message:"Method "+b+" does not exist."}}return c}var f,g=c.activeContext.peek(),h=[];if(!g)throw{name:"ContextDoesNotExistException",message:"Attempting to look up property '"+b+"' for an undefined context."};return d.forEach(function(a){h.push(a.getValue(c))}),"get"!==b.substr(0,3)||g[b]?"set"!==b.substr(0,3)||g[b]?"size"===b&&Array.isArray(g)?g.length:(f=e(g[b]),f?f.apply(g,h):null):g[b.charAt(3).toLowerCase()+b.substring(4)]=h[0]:e(g[b.charAt(3).toLowerCase()+b.substring(4)])},f}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.MethodReference=f},{"./SpelNode":39}],16:[function(a,b,c){"use strict";function d(a,b){var c=e.SpelNode.create("null",b);return c.getValue=function(){return null},c}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.NullLiteral=f},{"./SpelNode":39}],17:[function(a,b,c){"use strict";function d(a,b){var c=e.SpelNode.create("number",b);return c.getValue=function(){return a},c.setValue=function(b){return a=b},c}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.NumberLiteral=f},{"./SpelNode":39}],18:[function(a,b,c){"use strict";function d(a,b,c){var d=e.SpelNode.create("op-and",a,b,c);return d.getValue=function(a){return!!b.getValue(a)&&!!c.getValue(a)},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.OpAnd=f},{"./SpelNode":39}],19:[function(a,b,c){"use strict";function d(a,b,c){var d=e.SpelNode.create("op-dec",a,c);return d.getValue=function(a){var d=c.getValue(a);return c.setValue(d-1,a),b?d:d-1},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.OpDec=f},{"./SpelNode":39}],20:[function(a,b,c){"use strict";function d(a,b,c){var d=e.SpelNode.create("op-divide",a,b,c);return d.getValue=function(a){return b.getValue(a)/c.getValue(a)},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.OpDivide=f},{"./SpelNode":39}],21:[function(a,b,c){"use strict";function d(a,b,c){var d=e.SpelNode.create("op-eq",a,b,c);return d.getValue=function(a){return b.getValue(a)===c.getValue(a)},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.OpEQ=f},{"./SpelNode":39}],22:[function(a,b,c){"use strict";function d(a,b,c){var d=e.SpelNode.create("op-ge",a,b,c);return d.getValue=function(a){return b.getValue(a)>=c.getValue(a)},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.OpGE=f},{"./SpelNode":39}],23:[function(a,b,c){"use strict";function d(a,b,c){var d=e.SpelNode.create("op-gt",a,b,c);return d.getValue=function(a){return b.getValue(a)>c.getValue(a)},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.OpGT=f},{"./SpelNode":39}],24:[function(a,b,c){"use strict";function d(a,b,c){var d=e.SpelNode.create("op-inc",a,c);return d.getValue=function(a){var d=c.getValue(a);return c.setValue(d+1,a),b?d:d+1},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.OpInc=f},{"./SpelNode":39}],25:[function(a,b,c){"use strict";function d(a,b,c){var d=e.SpelNode.create("op-le",a,b,c);return d.getValue=function(a){return b.getValue(a)<=c.getValue(a)},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.OpLE=f},{"./SpelNode":39}],26:[function(a,b,c){"use strict";function d(a,b,c){var d=e.SpelNode.create("op-lt",a,b,c);return d.getValue=function(a){return b.getValue(a)g;g+=1)f+=d;return f}return null},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.OpMultiply=f},{"./SpelNode":39}],30:[function(a,b,c){"use strict";function d(a,b,c){var d=e.SpelNode.create("op-ne",a,b,c);return d.getValue=function(a){return b.getValue(a)!==c.getValue(a)},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.OpNE=f},{"./SpelNode":39}],31:[function(a,b,c){"use strict";function d(a,b){var c=e.SpelNode.create("op-not",a,b);return c.getValue=function(a){return!b.getValue(a)},c}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.OpNot=f},{"./SpelNode":39}],32:[function(a,b,c){"use strict";function d(a,b,c){var d=e.SpelNode.create("op-or",a,b,c);return d.getValue=function(a){return!!b.getValue(a)||!!c.getValue(a)},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.OpOr=f},{"./SpelNode":39}],33:[function(a,b,c){"use strict";function d(a,b,c){var d=e.SpelNode.create("op-plus",a,b,c);return d.getValue=function(a){return b.getValue(a)+c.getValue(a)},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.OpPlus=f},{"./SpelNode":39}],34:[function(a,b,c){"use strict";function d(a,b,c){var d=e.SpelNode.create("op-power",a,b,c);return d.getValue=function(a){return Math.pow(b.getValue(a),c.getValue(a))},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.OpPower=f},{"./SpelNode":39}],35:[function(a,b,c){"use strict";function d(a,b,c){return a.map(function(a){var d;return c.activeContext.push(a), +d=b.getValue(c),c.activeContext.pop(),d})}function e(a,b,c){var e=f.SpelNode.create("projection",b,c);return e.getValue=function(a){var b,e=a.activeContext.peek(),f=[];if(Array.isArray(e))return d(e,c,a);if("object"==typeof e){for(b in e)e.hasOwnProperty(b)&&f.push(e[b]);return d(f,c,a)}return null},e}Object.defineProperty(c,"__esModule",{value:!0});var f=a("./SpelNode"),g={create:e};c.Projection=g},{"./SpelNode":39}],36:[function(a,b,c){"use strict";function d(a,b,c){var d=e.SpelNode.create("property",c);return d.getValue=function(c){var d=c.activeContext.peek();if(!d)throw{name:"ContextDoesNotExistException",message:"Attempting to look up property '"+b+"' for an undefined context."};if(void 0===d[b]){if(a)return null;if("size"===b&&Array.isArray(d))return d.length;throw{name:"NullPointerException",message:"Property '"+b+"' does not exist."}}return d[b]},d.setValue=function(a,c){var d=c.activeContext.peek();if(!d)throw{name:"ContextDoesNotExistException",message:"Attempting to assign property '"+b+"' for an undefined context."};return d[b]=a},d.getName=function(){return b},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.PropertyReference=f},{"./SpelNode":39}],37:[function(a,b,c){"use strict";function d(a){var b=e.SpelNode.create("root",null,a);return b.getValue=function(){return b.getChildren()[0]?b.getChildren()[0].getValue():null},b}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.RootNode=f},{"./SpelNode":39}],38:[function(a,b,c){"use strict";function d(a,b,c){var d=!1;return c.activeContext.push(a),d=b.getValue(c),c.activeContext.pop(),d}function e(a,b,c,e){var f=a.filter(function(a){return d(a,c,e)});switch(b){case"ALL":return f;case"FIRST":return f[0]||null;case"LAST":return f.length?f[f.length-1]:null}}function f(a,b,c,e){var f,g,h={},i=[],j={};for(g in a)a.hasOwnProperty(g)&&(f={key:g,value:a[g]},d(f,c,e)&&i.push(f));switch(b){case"ALL":return i.forEach(function(a){h[a.key]=a.value}),h;case"FIRST":return i.length?(j[i[0].key]=i[0].value,j):null;case"LAST":return i.length?(j[i[i.length-1].key]=i[i.length-1].value,j):null}i.forEach(function(a){h[a.key]=a.value})}function g(a,b,c,d){var g=h.SpelNode.create("selection",c,d);return g.getValue=function(a){var c=a.activeContext.peek();if(c){if(Array.isArray(c))return e(c,b,d,a);if("object"==typeof c)return f(c,b,d,a)}return null},g}Object.defineProperty(c,"__esModule",{value:!0});var h=a("./SpelNode"),i={create:g,FIRST:"FIRST",LAST:"LAST",ALL:"ALL"};c.Selection=i},{"./SpelNode":39}],39:[function(a,b,c){"use strict";function d(a,b){var c,d={},e=a||"Abstract",f=[],g=null;if(d._type=e,d.getType=function(){return e},d.setType=function(a){e=a},d.getChildren=function(){return f},d.addChild=function(a){a.setParent(d),f.push(a)},d.getParent=function(){return g},d.setParent=function(a){g=a},d.getContext=function(a){return c||a.activeContext.peek()},d.setContext=function(a){c=a},d.getStartPosition=function(){return b>>16},d.getEndPosition=function(){return 65535&b},d.getValue=function(){throw{name:"MethodNotImplementedException",message:"SpelNode#getValue() must be overridden."}},d.toString=function(){var a="Kind: "+d.getType();a+=", Children: [";for(var b=0,c=d.getChildren().length;c>b;b+=1)a+="{"+d.getChildren()[b]+"}, ";return a+="]"},0===b)throw{name:"Error",message:"Position cannot be 0"};for(var h=arguments.length,i=Array(h>2?h-2:0),j=2;h>j;j++)i[j-2]=arguments[j];return i&&i.forEach(function(a){d.addChild(a)}),d}Object.defineProperty(c,"__esModule",{value:!0});var e={create:d};c.SpelNode=e},{}],40:[function(a,b,c){"use strict";function d(a,b){function c(a){return"'"===a[0]&&"'"===a[a.length-1]||'"'===a[0]&&'"'===a[a.length-1]?a.substring(1,a.length-1):a}var d=e.SpelNode.create("string",b);return a=c(a),d.getValue=function(){return a},d.setValue=function(b){return a=b},d}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.StringLiteral=f},{"./SpelNode":39}],41:[function(a,b,c){"use strict";function d(a,b,c,d){var f=e.SpelNode.create("ternary",a,b,c,d);return f.getValue=function(a){return b.getValue(a)?c.getValue(a):d.getValue(a)},f}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.Ternary=f},{"./SpelNode":39}],42:[function(a,b,c){"use strict";function d(a,b){var c=e.SpelNode.create("variable",b);return c.getValue=function(b){var c=b.activeContext.peek(),d=b.locals;if(!c)throw{name:"ContextDoesNotExistException",message:"Attempting to look up variable '"+a+"' for an undefined context."};return"this"===a?c:"root"===a?b.rootContext:d[a]},c.setValue=function(b,c){var d=c.locals;return d[a]=b},c}Object.defineProperty(c,"__esModule",{value:!0});var e=a("./SpelNode"),f={create:d};c.VariableReference=f},{"./SpelNode":39}],43:[function(a,b,c){"use strict";function d(a){this.elements=a||[]}Object.defineProperty(c,"__esModule",{value:!0}),c.Stack=d,d.prototype.push=function(a){return this.elements.push(a),a},d.prototype.pop=function(){return this.elements.pop()},d.prototype.peek=function(){return this.elements[this.elements.length-1]},d.prototype.empty=function(){return this.elements.length>0},d.prototype.search=function(a){return this.elements.length-this.elements.indexOf(a)}},{}],44:[function(a,b,c){"use strict";Object.defineProperty(c,"__esModule",{value:!0});var d=a("./SpelExpressionEvaluator"),e=a("./StandardContext");c.SpelExpressionEvaluator=d.SpelExpressionEvaluator,c.StandardContext=e.StandardContext},{"./SpelExpressionEvaluator":1,"./StandardContext":3}]},{},[44])(44)}); \ No newline at end of file diff --git a/gruntfile.js b/gruntfile.js index 960cf8c..ffc8239 100644 --- a/gruntfile.js +++ b/gruntfile.js @@ -35,7 +35,7 @@ module.exports = function (grunt) { complexity: { all: { - src: ['src/**/*.js'], + src: ['dist/spel2js.js'], options: { breakOnErrors: false, checkstyleXML: 'complexity/checkstyle.xml', // create checkstyle report @@ -45,26 +45,6 @@ module.exports = function (grunt) { } }, - concat: { - dist: { - src: [ - '<%= config.app %>/lib/**/*.js', - - '<%= config.app %>/TokenKind.js', - '<%= config.app %>/Token.js', - '<%= config.app %>/Tokenizer.js', - - '<%= config.app %>/ast/SpelNode.js', - '<%= config.app %>/ast/*.js', - - '<%= config.app %>/SpelExpressionParser.js', - '<%= config.app %>/SpelExpressionEvaluator.js', - '<%= config.app %>/StandardContext.js' - ], - dest: '<%= config.dist %>/spel2js.js' - } - }, - uglify: { dist: { src: '<%= config.dist %>/spel2js.js', @@ -76,7 +56,30 @@ module.exports = function (grunt) { unit: { configFile: 'test/karma.conf.js' } + }, + + + browserify: { + dist: { + options: { + transform: [ + [ + 'babelify', + { + stage: 0 + } + ] + ], + browserifyOptions: { + standalone: 'spel2js' + } + }, + files: { + 'dist/spel2js.js': 'src/main.js' + } + } } + }); grunt.registerTask('test', [ @@ -92,7 +95,7 @@ module.exports = function (grunt) { ]); grunt.registerTask('build', [ - 'concat', + 'browserify', 'uglify' ]); diff --git a/package.json b/package.json index 705b0ba..d51321f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "spel2js", - "version": "0.1.0", + "version": "0.2.0", "description": "Parse Spring Expression Language in JavaScript", "author": { "name": "Ben March", @@ -38,12 +38,15 @@ ], "dependencies": {}, "devDependencies": { + "babelify": "^6.1.2", "grunt": "^0.4.5", + "grunt-browserify": "^3.8.0", "grunt-complexity": "^0.3.0", "grunt-contrib-concat": "^0.5.1", "grunt-contrib-jshint": "^0.11.2", "grunt-contrib-uglify": "^0.9.1", "grunt-karma": "^0.11.0", + "karma-browserify": "^4.2.1", "karma-coverage": "^0.4.2", "karma-jasmine": "^0.3.5", "karma-phantomjs-launcher": "^0.2.0", diff --git a/src/SpelExpressionEvaluator.js b/src/SpelExpressionEvaluator.js index ba644cb..082eccd 100644 --- a/src/SpelExpressionEvaluator.js +++ b/src/SpelExpressionEvaluator.js @@ -1,47 +1,38 @@ -(function (exports) { - 'use strict'; +import {SpelExpressionParser as spelExpressionParser} from './SpelExpressionParser'; +import {Stack} from './lib/Stack'; - var spelExpressionEvaluator = {}, - SpelExpressionParser; +var spelExpressionEvaluator = {}; - try { - SpelExpressionParser = require('./SpelExpressionParser').SpelExpressionParser; - } catch(e) { - SpelExpressionParser = exports.SpelExpressionParser; +function evalCompiled(compiledExpression, context, locals) { + var activeContext = new Stack(), + state; + + if (!context) { + context = {}; } - spelExpressionEvaluator.compile = function (expression) { - var compiledExpression = SpelExpressionParser().parse(expression); - return { - eval: function (context, locals) { - return evalCompiled(compiledExpression, context, locals); - }, - _compiledExpression: compiledExpression - } - }; + activeContext.push(context); - spelExpressionEvaluator.eval = function (expression, context, locals) { - return spelExpressionEvaluator.compile(expression).eval(context, locals); + state = { + rootContext: context, + activeContext: activeContext, + locals: locals }; + return compiledExpression.getValue(state); +} + +spelExpressionEvaluator.compile = function (expression) { + var compiledExpression = spelExpressionParser().parse(expression); + return { + eval: function (context, locals) { + return evalCompiled(compiledExpression, context, locals); + }, + _compiledExpression: compiledExpression + }; +}; - function evalCompiled(compiledExpression, context, locals) { - var activeContext = new Stack(), - state; - - if (!context) { - context = {}; - } - - activeContext.push(context); - - state = { - rootContext: context, - activeContext: activeContext, - locals: locals - }; - return compiledExpression.getValue(state); - } - - exports.SpelExpressionEvaluator = spelExpressionEvaluator; +spelExpressionEvaluator.eval = function (expression, context, locals) { + return spelExpressionEvaluator.compile(expression).eval(context, locals); +}; -}(window || exports)); +export {spelExpressionEvaluator as SpelExpressionEvaluator}; diff --git a/src/SpelExpressionParser.js b/src/SpelExpressionParser.js index 2c652ab..fd240b1 100644 --- a/src/SpelExpressionParser.js +++ b/src/SpelExpressionParser.js @@ -1,1064 +1,986 @@ -(function (exports) { - - var TokenKind, - Tokenizer, - RootNode, - BooleanLiteral, - NumberLiteral, - StringLiteral, - NullLiteral, - FunctionReference, - MethodReference, - PropertyReference, - VariableReference, - CompoundExpression, - Indexer, - Assign, - OpEQ, - OpNE, - OpGE, - OpGT, - OpLE, - OpLT, - OpPlus, - OpMinus, - OpMultiply, - OpDivide, - OpModulus, - OpPower, - OpInc, - OpDec, - OpNot, - OpAnd, - OpOr, - Ternary, - Elvis, - InlineList, - InlineMap, - Selection, - Projection; - - try { - TokenKind = require('./TokenKind'); - Tokenizer = require('./Tokenizer'); - RootNode = require('./ast/RootNode'); - BooleanLiteral = require('./ast/BooleanLiteral'); - NumberLiteral = require('./ast/NumberLiteral'); - StringLiteral = require('./ast/StringLiteral'); - NullLiteral = require('./ast/NullLiteral'); - FunctionReference = require('./ast/FunctionReference'); - MethodReference = require('./ast/MethodReference'); - PropertyReference = require('./ast/PropertyReference'); - VariableReference = require('./ast/VariableReference'); - CompoundExpression = require('./ast/CompoundExpression'); - Indexer = require('./ast/Indexer'); - Assign = require('./ast/Assign'); - OpEQ = require('./ast/OpEQ'); - OpNE = require('./ast/OpNE'); - OpGE = require('./ast/OpGE'); - OpGT = require('./ast/OpGT'); - OpLE = require('./ast/OpLE'); - OpLT = require('./ast/OpLT'); - OpPlus = require('./ast/OpPlus'); - OpMinus = require('./ast/OpMinus'); - OpMultiply = require('./ast/OpMultiply'); - OpDivide = require('./ast/OpDivide'); - OpModulus = require('./ast/OpModulus'); - OpPower = require('./ast/OpPower'); - OpInc = require('./ast/OpInc'); - OpDec = require('./ast/OpDec'); - OpNot = require('./ast/OpNot'); - OpAnd = require('./ast/OpAnd'); - OpOr = require('./ast/OpOr'); - Ternary = require('./ast/Ternary'); - Elvis = require('./ast/Elvis'); - InlineList = require('./ast/InlineList'); - InlineMap = require('./ast/InlineMap'); - Selection = require('./ast/Selection'); - Projection = require('./ast/Projection'); - } catch (e) { - TokenKind = exports.TokenKind; - Tokenizer = exports.Tokenizer; - RootNode = exports.RootNode; - BooleanLiteral = exports.BooleanLiteral; - NumberLiteral = exports.NumberLiteral; - StringLiteral = exports.StringLiteral; - NullLiteral = exports.NullLiteral; - FunctionReference = exports.FunctionReference; - MethodReference = exports.MethodReference; - PropertyReference = exports.PropertyReference; - VariableReference = exports.VariableReference; - CompoundExpression = exports.CompoundExpression; - Indexer = exports.Indexer; - Assign = exports.Assign; - OpEQ = exports.OpEQ; - OpNE = exports.OpNE; - OpGE = exports.OpGE; - OpGT = exports.OpGT; - OpLE = exports.OpLE; - OpLT = exports.OpLT; - OpPlus = exports.OpPlus; - OpMinus = exports.OpMinus; - OpMultiply = exports.OpMultiply; - OpDivide = exports.OpDivide; - OpModulus = exports.OpModulus; - OpPower = exports.OpPower; - OpInc = exports.OpInc; - OpDec = exports.OpDec; - OpNot = exports.OpNot; - OpAnd = exports.OpAnd; - OpOr = exports.OpOr; - Ternary = exports.Ternary; - Elvis = exports.Elvis; - InlineList = exports.InlineList; - InlineMap = exports.InlineMap; - Selection = exports.Selection; - Projection = exports.Projection; +import {TokenKind} from './TokenKind'; +import {Tokenizer} from './Tokenizer'; +import {RootNode} from './ast/RootNode'; +import {BooleanLiteral} from './ast/BooleanLiteral'; +import {NumberLiteral} from './ast/NumberLiteral'; +import {StringLiteral} from './ast/StringLiteral'; +import {NullLiteral} from './ast/NullLiteral'; +import {FunctionReference} from './ast/FunctionReference'; +import {MethodReference} from './ast/MethodReference'; +import {PropertyReference} from './ast/PropertyReference'; +import {VariableReference} from './ast/VariableReference'; +import {CompoundExpression} from './ast/CompoundExpression'; +import {Indexer} from './ast/Indexer'; +import {Assign} from './ast/Assign'; +import {OpEQ} from './ast/OpEQ'; +import {OpNE} from './ast/OpNE'; +import {OpGE} from './ast/OpGE'; +import {OpGT} from './ast/OpGT'; +import {OpLE} from './ast/OpLE'; +import {OpLT} from './ast/OpLT'; +import {OpPlus} from './ast/OpPlus'; +import {OpMinus} from './ast/OpMinus'; +import {OpMultiply} from './ast/OpMultiply'; +import {OpDivide} from './ast/OpDivide'; +import {OpModulus} from './ast/OpModulus'; +import {OpPower} from './ast/OpPower'; +import {OpInc} from './ast/OpInc'; +import {OpDec} from './ast/OpDec'; +import {OpNot} from './ast/OpNot'; +import {OpAnd} from './ast/OpAnd'; +import {OpOr} from './ast/OpOr'; +import {Ternary} from './ast/Ternary'; +import {Elvis} from './ast/Elvis'; +import {InlineList} from './ast/InlineList'; +import {InlineMap} from './ast/InlineMap'; +import {Selection} from './ast/Selection'; +import {Projection} from './ast/Projection'; + +//not yet implemented +var OperatorInstanceof, + OperatorMatches, + OperatorBetween, + BeanReference, + TypeReference, + QualifiedIdentifier, + Identifier, + ConstructorReference; + + +export var SpelExpressionParser = function () { + + + var VALID_QUALIFIED_ID_PATTERN = new RegExp('[\\p{L}\\p{N}_$]+'); + + + var configuration; + + // For rules that build nodes, they are stacked here for return + var constructedNodes = []; + + // The expression being parsed + var expressionString; + + // The token stream constructed from that expression string + var tokenStream; + + // length of a populated token stream + var tokenStreamLength; + + // Current location in the token stream when processing tokens + var tokenStreamPointer; + + + /** + * Create a parser with some configured behavior. + * @param config custom configuration options + */ + function setConfiguration(config) { + configuration = config; } - var SpelExpressionParser = function () { - - - var VALID_QUALIFIED_ID_PATTERN = new RegExp("[\\p{L}\\p{N}_$]+"); - - - var configuration; - - // For rules that build nodes, they are stacked here for return - var constructedNodes = []; - - // The expression being parsed - var expressionString; - - // The token stream constructed from that expression string - var tokenStream; - - // length of a populated token stream - var tokenStreamLength; - - // Current location in the token stream when processing tokens - var tokenStreamPointer; - - - /** - * Create a parser with some configured behavior. - * @param config custom configuration options - */ - function setConfiguration(config) { - configuration = config; + function parse(expression, context) { + try { + expressionString = expression; + tokenStream = Tokenizer.tokenize(expression); + tokenStreamLength = tokenStream.length; + tokenStreamPointer = 0; + constructedNodes = []; + var ast = eatExpression(); + if (moreTokens()) { + raiseInternalException(peekToken().startPos, 'MORE_INPUT', nextToken().toString()); + } + //Assert.isTrue(this.constructedNodes.isEmpty()); + return ast; } + catch (e) { + throw e.message; + } + } - - function parse(expression, context) { - try { - expressionString = expression; - tokenStream = Tokenizer.tokenize(expression); - tokenStreamLength = tokenStream.length; - tokenStreamPointer = 0; - constructedNodes = []; - var ast = eatExpression(); - if (moreTokens()) { - raiseInternalException(peekToken().startPos, 'MORE_INPUT', nextToken().toString()); + // expression + // : logicalOrExpression + // ( (ASSIGN^ logicalOrExpression) + // | (DEFAULT^ logicalOrExpression) + // | (QMARK^ expression COLON! expression) + // | (ELVIS^ expression))?; + function eatExpression() { + var expr = eatLogicalOrExpression(); + if (moreTokens()) { + var token = peekToken(); + if (token.getKind() === TokenKind.ASSIGN) { // a=b + if (expr === null) { + expr = NullLiteral.create(toPosBounds(token.startPos - 1, token.endPos - 1)); } - //Assert.isTrue(this.constructedNodes.isEmpty()); - return ast; - } - catch (e) { - throw e.message; + nextToken(); + var assignedValue = eatLogicalOrExpression(); + return Assign.create(toPosToken(token), expr, assignedValue); } - } - // expression - // : logicalOrExpression - // ( (ASSIGN^ logicalOrExpression) - // | (DEFAULT^ logicalOrExpression) - // | (QMARK^ expression COLON! expression) - // | (ELVIS^ expression))?; - function eatExpression() { - var expr = eatLogicalOrExpression(); - if (moreTokens()) { - var token = peekToken(); - if (token.getKind() == TokenKind.ASSIGN) { // a=b - if (expr == null) { - expr = NullLiteral.create(toPosBounds(token.startPos - 1, token.endPos - 1)); - } - nextToken(); - var assignedValue = eatLogicalOrExpression(); - return Assign.create(toPosToken(token), expr, assignedValue); + if (token.getKind() === TokenKind.ELVIS) { // a?:b (a if it isn't null, otherwise b) + if (expr === null) { + expr = NullLiteral.create(toPosBounds(token.startPos - 1, token.endPos - 2)); } - - if (token.getKind() == TokenKind.ELVIS) { // a?:b (a if it isn't null, otherwise b) - if (expr == null) { - expr = NullLiteral.create(toPosBounds(token.startPos - 1, token.endPos - 2)); - } - nextToken(); // elvis has left the building - var valueIfNull = eatExpression(); - if (valueIfNull == null) { - valueIfNull = NullLiteral.create(toPosBounds(token.startPos + 1, token.endPos + 1)); - } - return Elvis.create(toPosToken(token), expr, valueIfNull); + nextToken(); // elvis has left the building + var valueIfNull = eatExpression(); + if (valueIfNull === null) { + valueIfNull = NullLiteral.create(toPosBounds(token.startPos + 1, token.endPos + 1)); } + return Elvis.create(toPosToken(token), expr, valueIfNull); + } - if (token.getKind() == TokenKind.QMARK) { // a?b:c - if (expr == null) { - expr = NullLiteral.create(toPosBounds(token.startPos - 1, token.endPos - 1)); - } - nextToken(); - var ifTrueExprValue = eatExpression(); - eatToken(TokenKind.COLON); - var ifFalseExprValue = eatExpression(); - return Ternary.create(toPosToken(token), expr, ifTrueExprValue, ifFalseExprValue); + if (token.getKind() === TokenKind.QMARK) { // a?b:c + if (expr === null) { + expr = NullLiteral.create(toPosBounds(token.startPos - 1, token.endPos - 1)); } + nextToken(); + var ifTrueExprValue = eatExpression(); + eatToken(TokenKind.COLON); + var ifFalseExprValue = eatExpression(); + return Ternary.create(toPosToken(token), expr, ifTrueExprValue, ifFalseExprValue); } - return expr; } + return expr; + } - //logicalOrExpression : logicalAndExpression (OR^ logicalAndExpression)*; - function eatLogicalOrExpression() { - var expr = eatLogicalAndExpression(); - while (peekIdentifierToken("or") || peekTokenOne(TokenKind.SYMBOLIC_OR)) { - var token = nextToken(); //consume OR - var rhExpr = eatLogicalAndExpression(); - checkOperands(token, expr, rhExpr); - expr = OpOr.create(toPosToken(token), expr, rhExpr); - } - return expr; + //logicalOrExpression : logicalAndExpression (OR^ logicalAndExpression)*; + function eatLogicalOrExpression() { + var expr = eatLogicalAndExpression(); + while (peekIdentifierToken('or') || peekTokenOne(TokenKind.SYMBOLIC_OR)) { + var token = nextToken(); //consume OR + var rhExpr = eatLogicalAndExpression(); + checkOperands(token, expr, rhExpr); + expr = OpOr.create(toPosToken(token), expr, rhExpr); } + return expr; + } - // logicalAndExpression : relationalExpression (AND^ relationalExpression)*; - function eatLogicalAndExpression() { - var expr = eatRelationalExpression(); - while (peekIdentifierToken("and") || peekTokenOne(TokenKind.SYMBOLIC_AND)) { - var token = nextToken(); // consume 'AND' - var rhExpr = eatRelationalExpression(); - checkOperands(token, expr, rhExpr); - expr = OpAnd.create(toPosToken(token), expr, rhExpr); - } - return expr; - } - - // relationalExpression : sumExpression (relationalOperator^ sumExpression)?; - function eatRelationalExpression() { - var expr = eatSumExpression(); - var relationalOperatorToken = maybeEatRelationalOperator(); - if (relationalOperatorToken != null) { - var token = nextToken(); // consume relational operator token - var rhExpr = eatSumExpression(); - checkOperands(token, expr, rhExpr); - var tk = relationalOperatorToken.kind; - - if (relationalOperatorToken.isNumericRelationalOperator()) { - var pos = toPosToken(token); - if (tk == TokenKind.GT) { - return OpGT.create(pos, expr, rhExpr); - } - if (tk == TokenKind.LT) { - return OpLT.create(pos, expr, rhExpr); - } - if (tk == TokenKind.LE) { - return OpLE.create(pos, expr, rhExpr); - } - if (tk == TokenKind.GE) { - return OpGE.create(pos, expr, rhExpr); - } - if (tk == TokenKind.EQ) { - return OpEQ.create(pos, expr, rhExpr); - } - //Assert.isTrue(tk == TokenKind.NE); - return OpNE.create(pos, expr, rhExpr); - } - - if (tk == TokenKind.INSTANCEOF) { - return new OperatorInstanceof(toPosToken(token), expr, rhExpr); - } + // logicalAndExpression : relationalExpression (AND^ relationalExpression)*; + function eatLogicalAndExpression() { + var expr = eatRelationalExpression(); + while (peekIdentifierToken('and') || peekTokenOne(TokenKind.SYMBOLIC_AND)) { + var token = nextToken(); // consume 'AND' + var rhExpr = eatRelationalExpression(); + checkOperands(token, expr, rhExpr); + expr = OpAnd.create(toPosToken(token), expr, rhExpr); + } + return expr; + } - if (tk == TokenKind.MATCHES) { - return new OperatorMatches(toPosToken(token), expr, rhExpr); - } + // relationalExpression : sumExpression (relationalOperator^ sumExpression)?; + function eatRelationalExpression() { + var expr = eatSumExpression(); + var relationalOperatorToken = maybeEatRelationalOperator(); + if (relationalOperatorToken !== null) { + var token = nextToken(); // consume relational operator token + var rhExpr = eatSumExpression(); + checkOperands(token, expr, rhExpr); + var tk = relationalOperatorToken.kind; - //Assert.isTrue(tk == TokenKind.BETWEEN); - return new OperatorBetween(toPosToken(token), expr, rhExpr); - } - return expr; - } - - //sumExpression: productExpression ( (PLUS^ | MINUS^) productExpression)*; - function eatSumExpression() { - var expr = eatProductExpression(); - while (peekTokenAny(TokenKind.PLUS, TokenKind.MINUS, TokenKind.INC)) { - var token = nextToken();//consume PLUS or MINUS or INC - var rhExpr = eatProductExpression(); - checkRightOperand(token, rhExpr); - if (token.getKind() == TokenKind.PLUS) { - expr = OpPlus.create(toPosToken(token), expr, rhExpr); + if (relationalOperatorToken.isNumericRelationalOperator()) { + var pos = toPosToken(token); + if (tk === TokenKind.GT) { + return OpGT.create(pos, expr, rhExpr); } - else if (token.getKind() == TokenKind.MINUS) { - expr = OpMinus.create(toPosToken(token), expr, rhExpr); + if (tk === TokenKind.LT) { + return OpLT.create(pos, expr, rhExpr); } - } - return expr; - } - - // productExpression: powerExpr ((STAR^ | DIV^| MOD^) powerExpr)* ; - function eatProductExpression() { - var expr = eatPowerIncDecExpression(); - while (peekTokenAny(TokenKind.STAR, TokenKind.DIV, TokenKind.MOD)) { - var token = nextToken(); // consume STAR/DIV/MOD - var rhExpr = eatPowerIncDecExpression(); - checkOperands(token, expr, rhExpr); - if (token.getKind() == TokenKind.STAR) { - expr = OpMultiply.create(toPosToken(token), expr, rhExpr); + if (tk === TokenKind.LE) { + return OpLE.create(pos, expr, rhExpr); } - else if (token.getKind() == TokenKind.DIV) { - expr = OpDivide.create(toPosToken(token), expr, rhExpr); + if (tk === TokenKind.GE) { + return OpGE.create(pos, expr, rhExpr); } - else { - //Assert.isTrue(token.getKind() == TokenKind.MOD); - expr = OpModulus.create(toPosToken(token), expr, rhExpr); + if (tk === TokenKind.EQ) { + return OpEQ.create(pos, expr, rhExpr); } + //Assert.isTrue(tk === TokenKind.NE); + return OpNE.create(pos, expr, rhExpr); } - return expr; - } - - // powerExpr : unaryExpression (POWER^ unaryExpression)? (INC || DEC) ; - function eatPowerIncDecExpression() { - var expr = eatUnaryExpression(), - token; - if (peekTokenOne(TokenKind.POWER)) { - token = nextToken(); //consume POWER - var rhExpr = eatUnaryExpression(); - checkRightOperand(token, rhExpr); - return OpPower.create(toPosToken(token), expr, rhExpr); + if (tk === TokenKind.INSTANCEOF) { + return new OperatorInstanceof(toPosToken(token), expr, rhExpr); } - if (expr != null && peekTokenAny(TokenKind.INC, TokenKind.DEC)) { - token = nextToken(); //consume INC/DEC - if (token.getKind() == TokenKind.INC) { - return OpInc.create(toPosToken(token), true, expr); - } - return OpDec.create(toPosToken(token), true, expr); + if (tk === TokenKind.MATCHES) { + return new OperatorMatches(toPosToken(token), expr, rhExpr); } - return expr; - } - - // unaryExpression: (PLUS^ | MINUS^ | BANG^ | INC^ | DEC^) unaryExpression | primaryExpression ; - function eatUnaryExpression() { - var token, - expr; - - if (peekTokenAny(TokenKind.PLUS, TokenKind.MINUS, TokenKind.NOT)) { - token = nextToken(); - expr = eatUnaryExpression(); - if (token.getKind() == TokenKind.NOT) { - return OpNot.create(toPosToken(token), expr); - } - - if (token.getKind() == TokenKind.PLUS) { - return OpPlus.create(toPosToken(token), expr); - } - //Assert.isTrue(token.getKind() == TokenKind.MINUS); - return OpMinus.create(toPosToken(token), expr); - - } - if (peekTokenAny(TokenKind.INC, TokenKind.DEC)) { - token = nextToken(); - expr = eatUnaryExpression(); - if (token.getKind() == TokenKind.INC) { - return OpInc.create(toPosToken(token), false, expr); - } - return OpDec.create(toPosToken(token), false, expr); - } - - return eatPrimaryExpression(); + //Assert.isTrue(tk === TokenKind.BETWEEN); + return new OperatorBetween(toPosToken(token), expr, rhExpr); } + return expr; + } - // primaryExpression : startNode (node)? -> ^(EXPRESSION startNode (node)?); - function eatPrimaryExpression() { - var nodes = []; - var start = eatStartNode(); // always a start node - nodes.push(start); - while (maybeEatNode()) { - nodes.push(pop()); + //sumExpression: productExpression ( (PLUS^ | MINUS^) productExpression)*; + function eatSumExpression() { + var expr = eatProductExpression(); + while (peekTokenAny(TokenKind.PLUS, TokenKind.MINUS, TokenKind.INC)) { + var token = nextToken();//consume PLUS or MINUS or INC + var rhExpr = eatProductExpression(); + checkRightOperand(token, rhExpr); + if (token.getKind() === TokenKind.PLUS) { + expr = OpPlus.create(toPosToken(token), expr, rhExpr); } - if (nodes.length == 1) { - return nodes[0]; + else if (token.getKind() === TokenKind.MINUS) { + expr = OpMinus.create(toPosToken(token), expr, rhExpr); } - return CompoundExpression.create(toPosBounds(start.getStartPosition(), nodes[nodes.length - 1].getEndPosition()), nodes); } + return expr; + } - // node : ((DOT dottedNode) | (SAFE_NAVI dottedNode) | nonDottedNode)+; - function maybeEatNode() { - var expr = null; - if (peekTokenAny(TokenKind.DOT, TokenKind.SAFE_NAVI)) { - expr = eatDottedNode(); + // productExpression: powerExpr ((STAR^ | DIV^| MOD^) powerExpr)* ; + function eatProductExpression() { + var expr = eatPowerIncDecExpression(); + while (peekTokenAny(TokenKind.STAR, TokenKind.DIV, TokenKind.MOD)) { + var token = nextToken(); // consume STAR/DIV/MOD + var rhExpr = eatPowerIncDecExpression(); + checkOperands(token, expr, rhExpr); + if (token.getKind() === TokenKind.STAR) { + expr = OpMultiply.create(toPosToken(token), expr, rhExpr); } - else { - expr = maybeEatNonDottedNode(); - } - - if (expr == null) { - return false; + else if (token.getKind() === TokenKind.DIV) { + expr = OpDivide.create(toPosToken(token), expr, rhExpr); } else { - push(expr); - return true; + //Assert.isTrue(token.getKind() === TokenKind.MOD); + expr = OpModulus.create(toPosToken(token), expr, rhExpr); } } + return expr; + } - // nonDottedNode: indexer; - function maybeEatNonDottedNode() { - if (peekTokenOne(TokenKind.LSQUARE)) { - if (maybeEatIndexer()) { - return pop(); - } - } - return null; + // powerExpr : unaryExpression (POWER^ unaryExpression)? (INC || DEC) ; + function eatPowerIncDecExpression() { + var expr = eatUnaryExpression(), + token; + + if (peekTokenOne(TokenKind.POWER)) { + token = nextToken(); //consume POWER + var rhExpr = eatUnaryExpression(); + checkRightOperand(token, rhExpr); + return OpPower.create(toPosToken(token), expr, rhExpr); } - //dottedNode - // : ((methodOrProperty - // | functionOrVar - // | projection - // | selection - // | firstSelection - // | lastSelection - // )) - // ; - function eatDottedNode() { - var token = nextToken();// it was a '.' or a '?.' - var nullSafeNavigation = token.getKind() == TokenKind.SAFE_NAVI; - if (maybeEatMethodOrProperty(nullSafeNavigation) || maybeEatFunctionOrVar() - || maybeEatProjection(nullSafeNavigation) - || maybeEatSelection(nullSafeNavigation)) { - return pop(); + if (expr !== null && peekTokenAny(TokenKind.INC, TokenKind.DEC)) { + token = nextToken(); //consume INC/DEC + if (token.getKind() === TokenKind.INC) { + return OpInc.create(toPosToken(token), true, expr); } - if (peekToken() == null) { - // unexpectedly ran out of data - raiseInternalException(token.startPos, 'OOD'); - } - else { - raiseInternalException(token.startPos, 'UNEXPECTED_DATA_AFTER_DOT', toString(peekToken())); - } - return null; + return OpDec.create(toPosToken(token), true, expr); } - // functionOrVar - // : (POUND ID LPAREN) => function - // | var - // - // function : POUND id=ID methodArgs -> ^(FUNCTIONREF[$id] methodArgs); - // var : POUND id=ID -> ^(VARIABLEREF[$id]); - function maybeEatFunctionOrVar() { - if (!peekTokenOne(TokenKind.HASH)) { - return false; + return expr; + } + + // unaryExpression: (PLUS^ | MINUS^ | BANG^ | INC^ | DEC^) unaryExpression | primaryExpression ; + function eatUnaryExpression() { + var token, + expr; + + if (peekTokenAny(TokenKind.PLUS, TokenKind.MINUS, TokenKind.NOT)) { + token = nextToken(); + expr = eatUnaryExpression(); + if (token.getKind() === TokenKind.NOT) { + return OpNot.create(toPosToken(token), expr); } - var token = nextToken(); - var functionOrVariableName = eatToken(TokenKind.IDENTIFIER); - var args = maybeEatMethodArgs(); - if (args == null) { - push(VariableReference.create(functionOrVariableName.data, toPosBounds(token.startPos, functionOrVariableName.endPos))); - return true; + + if (token.getKind() === TokenKind.PLUS) { + return OpPlus.create(toPosToken(token), expr); } + //Assert.isTrue(token.getKind() === TokenKind.MINUS); + return OpMinus.create(toPosToken(token), expr); - push(FunctionReference.create(functionOrVariableName.data, toPosBounds(token.startPos, functionOrVariableName.endPos), args)); - return true; } - - // methodArgs : LPAREN! (argument (COMMA! argument)* (COMMA!)?)? RPAREN!; - function maybeEatMethodArgs() { - if (!peekTokenOne(TokenKind.LPAREN)) { - return null; + if (peekTokenAny(TokenKind.INC, TokenKind.DEC)) { + token = nextToken(); + expr = eatUnaryExpression(); + if (token.getKind() === TokenKind.INC) { + return OpInc.create(toPosToken(token), false, expr); } - var args = []; - consumeArguments(args); - eatToken(TokenKind.RPAREN); - return args; + return OpDec.create(toPosToken(token), false, expr); } - function eatConstructorArgs(accumulatedArguments) { - if (!peekTokenOne(TokenKind.LPAREN)) { - raiseInternalException(toPosToken(peekToken()), 'MISSING_CONSTRUCTOR_ARGS'); - } - consumeArguments(accumulatedArguments); - eatToken(TokenKind.RPAREN); + return eatPrimaryExpression(); + } + + // primaryExpression : startNode (node)? -> ^(EXPRESSION startNode (node)?); + function eatPrimaryExpression() { + var nodes = []; + var start = eatStartNode(); // always a start node + nodes.push(start); + while (maybeEatNode()) { + nodes.push(pop()); } + if (nodes.length === 1) { + return nodes[0]; + } + return CompoundExpression.create(toPosBounds(start.getStartPosition(), nodes[nodes.length - 1].getEndPosition()), nodes); + } - /** - * Used for consuming arguments for either a method or a constructor call - */ - function consumeArguments(accumulatedArguments) { - var pos = peekToken().startPos; - var next; - do { - nextToken(); // consume ( (first time through) or comma (subsequent times) - var token = peekToken(); - if (token == null) { - raiseInternalException(pos, 'RUN_OUT_OF_ARGUMENTS'); - } - if (token.getKind() != TokenKind.RPAREN) { - accumulatedArguments.push(eatExpression()); - } - next = peekToken(); - } - while (next != null && next.kind == TokenKind.COMMA); + // node : ((DOT dottedNode) | (SAFE_NAVI dottedNode) | nonDottedNode)+; + function maybeEatNode() { + var expr = null; + if (peekTokenAny(TokenKind.DOT, TokenKind.SAFE_NAVI)) { + expr = eatDottedNode(); + } + else { + expr = maybeEatNonDottedNode(); + } - if (next == null) { - raiseInternalException(pos, 'RUN_OUT_OF_ARGUMENTS'); - } + if (expr === null) { + return false; + } + else { + push(expr); + return true; } + } - function positionOf(token) { - if (token == null) { - // if null assume the problem is because the right token was - // not found at the end of the expression - return expressionString.length; - } - return token.startPos; - } - - //startNode - // : parenExpr | literal - // | type - // | methodOrProperty - // | functionOrVar - // | projection - // | selection - // | firstSelection - // | lastSelection - // | indexer - // | constructor - function eatStartNode() { - if (maybeEatLiteral()) { - return pop(); - } - else if (maybeEatParenExpression()) { - return pop(); - } - else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstructorReference() || - maybeEatMethodOrProperty(false) || maybeEatFunctionOrVar()) { - return pop(); - } - else if (maybeEatBeanReference()) { - return pop(); - } - else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) { + // nonDottedNode: indexer; + function maybeEatNonDottedNode() { + if (peekTokenOne(TokenKind.LSQUARE)) { + if (maybeEatIndexer()) { return pop(); } - else if (maybeEatInlineListOrMap()) { - return pop(); - } - else { - return null; - } } + return null; + } - // parse: @beanname @'bean.name' - // quoted if dotted - function maybeEatBeanReference() { - if (peekTokenOne(TokenKind.BEAN_REF)) { - var beanRefToken = nextToken(); - var beanNameToken = null; - var beanName = null; - if (peekTokenOne(TokenKind.IDENTIFIER)) { - beanNameToken = eatToken(TokenKind.IDENTIFIER); - beanName = beanNameToken.data; - } - else if (peekTokenOne(TokenKind.LITERAL_STRING)) { - beanNameToken = eatToken(TokenKind.LITERAL_STRING); - beanName = beanNameToken.stringValue(); - beanName = beanName.substring(1, beanName.length() - 1); - } - else { - raiseInternalException(beanRefToken.startPos, 'INVALID_BEAN_REFERENCE'); - } + //dottedNode + // : ((methodOrProperty + // | functionOrVar + // | projection + // | selection + // | firstSelection + // | lastSelection + // )) + // ; + function eatDottedNode() { + var token = nextToken();// it was a '.' or a '?.' + var nullSafeNavigation = token.getKind() === TokenKind.SAFE_NAVI; + if (maybeEatMethodOrProperty(nullSafeNavigation) || maybeEatFunctionOrVar() || maybeEatProjection(nullSafeNavigation) || maybeEatSelection(nullSafeNavigation)) { + return pop(); + } + if (peekToken() === null) { + // unexpectedly ran out of data + raiseInternalException(token.startPos, 'OOD'); + } + else { + raiseInternalException(token.startPos, 'UNEXPECTED_DATA_AFTER_DOT', toString(peekToken())); + } + return null; + } - var beanReference = new BeanReference(toPosToken(beanNameToken), beanName); - push(beanReference); - return true; - } + // functionOrVar + // : (POUND ID LPAREN) => function + // | var + // + // function : POUND id=ID methodArgs -> ^(FUNCTIONREF[$id] methodArgs); + // var : POUND id=ID -> ^(VARIABLEREF[$id]); + function maybeEatFunctionOrVar() { + if (!peekTokenOne(TokenKind.HASH)) { return false; } + var token = nextToken(); + var functionOrVariableName = eatToken(TokenKind.IDENTIFIER); + var args = maybeEatMethodArgs(); + if (args === null) { + push(VariableReference.create(functionOrVariableName.data, toPosBounds(token.startPos, functionOrVariableName.endPos))); + return true; + } - function maybeEatTypeReference() { - if (peekTokenOne(TokenKind.IDENTIFIER)) { - var typeName = peekToken(); - if (typeName.stringValue() !== "T") { - return false; - } - // It looks like a type reference but is T being used as a map key? - var token = nextToken(); - if (peekTokenOne(TokenKind.RSQUARE)) { - // looks like 'T]' (T is map key) - push(PropertyReference.create(token.stringValue(), toPosToken(token))); - return true; - } - eatToken(TokenKind.LPAREN); - var node = eatPossiblyQualifiedId(); - // dotted qualified id - // Are there array dimensions? - var dims = 0; - while (peekTokenConsumeIfMatched(TokenKind.LSQUARE, true)) { - eatToken(TokenKind.RSQUARE); - dims++; - } - eatToken(TokenKind.RPAREN); - push(new TypeReference(toPosToken(typeName), node, dims)); - return true; - } - return false; + push(FunctionReference.create(functionOrVariableName.data, toPosBounds(token.startPos, functionOrVariableName.endPos), args)); + return true; + } + + // methodArgs : LPAREN! (argument (COMMA! argument)* (COMMA!)?)? RPAREN!; + function maybeEatMethodArgs() { + if (!peekTokenOne(TokenKind.LPAREN)) { + return null; } + var args = []; + consumeArguments(args); + eatToken(TokenKind.RPAREN); + return args; + } - function maybeEatNullReference() { - if (peekTokenOne(TokenKind.IDENTIFIER)) { - var nullToken = peekToken(); - if (nullToken.stringValue().toLowerCase() !== "null") { - return false; - } - nextToken(); - push(NullLiteral.create(toPosToken(nullToken))); - return true; - } - return false; + function eatConstructorArgs(accumulatedArguments) { + if (!peekTokenOne(TokenKind.LPAREN)) { + raiseInternalException(toPosToken(peekToken()), 'MISSING_CONSTRUCTOR_ARGS'); } + consumeArguments(accumulatedArguments); + eatToken(TokenKind.RPAREN); + } - //projection: PROJECT^ expression RCURLY!; - function maybeEatProjection(nullSafeNavigation) { + /** + * Used for consuming arguments for either a method or a constructor call + */ + function consumeArguments(accumulatedArguments) { + var pos = peekToken().startPos; + var next; + do { + nextToken(); // consume ( (first time through) or comma (subsequent times) var token = peekToken(); - if (!peekTokenConsumeIfMatched(TokenKind.PROJECT, true)) { - return false; + if (token === null) { + raiseInternalException(pos, 'RUN_OUT_OF_ARGUMENTS'); } - var expr = eatExpression(); - eatToken(TokenKind.RSQUARE); - push(Projection.create(nullSafeNavigation, toPosToken(token), expr)); - return true; + if (token.getKind() !== TokenKind.RPAREN) { + accumulatedArguments.push(eatExpression()); + } + next = peekToken(); } + while (next !== null && next.kind === TokenKind.COMMA); - // list = LCURLY (element (COMMA element)*) RCURLY - // map = LCURLY (key ':' value (COMMA key ':' value)*) RCURLY - function maybeEatInlineListOrMap() { - var token = peekToken(), - listElements = []; + if (next === null) { + raiseInternalException(pos, 'RUN_OUT_OF_ARGUMENTS'); + } + } - if (!peekTokenConsumeIfMatched(TokenKind.LCURLY, true)) { - return false; - } - var expr = null; - var closingCurly = peekToken(); - if (peekTokenConsumeIfMatched(TokenKind.RCURLY, true)) { - // empty list '{}' - expr = InlineList.create(toPosBounds(token.startPos, closingCurly.endPos)); + function positionOf(token) { + if (token === null) { + // if null assume the problem is because the right token was + // not found at the end of the expression + return expressionString.length; + } + return token.startPos; + } + + //startNode + // : parenExpr | literal + // | type + // | methodOrProperty + // | functionOrVar + // | projection + // | selection + // | firstSelection + // | lastSelection + // | indexer + // | constructor + function eatStartNode() { + if (maybeEatLiteral()) { + return pop(); + } + else if (maybeEatParenExpression()) { + return pop(); + } + else if (maybeEatTypeReference() || maybeEatNullReference() || maybeEatConstructorReference() || maybeEatMethodOrProperty(false) || maybeEatFunctionOrVar()) { + return pop(); + } + else if (maybeEatBeanReference()) { + return pop(); + } + else if (maybeEatProjection(false) || maybeEatSelection(false) || maybeEatIndexer()) { + return pop(); + } + else if (maybeEatInlineListOrMap()) { + return pop(); + } + else { + return null; + } + } + + // parse: @beanname @'bean.name' + // quoted if dotted + function maybeEatBeanReference() { + if (peekTokenOne(TokenKind.BEAN_REF)) { + var beanRefToken = nextToken(); + var beanNameToken = null; + var beanName = null; + if (peekTokenOne(TokenKind.IDENTIFIER)) { + beanNameToken = eatToken(TokenKind.IDENTIFIER); + beanName = beanNameToken.data; } - else if (peekTokenConsumeIfMatched(TokenKind.COLON, true)) { - closingCurly = eatToken(TokenKind.RCURLY); - // empty map '{:}' - expr = InlineMap.create(toPosBounds(token.startPos, closingCurly.endPos)); + else if (peekTokenOne(TokenKind.LITERAL_STRING)) { + beanNameToken = eatToken(TokenKind.LITERAL_STRING); + beanName = beanNameToken.stringValue(); + beanName = beanName.substring(1, beanName.length() - 1); } else { - var firstExpression = eatExpression(); - // Next is either: - // '}' - end of list - // ',' - more expressions in this list - // ':' - this is a map! - - if (peekTokenOne(TokenKind.RCURLY)) { // list with one item in it - listElements.push(firstExpression); - closingCurly = eatToken(TokenKind.RCURLY); - expr = InlineList.create(toPosBounds(token.startPos, closingCurly.endPos), listElements); - } - else if (peekTokenConsumeIfMatched(TokenKind.COMMA, true)) { // multi item list - listElements.push(firstExpression); - do { - listElements.push(eatExpression()); - } - while (peekTokenConsumeIfMatched(TokenKind.COMMA, true)); - closingCurly = eatToken(TokenKind.RCURLY); - expr = InlineList.create(toPosToken(token.startPos, closingCurly.endPos), listElements); - - } - else if (peekTokenConsumeIfMatched(TokenKind.COLON, true)) { // map! - var mapElements = []; - mapElements.push(firstExpression); - mapElements.push(eatExpression()); - while (peekTokenConsumeIfMatched(TokenKind.COMMA, true)) { - mapElements.push(eatExpression()); - eatToken(TokenKind.COLON); - mapElements.push(eatExpression()); - } - closingCurly = eatToken(TokenKind.RCURLY); - expr = InlineMap.create(toPosBounds(token.startPos, closingCurly.endPos), mapElements); - } - else { - raiseInternalException(token.startPos, 'OOD'); - } + raiseInternalException(beanRefToken.startPos, 'INVALID_BEAN_REFERENCE'); } - push(expr); + + var beanReference = new BeanReference(toPosToken(beanNameToken), beanName); + push(beanReference); return true; } + return false; + } - function maybeEatIndexer() { - var token = peekToken(); - if (!peekTokenConsumeIfMatched(TokenKind.LSQUARE, true)) { + function maybeEatTypeReference() { + if (peekTokenOne(TokenKind.IDENTIFIER)) { + var typeName = peekToken(); + if (typeName.stringValue() !== 'T') { return false; } - var expr = eatExpression(); - eatToken(TokenKind.RSQUARE); - push(Indexer.create(toPosToken(token), expr)); + // It looks like a type reference but is T being used as a map key? + var token = nextToken(); + if (peekTokenOne(TokenKind.RSQUARE)) { + // looks like 'T]' (T is map key) + push(PropertyReference.create(token.stringValue(), toPosToken(token))); + return true; + } + eatToken(TokenKind.LPAREN); + var node = eatPossiblyQualifiedId(); + // dotted qualified id + // Are there array dimensions? + var dims = 0; + while (peekTokenConsumeIfMatched(TokenKind.LSQUARE, true)) { + eatToken(TokenKind.RSQUARE); + dims++; + } + eatToken(TokenKind.RPAREN); + push(new TypeReference(toPosToken(typeName), node, dims)); return true; } + return false; + } - function maybeEatSelection(nullSafeNavigation) { - var token = peekToken(); - if (!peekSelectToken()) { + function maybeEatNullReference() { + if (peekTokenOne(TokenKind.IDENTIFIER)) { + var nullToken = peekToken(); + if (nullToken.stringValue().toLowerCase() !== 'null') { return false; } nextToken(); - var expr = eatExpression(); - if (expr == null) { - raiseInternalException(toPosToken(token), 'MISSING_SELECTION_EXPRESSION'); - } - eatToken(TokenKind.RSQUARE); - if (token.getKind() == TokenKind.SELECT_FIRST) { - push(Selection.create(nullSafeNavigation, Selection.FIRST, toPosToken(token), expr)); - } - else if (token.getKind() == TokenKind.SELECT_LAST) { - push(Selection.create(nullSafeNavigation, Selection.LAST, toPosToken(token), expr)); - } - else { - push(Selection.create(nullSafeNavigation, Selection.ALL, toPosToken(token), expr)); - } + push(NullLiteral.create(toPosToken(nullToken))); return true; } + return false; + } - /** - * Eat an identifier, possibly qualified (meaning that it is dotted). - * TODO AndyC Could create complete identifiers (a.b.c) here rather than a sequence of them? (a, b, c) - */ - function eatPossiblyQualifiedId() { - var qualifiedIdPieces = []; - var node = peekToken(); - while (isValidQualifiedId(node)) { - nextToken(); - if (node.kind != TokenKind.DOT) { - qualifiedIdPieces.push(new Identifier(node.stringValue(), toPosToken(node))); - } - node = peekToken(); + //projection: PROJECT^ expression RCURLY!; + function maybeEatProjection(nullSafeNavigation) { + var token = peekToken(); + if (!peekTokenConsumeIfMatched(TokenKind.PROJECT, true)) { + return false; + } + var expr = eatExpression(); + eatToken(TokenKind.RSQUARE); + push(Projection.create(nullSafeNavigation, toPosToken(token), expr)); + return true; + } + + // list = LCURLY (element (COMMA element)*) RCURLY + // map = LCURLY (key ':' value (COMMA key ':' value)*) RCURLY + function maybeEatInlineListOrMap() { + var token = peekToken(), + listElements = []; + + if (!peekTokenConsumeIfMatched(TokenKind.LCURLY, true)) { + return false; + } + var expr = null; + var closingCurly = peekToken(); + if (peekTokenConsumeIfMatched(TokenKind.RCURLY, true)) { + // empty list '{}' + expr = InlineList.create(toPosBounds(token.startPos, closingCurly.endPos)); + } + else if (peekTokenConsumeIfMatched(TokenKind.COLON, true)) { + closingCurly = eatToken(TokenKind.RCURLY); + // empty map '{:}' + expr = InlineMap.create(toPosBounds(token.startPos, closingCurly.endPos)); + } + else { + var firstExpression = eatExpression(); + // Next is either: + // '}' - end of list + // ',' - more expressions in this list + // ':' - this is a map! + + if (peekTokenOne(TokenKind.RCURLY)) { // list with one item in it + listElements.push(firstExpression); + closingCurly = eatToken(TokenKind.RCURLY); + expr = InlineList.create(toPosBounds(token.startPos, closingCurly.endPos), listElements); } - if (!qualifiedIdPieces.length) { - if (node == null) { - raiseInternalException(expressionString.length(), 'OOD'); + else if (peekTokenConsumeIfMatched(TokenKind.COMMA, true)) { // multi item list + listElements.push(firstExpression); + do { + listElements.push(eatExpression()); } - raiseInternalException(node.startPos, 'NOT_EXPECTED_TOKEN', "qualified ID", node.getKind().toString().toLowerCase()); - } - var pos = toPosBounds(qualifiedIdPieces[0].getStartPosition(), qualifiedIdPieces[qualifiedIdPieces.length - 1].getEndPosition()); - return new QualifiedIdentifier(pos, qualifiedIdPieces); - } + while (peekTokenConsumeIfMatched(TokenKind.COMMA, true)); + closingCurly = eatToken(TokenKind.RCURLY); + expr = InlineList.create(toPosToken(token.startPos, closingCurly.endPos), listElements); - function isValidQualifiedId(node) { - if (node == null || node.kind == TokenKind.LITERAL_STRING) { - return false; } - if (node.kind == TokenKind.DOT || node.kind == TokenKind.IDENTIFIER) { - return true; + else if (peekTokenConsumeIfMatched(TokenKind.COLON, true)) { // map! + var mapElements = []; + mapElements.push(firstExpression); + mapElements.push(eatExpression()); + while (peekTokenConsumeIfMatched(TokenKind.COMMA, true)) { + mapElements.push(eatExpression()); + eatToken(TokenKind.COLON); + mapElements.push(eatExpression()); + } + closingCurly = eatToken(TokenKind.RCURLY); + expr = InlineMap.create(toPosBounds(token.startPos, closingCurly.endPos), mapElements); + } + else { + raiseInternalException(token.startPos, 'OOD'); } - var value = node.stringValue(); - return (value.length && VALID_QUALIFIED_ID_PATTERN.test(value)); } + push(expr); + return true; + } - // This is complicated due to the support for dollars in identifiers. Dollars are normally separate tokens but - // there we want to combine a series of identifiers and dollars into a single identifier - function maybeEatMethodOrProperty(nullSafeNavigation) { - if (peekTokenOne(TokenKind.IDENTIFIER)) { - var methodOrPropertyName = nextToken(); - var args = maybeEatMethodArgs(); - if (args == null) { - // property - push(PropertyReference.create(nullSafeNavigation, methodOrPropertyName.stringValue(), toPosToken(methodOrPropertyName))); - return true; - } - // methodreference - push(MethodReference.create(nullSafeNavigation, methodOrPropertyName.stringValue(), toPosToken(methodOrPropertyName), args)); - // TODO what is the end position for a method reference? the name or the last arg? - return true; - } + function maybeEatIndexer() { + var token = peekToken(); + if (!peekTokenConsumeIfMatched(TokenKind.LSQUARE, true)) { return false; } + var expr = eatExpression(); + eatToken(TokenKind.RSQUARE); + push(Indexer.create(toPosToken(token), expr)); + return true; + } - //constructor - //: ('new' qualifiedId LPAREN) => 'new' qualifiedId ctorArgs -> ^(CONSTRUCTOR qualifiedId ctorArgs) - function maybeEatConstructorReference() { - if (peekIdentifierToken("new")) { - var newToken = nextToken(); - // It looks like a constructor reference but is NEW being used as a map key? - if (peekTokenOne(TokenKind.RSQUARE)) { - // looks like 'NEW]' (so NEW used as map key) - push(PropertyReference.create(newToken.stringValue(), toPosToken(newToken))); - return true; - } - var possiblyQualifiedConstructorName = eatPossiblyQualifiedId(); - var nodes = []; - nodes.push(possiblyQualifiedConstructorName); - if (peekTokenOne(TokenKind.LSQUARE)) { - // array initializer - var dimensions = []; - while (peekTokenConsumeIfMatched(TokenKind.LSQUARE, true)) { - if (!peekTokenOne(TokenKind.RSQUARE)) { - dimensions.push(eatExpression()); - } - else { - dimensions.push(null); - } - eatToken(TokenKind.RSQUARE); - } - if (maybeEatInlineListOrMap()) { - nodes.push(pop()); - } - push(new ConstructorReference(toPosToken(newToken), dimensions, nodes)); - } - else { - // regular constructor invocation - eatConstructorArgs(nodes); - // TODO correct end position? - push(new ConstructorReference(toPosToken(newToken), nodes)); - } - return true; - } + function maybeEatSelection(nullSafeNavigation) { + var token = peekToken(); + if (!peekSelectToken()) { return false; } - - function push(newNode) { - constructedNodes.push(newNode); + nextToken(); + var expr = eatExpression(); + if (expr === null) { + raiseInternalException(toPosToken(token), 'MISSING_SELECTION_EXPRESSION'); } - - function pop() { - return constructedNodes.pop(); + eatToken(TokenKind.RSQUARE); + if (token.getKind() === TokenKind.SELECT_FIRST) { + push(Selection.create(nullSafeNavigation, Selection.FIRST, toPosToken(token), expr)); + } + else if (token.getKind() === TokenKind.SELECT_LAST) { + push(Selection.create(nullSafeNavigation, Selection.LAST, toPosToken(token), expr)); + } + else { + push(Selection.create(nullSafeNavigation, Selection.ALL, toPosToken(token), expr)); } + return true; + } - // literal - // : INTEGER_LITERAL - // | boolLiteral - // | STRING_LITERAL - // | HEXADECIMAL_INTEGER_LITERAL - // | REAL_LITERAL - // | DQ_STRING_LITERAL - // | NULL_LITERAL - function maybeEatLiteral() { - var token = peekToken(); - if (token == null) { - return false; - } - if (token.getKind() === TokenKind.LITERAL_INT || - token.getKind() === TokenKind.LITERAL_LONG) { - push(NumberLiteral.create(parseInt(token.stringValue(), 10), toPosToken(token))); - } - else if ( token.getKind() === TokenKind.LITERAL_REAL || - token.getKind() === TokenKind.LITERAL_REAL_FLOAT) { - push(NumberLiteral.create(parseFloat(token.stringValue()), toPosToken(token))); - } - else if ( token.getKind() === TokenKind.LITERAL_HEXINT || - token.getKind() === TokenKind.LITERAL_HEXLONG) { - push(NumberLiteral.create(parseInt(token.stringValue(), 16), toPosToken(token))); - } - else if (peekIdentifierToken("true")) { - push(BooleanLiteral.create(true, toPosToken(token))); - } - else if (peekIdentifierToken("false")) { - push(BooleanLiteral.create(false, toPosToken(token))); - } - else if (token.getKind() === TokenKind.LITERAL_STRING) { - push(StringLiteral.create(token.stringValue(), toPosToken(token))); + /** + * Eat an identifier, possibly qualified (meaning that it is dotted). + * TODO AndyC Could create complete identifiers (a.b.c) here rather than a sequence of them? (a, b, c) + */ + function eatPossiblyQualifiedId() { + var qualifiedIdPieces = []; + var node = peekToken(); + while (isValidQualifiedId(node)) { + nextToken(); + if (node.kind !== TokenKind.DOT) { + qualifiedIdPieces.push(new Identifier(node.stringValue(), toPosToken(node))); } - else { - return false; + node = peekToken(); + } + if (!qualifiedIdPieces.length) { + if (node === null) { + raiseInternalException(expressionString.length(), 'OOD'); } - nextToken(); + raiseInternalException(node.startPos, 'NOT_EXPECTED_TOKEN', 'qualified ID', node.getKind().toString().toLowerCase()); + } + var pos = toPosBounds(qualifiedIdPieces[0].getStartPosition(), qualifiedIdPieces[qualifiedIdPieces.length - 1].getEndPosition()); + return new QualifiedIdentifier(pos, qualifiedIdPieces); + } + + function isValidQualifiedId(node) { + if (node === null || node.kind === TokenKind.LITERAL_STRING) { + return false; + } + if (node.kind === TokenKind.DOT || node.kind === TokenKind.IDENTIFIER) { return true; } + var value = node.stringValue(); + return (value.length && VALID_QUALIFIED_ID_PATTERN.test(value)); + } - //parenExpr : LPAREN! expression RPAREN!; - function maybeEatParenExpression() { - if (peekTokenOne(TokenKind.LPAREN)) { - nextToken(); - var expr = eatExpression(); - eatToken(TokenKind.RPAREN); - push(expr); + // This is complicated due to the support for dollars in identifiers. Dollars are normally separate tokens but + // there we want to combine a series of identifiers and dollars into a single identifier + function maybeEatMethodOrProperty(nullSafeNavigation) { + if (peekTokenOne(TokenKind.IDENTIFIER)) { + var methodOrPropertyName = nextToken(); + var args = maybeEatMethodArgs(); + if (args === null) { + // property + push(PropertyReference.create(nullSafeNavigation, methodOrPropertyName.stringValue(), toPosToken(methodOrPropertyName))); return true; } - else { - return false; - } + // methodreference + push(MethodReference.create(nullSafeNavigation, methodOrPropertyName.stringValue(), toPosToken(methodOrPropertyName), args)); + // TODO what is the end position for a method reference? the name or the last arg? + return true; } + return false; + } - // relationalOperator - // : EQUAL | NOT_EQUAL | LESS_THAN | LESS_THAN_OR_EQUAL | GREATER_THAN - // | GREATER_THAN_OR_EQUAL | INSTANCEOF | BETWEEN | MATCHES - function maybeEatRelationalOperator() { - var token = peekToken(); - if (token == null) { - return null; - } - if (token.isNumericRelationalOperator()) { - return token; + //constructor + //: ('new' qualifiedId LPAREN) => 'new' qualifiedId ctorArgs -> ^(CONSTRUCTOR qualifiedId ctorArgs) + function maybeEatConstructorReference() { + if (peekIdentifierToken('new')) { + var newToken = nextToken(); + // It looks like a constructor reference but is NEW being used as a map key? + if (peekTokenOne(TokenKind.RSQUARE)) { + // looks like 'NEW]' (so NEW used as map key) + push(PropertyReference.create(newToken.stringValue(), toPosToken(newToken))); + return true; } - if (token.isIdentifier()) { - var idString = token.stringValue(); - if (idString.toLowerCase() === "instanceof") { - return token.asInstanceOfToken(); - } - if (idString.toLowerCase() === "matches") { - return token.asMatchesToken(); + var possiblyQualifiedConstructorName = eatPossiblyQualifiedId(); + var nodes = []; + nodes.push(possiblyQualifiedConstructorName); + if (peekTokenOne(TokenKind.LSQUARE)) { + // array initializer + var dimensions = []; + while (peekTokenConsumeIfMatched(TokenKind.LSQUARE, true)) { + if (!peekTokenOne(TokenKind.RSQUARE)) { + dimensions.push(eatExpression()); + } + else { + dimensions.push(null); + } + eatToken(TokenKind.RSQUARE); } - if (idString.toLowerCase() === "between") { - return token.asBetweenToken(); + if (maybeEatInlineListOrMap()) { + nodes.push(pop()); } + push(new ConstructorReference(toPosToken(newToken), dimensions, nodes)); } - return null; - } - - function eatToken(expectedKind) { - var token = nextToken(); - if (token == null) { - raiseInternalException(expressionString.length, 'OOD'); - } - if (token.getKind() != expectedKind) { - raiseInternalException(token.startPos, 'NOT_EXPECTED_TOKEN', - expectedKind.toString().toLowerCase(), token.getKind().toString().toLowerCase()); + else { + // regular constructor invocation + eatConstructorArgs(nodes); + // TODO correct end position? + push(new ConstructorReference(toPosToken(newToken), nodes)); } - return token; + return true; } + return false; + } - function peekTokenOne(desiredTokenKind) { - return peekTokenConsumeIfMatched(desiredTokenKind, false); - } + function push(newNode) { + constructedNodes.push(newNode); + } - function peekTokenConsumeIfMatched(desiredTokenKind, consumeIfMatched) { - if (!moreTokens()) { - return false; - } - var token = peekToken(); - if (token.getKind() == desiredTokenKind) { - if (consumeIfMatched) { - tokenStreamPointer++; - } - return true; - } + function pop() { + return constructedNodes.pop(); + } - if (desiredTokenKind == TokenKind.IDENTIFIER) { - // might be one of the textual forms of the operators (e.g. NE for != ) - in which case we can treat it as an identifier - // The list is represented here: Tokenizer.alternativeOperatorNames and those ones are in order in the TokenKind enum - if (token.getKind().ordinal() >= TokenKind.DIV.ordinal() && token.getKind().ordinal() <= TokenKind.NOT.ordinal() && token.data != null) { - // if token.data were null, we'd know it wasn'token the textual form, it was the symbol form - return true; - } - } + // literal + // : INTEGER_LITERAL + // | boolLiteral + // | STRING_LITERAL + // | HEXADECIMAL_INTEGER_LITERAL + // | REAL_LITERAL + // | DQ_STRING_LITERAL + // | NULL_LITERAL + function maybeEatLiteral() { + var token = peekToken(); + if (token === null) { return false; } - - function peekTokenAny() { - if (!moreTokens()) { - return false; - } - var token = peekToken(); - var args = Array.prototype.slice.call(arguments); - for (var i = 0, l = args.length; i < l; i += 1) { - if (token.getKind() === args[i]) { - return true; - } - } + if (token.getKind() === TokenKind.LITERAL_INT || + token.getKind() === TokenKind.LITERAL_LONG) { + push(NumberLiteral.create(parseInt(token.stringValue(), 10), toPosToken(token))); + } + else if ( token.getKind() === TokenKind.LITERAL_REAL || + token.getKind() === TokenKind.LITERAL_REAL_FLOAT) { + push(NumberLiteral.create(parseFloat(token.stringValue()), toPosToken(token))); + } + else if ( token.getKind() === TokenKind.LITERAL_HEXINT || + token.getKind() === TokenKind.LITERAL_HEXLONG) { + push(NumberLiteral.create(parseInt(token.stringValue(), 16), toPosToken(token))); + } + else if (peekIdentifierToken('true')) { + push(BooleanLiteral.create(true, toPosToken(token))); + } + else if (peekIdentifierToken('false')) { + push(BooleanLiteral.create(false, toPosToken(token))); + } + else if (token.getKind() === TokenKind.LITERAL_STRING) { + push(StringLiteral.create(token.stringValue(), toPosToken(token))); + } + else { return false; } + nextToken(); + return true; + } - function peekIdentifierToken(identifierString) { - if (!moreTokens()) { - return false; - } - var token = peekToken(); - return token.getKind() === TokenKind.IDENTIFIER && token.stringValue().toLowerCase() === identifierString.toLowerCase(); + //parenExpr : LPAREN! expression RPAREN!; + function maybeEatParenExpression() { + if (peekTokenOne(TokenKind.LPAREN)) { + nextToken(); + var expr = eatExpression(); + eatToken(TokenKind.RPAREN); + push(expr); + return true; } + else { + return false; + } + } - function peekSelectToken() { - if (!moreTokens()) { - return false; + // relationalOperator + // : EQUAL | NOT_EQUAL | LESS_THAN | LESS_THAN_OR_EQUAL | GREATER_THAN + // | GREATER_THAN_OR_EQUAL | INSTANCEOF | BETWEEN | MATCHES + function maybeEatRelationalOperator() { + var token = peekToken(); + if (token === null) { + return null; + } + if (token.isNumericRelationalOperator()) { + return token; + } + if (token.isIdentifier()) { + var idString = token.stringValue(); + if (idString.toLowerCase() === 'instanceof') { + return token.asInstanceOfToken(); + } + if (idString.toLowerCase() === 'matches') { + return token.asMatchesToken(); + } + if (idString.toLowerCase() === 'between') { + return token.asBetweenToken(); } - var token = peekToken(); - return token.getKind() === TokenKind.SELECT || token.getKind() === TokenKind.SELECT_FIRST - || token.getKind() === TokenKind.SELECT_LAST; } + return null; + } - function moreTokens() { - return tokenStreamPointer < tokenStream.length; + function eatToken(expectedKind) { + var token = nextToken(); + if (token === null) { + raiseInternalException(expressionString.length, 'OOD'); + } + if (token.getKind() !== expectedKind) { + raiseInternalException(token.startPos, 'NOT_EXPECTED_TOKEN', + expectedKind.toString().toLowerCase(), token.getKind().toString().toLowerCase()); } + return token; + } - function nextToken() { - if (tokenStreamPointer >= tokenStreamLength) { - return null; + function peekTokenOne(desiredTokenKind) { + return peekTokenConsumeIfMatched(desiredTokenKind, false); + } + + function peekTokenConsumeIfMatched(desiredTokenKind, consumeIfMatched) { + if (!moreTokens()) { + return false; + } + var token = peekToken(); + if (token.getKind() === desiredTokenKind) { + if (consumeIfMatched) { + tokenStreamPointer++; } - return tokenStream[tokenStreamPointer++]; + return true; } - function peekToken() { - if (tokenStreamPointer >= tokenStreamLength) { - return null; + if (desiredTokenKind === TokenKind.IDENTIFIER) { + // might be one of the textual forms of the operators (e.g. NE for !== ) - in which case we can treat it as an identifier + // The list is represented here: Tokenizer.alternativeOperatorNames and those ones are in order in the TokenKind enum + if (token.getKind().ordinal() >= TokenKind.DIV.ordinal() && token.getKind().ordinal() <= TokenKind.NOT.ordinal() && token.data !== null) { + // if token.data were null, we'd know it wasn'token the textual form, it was the symbol form + return true; } - return tokenStream[tokenStreamPointer]; } + return false; + } - function raiseInternalException(pos, message, expected, actual) { - if (expected) { - message += '\nExpected: ' + expected; - } - if (actual) { - message += '\nActual: ' + actual; + function peekTokenAny() { + if (!moreTokens()) { + return false; + } + var token = peekToken(); + var args = Array.prototype.slice.call(arguments); + for (var i = 0, l = args.length; i < l; i += 1) { + if (token.getKind() === args[i]) { + return true; } - throw { - name: 'InternalParseException', - message: 'Error occurred while attempting to parse expression \'' + expressionString + '\' at position ' + pos + '. Message: ' + message - }; } + return false; + } - function toString(token) { - if (token.getKind().hasPayload()) { - return token.stringValue(); - } - return token.getKind().toString().toLowerCase(); + function peekIdentifierToken(identifierString) { + if (!moreTokens()) { + return false; } + var token = peekToken(); + return token.getKind() === TokenKind.IDENTIFIER && token.stringValue().toLowerCase() === identifierString.toLowerCase(); + } - function checkOperands(token, left, right) { - checkLeftOperand(token, left); - checkRightOperand(token, right); + function peekSelectToken() { + if (!moreTokens()) { + return false; } + var token = peekToken(); + return token.getKind() === TokenKind.SELECT || token.getKind() === TokenKind.SELECT_FIRST || token.getKind() === TokenKind.SELECT_LAST; + } - function checkLeftOperand(token, operandExpression) { - if (operandExpression == null) { - raiseInternalException(token.startPos, 'LEFT_OPERAND_PROBLEM'); - } + function moreTokens() { + return tokenStreamPointer < tokenStream.length; + } + + function nextToken() { + if (tokenStreamPointer >= tokenStreamLength) { + return null; } + return tokenStream[tokenStreamPointer++]; + } - function checkRightOperand(token, operandExpression) { - if (operandExpression == null) { - raiseInternalException(token.startPos, 'RIGHT_OPERAND_PROBLEM'); - } + function peekToken() { + if (tokenStreamPointer >= tokenStreamLength) { + return null; + } + return tokenStream[tokenStreamPointer]; + } + + function raiseInternalException(pos, message, expected, actual) { + if (expected) { + message += '\nExpected: ' + expected; + } + if (actual) { + message += '\nActual: ' + actual; } + throw { + name: 'InternalParseException', + message: 'Error occurred while attempting to parse expression \'' + expressionString + '\' at position ' + pos + '. Message: ' + message + }; + } - /** - * Compress the start and end of a token into a single int. - */ - function toPosToken(token) { - return (token.startPos << 16) + token.endPos; + function toString(token) { + if (token.getKind().hasPayload()) { + return token.stringValue(); } + return token.getKind().toString().toLowerCase(); + } + + function checkOperands(token, left, right) { + checkLeftOperand(token, left); + checkRightOperand(token, right); + } - function toPosBounds(start, end) { - return (start << 16) + end; + function checkLeftOperand(token, operandExpression) { + if (operandExpression === null) { + raiseInternalException(token.startPos, 'LEFT_OPERAND_PROBLEM'); } + } - return { - setConfiguration: setConfiguration, - parse: parse + function checkRightOperand(token, operandExpression) { + if (operandExpression === null) { + raiseInternalException(token.startPos, 'RIGHT_OPERAND_PROBLEM'); } - }; + } - exports.SpelExpressionParser = SpelExpressionParser; + /** + * Compress the start and end of a token into a single int. + */ + function toPosToken(token) { + return (token.startPos << 16) + token.endPos; + } -}(window || exports)); + function toPosBounds(start, end) { + return (start << 16) + end; + } + + return { + setConfiguration: setConfiguration, + parse: parse + }; +}; diff --git a/src/StandardContext.js b/src/StandardContext.js index 8d5efb3..1db0786 100644 --- a/src/StandardContext.js +++ b/src/StandardContext.js @@ -1,44 +1,39 @@ -(function (exports) { - 'use strict'; +function create(authentication, principal) { + var context = {}; - function create(authentication, principal) { - var context = {}; + context.authentication = authentication || {}; + context.principal = principal || {}; - context.authentication = authentication || {}; - context.principal = principal || {}; + context.hasRole = function (role) { + var hasRole = false; - context.hasRole = function (role) { - var hasRole = false; + if (!role) { + return false; + } + if (!context.authentication && !Array.isArray(context.authentication.authorities)) { + return false; + } - if (!role) { - return false; + context.authentication.authorities.forEach(function (grantedAuthority) { + if (grantedAuthority.authority.toLowerCase() === role.toLowerCase()) { + hasRole = true; } - if (!context.authentication && !Array.isArray(context.authentication.authorities)) { - return false; - } - - context.authentication.authorities.forEach(function (grantedAuthority) { - if (grantedAuthority.authority.toLowerCase() === role.toLowerCase()) { - hasRole = true; - } - }); - - return hasRole; - }; - - context.hasPermission = function (/*variable arguments*/) { - var args = Array.prototype.slice.call(arguments); + }); - if (args.length === 1) { - return context.hasRole(args[0]); - } - }; + return hasRole; + }; - return context; - } + context.hasPermission = function (/*variable arguments*/) { + var args = Array.prototype.slice.call(arguments); - exports.StandardContext = { - create: create + if (args.length === 1) { + return context.hasRole(args[0]); + } }; -}(window || exports)); + return context; +} + +export var StandardContext = { + create: create +}; diff --git a/src/Token.js b/src/Token.js index 04515ea..36db9a1 100644 --- a/src/Token.js +++ b/src/Token.js @@ -1,74 +1,60 @@ -(function (exports) { - 'use strict'; - - var TokenKind; - - try { - TokenKind = require('./TokenKind').TokenKind; - } catch(e) { - TokenKind = exports.TokenKind; - } - - function Token(tokenKind, tokenData, startPos, endPos) { - this.kind = tokenKind; - this.startPos = startPos; - this.endPos = endPos; - if (tokenData) { - this.data = tokenData; - } - if (!endPos) { - debugger; - } +import {TokenKind} from './TokenKind'; + +function Token(tokenKind, tokenData, startPos, endPos) { + this.kind = tokenKind; + this.startPos = startPos; + this.endPos = endPos; + if (tokenData) { + this.data = tokenData; } +} - Token.prototype.getKind = function () { - return this.kind; - }; - - Token.prototype.toString = function () { - var s = '['; - s += this.kind.toString(); - if (this.kind.hasPayload()) { - s += ':' + this.data; - } - s += ']'; - s += '(' + this.startPos + ',' + this.endPos + ')'; - return s; - }; +Token.prototype.getKind = function () { + return this.kind; +}; - Token.prototype.isIdentifier = function () { - return (this.kind == TokenKind.IDENTIFIER); - }; +Token.prototype.toString = function () { + var s = '['; + s += this.kind.toString(); + if (this.kind.hasPayload()) { + s += ':' + this.data; + } + s += ']'; + s += '(' + this.startPos + ',' + this.endPos + ')'; + return s; +}; - Token.prototype.isNumericRelationalOperator = function () { - return (this.kind == TokenKind.GT || this.kind == TokenKind.GE || this.kind == TokenKind.LT || - this.kind == TokenKind.LE || this.kind==TokenKind.EQ || this.kind==TokenKind.NE); - }; +Token.prototype.isIdentifier = function () { + return (this.kind === TokenKind.IDENTIFIER); +}; - Token.prototype.stringValue = function () { - return this.data; - }; +Token.prototype.isNumericRelationalOperator = function () { + return (this.kind === TokenKind.GT || this.kind === TokenKind.GE || this.kind === TokenKind.LT || + this.kind === TokenKind.LE || this.kind === TokenKind.EQ || this.kind === TokenKind.NE); +}; - Token.prototype.asInstanceOfToken = function () { - return new Token(TokenKind.INSTANCEOF, this.startPos, this.endPos); - }; +Token.prototype.stringValue = function () { + return this.data; +}; - Token.prototype.asMatchesToken = function () { - return new Token(TokenKind.MATCHES, this.startPos, this.endPos); - }; +Token.prototype.asInstanceOfToken = function () { + return new Token(TokenKind.INSTANCEOF, this.startPos, this.endPos); +}; - Token.prototype.asBetweenToken = function () { - return new Token(TokenKind.BETWEEN, this.startPos, this.endPos); - }; +Token.prototype.asMatchesToken = function () { + return new Token(TokenKind.MATCHES, this.startPos, this.endPos); +}; - Token.prototype.getStartPosition = function () { - return this.startPos; - }; +Token.prototype.asBetweenToken = function () { + return new Token(TokenKind.BETWEEN, this.startPos, this.endPos); +}; - Token.prototype.getEndPosition = function () { - return this.endPos; - }; +Token.prototype.getStartPosition = function () { + return this.startPos; +}; - exports.Token = Token; +Token.prototype.getEndPosition = function () { + return this.endPos; +}; -}(window || exports)); +export {Token}; diff --git a/src/TokenKind.js b/src/TokenKind.js index 2818e95..ff5987d 100644 --- a/src/TokenKind.js +++ b/src/TokenKind.js @@ -1,144 +1,138 @@ -(function (exports) { - 'use strict'; +var types = { - // ordered by priority - operands first - var types = { + LITERAL_INT: 1, //tested - LITERAL_INT: 1, //tested + LITERAL_LONG: 2, //tested - LITERAL_LONG: 2, //tested + LITERAL_HEXINT: 3, //tested - LITERAL_HEXINT: 3, //tested + LITERAL_HEXLONG: 4, //tested - LITERAL_HEXLONG: 4, //tested + LITERAL_STRING: 5, //tested - LITERAL_STRING: 5, //tested + LITERAL_REAL: 6, //tested - LITERAL_REAL: 6, //tested + LITERAL_REAL_FLOAT: 7, //tested - LITERAL_REAL_FLOAT: 7, //tested + LPAREN: '(', //tested - LPAREN: "(", //tested + RPAREN: ')', //tested - RPAREN: ")", //tested + COMMA: ',', //tested - COMMA: ",", //tested + IDENTIFIER: 0, //tested - IDENTIFIER: 0, //tested + COLON: ':', //tested - COLON: ":", //tested + HASH: '#', //tested - HASH: "#", //tested + RSQUARE: ']', //tested - RSQUARE: "]", //tested + LSQUARE: '[', //tested - LSQUARE: "[", //tested + LCURLY: '{', //tested - LCURLY: "{", //tested + RCURLY: '}', //tested - RCURLY: "}", //tested + DOT: '.', //tested - DOT: ".", //tested + PLUS: '+', //tested - PLUS: "+", //tested + STAR: '*', //tested - STAR: "*", //tested + MINUS: '-', //tested - MINUS: "-", //tested + SELECT_FIRST: '^[', //tested - SELECT_FIRST: "^[", //tested + SELECT_LAST: '$[', //tested - SELECT_LAST: "$[", //tested + QMARK: '?', //tested - QMARK: "?", //tested + PROJECT: '![', //tested - PROJECT: "![", //tested + DIV: '/', //tested - DIV: "/", //tested + GE: '>=', //tested - GE: ">=", //tested + GT: '>', //tested - GT: ">", //tested + LE: '<=', //tested - LE: "<=", //tested + LT: '<', //tested - LT: "<", //tested + EQ: '==', //tested - EQ: "==", //tested + NE: '!=', //tested - NE: "!=", //tested + MOD: '%', //tested - MOD: "%", //tested + NOT: '!', //tested - NOT: "!", //tested + ASSIGN: '=', //tested - ASSIGN: "=", //tested + INSTANCEOF: 'instanceof', //test fails - INSTANCEOF: "instanceof", //test fails + MATCHES: 'matches', //test fails - MATCHES: "matches", //test fails + BETWEEN: 'between', //test fails - BETWEEN: "between", //test fails + SELECT: '?[', //tested - SELECT: "?[", //tested + POWER: '^', //tested - POWER: "^", //tested + ELVIS: '?:', //tested - ELVIS: "?:", //tested + SAFE_NAVI: '?.', //tested - SAFE_NAVI: "?.", //tested + BEAN_REF: '@', //tested - BEAN_REF: "@", //tested + SYMBOLIC_OR: '||', //tested - SYMBOLIC_OR: "||", //tested + SYMBOLIC_AND: '&&', //tested - SYMBOLIC_AND: "&&", //tested + INC: '++', //tested - INC: "++", //tested + DEC: '--' //tested +}; - DEC: "--" //tested - }; - - function TokenKind(type) { - this.type = type; - this.tokenChars = types[type]; - this._hasPayload = typeof types[type] !== 'string'; - if (typeof types[type] === 'number') { - this._ordinal = types[type]; - } +function TokenKind(type) { + this.type = type; + this.tokenChars = types[type]; + this._hasPayload = typeof types[type] !== 'string'; + if (typeof types[type] === 'number') { + this._ordinal = types[type]; } +} - //create enum - for (var t in types) { - if (types.hasOwnProperty(t)) { - TokenKind[t] = new TokenKind(t); - } +//create enum +for (var t in types) { + if (types.hasOwnProperty(t)) { + TokenKind[t] = new TokenKind(t); } +} - TokenKind.prototype.toString = function () { - return this.type + (this.tokenChars.length != 0 ? "(" + this.tokenChars + ")" : "") - }; +TokenKind.prototype.toString = function () { + return this.type + (this.tokenChars.length !== 0 ? '(' + this.tokenChars + ')' : ''); +}; - TokenKind.prototype.getLength = function () { - return this.tokenChars.length; - }; +TokenKind.prototype.getLength = function () { + return this.tokenChars.length; +}; - TokenKind.prototype.hasPayload = function () { - return this._hasPayload; - }; +TokenKind.prototype.hasPayload = function () { + return this._hasPayload; +}; - TokenKind.prototype.valueOf = function (id) { - for (var t in types) { - if (types.hasOwnProperty(t) && types[t] === id) { - return TokenKind[t]; - } +TokenKind.prototype.valueOf = function (id) { + for (var t in types) { + if (types.hasOwnProperty(t) && types[t] === id) { + return TokenKind[t]; } - }; - - TokenKind.prototype.ordinal = function () { - return this._ordinal; - }; + } +}; - exports.TokenKind = TokenKind; +TokenKind.prototype.ordinal = function () { + return this._ordinal; +}; -}(window || exports)); +export {TokenKind}; diff --git a/src/Tokenizer.js b/src/Tokenizer.js index 73e5535..6498b99 100644 --- a/src/Tokenizer.js +++ b/src/Tokenizer.js @@ -1,593 +1,580 @@ -(function (exports) { - 'use strict'; - - var ALTERNATIVE_OPERATOR_NAMES = ["DIV", "EQ", "GE", "GT", "LE", "LT", "MOD", "NE", "NOT"], - FLAGS = [], - IS_DIGIT = 1, - IS_HEXDIGIT = 2, - IS_ALPHA = 4, - - Token, - TokenKind; - - try { - Token = require('./Token').Token; - TokenKind = require('./TokenKind').TokenKind; - } catch(e) { - Token = exports.Token; - TokenKind = exports.TokenKind; - } +import {Token} from './Token'; +import {TokenKind} from './TokenKind'; - function init() { - var ch; +var ALTERNATIVE_OPERATOR_NAMES = ['DIV', 'EQ', 'GE', 'GT', 'LE', 'LT', 'MOD', 'NE', 'NOT'], + FLAGS = [], + IS_DIGIT = 1, + IS_HEXDIGIT = 2, + IS_ALPHA = 4; - for (ch = '0'.charCodeAt(0); ch <= '9'.charCodeAt(0); ch += 1) { - FLAGS[ch] |= IS_DIGIT | IS_HEXDIGIT; - } - for (ch = 'A'.charCodeAt(0); ch <= 'F'.charCodeAt(0); ch += 1) { - FLAGS[ch] |= IS_HEXDIGIT; - } - for (ch = 'a'.charCodeAt(0); ch <= 'f'.charCodeAt(0); ch += 1) { - FLAGS[ch] |= IS_HEXDIGIT; - } - for (ch = 'A'.charCodeAt(0); ch <= 'Z'.charCodeAt(0); ch += 1) { - FLAGS[ch] |= IS_ALPHA; - } - for (ch = 'a'.charCodeAt(0); ch <= 'z'.charCodeAt(0); ch += 1) { - FLAGS[ch] |= IS_ALPHA; - } +function init() { + var ch; + + for (ch = '0'.charCodeAt(0); ch <= '9'.charCodeAt(0); ch += 1) { + FLAGS[ch] |= IS_DIGIT | IS_HEXDIGIT; + } + for (ch = 'A'.charCodeAt(0); ch <= 'F'.charCodeAt(0); ch += 1) { + FLAGS[ch] |= IS_HEXDIGIT; + } + for (ch = 'a'.charCodeAt(0); ch <= 'f'.charCodeAt(0); ch += 1) { + FLAGS[ch] |= IS_HEXDIGIT; } + for (ch = 'A'.charCodeAt(0); ch <= 'Z'.charCodeAt(0); ch += 1) { + FLAGS[ch] |= IS_ALPHA; + } + for (ch = 'a'.charCodeAt(0); ch <= 'z'.charCodeAt(0); ch += 1) { + FLAGS[ch] |= IS_ALPHA; + } +} - init(); +init(); - function tokenize(inputData) { - var expressionString = inputData, - toProcess = inputData + '\0', - max = toProcess.length, - pos = 0, - tokens = []; +function tokenize(inputData) { + var expressionString = inputData, + toProcess = inputData + '\0', + max = toProcess.length, + pos = 0, + tokens = []; - function process() { - var ch; + function process() { + var ch; - while (pos < max) { - ch = toProcess[pos]; - if (isAlphabetic(ch)) { + while (pos < max) { + ch = toProcess[pos]; + if (isAlphabetic(ch)) { + lexIdentifier(); + } + else { + switch (ch) { + case '+': + if (isTwoCharToken(TokenKind.INC)) { + pushPairToken(TokenKind.INC); + } + else { + pushCharToken(TokenKind.PLUS); + } + break; + case '_': // the other way to start an identifier lexIdentifier(); - } - else { - switch (ch) { - case '+': - if (isTwoCharToken(TokenKind.INC)) { - pushPairToken(TokenKind.INC); - } - else { - pushCharToken(TokenKind.PLUS); - } - break; - case '_': // the other way to start an identifier - lexIdentifier(); - break; - case '-': - if (isTwoCharToken(TokenKind.DEC)) { - pushPairToken(TokenKind.DEC); - } - else { - pushCharToken(TokenKind.MINUS); - } - break; - case ':': - pushCharToken(TokenKind.COLON); - break; - case '.': - pushCharToken(TokenKind.DOT); - break; - case ',': - pushCharToken(TokenKind.COMMA); - break; - case '*': - pushCharToken(TokenKind.STAR); - break; - case '/': - pushCharToken(TokenKind.DIV); - break; - case '%': - pushCharToken(TokenKind.MOD); - break; - case '(': - pushCharToken(TokenKind.LPAREN); - break; - case ')': - pushCharToken(TokenKind.RPAREN); - break; - case '[': - pushCharToken(TokenKind.LSQUARE); - break; - case '#': - pushCharToken(TokenKind.HASH); - break; - case ']': - pushCharToken(TokenKind.RSQUARE); - break; - case '{': - pushCharToken(TokenKind.LCURLY); - break; - case '}': - pushCharToken(TokenKind.RCURLY); - break; - case '@': - pushCharToken(TokenKind.BEAN_REF); - break; - case '^': - if (isTwoCharToken(TokenKind.SELECT_FIRST)) { - pushPairToken(TokenKind.SELECT_FIRST); - } - else { - pushCharToken(TokenKind.POWER); - } - break; - case '!': - if (isTwoCharToken(TokenKind.NE)) { - pushPairToken(TokenKind.NE); - } - else if (isTwoCharToken(TokenKind.PROJECT)) { - pushPairToken(TokenKind.PROJECT); - } - else { - pushCharToken(TokenKind.NOT); - } - break; - case '=': - if (isTwoCharToken(TokenKind.EQ)) { - pushPairToken(TokenKind.EQ); - } - else { - pushCharToken(TokenKind.ASSIGN); - } - break; - case '&': - if (!isTwoCharToken(TokenKind.SYMBOLIC_AND)) { - throw { - name: 'SpelParseException', - message: 'Missing character \'&\' in expression (' + expressionString + ') at position ' + pos - }; - } - pushPairToken(TokenKind.SYMBOLIC_AND); - break; - case '|': - if (!isTwoCharToken(TokenKind.SYMBOLIC_OR)) { - throw { - name: 'SpelParseException', - message: 'Missing character \'|\' in expression (' + expressionString + ') at position ' + pos - }; - } - pushPairToken(TokenKind.SYMBOLIC_OR); - break; - case '?': - if (isTwoCharToken(TokenKind.SELECT)) { - pushPairToken(TokenKind.SELECT); - } - else if (isTwoCharToken(TokenKind.ELVIS)) { - pushPairToken(TokenKind.ELVIS); - } - else if (isTwoCharToken(TokenKind.SAFE_NAVI)) { - pushPairToken(TokenKind.SAFE_NAVI); - } - else { - pushCharToken(TokenKind.QMARK); - } - break; - case '$': - if (isTwoCharToken(TokenKind.SELECT_LAST)) { - pushPairToken(TokenKind.SELECT_LAST); - } - else { - lexIdentifier(); - } - break; - case '>': - if (isTwoCharToken(TokenKind.GE)) { - pushPairToken(TokenKind.GE); - } - else { - pushCharToken(TokenKind.GT); - } - break; - case '<': - if (isTwoCharToken(TokenKind.LE)) { - pushPairToken(TokenKind.LE); - } - else { - pushCharToken(TokenKind.LT); - } - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - lexNumericLiteral(ch === '0'); - break; - case ' ': - case '\t': - case '\r': - case '\n': - // drift over white space - pos += 1; - break; - case '\'': - lexQuotedStringLiteral(); - break; - case '"': - lexDoubleQuotedStringLiteral(); - break; - case '\0': - // hit sentinel at end of value - pos += 1; // will take us to the end - break; - case '\\': + break; + case '-': + if (isTwoCharToken(TokenKind.DEC)) { + pushPairToken(TokenKind.DEC); + } + else { + pushCharToken(TokenKind.MINUS); + } + break; + case ':': + pushCharToken(TokenKind.COLON); + break; + case '.': + pushCharToken(TokenKind.DOT); + break; + case ',': + pushCharToken(TokenKind.COMMA); + break; + case '*': + pushCharToken(TokenKind.STAR); + break; + case '/': + pushCharToken(TokenKind.DIV); + break; + case '%': + pushCharToken(TokenKind.MOD); + break; + case '(': + pushCharToken(TokenKind.LPAREN); + break; + case ')': + pushCharToken(TokenKind.RPAREN); + break; + case '[': + pushCharToken(TokenKind.LSQUARE); + break; + case '#': + pushCharToken(TokenKind.HASH); + break; + case ']': + pushCharToken(TokenKind.RSQUARE); + break; + case '{': + pushCharToken(TokenKind.LCURLY); + break; + case '}': + pushCharToken(TokenKind.RCURLY); + break; + case '@': + pushCharToken(TokenKind.BEAN_REF); + break; + case '^': + if (isTwoCharToken(TokenKind.SELECT_FIRST)) { + pushPairToken(TokenKind.SELECT_FIRST); + } + else { + pushCharToken(TokenKind.POWER); + } + break; + case '!': + if (isTwoCharToken(TokenKind.NE)) { + pushPairToken(TokenKind.NE); + } + else if (isTwoCharToken(TokenKind.PROJECT)) { + pushPairToken(TokenKind.PROJECT); + } + else { + pushCharToken(TokenKind.NOT); + } + break; + case '=': + if (isTwoCharToken(TokenKind.EQ)) { + pushPairToken(TokenKind.EQ); + } + else { + pushCharToken(TokenKind.ASSIGN); + } + break; + case '&': + if (!isTwoCharToken(TokenKind.SYMBOLIC_AND)) { throw { name: 'SpelParseException', - message: 'Unexpected escape character in expression (' + expressionString + ') at position ' + pos + message: 'Missing character \'&\' in expression (' + expressionString + ') at position ' + pos }; - default: + } + pushPairToken(TokenKind.SYMBOLIC_AND); + break; + case '|': + if (!isTwoCharToken(TokenKind.SYMBOLIC_OR)) { throw { name: 'SpelParseException', - message: 'Cannot handle character \'' + ch + '\' in expression (' + expressionString + ') at position ' + pos + message: 'Missing character \'|\' in expression (' + expressionString + ') at position ' + pos }; } - } - } - } - - function lexQuotedStringLiteral() { - var start = pos, - terminated = false, - ch; - - while (!terminated) { - pos += 1; - ch = toProcess[pos]; - if (ch == '\'') { - // may not be the end if the char after is also a ' - if (toProcess[pos + 1] == '\'') { - pos += 1; // skip over that too, and continue + pushPairToken(TokenKind.SYMBOLIC_OR); + break; + case '?': + if (isTwoCharToken(TokenKind.SELECT)) { + pushPairToken(TokenKind.SELECT); + } + else if (isTwoCharToken(TokenKind.ELVIS)) { + pushPairToken(TokenKind.ELVIS); + } + else if (isTwoCharToken(TokenKind.SAFE_NAVI)) { + pushPairToken(TokenKind.SAFE_NAVI); } else { - terminated = true; + pushCharToken(TokenKind.QMARK); } - } - if (ch.charCodeAt(0) === 0) { - throw { - name: 'SpelParseException', - message: 'Non-terminating quoted string in expression (' + expressionString + ') at position ' + pos - }; - } - } - pos += 1; - tokens.push(new Token(TokenKind.LITERAL_STRING, subarray(start, pos), start, pos)); - } - function lexDoubleQuotedStringLiteral() { - var start = pos, - terminated = false, - ch; - - while (!terminated) { - pos += 1; - ch = toProcess[pos]; - if (ch == '"') { - // may not be the end if the char after is also a ' - if (toProcess[pos + 1] == '"') { - pos += 1; // skip over that too, and continue + break; + case '$': + if (isTwoCharToken(TokenKind.SELECT_LAST)) { + pushPairToken(TokenKind.SELECT_LAST); } else { - terminated = true; + lexIdentifier(); } - } - if (ch.charCodeAt(0) === 0) { + break; + case '>': + if (isTwoCharToken(TokenKind.GE)) { + pushPairToken(TokenKind.GE); + } + else { + pushCharToken(TokenKind.GT); + } + break; + case '<': + if (isTwoCharToken(TokenKind.LE)) { + pushPairToken(TokenKind.LE); + } + else { + pushCharToken(TokenKind.LT); + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + lexNumericLiteral(ch === '0'); + break; + case ' ': + case '\t': + case '\r': + case '\n': + // drift over white space + pos += 1; + break; + case '\'': + lexQuotedStringLiteral(); + break; + case '"': + lexDoubleQuotedStringLiteral(); + break; + case '\0': + // hit sentinel at end of value + pos += 1; // will take us to the end + break; + case '\\': + throw { + name: 'SpelParseException', + message: 'Unexpected escape character in expression (' + expressionString + ') at position ' + pos + }; + default: throw { name: 'SpelParseException', - message: 'Non-terminating double-quoted string in expression (' + expressionString + ') at position ' + pos + message: 'Cannot handle character \'' + ch + '\' in expression (' + expressionString + ') at position ' + pos }; } } - pos += 1; - tokens.push(new Token(TokenKind.LITERAL_STRING, subarray(start, pos), start, pos)); } + } - // REAL_LITERAL : - // ('.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) | - // ((DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) | - // ((DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)?) | - // ((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX)); - // fragment INTEGER_TYPE_SUFFIX : ( 'L' | 'l' ); - // fragment HEX_DIGIT : - // '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'A'|'B'|'C'|'D'|'E'|'F'|'a'|'b'|'c'|'d'|'e'|'f'; - // - // fragment EXPONENT_PART : 'e' (SIGN)* (DECIMAL_DIGIT)+ | 'E' (SIGN)* - // (DECIMAL_DIGIT)+ ; - // fragment SIGN : '+' | '-' ; - // fragment REAL_TYPE_SUFFIX : 'F' | 'f' | 'D' | 'd'; - // INTEGER_LITERAL - // : (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)?; - - function lexNumericLiteral(firstCharIsZero) { - var isReal = false, - start = pos, - ch = toProcess[pos + 1], - isHex = ch == 'x' || ch == 'X', - dotpos, - endOfNumber, - possibleSign, - isFloat; - - // deal with hexadecimal - if (firstCharIsZero && isHex) { - pos = pos + 1; - do { - pos += 1; - } - while (isHexadecimalDigit(toProcess[pos])); - if (isChar('L', 'l')) { - pushHexIntToken(subarray(start + 2, pos), true, start, pos); - pos += 1; + function lexQuotedStringLiteral() { + var start = pos, + terminated = false, + ch; + + while (!terminated) { + pos += 1; + ch = toProcess[pos]; + if (ch === '\'') { + // may not be the end if the char after is also a ' + if (toProcess[pos + 1] === '\'') { + pos += 1; // skip over that too, and continue } else { - pushHexIntToken(subarray(start + 2, pos), false, start, pos); + terminated = true; } - return; } - - // real numbers must have leading digits - - // Consume first part of number - do { - pos += 1; + if (ch.charCodeAt(0) === 0) { + throw { + name: 'SpelParseException', + message: 'Non-terminating quoted string in expression (' + expressionString + ') at position ' + pos + }; } - while (isDigit(toProcess[pos])); + } + pos += 1; + tokens.push(new Token(TokenKind.LITERAL_STRING, subarray(start, pos), start, pos)); + } + function lexDoubleQuotedStringLiteral() { + var start = pos, + terminated = false, + ch; - // a '.' indicates this number is a real + while (!terminated) { + pos += 1; ch = toProcess[pos]; - if (ch == '.') { - isReal = true; - dotpos = pos; - // carry on consuming digits - do { - pos += 1; + if (ch === '"') { + // may not be the end if the char after is also a ' + if (toProcess[pos + 1] === '"') { + pos += 1; // skip over that too, and continue } - while (isDigit(toProcess[pos])); - if (pos === dotpos + 1) { - // the number is something like '3.'. It is really an int but may be - // part of something like '3.toString()'. In this case process it as - // an int and leave the dot as a separate token. - pos = dotpos; - pushIntToken(subarray(start, pos), false, start, pos); - return; + else { + terminated = true; } } + if (ch.charCodeAt(0) === 0) { + throw { + name: 'SpelParseException', + message: 'Non-terminating double-quoted string in expression (' + expressionString + ') at position ' + pos + }; + } + } + pos += 1; + tokens.push(new Token(TokenKind.LITERAL_STRING, subarray(start, pos), start, pos)); + } - endOfNumber = pos; - - // Now there may or may not be an exponent - - // is it a long ? - if (isChar('L', 'l')) { - if (isReal) { // 3.4L - not allowed - throw { - name: 'SpelParseException', - message: 'Real cannot be long in expression (' + expressionString + ') at position ' + pos - }; - } - pushIntToken(subarray(start, endOfNumber), true, start, endOfNumber); + // REAL_LITERAL : + // ('.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) | + // ((DECIMAL_DIGIT)+ '.' (DECIMAL_DIGIT)+ (EXPONENT_PART)? (REAL_TYPE_SUFFIX)?) | + // ((DECIMAL_DIGIT)+ (EXPONENT_PART) (REAL_TYPE_SUFFIX)?) | + // ((DECIMAL_DIGIT)+ (REAL_TYPE_SUFFIX)); + // fragment INTEGER_TYPE_SUFFIX : ( 'L' | 'l' ); + // fragment HEX_DIGIT : + // '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9'|'A'|'B'|'C'|'D'|'E'|'F'|'a'|'b'|'c'|'d'|'e'|'f'; + // + // fragment EXPONENT_PART : 'e' (SIGN)* (DECIMAL_DIGIT)+ | 'E' (SIGN)* + // (DECIMAL_DIGIT)+ ; + // fragment SIGN : '+' | '-' ; + // fragment REAL_TYPE_SUFFIX : 'F' | 'f' | 'D' | 'd'; + // INTEGER_LITERAL + // : (DECIMAL_DIGIT)+ (INTEGER_TYPE_SUFFIX)?; + + function lexNumericLiteral(firstCharIsZero) { + var isReal = false, + start = pos, + ch = toProcess[pos + 1], + isHex = ch === 'x' || ch === 'X', + dotpos, + endOfNumber, + possibleSign, + isFloat; + + // deal with hexadecimal + if (firstCharIsZero && isHex) { + pos = pos + 1; + do { pos += 1; } - else if (isExponentChar(toProcess[pos])) { - isReal = true; // if it wasn't before, it is now + while (isHexadecimalDigit(toProcess[pos])); + if (isChar('L', 'l')) { + pushHexIntToken(subarray(start + 2, pos), true, start, pos); pos += 1; - possibleSign = toProcess[pos]; - if (isSign(possibleSign)) { - pos += 1; - } - - // exponent digits - do { - pos += 1; - } - while (isDigit(toProcess[pos])); - isFloat = false; - if (isFloatSuffix(toProcess[pos])) { - isFloat = true; - pos += 1; - endOfNumber = pos; - } - else if (isDoubleSuffix(toProcess[pos])) { - pos += 1; - endOfNumber = pos; - } - pushRealToken(subarray(start, pos), isFloat, start, pos); } else { - ch = toProcess[pos]; - isFloat = false; - if (isFloatSuffix(ch)) { - isReal = true; - isFloat = true; - pos += 1; - endOfNumber = pos; - } - else if (isDoubleSuffix(ch)) { - isReal = true; - pos += 1; - endOfNumber = pos; - } - if (isReal) { - pushRealToken(subarray(start, endOfNumber), isFloat, start, endOfNumber); - } - else { - pushIntToken(subarray(start, endOfNumber), false, start, endOfNumber); - } + pushHexIntToken(subarray(start + 2, pos), false, start, pos); } + return; } - function lexIdentifier() { - var start = pos, - substring, - asString, - idx; + // real numbers must have leading digits + + // Consume first part of number + do { + pos += 1; + } + while (isDigit(toProcess[pos])); + + // a '.' indicates this number is a real + ch = toProcess[pos]; + if (ch === '.') { + isReal = true; + dotpos = pos; + // carry on consuming digits do { pos += 1; } - while (isIdentifier(toProcess[pos])); - substring = subarray(start, pos); - - // Check if this is the alternative (textual) representation of an operator (see - // alternativeOperatorNames) - if ((pos - start) === 2 || (pos - start) === 3) { - asString = substring.toUpperCase(); - idx = ALTERNATIVE_OPERATOR_NAMES.indexOf(asString); - if (idx >= 0) { - pushOneCharOrTwoCharToken(TokenKind.valueOf(asString), start, substring); - return; - } + while (isDigit(toProcess[pos])); + if (pos === dotpos + 1) { + // the number is something like '3.'. It is really an int but may be + // part of something like '3.toString()'. In this case process it as + // an int and leave the dot as a separate token. + pos = dotpos; + pushIntToken(subarray(start, pos), false, start, pos); + return; } - tokens.push(new Token(TokenKind.IDENTIFIER, substring.replace('\0', ''), start, pos)); } - function pushIntToken(data, isLong, start, end) { - if (isLong) { - tokens.push(new Token(TokenKind.LITERAL_LONG, data, start, end)); - } - else { - tokens.push(new Token(TokenKind.LITERAL_INT, data, start, end)); + endOfNumber = pos; + + // Now there may or may not be an exponent + + // is it a long ? + if (isChar('L', 'l')) { + if (isReal) { // 3.4L - not allowed + throw { + name: 'SpelParseException', + message: 'Real cannot be long in expression (' + expressionString + ') at position ' + pos + }; } + pushIntToken(subarray(start, endOfNumber), true, start, endOfNumber); + pos += 1; } + else if (isExponentChar(toProcess[pos])) { + isReal = true; // if it wasn't before, it is now + pos += 1; + possibleSign = toProcess[pos]; + if (isSign(possibleSign)) { + pos += 1; + } - function pushHexIntToken(data, isLong, start, end) { - if (data.length === 0) { - if (isLong) { - throw { - name: 'SpelParseException', - message: 'Not a long in expression (' + expressionString + ') at position ' + pos - }; - } - else { - throw { - name: 'SpelParseException', - message: 'Not an int in expression (' + expressionString + ') at position ' + pos - }; - } + // exponent digits + do { + pos += 1; } - if (isLong) { - tokens.push(new Token(TokenKind.LITERAL_HEXLONG, data, start, end)); + while (isDigit(toProcess[pos])); + isFloat = false; + if (isFloatSuffix(toProcess[pos])) { + isFloat = true; + pos += 1; + endOfNumber = pos; } - else { - tokens.push(new Token(TokenKind.LITERAL_HEXINT, data, start, end)); + else if (isDoubleSuffix(toProcess[pos])) { + pos += 1; + endOfNumber = pos; } + pushRealToken(subarray(start, pos), isFloat, start, pos); } - - function pushRealToken(data, isFloat, start, end) { - if (isFloat) { - tokens.push(new Token(TokenKind.LITERAL_REAL_FLOAT, data, start, end)); + else { + ch = toProcess[pos]; + isFloat = false; + if (isFloatSuffix(ch)) { + isReal = true; + isFloat = true; + pos += 1; + endOfNumber = pos; + } + else if (isDoubleSuffix(ch)) { + isReal = true; + pos += 1; + endOfNumber = pos; + } + if (isReal) { + pushRealToken(subarray(start, endOfNumber), isFloat, start, endOfNumber); } else { - tokens.push(new Token(TokenKind.LITERAL_REAL, data, start, end)); + pushIntToken(subarray(start, endOfNumber), false, start, endOfNumber); } } + } - function subarray(start, end) { - return toProcess.substring(start, end); + function lexIdentifier() { + var start = pos, + substring, + asString, + idx; + do { + pos += 1; } - - /** - * Check if this might be a two character token. - */ - function isTwoCharToken(kind) { - if (kind.tokenChars.length === 2 && toProcess[pos] == kind.tokenChars[0]) { - return toProcess[pos + 1] === kind.tokenChars[1]; + while (isIdentifier(toProcess[pos])); + substring = subarray(start, pos); + + // Check if this is the alternative (textual) representation of an operator (see + // alternativeOperatorNames) + if ((pos - start) === 2 || (pos - start) === 3) { + asString = substring.toUpperCase(); + idx = ALTERNATIVE_OPERATOR_NAMES.indexOf(asString); + if (idx >= 0) { + pushOneCharOrTwoCharToken(TokenKind.valueOf(asString), start, substring); + return; } - return false; } + tokens.push(new Token(TokenKind.IDENTIFIER, substring.replace('\0', ''), start, pos)); + } - /** - * Push a token of just one character in length. - */ - function pushCharToken(kind) { - tokens.push(new Token(kind, null, pos, pos + 1)); - pos += 1; + function pushIntToken(data, isLong, start, end) { + if (isLong) { + tokens.push(new Token(TokenKind.LITERAL_LONG, data, start, end)); } - - /** - * Push a token of two characters in length. - */ - function pushPairToken(kind) { - tokens.push(new Token(kind, null, pos, pos + 2)); - pos += 2; + else { + tokens.push(new Token(TokenKind.LITERAL_INT, data, start, end)); } + } - function pushOneCharOrTwoCharToken(kind, pos, data) { - tokens.push(new Token(kind, data, pos, pos + kind.getLength())); + function pushHexIntToken(data, isLong, start, end) { + if (data.length === 0) { + if (isLong) { + throw { + name: 'SpelParseException', + message: 'Not a long in expression (' + expressionString + ') at position ' + pos + }; + } + else { + throw { + name: 'SpelParseException', + message: 'Not an int in expression (' + expressionString + ') at position ' + pos + }; + } } - - // ID: ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9'|DOT_ESCAPED)*; - function isIdentifier(ch) { - return isAlphabetic(ch) || isDigit(ch) || ch === '_' || ch === '$'; + if (isLong) { + tokens.push(new Token(TokenKind.LITERAL_HEXLONG, data, start, end)); } - - function isChar(a, b) { - var ch = toProcess[pos]; - return ch === a || ch === b; + else { + tokens.push(new Token(TokenKind.LITERAL_HEXINT, data, start, end)); } + } - function isExponentChar(ch) { - return ch === 'e' || ch === 'E'; + function pushRealToken(data, isFloat, start, end) { + if (isFloat) { + tokens.push(new Token(TokenKind.LITERAL_REAL_FLOAT, data, start, end)); } - - function isFloatSuffix(ch) { - return ch === 'f' || ch === 'F'; + else { + tokens.push(new Token(TokenKind.LITERAL_REAL, data, start, end)); } + } - function isDoubleSuffix(ch) { - return ch === 'd' || ch === 'D'; - } + function subarray(start, end) { + return toProcess.substring(start, end); + } - function isSign(ch) { - return ch === '+' || ch === '-'; + /** + * Check if this might be a two character token. + */ + function isTwoCharToken(kind) { + if (kind.tokenChars.length === 2 && toProcess[pos] === kind.tokenChars[0]) { + return toProcess[pos + 1] === kind.tokenChars[1]; } + return false; + } - function isDigit(ch) { - if (ch.charCodeAt(0) > 255) { - return false; - } - return (FLAGS[ch.charCodeAt(0)] & IS_DIGIT) !== 0; - } + /** + * Push a token of just one character in length. + */ + function pushCharToken(kind) { + tokens.push(new Token(kind, null, pos, pos + 1)); + pos += 1; + } - function isAlphabetic(ch) { - if (ch.charCodeAt(0) > 255) { - return false; - } - return (FLAGS[ch.charCodeAt(0)] & IS_ALPHA) !== 0; + /** + * Push a token of two characters in length. + */ + function pushPairToken(kind) { + tokens.push(new Token(kind, null, pos, pos + 2)); + pos += 2; + } + + function pushOneCharOrTwoCharToken(kind, pos, data) { + tokens.push(new Token(kind, data, pos, pos + kind.getLength())); + } + + // ID: ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9'|DOT_ESCAPED)*; + function isIdentifier(ch) { + return isAlphabetic(ch) || isDigit(ch) || ch === '_' || ch === '$'; + } + + function isChar(a, b) { + var ch = toProcess[pos]; + return ch === a || ch === b; + } + + function isExponentChar(ch) { + return ch === 'e' || ch === 'E'; + } + + function isFloatSuffix(ch) { + return ch === 'f' || ch === 'F'; + } + + function isDoubleSuffix(ch) { + return ch === 'd' || ch === 'D'; + } + + function isSign(ch) { + return ch === '+' || ch === '-'; + } + + function isDigit(ch) { + if (ch.charCodeAt(0) > 255) { + return false; } + return (FLAGS[ch.charCodeAt(0)] & IS_DIGIT) !== 0; + } - function isHexadecimalDigit(ch) { - if (ch.charCodeAt(0) > 255) { - return false; - } - return (FLAGS[ch.charCodeAt(0)] & IS_HEXDIGIT) !== 0; + function isAlphabetic(ch) { + if (ch.charCodeAt(0) > 255) { + return false; } + return (FLAGS[ch.charCodeAt(0)] & IS_ALPHA) !== 0; + } - process(); + function isHexadecimalDigit(ch) { + if (ch.charCodeAt(0) > 255) { + return false; + } + return (FLAGS[ch.charCodeAt(0)] & IS_HEXDIGIT) !== 0; + } - return tokens; + process(); - } + return tokens; - exports.Tokenizer = { - tokenize: tokenize - }; +} -}(window || exports)); +export var Tokenizer = { + tokenize: tokenize +}; diff --git a/src/ast/Assign.js b/src/ast/Assign.js index 99c0b3e..b8bb417 100644 --- a/src/ast/Assign.js +++ b/src/ast/Assign.js @@ -1,34 +1,24 @@ -(function (exports, undefined) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, property, assignedValue) { + var node = SpelNode.create('assign', position, property, assignedValue); - function createNode(position, property, assignedValue) { - var node = SpelNode.create('assign', position, property, assignedValue); + node.getValue = function (state) { + var context = state.activeContext.peek(); - node.getValue = function (state) { - var context = state.activeContext.peek(); + if (!context) { + throw { + name: 'ContextDoesNotExistException', + message: 'Attempting to assign property \''+ property.getValue(state) +'\' for an undefined context.' + }; + } - if (!context) { - throw { - name: 'ContextDoesNotExistException', - message: 'Attempting to assign property \''+ property.getValue(state) +'\' for an undefined context.' - } - } - - return property.setValue(assignedValue.getValue(state), state); - }; - - return node; - } - - exports.Assign = { - create: createNode + return property.setValue(assignedValue.getValue(state), state); }; -}(window || exports)); + return node; +} + +export var Assign = { + create: createNode +}; diff --git a/src/ast/BooleanLiteral.js b/src/ast/BooleanLiteral.js index 91412d4..085e9d9 100644 --- a/src/ast/BooleanLiteral.js +++ b/src/ast/BooleanLiteral.js @@ -1,29 +1,21 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(value, position) { + var node = SpelNode.create('boolean', position); - function createNode(value, position) { - var node = SpelNode.create('boolean', position); - - node.getValue = function () { - return value; - }; - - node.setValue = function (newValue) { - value = newValue; - }; - - return node; - } + node.getValue = function () { + return value; + }; - exports.BooleanLiteral = { - create: createNode + node.setValue = function (newValue) { + /*jshint -W093 */ + return value = newValue; + /*jshint +W093 */ }; -}(window || exports)); + return node; +} + +export var BooleanLiteral = { + create: createNode +}; diff --git a/src/ast/CompoundExpression.js b/src/ast/CompoundExpression.js index d96fd98..cb9b292 100644 --- a/src/ast/CompoundExpression.js +++ b/src/ast/CompoundExpression.js @@ -1,76 +1,66 @@ -(function (exports) { - 'use strict'; - - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } - - function createNode(position, expressionComponents) { - var node = SpelNode.create.apply(null, ['compound', position].concat(expressionComponents)); +import {SpelNode} from './SpelNode'; - function buildContextStack(state) { - var childrenCount = node.getChildren().length, - i; +function createNode(position, expressionComponents) { + var node = SpelNode.create.apply(null, ['compound', position].concat(expressionComponents)); - for (i = 0; i < childrenCount; i += 1) { - if (node.getChildren()[i].getType() === 'indexer') { - state.activeContext.push(state.activeContext.peek()[node.getChildren()[i].getValue(state)]); - } else { - state.activeContext.push(node.getChildren()[i].getValue(state)); - } - } + function buildContextStack(state) { + var childrenCount = node.getChildren().length, + i; - return function unbuildContextStack() { - for (i = 0; i < childrenCount; i += 1) { - state.activeContext.pop(); - } + for (i = 0; i < childrenCount; i += 1) { + if (node.getChildren()[i].getType() === 'indexer') { + state.activeContext.push(state.activeContext.peek()[node.getChildren()[i].getValue(state)]); + } else { + state.activeContext.push(node.getChildren()[i].getValue(state)); } } - node.getValue = function (state) { - var context = state.activeContext.peek(), - value; - - if (!context) { - throw { - name: 'ContextDoesNotExistException', - message: 'Attempting to evaluate compound expression with an undefined context.' - }; + return function unbuildContextStack() { + for (i = 0; i < childrenCount; i += 1) { + state.activeContext.pop(); } + }; + } - var unbuildContextStack = buildContextStack(state); + node.getValue = function (state) { + var context = state.activeContext.peek(), + value; - value = state.activeContext.peek(); + if (!context) { + throw { + name: 'ContextDoesNotExistException', + message: 'Attempting to evaluate compound expression with an undefined context.' + }; + } - unbuildContextStack(); + var unbuildContextStack = buildContextStack(state); - return value; - }; + value = state.activeContext.peek(); - node.setValue = function (value, state) { - var unbuildContextStack = buildContextStack(state), - childCount = node.getChildren().length; + unbuildContextStack(); - state.activeContext.pop(); + return value; + }; - value = node.getChildren()[childCount - 1].setValue(value, state); + node.setValue = function (value, state) { + var unbuildContextStack = buildContextStack(state), + childCount = node.getChildren().length; - state.activeContext.push(null); + state.activeContext.pop(); - unbuildContextStack(); + value = node.getChildren()[childCount - 1].setValue(value, state); - return value; + state.activeContext.push(null); - }; + unbuildContextStack(); - return node; - } + return value; - exports.CompoundExpression = { - create: createNode }; -}(window || exports)); + return node; +} + +export var CompoundExpression = { + create: createNode +}; diff --git a/src/ast/Elvis.js b/src/ast/Elvis.js index f873e19..3be3444 100644 --- a/src/ast/Elvis.js +++ b/src/ast/Elvis.js @@ -1,25 +1,15 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, expression, ifFalse) { + var node = SpelNode.create('elvis', position, expression, ifFalse); - function createNode(position, expression, ifFalse) { - var node = SpelNode.create('elvis', position, expression, ifFalse); - - node.getValue = function (state) { - return expression.getValue(state) !== null ? expression.getValue(state) : ifFalse.getValue(state); - }; - - return node; - } - - exports.Elvis = { - create: createNode + node.getValue = function (state) { + return expression.getValue(state) !== null ? expression.getValue(state) : ifFalse.getValue(state); }; -}(window || exports)); + return node; +} + +export var Elvis = { + create: createNode +}; diff --git a/src/ast/FunctionReference.js b/src/ast/FunctionReference.js index 3b7ddd1..0a3efde 100644 --- a/src/ast/FunctionReference.js +++ b/src/ast/FunctionReference.js @@ -1,40 +1,30 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(parent, functionName) { + var node = SpelNode.create('method', parent); - function createNode(parent, functionName) { - var node = SpelNode.create('method', parent); - - node.getValue = function () { - var refNode = node, - context = null; - do { - if (refNode.getParent()) { - refNode = refNode.getParent(); - } else { - context = refNode.getContext(); - } - } while (refNode); - if (context[functionName]) { - return context[functionName].call(context); - } - throw { - name: 'FunctionDoesNotExistException', - message: 'Function \'' + functionName + '\' does not exist.' + node.getValue = function () { + var refNode = node, + context = null; + do { + if (refNode.getParent()) { + refNode = refNode.getParent(); + } else { + context = refNode.getContext(); } + } while (refNode); + if (context[functionName]) { + return context[functionName].call(context); + } + throw { + name: 'FunctionDoesNotExistException', + message: 'Function \'' + functionName + '\' does not exist.' }; - - return node; - } - - exports.FunctionReference = { - create: createNode }; -}(window || exports)); + return node; +} + +export var FunctionReference = { + create: createNode +}; diff --git a/src/ast/Indexer.js b/src/ast/Indexer.js index 0fdab6e..856999c 100644 --- a/src/ast/Indexer.js +++ b/src/ast/Indexer.js @@ -1,57 +1,48 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; +import {Stack} from '../lib/Stack'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, expressionComponents) { + var node = SpelNode.create.apply(null, ['indexer', position].concat(expressionComponents)); - function createNode(position, expressionComponents) { - var node = SpelNode.create.apply(null, ['indexer', position].concat(expressionComponents)); + node.getValue = function (state) { + var activeContext = state.activeContext, + context, + childrenCount = node.getChildren().length, + i, + value; - node.getValue = function (state) { - var activeContext = state.activeContext, - context, - childrenCount = node.getChildren().length, - i, - value; + state.activeContext = new Stack(); + state.activeContext.push(state.rootContext); - state.activeContext = new Stack(); - state.activeContext.push(state.rootContext); + context = state.activeContext.peek(); - context = state.activeContext.peek(); + if (!context) { + throw { + name: 'ContextDoesNotExistException', + message: 'Attempting to evaluate compound expression with an undefined context.' + }; + } - if (!context) { - throw { - name: 'ContextDoesNotExistException', - message: 'Attempting to evaluate compound expression with an undefined context.' - }; - } + for (i = 0; i < childrenCount; i += 1) { + state.activeContext.push(node.getChildren()[i].getValue(state)); + } - for (i = 0; i < childrenCount; i += 1) { - state.activeContext.push(node.getChildren()[i].getValue(state)); - } + value = state.activeContext.peek(); - value = state.activeContext.peek(); + for (i = 0; i < childrenCount; i += 1) { + state.activeContext.pop(); + } - for (i = 0; i < childrenCount; i += 1) { - state.activeContext.pop(); - } + state.activeContext = activeContext; - state.activeContext = activeContext; - - return value; - }; - - //node.setContext(node.getValue()); + return value; + }; - return node; - } + //node.setContext(node.getValue()); - exports.Indexer = { - create: createNode - }; + return node; +} -}(window || exports)); +export var Indexer = { + create: createNode +}; diff --git a/src/ast/InlineList.js b/src/ast/InlineList.js index 3b74c0f..f15ef9f 100644 --- a/src/ast/InlineList.js +++ b/src/ast/InlineList.js @@ -1,28 +1,17 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; +function createNode(position, elements) { + var node = SpelNode.create('list', position), + list = [].concat(elements || []); - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } - - function createNode(position, elements) { - var node = SpelNode.create('list', position), - list = [].concat(elements || []); - - node.getValue = function (state) { - return list.map(function (element) { - return element.getValue(state); - }); - }; - - return node; - } - - exports.InlineList = { - create: createNode + node.getValue = function (state) { + return list.map(function (element) { + return element.getValue(state); + }); }; -}(window || exports)); + return node; +} + +export var InlineList = { + create: createNode +}; diff --git a/src/ast/InlineMap.js b/src/ast/InlineMap.js index 3187910..77f604c 100644 --- a/src/ast/InlineMap.js +++ b/src/ast/InlineMap.js @@ -1,44 +1,34 @@ -(function (exports) { - 'use strict'; - - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } - - function createNode(position, elements) { - var node = SpelNode.create('map', position), - mapPieces = [].concat(elements || []); - - node.getValue = function (state) { - var key = true, - keyValue = null, - map = {}; - - mapPieces.forEach(function (piece) { - if (key) { - //unquoted property names come as type "property" but should be treated as strings - if (piece.getType() === 'property') { - keyValue = piece.getName(); - } else { - keyValue = piece.getValue(state); - } +import {SpelNode} from './SpelNode'; + +function createNode(position, elements) { + var node = SpelNode.create('map', position), + mapPieces = [].concat(elements || []); + + node.getValue = function (state) { + var key = true, + keyValue = null, + map = {}; + + mapPieces.forEach(function (piece) { + if (key) { + //unquoted property names come as type "property" but should be treated as strings + if (piece.getType() === 'property') { + keyValue = piece.getName(); } else { - map[keyValue] = piece.getValue(state); + keyValue = piece.getValue(state); } - key = !key; - }); + } else { + map[keyValue] = piece.getValue(state); + } + key = !key; + }); - return map; - }; - - return node; - } - - exports.InlineMap = { - create: createNode + return map; }; -}(window || exports)); + return node; +} + +export var InlineMap = { + create: createNode +}; diff --git a/src/ast/MethodReference.js b/src/ast/MethodReference.js index 2fb5dd3..5b61489 100644 --- a/src/ast/MethodReference.js +++ b/src/ast/MethodReference.js @@ -1,74 +1,66 @@ -(function (exports, undefined) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(nullSafeNavigation, methodName, position, args) { + var node = SpelNode.create('method', position); - function createNode(nullSafeNavigation, methodName, position, args) { - var node = SpelNode.create('method', position); + node.getValue = function (state) { + var context = state.activeContext.peek(), + compiledArgs = [], + method; - node.getValue = function (state) { - var context = state.activeContext.peek(), - compiledArgs = [], - method; + if (!context) { + throw { + name: 'ContextDoesNotExistException', + message: 'Attempting to look up property \''+ methodName +'\' for an undefined context.' + }; + } + + //handle safe navigation + function maybeHandleNullSafeNavigation(member) { + if (member === undefined) { + if (nullSafeNavigation) { + return null; + } - if (!context) { throw { - name: 'ContextDoesNotExistException', - message: 'Attempting to look up property \''+ methodName +'\' for an undefined context.' + name: 'NullPointerException', + message: 'Method ' + methodName + ' does not exist.' }; } - //handle safe navigation - function maybeHandleNullSafeNavigation(member) { - if (member === undefined) { - if (nullSafeNavigation) { - return null; - } + return member; + } - throw { - name: 'NullPointerException', - message: 'Method ' + methodName + ' does not exist.' - }; - } + //populate arguments + args.forEach(function (arg) { + compiledArgs.push(arg.getValue(state)); + }); - return member; - } + //accessors might not be available + if (methodName.substr(0, 3) === 'get' && !context[methodName]) { + return maybeHandleNullSafeNavigation(context[methodName.charAt(3).toLowerCase() + methodName.substring(4)]); + } + if (methodName.substr(0, 3) === 'set' && !context[methodName]) { + /*jshint -W093 */ + return context[methodName.charAt(3).toLowerCase() + methodName.substring(4)] = compiledArgs[0]; + /*jshint +W093 */ + } - //populate arguments - args.forEach(function (arg) { - compiledArgs.push(arg.getValue(state)); - }); - - //accessors might not be available - if (methodName.substr(0, 3) === 'get' && !context[methodName]) { - return maybeHandleNullSafeNavigation(context[methodName.charAt(3).toLowerCase() + methodName.substring(4)]); - } - if (methodName.substr(0, 3) === 'set' && !context[methodName]) { - return context[methodName.charAt(3).toLowerCase() + methodName.substring(4)] = compiledArgs[0]; - } + //size() -> length + if (methodName === 'size' && Array.isArray(context)) { + return context.length; + } - //size() -> length - if (methodName === 'size' && Array.isArray(context)) { - return context.length; - } - - method = maybeHandleNullSafeNavigation(context[methodName]); - if (method) { - return method.apply(context, compiledArgs); - } - return null; - }; - - return node; - } - - exports.MethodReference = { - create: createNode + method = maybeHandleNullSafeNavigation(context[methodName]); + if (method) { + return method.apply(context, compiledArgs); + } + return null; }; -}(window || exports)); + return node; +} + +export var MethodReference = { + create: createNode +}; diff --git a/src/ast/NullLiteral.js b/src/ast/NullLiteral.js index 4ad5d10..c721873 100644 --- a/src/ast/NullLiteral.js +++ b/src/ast/NullLiteral.js @@ -1,25 +1,14 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; +function createNode(value, position) { + var node = SpelNode.create('null', position); - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } - - function createNode(value, position) { - var node = SpelNode.create('null', position); - - node.getValue = function () { - return null; - }; - - return node; - } - - exports.NullLiteral = { - create: createNode + node.getValue = function () { + return null; }; -}(window || exports)); + return node; +} + +export var NullLiteral = { + create: createNode +}; diff --git a/src/ast/NumberLiteral.js b/src/ast/NumberLiteral.js index faa6a7f..399eb22 100644 --- a/src/ast/NumberLiteral.js +++ b/src/ast/NumberLiteral.js @@ -1,29 +1,21 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(value, position) { + var node = SpelNode.create('number', position); - function createNode(value, position) { - var node = SpelNode.create('number', position); - - node.getValue = function () { - return value; - }; - - node.setValue = function (newValue) { - value = newValue; - }; - - return node; - } + node.getValue = function () { + return value; + }; - exports.NumberLiteral = { - create: createNode + node.setValue = function (newValue) { + /*jshint -W093 */ + return value = newValue; + /*jshint +W093 */ }; -}(window || exports)); + return node; +} + +export var NumberLiteral = { + create: createNode +}; diff --git a/src/ast/OpAnd.js b/src/ast/OpAnd.js index 4e5ae5f..a3e33bf 100644 --- a/src/ast/OpAnd.js +++ b/src/ast/OpAnd.js @@ -1,26 +1,16 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, left, right) { + var node = SpelNode.create('op-and', position, left, right); - function createNode(position, left, right) { - var node = SpelNode.create('op-and', position, left, right); - - node.getValue = function (state) { - //double bang for javascript - return !!left.getValue(state) && !!right.getValue(state); - }; - - return node; - } - - exports.OpAnd = { - create: createNode + node.getValue = function (state) { + //double bang for javascript + return !!left.getValue(state) && !!right.getValue(state); }; -}(window || exports)); + return node; +} + +export var OpAnd = { + create: createNode +}; diff --git a/src/ast/OpDec.js b/src/ast/OpDec.js index 3cfa80f..82e681e 100644 --- a/src/ast/OpDec.js +++ b/src/ast/OpDec.js @@ -1,30 +1,20 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, postfix, int) { + var node = SpelNode.create('op-dec', position, int); - function createNode(position, postfix, int) { - var node = SpelNode.create('op-dec', position, int); - - node.getValue = function (state) { - var cur = int.getValue(state); - int.setValue(cur - 1, state); - if (postfix) { - return cur; - } - return cur - 1; - }; - - return node; - } - - exports.OpDec = { - create: createNode + node.getValue = function (state) { + var cur = int.getValue(state); + int.setValue(cur - 1, state); + if (postfix) { + return cur; + } + return cur - 1; }; -}(window || exports)); + return node; +} + +export var OpDec = { + create: createNode +}; diff --git a/src/ast/OpDivide.js b/src/ast/OpDivide.js index dd6b494..6d66d23 100644 --- a/src/ast/OpDivide.js +++ b/src/ast/OpDivide.js @@ -1,25 +1,15 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, left, right) { + var node = SpelNode.create('op-divide', position, left, right); - function createNode(position, left, right) { - var node = SpelNode.create('op-divide', position, left, right); - - node.getValue = function (state) { - return left.getValue(state) / right.getValue(state); - }; - - return node; - } - - exports.OpDivide = { - create: createNode + node.getValue = function (state) { + return left.getValue(state) / right.getValue(state); }; -}(window || exports)); + return node; +} + +export var OpDivide = { + create: createNode +}; diff --git a/src/ast/OpEQ.js b/src/ast/OpEQ.js index c689f09..ab836fa 100644 --- a/src/ast/OpEQ.js +++ b/src/ast/OpEQ.js @@ -1,25 +1,15 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, left, right) { + var node = SpelNode.create('op-eq', position, left, right); - function createNode(position, left, right) { - var node = SpelNode.create('op-eq', position, left, right); - - node.getValue = function (state) { - return left.getValue(state) === right.getValue(state); - }; - - return node; - } - - exports.OpEQ = { - create: createNode + node.getValue = function (state) { + return left.getValue(state) === right.getValue(state); }; -}(window || exports)); + return node; +} + +export var OpEQ = { + create: createNode +}; diff --git a/src/ast/OpGE.js b/src/ast/OpGE.js index 29519a0..284f49e 100644 --- a/src/ast/OpGE.js +++ b/src/ast/OpGE.js @@ -1,25 +1,15 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, left, right) { + var node = SpelNode.create('op-ge', position, left, right); - function createNode(position, left, right) { - var node = SpelNode.create('op-ge', position, left, right); - - node.getValue = function (state) { - return left.getValue(state) >= right.getValue(state); - }; - - return node; - } - - exports.OpGE = { - create: createNode + node.getValue = function (state) { + return left.getValue(state) >= right.getValue(state); }; -}(window || exports)); + return node; +} + +export var OpGE = { + create: createNode +}; diff --git a/src/ast/OpGT.js b/src/ast/OpGT.js index e2dfbb9..5788cde 100644 --- a/src/ast/OpGT.js +++ b/src/ast/OpGT.js @@ -1,25 +1,15 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, left, right) { + var node = SpelNode.create('op-gt', position, left, right); - function createNode(position, left, right) { - var node = SpelNode.create('op-gt', position, left, right); - - node.getValue = function (state) { - return left.getValue(state) > right.getValue(state); - }; - - return node; - } - - exports.OpGT = { - create: createNode + node.getValue = function (state) { + return left.getValue(state) > right.getValue(state); }; -}(window || exports)); + return node; +} + +export var OpGT = { + create: createNode +}; diff --git a/src/ast/OpInc.js b/src/ast/OpInc.js index 3836d3e..69df4a1 100644 --- a/src/ast/OpInc.js +++ b/src/ast/OpInc.js @@ -1,30 +1,20 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, postfix, int) { + var node = SpelNode.create('op-inc', position, int); - function createNode(position, postfix, int) { - var node = SpelNode.create('op-inc', position, int); - - node.getValue = function (state) { - var cur = int.getValue(state); - int.setValue(cur + 1, state); - if (postfix) { - return cur; - } - return cur + 1; - }; - - return node; - } - - exports.OpInc = { - create: createNode + node.getValue = function (state) { + var cur = int.getValue(state); + int.setValue(cur + 1, state); + if (postfix) { + return cur; + } + return cur + 1; }; -}(window || exports)); + return node; +} + +export var OpInc = { + create: createNode +}; diff --git a/src/ast/OpLE.js b/src/ast/OpLE.js index 4931eb6..11ccc6f 100644 --- a/src/ast/OpLE.js +++ b/src/ast/OpLE.js @@ -1,25 +1,15 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, left, right) { + var node = SpelNode.create('op-le', position, left, right); - function createNode(position, left, right) { - var node = SpelNode.create('op-le', position, left, right); - - node.getValue = function (state) { - return left.getValue(state) <= right.getValue(state); - }; - - return node; - } - - exports.OpLE = { - create: createNode + node.getValue = function (state) { + return left.getValue(state) <= right.getValue(state); }; -}(window || exports)); + return node; +} + +export var OpLE = { + create: createNode +}; diff --git a/src/ast/OpLT.js b/src/ast/OpLT.js index b6a8f24..0dd8b09 100644 --- a/src/ast/OpLT.js +++ b/src/ast/OpLT.js @@ -1,25 +1,15 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, left, right) { + var node = SpelNode.create('op-lt', position, left, right); - function createNode(position, left, right) { - var node = SpelNode.create('op-lt', position, left, right); - - node.getValue = function (state) { - return left.getValue(state) < right.getValue(state); - }; - - return node; - } - - exports.OpLT = { - create: createNode + node.getValue = function (state) { + return left.getValue(state) < right.getValue(state); }; -}(window || exports)); + return node; +} + +export var OpLT = { + create: createNode +}; diff --git a/src/ast/OpMinus.js b/src/ast/OpMinus.js index 5dff7d9..432f67f 100644 --- a/src/ast/OpMinus.js +++ b/src/ast/OpMinus.js @@ -1,25 +1,15 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, left, right) { + var node = SpelNode.create('op-minus', position, left, right); - function createNode(position, left, right) { - var node = SpelNode.create('op-minus', position, left, right); - - node.getValue = function (state) { - return left.getValue(state) - right.getValue(state); - }; - - return node; - } - - exports.OpMinus = { - create: createNode + node.getValue = function (state) { + return left.getValue(state) - right.getValue(state); }; -}(window || exports)); + return node; +} + +export var OpMinus = { + create: createNode +}; diff --git a/src/ast/OpModulus.js b/src/ast/OpModulus.js index db37d10..90645b4 100644 --- a/src/ast/OpModulus.js +++ b/src/ast/OpModulus.js @@ -1,25 +1,15 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, left, right) { + var node = SpelNode.create('op-modulus', position, left, right); - function createNode(position, left, right) { - var node = SpelNode.create('op-modulus', position, left, right); - - node.getValue = function (state) { - return left.getValue(state) % right.getValue(state); - }; - - return node; - } - - exports.OpModulus = { - create: createNode + node.getValue = function (state) { + return left.getValue(state) % right.getValue(state); }; -}(window || exports)); + return node; +} + +export var OpModulus = { + create: createNode +}; diff --git a/src/ast/OpMultiply.js b/src/ast/OpMultiply.js index 1f5162a..a2a8be4 100644 --- a/src/ast/OpMultiply.js +++ b/src/ast/OpMultiply.js @@ -1,42 +1,32 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, left, right) { + var node = SpelNode.create('op-multiply', position, left, right); - function createNode(position, left, right) { - var node = SpelNode.create('op-multiply', position, left, right); + node.getValue = function (state) { + var leftValue = left.getValue(state), + rightValue = right.getValue(state); - node.getValue = function (state) { - var leftValue = left.getValue(state), - rightValue = right.getValue(state); + if (typeof leftValue === 'number' && typeof rightValue === 'number') { + return leftValue * rightValue; + } - if (typeof leftValue === 'number' && typeof rightValue === 'number') { - return leftValue * rightValue; + //repeats (ex. 'abc' * 2 = 'abcabc') + if (typeof leftValue === 'string' && typeof rightValue === 'number') { + var s = '', + i = 0; + for (; i < rightValue; i += 1) { + s += leftValue; } + return s; + } - //repeats (ex. 'abc' * 2 = 'abcabc') - if (typeof leftValue === 'string' && typeof rightValue === 'number') { - var s = '', - i = 0; - for (; i < rightValue; i += 1) { - s += leftValue; - } - return s; - } - - return null; - }; - - return node; - } - - exports.OpMultiply = { - create: createNode + return null; }; -}(window || exports)); + return node; +} + +export var OpMultiply = { + create: createNode +}; diff --git a/src/ast/OpNE.js b/src/ast/OpNE.js index cb9f943..b2c8794 100644 --- a/src/ast/OpNE.js +++ b/src/ast/OpNE.js @@ -1,25 +1,14 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; +function createNode(position, left, right) { + var node = SpelNode.create('op-ne', position, left, right); - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } - - function createNode(position, left, right) { - var node = SpelNode.create('op-ne', position, left, right); - - node.getValue = function (state) { - return left.getValue(state) !== right.getValue(state); - }; - - return node; - } - - exports.OpNE = { - create: createNode + node.getValue = function (state) { + return left.getValue(state) !== right.getValue(state); }; -}(window || exports)); + return node; +} + +export var OpNE = { + create: createNode +}; diff --git a/src/ast/OpNot.js b/src/ast/OpNot.js index fda3293..5bc991e 100644 --- a/src/ast/OpNot.js +++ b/src/ast/OpNot.js @@ -1,25 +1,15 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, expr) { + var node = SpelNode.create('op-not', position, expr); - function createNode(position, expr) { - var node = SpelNode.create('op-not', position, expr); - - node.getValue = function (state) { - return !expr.getValue(state); - }; - - return node; - } - - exports.OpNot = { - create: createNode + node.getValue = function (state) { + return !expr.getValue(state); }; -}(window || exports)); + return node; +} + +export var OpNot = { + create: createNode +}; diff --git a/src/ast/OpOr.js b/src/ast/OpOr.js index 20d8673..0e7900e 100644 --- a/src/ast/OpOr.js +++ b/src/ast/OpOr.js @@ -1,26 +1,16 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, left, right) { + var node = SpelNode.create('op-or', position, left, right); - function createNode(position, left, right) { - var node = SpelNode.create('op-or', position, left, right); - - node.getValue = function (state) { - //double bang for javascript - return !!left.getValue(state) || !!right.getValue(state); - }; - - return node; - } - - exports.OpOr = { - create: createNode + node.getValue = function (state) { + //double bang for javascript + return !!left.getValue(state) || !!right.getValue(state); }; -}(window || exports)); + return node; +} + +export var OpOr = { + create: createNode +}; diff --git a/src/ast/OpPlus.js b/src/ast/OpPlus.js index 9665bf6..ef395c6 100644 --- a/src/ast/OpPlus.js +++ b/src/ast/OpPlus.js @@ -1,26 +1,16 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, left, right) { + var node = SpelNode.create('op-plus', position, left, right); - function createNode(position, left, right) { - var node = SpelNode.create('op-plus', position, left, right); - - node.getValue = function (state) { - //javascript will handle string concatenation or addition depending on types - return left.getValue(state) + right.getValue(state); - }; - - return node; - } - - exports.OpPlus = { - create: createNode + node.getValue = function (state) { + //javascript will handle string concatenation or addition depending on types + return left.getValue(state) + right.getValue(state); }; -}(window || exports)); + return node; +} + +export var OpPlus = { + create: createNode +}; diff --git a/src/ast/OpPower.js b/src/ast/OpPower.js index 6cd84b1..aeb9fa7 100644 --- a/src/ast/OpPower.js +++ b/src/ast/OpPower.js @@ -1,25 +1,15 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, base, exp) { + var node = SpelNode.create('op-power', position, base, exp); - function createNode(position, base, exp) { - var node = SpelNode.create('op-power', position, base, exp); - - node.getValue = function (state) { - return Math.pow(base.getValue(state), exp.getValue(state)); - }; - - return node; - } - - exports.OpPower = { - create: createNode + node.getValue = function (state) { + return Math.pow(base.getValue(state), exp.getValue(state)); }; -}(window || exports)); + return node; +} + +export var OpPower = { + create: createNode +}; diff --git a/src/ast/Projection.js b/src/ast/Projection.js index f5f730f..8f295ca 100644 --- a/src/ast/Projection.js +++ b/src/ast/Projection.js @@ -1,51 +1,41 @@ -(function (exports) { - 'use strict'; - - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } - - function projectCollection(collection, expr, state) { - return collection.map(function (element) { - var matches; - state.activeContext.push(element); - matches = expr.getValue(state); - state.activeContext.pop(); - return matches; - }); - } - - function createNode(nullSafeNavigation, position, expr) { - var node = SpelNode.create('projection', position, expr); - - node.getValue = function (state) { - var collection = state.activeContext.peek(), - entries = [], - key; - - if (Array.isArray(collection)) { - return projectCollection(collection, expr, state); - } - else if (typeof collection === 'object') { - for (key in collection) { - if (collection.hasOwnProperty(key)) { - entries.push(collection[key]); - } +import {SpelNode} from './SpelNode'; + +function projectCollection(collection, expr, state) { + return collection.map(function (element) { + var matches; + state.activeContext.push(element); + matches = expr.getValue(state); + state.activeContext.pop(); + return matches; + }); +} + +function createNode(nullSafeNavigation, position, expr) { + var node = SpelNode.create('projection', position, expr); + + node.getValue = function (state) { + var collection = state.activeContext.peek(), + entries = [], + key; + + if (Array.isArray(collection)) { + return projectCollection(collection, expr, state); + } + else if (typeof collection === 'object') { + for (key in collection) { + if (collection.hasOwnProperty(key)) { + entries.push(collection[key]); } - return projectCollection(entries, expr, state); } + return projectCollection(entries, expr, state); + } - return null; - }; - - return node; - } - - exports.Projection = { - create: createNode + return null; }; -}(window || exports)); + return node; +} + +export var Projection = { + create: createNode +}; diff --git a/src/ast/PropertyReference.js b/src/ast/PropertyReference.js index c8f3115..4dfa9a7 100644 --- a/src/ast/PropertyReference.js +++ b/src/ast/PropertyReference.js @@ -1,68 +1,60 @@ -(function (exports, undefined) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(nullSafeNavigation, propertyName, position) { + var node = SpelNode.create('property', position); - function createNode(nullSafeNavigation, propertyName, position) { - var node = SpelNode.create('property', position); + node.getValue = function (state) { + var context = state.activeContext.peek(); - node.getValue = function (state) { - var context = state.activeContext.peek(); + if (!context) { + throw { + name: 'ContextDoesNotExistException', + message: 'Attempting to look up property \''+ propertyName +'\' for an undefined context.' + }; + } - if (!context) { - throw { - name: 'ContextDoesNotExistException', - message: 'Attempting to look up property \''+ propertyName +'\' for an undefined context.' - } + if (context[propertyName] === undefined) { + //handle safe navigation + if (nullSafeNavigation) { + return null; } - if (context[propertyName] === undefined) { - //handle safe navigation - if (nullSafeNavigation) { - return null; - } - - //handle conversion of Java properties to JavaScript properties - if (propertyName === 'size' && Array.isArray(context)) { - return context.length; - } - - throw { - name: 'NullPointerException', - message: 'Property \'' + propertyName + '\' does not exist.' - }; + //handle conversion of Java properties to JavaScript properties + if (propertyName === 'size' && Array.isArray(context)) { + return context.length; } - return context[propertyName]; - }; + throw { + name: 'NullPointerException', + message: 'Property \'' + propertyName + '\' does not exist.' + }; + } - node.setValue = function (value, state) { - var context = state.activeContext.peek(); - - if (!context) { - throw { - name: 'ContextDoesNotExistException', - message: 'Attempting to assign property \''+ propertyName +'\' for an undefined context.' - } - } + return context[propertyName]; + }; - return context[propertyName] = value; - }; + node.setValue = function (value, state) { + var context = state.activeContext.peek(); - node.getName = function () { - return propertyName; - }; + if (!context) { + throw { + name: 'ContextDoesNotExistException', + message: 'Attempting to assign property \''+ propertyName +'\' for an undefined context.' + }; + } - return node; - } + /*jshint -W093 */ + return context[propertyName] = value; + /*jshint +W093 */ + }; - exports.PropertyReference = { - create: createNode + node.getName = function () { + return propertyName; }; -}(window || exports)); + return node; +} + +export var PropertyReference = { + create: createNode +}; diff --git a/src/ast/RootNode.js b/src/ast/RootNode.js index 64b0c46..f9c61bb 100644 --- a/src/ast/RootNode.js +++ b/src/ast/RootNode.js @@ -1,28 +1,18 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(context) { + var node = SpelNode.create('root', null, context); - function createNode(context) { - var node = SpelNode.create('root', null, context); - - node.getValue = function () { - if (node.getChildren()[0]) { - return node.getChildren()[0].getValue(); - } - return null; - }; - - return node; - } - - exports.RootNode = { - create: createNode + node.getValue = function () { + if (node.getChildren()[0]) { + return node.getChildren()[0].getValue(); + } + return null; }; -}(window || exports)); + return node; +} + +export var RootNode = { + create: createNode +}; diff --git a/src/ast/Selection.js b/src/ast/Selection.js index e6d470e..29a5b65 100644 --- a/src/ast/Selection.js +++ b/src/ast/Selection.js @@ -1,115 +1,99 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function matches(element, expr, state) { + var doesMatch = false; + state.activeContext.push(element); + doesMatch = expr.getValue(state); + state.activeContext.pop(); + return doesMatch; +} - function matches(element, expr, state) { - var matches = false; - state.activeContext.push(element); - matches = expr.getValue(state); - state.activeContext.pop(); - return matches; - } +function selectFromArray(collection, whichElement, expr, state) { + var newCollection = collection.filter(function (element) { + return matches(element, expr, state); + }); - function selectFromArray(collection, whichElement, expr, state) { - var newCollection = collection.filter(function (element) { - return matches(element, expr, state); - }); - - switch (whichElement) { - case 'ALL': - return newCollection; - break; - case 'FIRST': - return newCollection[0] || null; - break; - case 'LAST': - if (newCollection.length) { - return newCollection[newCollection.length - 1]; - break; - } - return null; + switch (whichElement) { + case 'ALL': + return newCollection; + case 'FIRST': + return newCollection[0] || null; + case 'LAST': + if (newCollection.length) { + return newCollection[newCollection.length - 1]; } + return null; } +} - function selectFromMap(collection, whichElement, expr, state) { - var newCollection = {}, - entry, - key, - entries = [], - returnValue = {}; +function selectFromMap(collection, whichElement, expr, state) { + var newCollection = {}, + entry, + key, + entries = [], + returnValue = {}; - for (key in collection) { - if (collection.hasOwnProperty(key)) { - entry = { - key: key, - value: collection[key] - }; - if (matches(entry, expr, state)) { - entries.push(entry); - } + for (key in collection) { + if (collection.hasOwnProperty(key)) { + entry = { + key: key, + value: collection[key] + }; + if (matches(entry, expr, state)) { + entries.push(entry); } } + } - switch (whichElement) { - case 'ALL': - entries.forEach(function (entry) { - newCollection[entry.key] = entry.value; - }); - return newCollection; - break; - case 'FIRST': - if (entries.length) { - returnValue[entries[0].key] = entries[0].value; - return returnValue; - } - return null; - break; - case 'LAST': - if (entries.length) { - returnValue[entries[entries.length - 1].key] = entries[entries.length - 1].value; - return returnValue; - } - return null; - break; - } - + switch (whichElement) { + case 'ALL': entries.forEach(function (entry) { newCollection[entry.key] = entry.value; }); + return newCollection; + case 'FIRST': + if (entries.length) { + returnValue[entries[0].key] = entries[0].value; + return returnValue; + } + return null; + case 'LAST': + if (entries.length) { + returnValue[entries[entries.length - 1].key] = entries[entries.length - 1].value; + return returnValue; + } + return null; } - function createNode(nullSafeNavigation, whichElement, position, expr) { - var node = SpelNode.create('selection', position, expr); + entries.forEach(function (entry) { + newCollection[entry.key] = entry.value; + }); +} - node.getValue = function (state) { - var collection = state.activeContext.peek(); +function createNode(nullSafeNavigation, whichElement, position, expr) { + var node = SpelNode.create('selection', position, expr); - if (collection) { - if (Array.isArray(collection)) { - return selectFromArray(collection, whichElement, expr, state); - } - else if (typeof collection === 'object') { - return selectFromMap(collection, whichElement, expr, state); - } - } + node.getValue = function (state) { + var collection = state.activeContext.peek(); - return null; - }; - - return node; - } + if (collection) { + if (Array.isArray(collection)) { + return selectFromArray(collection, whichElement, expr, state); + } + else if (typeof collection === 'object') { + return selectFromMap(collection, whichElement, expr, state); + } + } - exports.Selection = { - create: createNode, - FIRST: 'FIRST', - LAST: 'LAST', - ALL: 'ALL' + return null; }; -}(window || exports)); + return node; +} + +export var Selection = { + create: createNode, + FIRST: 'FIRST', + LAST: 'LAST', + ALL: 'ALL' +}; diff --git a/src/ast/SpelNode.js b/src/ast/SpelNode.js index 9dcd3bd..40eb8b6 100644 --- a/src/ast/SpelNode.js +++ b/src/ast/SpelNode.js @@ -1,93 +1,87 @@ -(function (exports) { - 'use strict'; +function createSpelNode(nodeType, position, ...operands) { + var node = {}, + type = nodeType || 'Abstract', + children = [], + parent = null, + activeContext; - function createSpelNode(nodeType, position /*, operands */) { - var node = {}, - type = nodeType || 'Abstract', - children = [], - parent = null, - args = Array.prototype.slice.call(arguments), - operands = args.length > 2 ? args.slice(2) : null, - activeContext; + node._type = type; - node._type = type; + node.getType = function () { + return type; + }; + node.setType = function (nodeType) { + type = nodeType; + }; - node.getType = function () { - return type; - }; - node.setType = function (nodeType) { - type = nodeType; - }; + node.getChildren = function () { + return children; + }; + node.addChild = function (childNode) { + childNode.setParent(node); + children.push(childNode); + }; - node.getChildren = function () { - return children; - }; - node.addChild = function (childNode) { - childNode.setParent(node); - children.push(childNode); - }; + node.getParent = function () { + return parent; + }; + node.setParent = function (parentNode) { + parent = parentNode; + }; - node.getParent = function () { - return parent; - }; - node.setParent = function (parentNode) { - parent = parentNode; - }; + node.getContext = function (state) { + return activeContext || state.activeContext.peek(); + }; + node.setContext = function (nodeContext) { + activeContext = nodeContext; + }; - node.getContext = function (state) { - return activeContext || state.activeContext.peek(); - }; - node.setContext = function (nodeContext) { - activeContext = nodeContext; - }; + node.getStartPosition = function () { + return (position >> 16); + }; - node.getStartPosition = function () { - return (position >> 16); - }; + node.getEndPosition = function () { + return (position & 0xffff); + }; - node.getEndPosition = function () { - return (position & 0xffff); + //must override + node.getValue = function () { + throw { + name: 'MethodNotImplementedException', + message: 'SpelNode#getValue() must be overridden.' }; + }; - //must override - node.getValue = function () { - throw { - name: 'MethodNotImplementedException', - message: 'SpelNode#getValue() must be overridden.' - } - }; + node.toString = function () { + var s = 'Kind: ' + node.getType(); + //s += ', Value: ' + node.getValue(); + s += ', Children: ['; + for (var i = 0, l = node.getChildren().length; i < l; i += 1) { + s += '{' + node.getChildren()[i] + '}, '; + } + s += ']'; + return s; + }; - node.toString = function () { - var s = 'Kind: ' + node.getType(); - //s += ', Value: ' + node.getValue(); - s += ', Children: ['; - for (var i = 0, l = node.getChildren().length; i < l; i += 1) { - s += '{' + node.getChildren()[i] + '}, '; - } - s += ']'; - return s; + //constructor + if (position === 0) { + throw { + name: 'Error', + message: 'Position cannot be 0' }; + } - //constructor - if (position === 0) { - throw { - name: 'Error', - message: 'Position cannot be 0' - }; - } - - if (operands) { - operands.forEach(function (operand) { - node.addChild(operand); - }); - } + if (operands) { + operands.forEach(function (operand) { + node.addChild(operand); + }); + } - return node; - } + return node; +} - exports.SpelNode = { - create: createSpelNode - }; +export var SpelNode = { + create: createSpelNode +}; -}(window || exports)); diff --git a/src/ast/StringLiteral.js b/src/ast/StringLiteral.js index 1347b6d..eb3a06f 100644 --- a/src/ast/StringLiteral.js +++ b/src/ast/StringLiteral.js @@ -1,40 +1,32 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } - - function createNode(value, position) { - var node = SpelNode.create('string', position); +function createNode(value, position) { + var node = SpelNode.create('string', position); - function stripQuotes(value) { - if ((value[0] === '\'' && value[value.length - 1] === '\'') || - (value[0] === '"' && value[value.length - 1] === '"')) { - return value.substring(1, value.length - 1); - } - return value; + function stripQuotes(value) { + if ((value[0] === '\'' && value[value.length - 1] === '\'') || + (value[0] === '"' && value[value.length - 1] === '"')) { + return value.substring(1, value.length - 1); } + return value; + } - //value cannot be null so no check - value = stripQuotes(value); - - node.getValue = function () { - return value; - }; - - node.setValue = function (newValue) { - value = newValue; - }; + //value cannot be null so no check + value = stripQuotes(value); - return node; - } + node.getValue = function () { + return value; + }; - exports.StringLiteral = { - create: createNode + node.setValue = function (newValue) { + /*jshint -W093 */ + return value = newValue; + /*jshint +W093 */ }; -}(window || exports)); + return node; +} + +export var StringLiteral = { + create: createNode +}; diff --git a/src/ast/Ternary.js b/src/ast/Ternary.js index 7d21e2b..ad8722c 100644 --- a/src/ast/Ternary.js +++ b/src/ast/Ternary.js @@ -1,25 +1,15 @@ -(function (exports) { - 'use strict'; +import {SpelNode} from './SpelNode'; - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } +function createNode(position, expression, ifTrue, ifFalse) { + var node = SpelNode.create('ternary', position, expression, ifTrue, ifFalse); - function createNode(position, expression, ifTrue, ifFalse) { - var node = SpelNode.create('ternary', position, expression, ifTrue, ifFalse); - - node.getValue = function (state) { - return expression.getValue(state) ? ifTrue.getValue(state) : ifFalse.getValue(state); - }; - - return node; - } - - exports.Ternary = { - create: createNode + node.getValue = function (state) { + return expression.getValue(state) ? ifTrue.getValue(state) : ifFalse.getValue(state); }; -}(window || exports)); + return node; +} + +export var Ternary = { + create: createNode +}; diff --git a/src/ast/VariableReference.js b/src/ast/VariableReference.js index 4932b95..904fdb2 100644 --- a/src/ast/VariableReference.js +++ b/src/ast/VariableReference.js @@ -1,49 +1,41 @@ -(function (exports) { - 'use strict'; - - var SpelNode; - try { - SpelNode = require('./SpelNode').SpelNode; - } catch (e) { - SpelNode = exports.SpelNode; - } - - function createNode(variableName, position) { - var node = SpelNode.create('variable', position); - - node.getValue = function (state) { - var context = state.activeContext.peek(), - locals = state.locals; - - if (!context) { - throw { - name: 'ContextDoesNotExistException', - message: 'Attempting to look up variable \''+ variableName +'\' for an undefined context.' - } - } - - //there are 2 keywords (root, this) that need to be dealt with - if (variableName === 'this') { - return context; - } - if (variableName === 'root') { - return state.rootContext; - } - - return locals[variableName]; - }; - - node.setValue = function (value, state) { - var locals = state.locals; - - return locals[variableName] = value; - }; - - return node; - } - - exports.VariableReference = { - create: createNode +import {SpelNode} from './SpelNode'; + +function createNode(variableName, position) { + var node = SpelNode.create('variable', position); + + node.getValue = function (state) { + var context = state.activeContext.peek(), + locals = state.locals; + + if (!context) { + throw { + name: 'ContextDoesNotExistException', + message: 'Attempting to look up variable \''+ variableName +'\' for an undefined context.' + }; + } + + //there are 2 keywords (root, this) that need to be dealt with + if (variableName === 'this') { + return context; + } + if (variableName === 'root') { + return state.rootContext; + } + + return locals[variableName]; }; -}(window || exports)); + node.setValue = function (value, state) { + var locals = state.locals; + + /*jshint -W093 */ + return locals[variableName] = value; + /*jshint +W093 */ + }; + + return node; +} + +export var VariableReference = { + create: createNode +}; diff --git a/src/lib/Stack.js b/src/lib/Stack.js index 7642cb8..2ecd482 100644 --- a/src/lib/Stack.js +++ b/src/lib/Stack.js @@ -1,4 +1,4 @@ -function Stack(startingElements) { +export function Stack(startingElements) { this.elements = startingElements || []; } diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..ff1b813 --- /dev/null +++ b/src/main.js @@ -0,0 +1,4 @@ +import {SpelExpressionEvaluator} from './SpelExpressionEvaluator'; +import {StandardContext} from './StandardContext'; + +export {SpelExpressionEvaluator, StandardContext}; diff --git a/test/karma.conf.js b/test/karma.conf.js index 85c40d5..569ee52 100644 --- a/test/karma.conf.js +++ b/test/karma.conf.js @@ -27,7 +27,8 @@ module.exports = function (config) { Test pre-processors */ preprocessors: { - 'spel2js.js': ['coverage'] + 'src/**/*.js': ['browserify'], + 'test/**/*spec.js': ['browserify'] }, /* @@ -44,6 +45,11 @@ module.exports = function (config) { }] }, + browserify: { + debug: true, + transform: [ 'babelify' ] + }, + /* Locally installed browsers Chrome, ChromeCanary, PhantomJS, Firefox, Opera, IE, Safari, iOS etc. @@ -81,22 +87,10 @@ module.exports = function (config) { Test framework to use: jasmine, mocha, qunit etc. */ - frameworks: ['jasmine'], + frameworks: ['browserify', 'jasmine'], files: [ - 'src/lib/**/*.js', - - 'src/TokenKind.js', - 'src/Token.js', - 'src/Tokenizer.js', - - 'src/ast/SpelNode.js', - 'src/ast/*.js', - - 'src/SpelExpressionParser.js', - 'src/SpelExpressionEvaluator.js', - '<%= config.app %>/StandardContext.js', - + 'src/**/*.js', 'test/spec/**/*.spec.js' ] }); diff --git a/test/spec/SpelExpressionEvaluator.spec.js b/test/spec/SpelExpressionEvaluator.spec.js index 02367ff..454e50d 100644 --- a/test/spec/SpelExpressionEvaluator.spec.js +++ b/test/spec/SpelExpressionEvaluator.spec.js @@ -1,28 +1,16 @@ -/** - * @file - * - * ### Responsibilities - * - unit test spel2js.js - * - * Scaffolded with generator-microjs v0.1.2 - * - * @author <> - */ -'use strict'; - -/*global spel2js*/ -describe('spel expression evaluator', function () { - var evaluator = window.SpelExpressionEvaluator; - - beforeEach(function () { +import {SpelExpressionEvaluator as evaluator} from '../../src/SpelExpressionEvaluator.js'; + +describe('spel expression evaluator', ()=>{ + + beforeEach(()=>{ // add spies }); - describe('compile', function () { + describe('compile', ()=>{ - it('should compile an expression an return an evaluator', function () { + it('should compile an expression an return an evaluator', ()=>{ //when - var compiledExpression = evaluator.compile('1234'); + let compiledExpression = evaluator.compile('1234'); //then expect(compiledExpression.eval).toBeDefined(); @@ -31,34 +19,34 @@ describe('spel expression evaluator', function () { }); - describe('parse', function () { + describe('parse', ()=>{ - describe('primitives', function () { + describe('primitives', ()=>{ - it('should evaluate a number', function () { + it('should evaluate a number', ()=>{ //when - var numberInt = evaluator.eval('123'); - var numberFloat = evaluator.eval('123.4'); + let numberInt = evaluator.eval('123'); + let numberFloat = evaluator.eval('123.4'); //then expect(numberInt).toBe(123); expect(numberFloat).toBe(123.4); }); - it('should evaluate a string', function () { + it('should evaluate a string', ()=>{ //when - var stringSingle = evaluator.eval('\'hello world!\''); - var stringDouble = evaluator.eval('"hello world!"'); + let stringSingle = evaluator.eval('\'hello world!\''); + let stringDouble = evaluator.eval('"hello world!"'); //then expect(stringSingle).toBe('hello world!'); expect(stringDouble).toBe('hello world!'); }); - it('should evaluate a boolean', function () { + it('should evaluate a boolean', ()=>{ //when - var boolTrue = evaluator.eval('true'); - var boolFalse = evaluator.eval('false'); + let boolTrue = evaluator.eval('true'); + let boolFalse = evaluator.eval('false'); //then expect(boolTrue).toBe(true); @@ -67,11 +55,11 @@ describe('spel expression evaluator', function () { }); - describe('lookups', function () { + describe('lookups', ()=>{ - var context; + let context; - beforeEach(function () { + beforeEach(()=>{ context = { iAmANumber: 1, iAmANestedPropertyName: 'propLookup', @@ -86,58 +74,58 @@ describe('spel expression evaluator', function () { }; }); - it('should look up a primitive in the context', function () { + it('should look up a primitive in the context', ()=>{ //when - var number = evaluator.eval('iAmANumber', context); + let number = evaluator.eval('iAmANumber', context); //then expect(number).toBe(1); }); - it('should look up a nested primitive in the context using dot notation', function () { + it('should look up a nested primitive in the context using dot notation', ()=>{ //when - var string = evaluator.eval('nested.iAmAString', context); + let string = evaluator.eval('nested.iAmAString', context); //then expect(string).toBe('hi'); }); - it('should look up a doubly nested primitive in the context using dot notation', function () { + it('should look up a doubly nested primitive in the context using dot notation', ()=>{ //when - var bool = evaluator.eval('nested.reallyNested.iAmTrue', context); + let bool = evaluator.eval('nested.reallyNested.iAmTrue', context); //then expect(bool).toBe(true); }); - it('should look up a nested primitive in the context using bracket notation literal', function () { + it('should look up a nested primitive in the context using bracket notation literal', ()=>{ //when - var string = evaluator.eval('nested["iAmAString"]', context); + let string = evaluator.eval('nested["iAmAString"]', context); //then expect(string).toBe('hi'); }); - it('should look up a nested primitive in the context using bracket notation', function () { + it('should look up a nested primitive in the context using bracket notation', ()=>{ //when - var string = evaluator.eval('nested[iAmANestedPropertyName]', context); + let string = evaluator.eval('nested[iAmANestedPropertyName]', context); //then expect(string).toBe('Found!'); }); - it('should look up a really nested primitive in the context using bracket notation', function () { + it('should look up a really nested primitive in the context using bracket notation', ()=>{ //when - var string = evaluator.eval('nested.reallyNested[nested.iAmAString]', context); + let string = evaluator.eval('nested.reallyNested[nested.iAmAString]', context); //then expect(string).toBe('bye'); }); - it('should return null instead of throw error when using safe navigation', function () { + it('should return null instead of throw error when using safe navigation', ()=>{ //when - var willThrow = function () {evaluator.eval('nested.doesNotExist');} - var willBeNull = evaluator.eval('nested?.doesNotExist', context); + let willThrow = ()=>{evaluator.eval('nested.doesNotExist');}; + let willBeNull = evaluator.eval('nested?.doesNotExist', context); //then expect(willThrow).toThrow(); @@ -146,57 +134,57 @@ describe('spel expression evaluator', function () { }); - describe('comparisons', function () { + describe('comparisons', ()=>{ - it('should evaluate an equality', function () { + it('should evaluate an equality', ()=>{ //when - var comp1 = evaluator.eval('1 == 1'); - var comp2 = evaluator.eval('1 == 2'); + let comp1 = evaluator.eval('1 == 1'); + let comp2 = evaluator.eval('1 == 2'); //then expect(comp1).toBe(true); expect(comp2).toBe(false); }); - it('should evaluate an equality with lookups', function () { + it('should evaluate an equality with lookups', ()=>{ //given - var context = { + let context = { left: 1, right: 1 }; //when - var comp = evaluator.eval('left == right', context); + let comp = evaluator.eval('left == right', context); //then expect(comp).toBe(true); }); - it('should evaluate an inequality (not equal)', function () { + it('should evaluate an inequality (not equal)', ()=>{ //when - var comp1 = evaluator.eval('1 != 2'); - var comp2 = evaluator.eval('1 != 1'); + let comp1 = evaluator.eval('1 != 2'); + let comp2 = evaluator.eval('1 != 1'); //then expect(comp1).toBe(true); expect(comp2).toBe(false); }); - it('should evaluate an inequality (greater than)', function () { + it('should evaluate an inequality (greater than)', ()=>{ //when - var comp1 = evaluator.eval('2 > 1'); - var comp2 = evaluator.eval('1 > 1'); + let comp1 = evaluator.eval('2 > 1'); + let comp2 = evaluator.eval('1 > 1'); //then expect(comp1).toBe(true); expect(comp2).toBe(false); }); - it('should evaluate an inequality (greater than or equal to)', function () { + it('should evaluate an inequality (greater than or equal to)', ()=>{ //when - var comp1 = evaluator.eval('1 >= 1'); - var comp2 = evaluator.eval('2 >= 1'); - var comp3 = evaluator.eval('1 >= 2'); + let comp1 = evaluator.eval('1 >= 1'); + let comp2 = evaluator.eval('2 >= 1'); + let comp3 = evaluator.eval('1 >= 2'); //then expect(comp1).toBe(true); @@ -204,21 +192,21 @@ describe('spel expression evaluator', function () { expect(comp3).toBe(false); }); - it('should evaluate an inequality (less than)', function () { + it('should evaluate an inequality (less than)', ()=>{ //when - var comp1 = evaluator.eval('1 < 2'); - var comp2 = evaluator.eval('1 < 1'); + let comp1 = evaluator.eval('1 < 2'); + let comp2 = evaluator.eval('1 < 1'); //then expect(comp1).toBe(true); expect(comp2).toBe(false); }); - it('should evaluate an inequality (less than or equal to)', function () { + it('should evaluate an inequality (less than or equal to)', ()=>{ //when - var comp1 = evaluator.eval('1 <= 2'); - var comp2 = evaluator.eval('1 <= 2'); - var comp3 = evaluator.eval('2 <= 1'); + let comp1 = evaluator.eval('1 <= 2'); + let comp2 = evaluator.eval('1 <= 2'); + let comp3 = evaluator.eval('2 <= 1'); //then expect(comp1).toBe(true); @@ -226,9 +214,9 @@ describe('spel expression evaluator', function () { expect(comp3).toBe(false); }); - it('should evaluate a complex inequality', function () { + it('should evaluate a complex inequality', ()=>{ //when - var comp = evaluator.eval('"abc".length <= "abcde".length'); + let comp = evaluator.eval('"abc".length <= "abcde".length'); //then expect(comp).toBe(true); @@ -237,48 +225,48 @@ describe('spel expression evaluator', function () { }); - describe('method invocation', function () { + describe('method invocation', ()=>{ - var context = { - funky: function () { + let context = { + funky: ()=>{ return 'fresh'; }, - argumentative: function (arg) { + argumentative: (arg)=>{ return arg; }, name: 'ben' }; - it('should look up and invoke a function', function () { + it('should look up and invoke a function', ()=>{ //when - var ret = evaluator.eval('funky()', context); + let ret = evaluator.eval('funky()', context); //then expect(ret).toBe('fresh'); }); - it('should look up and invoke a function with arguments', function () { + it('should look up and invoke a function with arguments', ()=>{ //when - var ret = evaluator.eval('argumentative("i disagree!")', context); + let ret = evaluator.eval('argumentative("i disagree!")', context); //then expect(ret).toBe('i disagree!'); }); - it('should use a property if getter not available', function () { + it('should use a property if getter not available', ()=>{ //when - var ret = evaluator.eval('getName()', context); + let ret = evaluator.eval('getName()', context); //then expect(ret).toBe('ben'); }); - it('should set a property if setter not available', function () { + it('should set a property if setter not available', ()=>{ //given evaluator.eval('setName("steve")', context); //when - var ret = evaluator.eval('getName()', context); + let ret = evaluator.eval('getName()', context); //then expect(ret).toBe('steve'); @@ -288,11 +276,11 @@ describe('spel expression evaluator', function () { }); - describe('locals', function () { + describe('locals', ()=>{ - it('should refer to a local variable', function () { + it('should refer to a local variable', ()=>{ //given - var context = { + let context = { myString: 'global context' }, locals = { @@ -300,15 +288,15 @@ describe('spel expression evaluator', function () { }; //when - var local = evaluator.eval('#myString == "hello world!"', context, locals); + let local = evaluator.eval('#myString == "hello world!"', context, locals); //then expect(local).toBe(true); }); - it('should refer to the root context', function () { + it('should refer to the root context', ()=>{ //given - var context = { + let context = { myString: 'global context' }, locals = { @@ -316,15 +304,15 @@ describe('spel expression evaluator', function () { }; //when - var root = evaluator.eval('#root', context, locals); + let root = evaluator.eval('#root', context, locals); //then expect(root).toBe(context); }); - it('should refer the "this" context', function () { + it('should refer the "this" context', ()=>{ //given - var context = { + let context = { myString: 'global context' }, locals = { @@ -332,7 +320,7 @@ describe('spel expression evaluator', function () { }; //when - var that = evaluator.eval('#this', context, locals); + let that = evaluator.eval('#this', context, locals); //then expect(that).toBe(context); @@ -341,68 +329,68 @@ describe('spel expression evaluator', function () { }); - describe('math', function () { + describe('math', ()=>{ - it('should add 2 numbers', function () { + it('should add 2 numbers', ()=>{ //when - var sum = evaluator.eval('1 + 1'); + let sum = evaluator.eval('1 + 1'); //then expect(sum).toBe(2); }); - it('should add 3 numbers', function () { + it('should add 3 numbers', ()=>{ //when - var sum = evaluator.eval('1 + 1 + 1'); + let sum = evaluator.eval('1 + 1 + 1'); //then expect(sum).toBe(3); }); - it('should subtract 2 numbers', function () { + it('should subtract 2 numbers', ()=>{ //when - var difference = evaluator.eval('1 + 1'); + let difference = evaluator.eval('1 + 1'); //then expect(difference).toBe(2); }); - it('should multiply 2 numbers', function () { + it('should multiply 2 numbers', ()=>{ //when - var product = evaluator.eval('1 + 1'); + let product = evaluator.eval('1 + 1'); //then expect(product).toBe(2); }); - it('should divide 2 numbers', function () { + it('should divide 2 numbers', ()=>{ //when - var quotient = evaluator.eval('1 + 1'); + let quotient = evaluator.eval('1 + 1'); //then expect(quotient).toBe(2); }); - it('should find the modulus of 2 numbers', function () { + it('should find the modulus of 2 numbers', ()=>{ //when - var mod = evaluator.eval('10 % 8'); + let mod = evaluator.eval('10 % 8'); //then expect(mod).toBe(2); }); - it('should evaluate an exponent', function () { + it('should evaluate an exponent', ()=>{ //when - var mod = evaluator.eval('10^2'); + let mod = evaluator.eval('10^2'); //then expect(mod).toBe(100); }); - it('should honor standard order of operations', function () { + it('should honor standard order of operations', ()=>{ //when - var math = evaluator.eval('8 + 4 * 6 - 2 * 3 / 2'); //8+(4*6)-(2*3/2) = 29 + let math = evaluator.eval('8 + 4 * 6 - 2 * 3 / 2'); //8+(4*6)-(2*3/2) = 29 //then expect(math).toBe(29); @@ -411,28 +399,28 @@ describe('spel expression evaluator', function () { }); - describe('ternary', function () { + describe('ternary', ()=>{ - it('should return first argument if true', function () { + it('should return first argument if true', ()=>{ //when - var tern = evaluator.eval('true ? "yes" : "no"'); + let tern = evaluator.eval('true ? "yes" : "no"'); //then expect(tern).toBe('yes'); }); - it('should return second argument if false', function () { + it('should return second argument if false', ()=>{ //when - var tern = evaluator.eval('false ? "yes" : "no"'); + let tern = evaluator.eval('false ? "yes" : "no"'); //then expect(tern).toBe('no'); }); - it('should return expression if truthy, or ifFalseExpression if null ', function () { + it('should return expression if truthy, or ifFalseExpression if null ', ()=>{ //when - var elvisTruthy = evaluator.eval('"Thank you." ?: "Thank you very much."'); - var elvisFalsy = evaluator.eval('null ?: "Thank you very much."'); + let elvisTruthy = evaluator.eval('"Thank you." ?: "Thank you very much."'); + let elvisFalsy = evaluator.eval('null ?: "Thank you very much."'); //then expect(elvisTruthy).toBe('Thank you.'); @@ -442,15 +430,15 @@ describe('spel expression evaluator', function () { }); - describe('assignment', function () { + describe('assignment', ()=>{ - it('should assign a value to the proper context with the specified property name', function () { + it('should assign a value to the proper context with the specified property name', ()=>{ //given - var context = { + let context = { name: 'Nikola Tesla', heritage: 'Serbian' }; - var locals = { + let locals = { newName: 'Mike Tesla' }; @@ -461,14 +449,14 @@ describe('spel expression evaluator', function () { expect(context.name).toBe('Mike Tesla'); }); - it('should assign to a nested context', function () { + it('should assign to a nested context', ()=>{ //given - var context = { + let context = { nested: { name: 'Nikola Tesla' } }; - var locals = { + let locals = { newName: 'Mike Tesla' }; @@ -482,19 +470,19 @@ describe('spel expression evaluator', function () { }); - describe('complex literals', function () { + describe('complex literals', ()=>{ - it('should create an array', function () { + it('should create an array', ()=>{ //when - var arr = evaluator.eval('{1, 2, 3, 4}'); + let arr = evaluator.eval('{1, 2, 3, 4}'); //then expect(arr).toEqual([1, 2, 3, 4]); }); - it('should create a map', function () { + it('should create a map', ()=>{ //when - var map = evaluator.eval("{name:'Nikola',dob:'10-July-1856'}"); + let map = evaluator.eval('{name:"Nikola",dob:"10-July-1856"}'); //then expect(map).toEqual({name: 'Nikola', dob: '10-July-1856'}); @@ -502,63 +490,63 @@ describe('spel expression evaluator', function () { }); - describe('unary', function () { + describe('unary', ()=>{ - it('should increment an integer but return original value', function () { + it('should increment an integer but return original value', ()=>{ //given - var parsed = evaluator.compile('123++'); + let parsed = evaluator.compile('123++'); //when - var inc1 = parsed.eval(); - var inc2 = parsed.eval(); + let inc1 = parsed.eval(); + let inc2 = parsed.eval(); //then expect(inc1).toBe(123); expect(inc2).toBe(124); }); - it('should decrement an integer but return original value', function () { + it('should decrement an integer but return original value', ()=>{ //given - var parsed = evaluator.compile('123--'); + let parsed = evaluator.compile('123--'); //when - var dec1 = parsed.eval(); - var dec2 = parsed.eval(); + let dec1 = parsed.eval(); + let dec2 = parsed.eval(); //then expect(dec1).toBe(123); expect(dec2).toBe(122); }); - it('should increment an integer and return new value', function () { + it('should increment an integer and return new value', ()=>{ //given - var parsed = evaluator.compile('++123'); + let parsed = evaluator.compile('++123'); //when - var inc1 = parsed.eval(); - var inc2 = parsed.eval(); + let inc1 = parsed.eval(); + let inc2 = parsed.eval(); //then expect(inc1).toBe(124); expect(inc2).toBe(125); }); - it('should decrement an integer and return new value', function () { + it('should decrement an integer and return new value', ()=>{ //given - var parsed = evaluator.compile('--123'); + let parsed = evaluator.compile('--123'); //when - var dec1 = parsed.eval(); - var dec2 = parsed.eval(); + let dec1 = parsed.eval(); + let dec2 = parsed.eval(); //then expect(dec1).toBe(122); expect(dec2).toBe(121); }); - it('should increment a property on the context', function () { + it('should increment a property on the context', ()=>{ //given - var context = { + let context = { int: 123 }; @@ -569,12 +557,12 @@ describe('spel expression evaluator', function () { expect(context.int).toBe(124); }); - it('should increment a local variable', function () { + it('should increment a local variable', ()=>{ //given - var context = { + let context = { int: 123 }; - var locals = { + let locals = { int: 321 }; @@ -585,9 +573,9 @@ describe('spel expression evaluator', function () { expect(locals.int).toBe(322); }); - it('should invert a boolean', function () { + it('should invert a boolean', ()=>{ //when - var bool = evaluator.eval('!true'); + let bool = evaluator.eval('!true'); //then expect(bool).toBe(false); @@ -596,14 +584,14 @@ describe('spel expression evaluator', function () { }); - describe('logical operators', function () { + describe('logical operators', ()=>{ - it('should evaluate "and" expressions', function () { + it('should evaluate "and" expressions', ()=>{ //when - var and1 = evaluator.eval('true && true'); - var and2 = evaluator.eval('true && false'); - var and3 = evaluator.eval('false && true'); - var and4 = evaluator.eval('false && false'); + let and1 = evaluator.eval('true && true'); + let and2 = evaluator.eval('true && false'); + let and3 = evaluator.eval('false && true'); + let and4 = evaluator.eval('false && false'); //then expect(and1).toBe(true); @@ -612,12 +600,12 @@ describe('spel expression evaluator', function () { expect(and4).toBe(false); }); - it('should evaluate "and" expressions', function () { + it('should evaluate "and" expressions', ()=>{ //when - var or1 = evaluator.eval('true || true'); - var or2 = evaluator.eval('true || false'); - var or3 = evaluator.eval('false || true'); - var or4 = evaluator.eval('false || false'); + let or1 = evaluator.eval('true || true'); + let or2 = evaluator.eval('true || false'); + let or3 = evaluator.eval('false || true'); + let or4 = evaluator.eval('false || false'); //then expect(or1).toBe(true); @@ -629,24 +617,24 @@ describe('spel expression evaluator', function () { }); - describe('selection/projection', function () { + describe('selection/projection', ()=>{ - it('should return a new list based on selection expression', function () { + it('should return a new list based on selection expression', ()=>{ //given - var context = { + let context = { collection: [1, 2, 3, 4, 5, 6] }; //when - var newCollection = evaluator.eval('collection.?[#this <= 3]', context); + let newCollection = evaluator.eval('collection.?[#this <= 3]', context); //then expect(newCollection).toEqual([1, 2, 3]); }); - it('should return a new map based on selection expression', function () { + it('should return a new map based on selection expression', ()=>{ //given - var context = { + let context = { collection: { a: 1, b: 2, @@ -657,17 +645,17 @@ describe('spel expression evaluator', function () { }; //when - var newCollection1 = evaluator.eval('collection.?[value <= 3]', context); - var newCollection2 = evaluator.eval('collection.?[key == "a"]', context); + let newCollection1 = evaluator.eval('collection.?[value <= 3]', context); + let newCollection2 = evaluator.eval('collection.?[key == "a"]', context); //then expect(newCollection1).toEqual({a: 1, b: 2, c: 3}); expect(newCollection2).toEqual({a: 1}); }); - it('should return the first element of list or map', function () { + it('should return the first element of list or map', ()=>{ //given - var context = { + let context = { list: [1, 2, 3, 4, 5, 6], map: { a: 1, @@ -679,17 +667,17 @@ describe('spel expression evaluator', function () { }; //when - var listFirst = evaluator.eval('list.^[#this <= 3]', context); - var mapFirst = evaluator.eval('map.^[value <= 3]', context); + let listFirst = evaluator.eval('list.^[#this <= 3]', context); + let mapFirst = evaluator.eval('map.^[value <= 3]', context); //then expect(listFirst).toEqual(1); expect(mapFirst).toEqual({a: 1}); }); - it('should return the last element of list or map', function () { + it('should return the last element of list or map', ()=>{ //given - var context = { + let context = { list: [1, 2, 3, 4, 5, 6], map: { a: 1, @@ -701,17 +689,17 @@ describe('spel expression evaluator', function () { }; //when - var listFirst = evaluator.eval('list.$[#this <= 3]', context); - var mapFirst = evaluator.eval('map.$[value <= 3]', context); + let listFirst = evaluator.eval('list.$[#this <= 3]', context); + let mapFirst = evaluator.eval('map.$[value <= 3]', context); //then expect(listFirst).toEqual(3); expect(mapFirst).toEqual({c: 3}); }); - it('should return a list of projected values from a list of objects', function () { + it('should return a list of projected values from a list of objects', ()=>{ //given - var context = { + let context = { list: [ { name: 'Ben' @@ -726,15 +714,15 @@ describe('spel expression evaluator', function () { }; //when - var names = evaluator.eval('list.![name]', context); + let names = evaluator.eval('list.![name]', context); //then expect(names).toEqual(['Ben', 'Kris', 'Ansy']); }); - it('should return a list of entries from a map (not quite like in Java because key must be a string)', function () { + it('should return a list of entries from a map (not quite like in Java because key must be a string)', ()=>{ //given - var context = { + let context = { map: { ben: { hometown: 'Newton' @@ -749,7 +737,7 @@ describe('spel expression evaluator', function () { }; //when - var hometowns = evaluator.eval('map.![hometown]', context); + let hometowns = evaluator.eval('map.![hometown]', context); //then expect(hometowns).toEqual(['Newton', 'Peabody', 'Brockton']); diff --git a/test/spec/Tokenizer.spec.js b/test/spec/Tokenizer.spec.js index 6f6f2e1..01709db 100644 --- a/test/spec/Tokenizer.spec.js +++ b/test/spec/Tokenizer.spec.js @@ -1,27 +1,16 @@ -/** - * @file - * - * ### Responsibilities - * - unit test spel2js.js - * - * Scaffolded with generator-microjs v0.1.2 - * - * @author <> - */ -'use strict'; - -/*global spel2js*/ -describe('tokenizer', function () { - var tokenize = window.Tokenizer.tokenize, - TokenKind = window.TokenKind; - - beforeEach(function () { +import {Tokenizer} from '../../src/Tokenizer'; +import {TokenKind} from '../../src/TokenKind'; + +describe('tokenizer', ()=>{ + let tokenize = Tokenizer.tokenize; + + beforeEach(()=>{ // add spies }); - it('should return an array of one Int token', function () { + it('should return an array of one Int token', ()=>{ //when - var tokens = tokenize('123'); + let tokens = tokenize('123'); //then expect(tokens).toBeDefined(); @@ -30,9 +19,9 @@ describe('tokenizer', function () { expect(tokens[0].stringValue()).toBe('123'); }); - it('should return an array of one long token', function () { + it('should return an array of one long token', ()=>{ //when - var tokens = tokenize('123l'); + let tokens = tokenize('123l'); //then expect(tokens).toBeDefined(); @@ -41,9 +30,9 @@ describe('tokenizer', function () { expect(tokens[0].stringValue()).toBe('123'); }); - it('should return an array of one hex int token', function () { + it('should return an array of one hex int token', ()=>{ //when - var tokens = tokenize('0xFF'); + let tokens = tokenize('0xFF'); //then expect(tokens).toBeDefined(); @@ -52,9 +41,9 @@ describe('tokenizer', function () { expect(tokens[0].stringValue()).toBe('FF'); }); - it('should return an array of one hex long token', function () { + it('should return an array of one hex long token', ()=>{ //when - var tokens = tokenize('0xFFl'); + let tokens = tokenize('0xFFl'); //then expect(tokens).toBeDefined(); @@ -63,9 +52,9 @@ describe('tokenizer', function () { expect(tokens[0].stringValue()).toBe('FF'); }); - it('should return an array of one string token', function () { + it('should return an array of one string token', ()=>{ //when - var tokens = tokenize('\'hello world!\''); + let tokens = tokenize('\'hello world!\''); //then expect(tokens).toBeDefined(); @@ -74,9 +63,9 @@ describe('tokenizer', function () { expect(tokens[0].stringValue()).toBe('\'hello world!\''); }); - it('should return an array of one real token', function () { + it('should return an array of one real token', ()=>{ //when - var tokens = tokenize('123.4'); + let tokens = tokenize('123.4'); //then expect(tokens).toBeDefined(); @@ -85,9 +74,9 @@ describe('tokenizer', function () { expect(tokens[0].stringValue()).toBe('123.4'); }); - it('should return an array of one real float token', function () { + it('should return an array of one real float token', ()=>{ //when - var tokens = tokenize('123.4f'); + let tokens = tokenize('123.4f'); //then expect(tokens).toBeDefined(); @@ -98,9 +87,9 @@ describe('tokenizer', function () { //more complex expressions - it('should return an array of 4 tokens when given a function with 1 arg', function () { + it('should return an array of 4 tokens when given a function with 1 arg', ()=>{ //when - var tokens = tokenize('hasPermission(\'DISCUSSION_POST\')'); + let tokens = tokenize('hasPermission(\'DISCUSSION_POST\')'); //then expect(tokens).toBeDefined(); @@ -111,9 +100,9 @@ describe('tokenizer', function () { expect(tokens[3].getKind()).toBe(TokenKind.RPAREN); }); - it('should return an array of 11 tokens when given comparison of principal to arg property', function () { + it('should return an array of 11 tokens when given comparison of principal to arg property', ()=>{ //when - var tokens = tokenize('principal.email == #comment.user[\'email\']'); + let tokens = tokenize('principal.email == #comment.user[\'email\']'); //then expect(tokens).toBeDefined(); @@ -131,9 +120,9 @@ describe('tokenizer', function () { expect(tokens[10].getKind()).toBe(TokenKind.RSQUARE); }); - it('should tokenize unary operators', function () { + it('should tokenize unary operators', ()=>{ //when - var tokens = tokenize('+ - * / ^ % ++ -- ! ='); + let tokens = tokenize('+ - * / ^ % ++ -- ! ='); //then expect(tokens).toBeDefined(); @@ -150,9 +139,9 @@ describe('tokenizer', function () { expect(tokens[9].getKind()).toBe(TokenKind.ASSIGN); }); - it('should tokenize comparison operators', function () { + it('should tokenize comparison operators', ()=>{ //when - var tokens = tokenize('>= > <= < == != && ||'); + let tokens = tokenize('>= > <= < == != && ||'); //then expect(tokens).toBeDefined(); @@ -168,9 +157,9 @@ describe('tokenizer', function () { }); //this test fails. The tokenizer from Spring does not support these - /*it('should tokenize keywords', function () { + /*it('should tokenize keywords', ()=>{ //when - var tokens = tokenize('instanceOf matches between'); + let tokens = tokenize('instanceOf matches between'); //then expect(tokens).toBeDefined(); @@ -180,9 +169,9 @@ describe('tokenizer', function () { expect(tokens[2].getKind()).toBe(TokenKind.BETWEEN); });*/ - it('should tokenize regex tokens', function () { + it('should tokenize regex tokens', ()=>{ //when - var tokens = tokenize('^[ $[ ? ![ ?['); + let tokens = tokenize('^[ $[ ? ![ ?['); //then expect(tokens).toBeDefined(); @@ -194,9 +183,9 @@ describe('tokenizer', function () { expect(tokens[4].getKind()).toBe(TokenKind.SELECT); }); - it('should tokenize miscellaneous tokens', function () { + it('should tokenize miscellaneous tokens', ()=>{ //when - var tokens = tokenize(', : { } ?: ?. @'); + let tokens = tokenize(', : { } ?: ?. @'); //then expect(tokens).toBeDefined(); @@ -210,59 +199,59 @@ describe('tokenizer', function () { expect(tokens[6].getKind()).toBe(TokenKind.BEAN_REF); }); - it('should throw exception if using bitwise operators', function () { + it('should throw exception if using bitwise operators', ()=>{ //given - function shouldThrow1() { + let shouldThrow1 = ()=>{ tokenize('|'); - } - function shouldThrow2() { + }; + let shouldThrow2 = ()=>{ tokenize('&'); - } + }; //then expect(shouldThrow1).toThrow(); expect(shouldThrow2).toThrow(); }); - it('should throw exception if escape character is used', function () { + it('should throw exception if escape character is used', ()=>{ //given - function shouldThrow() { + let shouldThrow = ()=>{ tokenize('\\hello'); - } + }; //then expect(shouldThrow).toThrow(); }); - it('should throw exception if unsupported character is used', function () { + it('should throw exception if unsupported character is used', ()=>{ //given - function shouldThrow() { + let shouldThrow = ()=>{ tokenize('ΒΆ'); - } + }; //then expect(shouldThrow).toThrow(); }); - it('should throw exception if string literal is unterminated', function () { + it('should throw exception if string literal is unterminated', ()=>{ //given - function shouldThrowSingleQuote() { + let shouldThrowSingleQuote = ()=>{ tokenize('\'this is an unterminated stateme'); - } - function shouldThrowDoubleQuote() { + }; + let shouldThrowDoubleQuote = ()=>{ tokenize('"this is an unterminated stateme'); - } + }; //then expect(shouldThrowSingleQuote).toThrow(); expect(shouldThrowDoubleQuote).toThrow(); }); - it('should throw exception if long identifier used on real', function () { + it('should throw exception if long identifier used on real', ()=>{ //given - function shouldThrow() { + let shouldThrow = ()=>{ tokenize('3.4L'); - } + }; //then expect(shouldThrow).toThrow();