UNPKG

jaydata-reso

Version:

Jaydata fork for RESO Web API

793 lines (761 loc) 37 kB
(function () { var ASCII = $data.oDataParser.ASCII; var CharType = $data.oDataParser.CharType; var TokenType = $data.oDataParser.TokenType; // ODataQueryRequest $data.Class.define('$data.oDataParser.QueryRequest', $data.Base, null, { constructor: function () { this.filter = ""; this.orderby = ""; this.skip = ""; this.top = ""; this.select = ""; this.expand = ""; this.format = ""; this.inlinecount = ""; }/*, filter: { type: 'string' }, orderby: { type: 'string' }, skip: { type: 'string' }, top: { type: 'string' }, select: { type: 'string' }, expand: { type: 'string' }, format: { type: 'string' }, inlinecount: { type: 'string' }*/ }); // ODataRequestParser $data.Class.define('$data.oDataParser.RequestParser', null, null, { constructor: function () { this.req = null; this.lexer = null; this.builder = new $data.oDataParser.RequestExpressionBuilder(); this.reservedNs = ['geo']; this.lambdaParameter = []; }, parse: function (req) { ///<param name="req" type="$data.oDataParser.QueryRequest" /> this.req = req; if (req.filter.length > 0) this.parseFilterExpr(); if (req.orderby.length > 0) this.parseOrderByExpr(); if (req.skip.length > 0) this.parseSkipExpr(); if (req.top.length > 0) this.parseTopExpr(); if (req.select.length > 0) this.parseSelectExpr(); if (req.expand.length > 0) this.parseExpandExpr(); //if(req.format.length>0) this.parseFormatExpr(); //if(req.inlinecount.length>0) this.parseInlineCountExpr(); }, parseFilterExpr: function () { this.lexer = new $data.oDataParser.RequestLexer(this.req.filter); var expr = this.parseExpr(); var token = this.lexer.token; if (token && token.tokenType != TokenType.EOF) $data.oDataParser.RequestParser.SyntaxError.call(this, "Unexpected " + this.tokenName(token.tokenType) + " in $filter: '" + token.value + "'.", "parseFilterExpr"); this.req.filter = expr; }, parseOrderByExpr: function () { this.lexer = new $data.oDataParser.RequestLexer(this.req.orderby); var expr = this.parseOrderBy(); var token = this.lexer.token; if (token && token.tokenType != TokenType.EOF) { $data.oDataParser.RequestParser.SyntaxError.call(this, "Unexpected " + this.tokenName(token.tokenType) + " in $orderby: '" + token.value + "'.", "parseOrderByExpr"); } this.req.orderby = expr; }, parseSkipExpr: function () { this.lexer = new $data.oDataParser.RequestLexer(this.req.skip); var token = this.lexer.token; if (this.lexer.token.tokenType != TokenType.DIGITS) $data.oDataParser.RequestParser.SyntaxError.call(this, "Invalid expression in $skip: '" + token.value + "'.", "parseSkipExpr"); var expr = this.parseNumberLiteral(); token = this.lexer.token; if (token && token.tokenType != TokenType.EOF) $data.oDataParser.RequestParser.SyntaxError.call(this, "Unexpected " + this.tokenName(token.tokenType) + " in $skip: '" + token.value + "'.", "parseSkipExpr"); this.req.skip = expr; }, parseTopExpr: function () { this.lexer = new $data.oDataParser.RequestLexer(this.req.top); var token = this.lexer.token; if (this.lexer.token.tokenType != TokenType.DIGITS) $data.oDataParser.RequestParser.SyntaxError.call(this, "Invalid expression in $top: '" + token.value + "'.", "parseTopExpr"); var expr = this.parseNumberLiteral(); token = this.lexer.token; if (token && token.tokenType != TokenType.EOF) $data.oDataParser.RequestParser.SyntaxError.call(this, "Unexpected " + this.tokenName(token.tokenType) + " in $top: '" + token.value + "'.", "parseTopExpr"); this.req.top = expr; }, parseSelectExpr: function () { this.lexer = new $data.oDataParser.RequestLexer(this.req.select); var expressions = []; var expr = this.parseExpr(); expressions.push(expr); var token = this.lexer.token; while (token && token.value == ASCII.COMMA) { this.lexer.nextToken(); expr = this.parseExpr(); expressions.push(expr); token = this.lexer.token; } if (token.tokenType != TokenType.EOF) $data.oDataParser.RequestParser.SyntaxError.call(this, "Unexpected " + this.tokenName(token.tokenType) + " in $filter: '" + token.value + "'.", "parseSelectExpr"); this.req.select = expressions; }, parseExpandExpr: function () { this.lexer = new $data.oDataParser.RequestLexer(this.req.expand); this.req.expand = this.parseExpand(); var token = this.lexer.token; if (token.tokenType != TokenType.EOF) $data.oDataParser.RequestParser.SyntaxError.call(this, "Unexpected " + this.tokenName(token.tokenType) + " in $filter: '" + token.value + "'.", "parseExpandExpr"); }, /* parseInlineCountExpr: function() { //TO DO: parseInlineCountExpr }, parseFormatExpr: function() { //TO DO: parseFormatExpr }, */ tokenName: function (tokenType) { var tokenName; for (var key in TokenType) if (TokenType[tokenName = key] == tokenType) break; return tokenName; }, /*************************************************** BNF *****************************************************/ functionNames: { value: [ //string "substringof", "endswith", "startswith", "length", "indexof", "replace", "substring", "tolower", "toupper", "trim", "concat", //Date "day", "hour", "minute", "month", "second", "year", //Math "round", "floor", "ceiling", //Type "IsOf", //Geo "geo.distance", "geo.intersects", "geo.length" ] }, functionTypes: { value: [ //string "boolean", "boolean", "boolean", "int", "int", "string", "string", "string", "string", "string", "string", //Date "int", "int", "int", "int", "int", "int", //Math "number", "number", "number", //Type "boolean", //Geo "number", "boolean", "number" ] }, reservedNs: {}, parseExpr: function () { //bnf: Expr : OrExpr; return this.parseOrExpr(); }, parseOrExpr: function () { //bnf: OrExpr : AndExpr | OrExpr "or" AndExpr; var expr = this.parseAndExpr(); if (this.lexer.token.value == "or") { this.lexer.nextToken(); var right = this.parseOrExpr(); if (!right) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: Expr.", "parseOrExpr"); expr = this.builder.buildSimpleBinary(expr, right, "or", "or"); } return expr; }, parseAndExpr: function () { //bnf: AndExpr : EqualityExpr | AndExpr "and" EqualityExpr; var expr = this.parseEqualityExpr(); if (this.lexer.token.value == "and") { this.lexer.nextToken(); var right = this.parseAndExpr(); if (!right) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: Expr.", "parseAndExpr"); expr = this.builder.buildSimpleBinary(expr, right, "and", "and"); } return expr; }, parseEqualityExpr: function () { //bnf: EqualityExpr : RelationalExpr | EqualityExpr "eq" RelationalExpr | EqualityExpr "ne" RelationalExpr; var expr = this.parseRelationalExpr(); var token = this.lexer.token; if (token.value == "eq" || token.value == "ne") { this.lexer.nextToken(); var right = this.parseEqualityExpr(); if (!right) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: Expr.", "parseEqualityExpr"); expr = this.builder.buildSimpleBinary(expr, right, token.value, token.value); } return expr; }, parseRelationalExpr: function () { //bnf: RelationalExpr : AdditiveExpr | RelationalExpr ( "lt" | "gt" | "le" | "ge" ) AdditiveExpr; var expr = this.parseAdditiveExpr(); var token = this.lexer.token; if (token.value == "lt" || token.value == "gt" || token.value == "le" || token.value == "ge") { this.lexer.nextToken(); var right = this.parseRelationalExpr(); if (!right) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: Expr.", "RelationalExpr"); expr = this.builder.buildSimpleBinary(expr, right, token.value, token.value); } return expr; }, parseAdditiveExpr: function () { //bnf: AdditiveExpr : MultiplicativeExpr | AdditiveExpr "add" MultiplicativeExpr | AdditiveExpr "sub" MultiplicativeExpr; var expr = this.parseMultiplicativeExpr(); var token = this.lexer.token; if (token.value == "add" || token.value == "sub") { this.lexer.nextToken(); var right = this.parseAdditiveExpr(); if (!right) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: Expr.", "AdditiveExpr"); expr = this.builder.buildSimpleBinary(expr, right, token.value, token.value); } return expr; }, parseMultiplicativeExpr: function () { //bnf: MultiplicativeExpr : UnaryExpr | MultiplicativeExpr "mul" UnaryExpr | MultiplicativeExpr "div" UnaryExpr | MultiplicativeExpr "mod" UnaryExpr; var expr = this.parseUnaryExpr(); var token = this.lexer.token; if (token.value == "mul" || token.value == "div" || token.value == "mod") { this.lexer.nextToken(); var right = this.parseMultiplicativeExpr(); if (!right) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: Expr.", "MultiplicativeExpr"); expr = this.builder.buildSimpleBinary(expr, right, token.value, token.value); } return expr; }, parseUnaryExpr: function () { //bnf: UnaryExpr : PrimaryExpr | "-" PrimaryExpr | "not" PrimaryExpr; var expr = this.parsePrimaryExpr(); if (expr) return expr; var token = this.lexer.token; if (token.value == ASCII.MINUS) { this.lexer.nextToken(); expr = this.parseUnaryExpr(); if (!expr) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: Expr.", "parseUnaryExpr"); return this.builder.buildUnary(expr, "minus") } if (token.value == ASCII.PLUS) { this.lexer.nextToken(); expr = this.parseUnaryExpr(); if (!expr) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: Expr.", "parseUnaryExpr"); return this.builder.buildUnary(expr, "plus") } if (token.value == "not") { this.lexer.nextToken(); expr = this.parseUnaryExpr(); if (!expr) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: Expr.", "parseUnaryExpr"); return this.builder.buildUnary(expr, "not") } }, parsePrimaryExpr: function () { //bnf: PrimaryExpr : ParenExpr | LiteralExpr | FunctionCall | MemberPath var expr; if (expr = this.parseNULLLiteral()) return expr; if (expr = this.parseLiteralExpr()) return expr; if (expr = this.parseFunctionCall()) return expr; if (expr = this.parseMemberPath()) return expr; if (expr = this.parseParenExpr()) return expr; return null; }, parseNULLLiteral: function () { //TODO: validate by gyeby var v = this.lexer.token.value; if (v == "NULL" || v == "null") { this.lexer.nextToken(); return this.builder.buildConstant(null, typeof null); } return null; }, parseParenExpr: function () { //bnf: ParenExpr : "(" Expr ")" if (this.lexer.token.value != ASCII.LPAREN) return null; this.lexer.nextToken(); var expr = this.parseExpr(); if (this.lexer.token.value != ASCII.RPAREN) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected ')'.", "parseParenExpr"); this.lexer.nextToken(); return expr; }, parseLiteralExpr: function () { //bnf: LiteralExpr : DatetimeLiteral | StringLiteral | BoolLiteral | NumberLiteral var expr; if (expr = this.parseDatetimeLiteral()) return expr; if (expr = this.parseDatetimeOffsetLiteral()) return expr; if (expr = this.parseTimeLiteral()) return expr; if (expr = this.parseStringLiteral()) return expr; if (expr = this.parseGuidLiteral()) return expr; if (expr = this.parseBoolLiteral()) return expr; if (expr = this.parseNumberLiteral()) return expr; if (expr = this.parseGeographyLiteral()) return expr; if (expr = this.parseGeometryLiteral()) return expr; if (expr = this.parseBinaryLiteral()) return expr; if (expr = this.parseXLiteral()) return expr; return null; }, parseDatetimeLiteral: function () { //bnf: DatetimeLiteral : "datetime" "'" DIGITS "-" DIGITS "-" DIGITS [ "T" DIGITS ":" DIGITS ":" DIGITS [ "." DIGITS ] [ ( SIGN ) DIGITS ":" DIGTS ] [ "Z" ] //example: datetime'2010-07-15' // datetime'2010-07-15T16:19:54Z'. // datetime'2011-06-07T13:18:25.0348565-07:00' if (this.lexer.token.value != "datetime") return null; this.lexer.nextToken(); var token = this.lexer.token; if (token.tokenType != TokenType.STRING) $data.oDataParser.RequestParser.SyntaxError.call(this, "Invalid date format.", "parseDatetimeLiteral"); var d; try { d = new Date(token.value); } catch (e) { $data.oDataParser.RequestParser.SyntaxError.call(this, "Invalid date format.", "parseDatetimeLiteral"); } this.lexer.nextToken(); return this.builder.buildConstant(d, "datetime"); }, parseDatetimeOffsetLiteral: function () { //bnf: DatetimeLiteral : "datetimeoffset" "'" DIGITS "-" DIGITS "-" DIGITS [ "T" DIGITS ":" DIGITS ":" DIGITS [ "." DIGITS ] [ ( SIGN ) DIGITS ":" DIGTS ] [ "Z" ] //example: datetimeoffset'2010-07-15' // datetimeoffset'2010-07-15T16:19:54Z'. // datetimeoffset'2011-06-07T13:18:25.0348565-07:00' if (this.lexer.token.value != "datetimeoffset") return null; this.lexer.nextToken(); var token = this.lexer.token; if (token.tokenType != TokenType.STRING) $data.oDataParser.RequestParser.SyntaxError.call(this, "Invalid date format.", "parseDatetimeLiteral"); var d; try { d = new Date(token.value); } catch (e) { $data.oDataParser.RequestParser.SyntaxError.call(this, "Invalid date format.", "parseDatetimeLiteral"); } this.lexer.nextToken(); return this.builder.buildConstant(d, "datetime"); }, parseTimeLiteral: function () { if (this.lexer.token.value != "time") return null; this.lexer.nextToken(); var token = this.lexer.token; if (token.tokenType != TokenType.STRING) $data.oDataParser.RequestParser.SyntaxError.call(this, "Invalid date format.", "parseDatetimeLiteral"); var d = token.value; this.lexer.nextToken(); return this.builder.buildConstant(d, "string"); }, parseBinaryLiteral: function () { if (this.lexer.token.value != "binary") return null; this.lexer.nextToken(); var token = this.lexer.token; if (token.tokenType != TokenType.STRING) $data.oDataParser.RequestParser.SyntaxError.call(this, "Invalid date format.", "parseDatetimeLiteral"); var d = $data.Blob.createFromHexString(token.value); this.lexer.nextToken(); return this.builder.buildConstant(d, "blob"); }, parseXLiteral: function () { if (this.lexer.token.value != "X") return null; this.lexer.nextToken(); var token = this.lexer.token; if (token.tokenType != TokenType.STRING) $data.oDataParser.RequestParser.SyntaxError.call(this, "Invalid date format.", "parseDatetimeLiteral"); var d = $data.Blob.createFromHexString(token.value); this.lexer.nextToken(); return this.builder.buildConstant(d, "blob"); }, parseGuidLiteral: function () { if (this.lexer.token.value != "guid") return null; this.lexer.nextToken(); var token = this.lexer.token; if (token.tokenType != TokenType.STRING) $data.oDataParser.RequestParser.SyntaxError.call(this, "Invalid guid format.", "parseGuidLiteral"); var d; try { d = $data.parseGuid(token.value); } catch (e) { $data.oDataParser.RequestParser.SyntaxError.call(this, "Invalid guid format.", "parseGuidLiteral"); } this.lexer.nextToken(); return this.builder.buildConstant(d, "guid"); }, parseStringLiteral: function () { //bnf: StringLiteral : STRING if (this.lexer.token.tokenType != TokenType.STRING) return null; var v = this.lexer.token.value; this.lexer.nextToken(); return this.builder.buildConstant(v, "string"); }, parseBoolLiteral: function () { //bnf: BoolLiteral : "true" | "false" | "0" | "1" var v = this.lexer.token.value; if (v == "true"/*||v=="1"*/) { this.lexer.nextToken(); return this.builder.buildConstant(true, typeof true); } if (v == "false"/*||v=="0"*/) { this.lexer.nextToken(); return this.builder.buildConstant(false, typeof false); } return null; }, parseNumberLiteral: function () { //bnf: NumberLiteral : 1*DIGIT [ "." 1*DIGIT ] [ "E" [ Sign ] 1*DIGIT ] [ "M" | "m" ] | //double //bnf: 1*DIGIT [ "." 1*DIGIT ] [ "f" ] | //single //bnf: 1*DIGIT [ "L" ] //long var sign, digits1, digits2, digits3; var v = ""; var token = this.lexer.token; if (token.tokenType != TokenType.DIGITS) return null; digits1 = token.value; v += digits1; var isInteger = true; this.lexer.nextToken(); token = this.lexer.token; if (token.value == "L") { this.lexer.nextToken(); return this.builder.buildConstant(v, "string"); // long } if (token.value == ASCII.DOT) { isInteger = false; this.lexer.nextToken(); token = this.lexer.token; if (token.tokenType != TokenType.DIGITS) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected DIGITS (after '.').", "parseDoubleStrict"); digits2 = token.value; v += "." + digits2; this.lexer.nextToken(); token = this.lexer.token; } if (token.value == "f") { this.lexer.nextToken(); return this.builder.buildConstant(parseFloat(v), "number"); // single } if (token.value == "e") { isInteger = false; this.lexer.nextToken(); sign = this.parseSign(); token = this.lexer.token; if (token.tokenType != TokenType.DIGITS) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected DIGITS (after Sign).", "parseDoubleStrict"); digits3 = token.value; v += "e" + (sign ? String.fromCharCode(sign) : "") + digits3; this.lexer.nextToken(); token = this.lexer.token; } if (token.value == "m" || token.value == "M") { this.lexer.nextToken(); return this.builder.buildConstant(v, "string"); // decimal } var n = isInteger ? parseInt(v) : parseFloat(v); return this.builder.buildConstant(n, "number"); }, parseGeographyLiteral: function () { if (this.lexer.token.value != "geography") return null; this.lexer.nextToken(); var token = this.lexer.token; if (token.tokenType != TokenType.STRING) $data.oDataParser.RequestParser.SyntaxError.call(this, "Invalid geography format.", "parseGeographyLiteral"); var g; try { g = $data.GeographyBase.parseFromString(token.value); } catch (e) { $data.oDataParser.RequestParser.SyntaxError.call(this, "Invalid geography format.", "parseGeographyLiteral"); } this.lexer.nextToken(); var typeName = Container.getTypeName(g); return this.builder.buildConstant(g, typeName.substring(typeName.lastIndexOf('.') + 1)); }, parseGeometryLiteral: function () { if (this.lexer.token.value != "geometry") return null; this.lexer.nextToken(); var token = this.lexer.token; if (token.tokenType != TokenType.STRING) $data.oDataParser.RequestParser.SyntaxError.call(this, "Invalid geometry format.", "parseGeometryLiteral"); var g; try { g = $data.GeometryBase.parseFromString(token.value); } catch (e) { $data.oDataParser.RequestParser.SyntaxError.call(this, "Invalid geometry format.", "parseGeometryLiteral"); } this.lexer.nextToken(); var typeName = Container.getTypeName(g); return this.builder.buildConstant(g, typeName.substring(typeName.lastIndexOf('.') + 1)); }, parseGeographyArg: function () { var exp = this.parseExpr(); if (!exp) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: expression", "parseGeographyLiteral"); else { if (exp instanceof $data.Expressions.UnaryExpression && exp.operand instanceof $data.Expressions.ConstantExpression && exp.operand.type === 'number') { return this.builder.buildConstant(exp.operator.implementation(exp.operand.value), "number"); } else if (exp instanceof $data.Expressions.ConstantExpression && exp.type === 'number') { return exp; } else { $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected expression number", "parseGeographyLiteral"); } } }, parseSign: function () { // returns "+", "-" or null //bnf: Sign : "+" | "-" var token = this.lexer.token; if (token.value != ASCII.PLUS && token.value != ASCII.MINUS) return null; this.lexer.nextToken(); return token.value; }, parseFunctionCall: function () { //bnf: FunctionCall : FunctionName "(" [ Arguments ] ")"; var fn = this.parseFunctionName(); if (fn == null) return null; var token = this.lexer.token; if (token.value != ASCII.LPAREN) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected '('.", "parseFunctionCall"); this.lexer.nextToken(); token = this.lexer.token; var args; if (token.value != ASCII.RPAREN) { args = this.parseArguments(); token = this.lexer.token; } if (token.value != ASCII.RPAREN) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected ')'.", "parseFunctionCall"); this.lexer.nextToken(); return this.builder.buildGlobalCall(fn.type, fn.name, args); }, parseFunctionName: function () { //returns {name:, type:} //bnf: FunctionName : "startswith" | "endswith" | ...; var token = this.lexer.token; var line = this.lexer.line; var column = this.lexer.column; var srcIndex = this.lexer.srcIndex; if (token.tokenType != TokenType.WORD) return; var name = token.value.toLowerCase(); for (var i = 0; i < this.reservedNs.length; i++) { if (this.reservedNs[i] == name) { this.lexer.nextToken(); token = this.lexer.token; if (token.tokenType != TokenType.CHAR) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected '.'.", "parseFunctionName"); this.lexer.nextToken(); token = this.lexer.token; if (token.tokenType != TokenType.WORD) return; name = name + '.' + token.value; } } for (var i = 0; i < this.functionNames.length; i++) { if (this.functionNames[i] == name) { this.lexer.nextToken(); if (this.lexer.token.value != ASCII.LPAREN){ this.lexer.token = token; this.lexer.line = line; this.lexer.column = column; this.lexer.srcIndex = srcIndex; return null; } return { name: name, type: this.functionTypes[i] }; } } return null; }, parseArguments: function () { //bnf: Arguments : Argument | Arguments "," Argument; var args = []; while (true) { var expr = this.parseArgument(); if (!expr) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: expression", "parseArguments"); args.push(expr); if (this.lexer.token.value != ASCII.COMMA) break; this.lexer.nextToken(); } return args; }, parseArgument: function () { //bnf: Argument : Expr; return this.parseExpr(); }, parseMemberPath: function () { //bnf: MemberPath : [ Namespace "/" ] *(NavigationProperty "/") Field //bnf: Namespace : NAME *("." NAME) //bnf: NavigationProperty : NAME //bnf: Field : NAME //short: MemberPath : [ Name *("." Name) "/" ] *(Name "/") Name if (this.lexer.token.value == "not") return null; var name = this.parseName(); var token = this.lexer.token; if (!name) { return null; //var msg = "Expected: member"; //if (token.value == ASCII.DOT) // msg += " or DIGIT"; //msg += " before dot ('.')."; //$data.oDataParser.RequestParser.SyntaxError.call(this, msg, "parseMemberPath"); } var member = name; var hasDot = false; while (token.value == ASCII.DOT) { member += "."; hasDot = true; this.lexer.nextToken(); name = this.parseName(); token = this.lexer.token; if (!name) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: name after dot ('.').", "parseMemberPath"); member += name; } var steps = []; if (!this.lambdaParameter.length || this.lambdaParameter[this.lambdaParameter.length - 1] != member) steps.push(member); var hasSlash = false; while (token.value == ASCII.SLASH) { member += "/"; hasSlash = true; this.lexer.nextToken(); name = this.parseName(); token = this.lexer.token; if (!name) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: name.", "parseMemberPath"); if (name == 'all' || name == 'any'){ var frame = { type: name == 'all' ? $data.Expressions.EveryExpression : $data.Expressions.SomeExpression, entitySet: steps[steps.length - 1] }; if (token.value != ASCII.LPAREN) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: left parenthesis.", "parseMemberPath"); token = this.lexer.nextToken(); if (token.value != ASCII.RPAREN){ frame.lambda = token.value; token = this.lexer.nextToken(); if (token.value == ASCII.COLON){ this.lambdaParameter.push(frame.lambda); this.lexer.nextToken(); frame.expression = this.parseExpr(); this.lambdaParameter.pop(); }else $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: colon (:) after lamba parameter.", "parseMemberPath"); }else token = this.lexer.nextToken(); this.lexer.nextToken(); steps.push(frame); return this.builder.buildMemberPath(steps); }else if (name != '*'){ member += name; steps.push(name); } } if (hasDot && !hasSlash) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: / after namespace.", "parseMemberPath"); if (this.lexer.token.value != ASCII.LPAREN) return this.builder.buildMemberPath(steps, this.lambdaParameter[this.lambdaParameter.length - 1]); //-- parse instance method this.lexer.nextToken(); if (this.lexer.token.value != ASCII.RPAREN) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: right parenthesis: ')'.", "parseMemberPath"); this.lexer.nextToken(); return this.builder.buildMemberPath(steps, this.lambdaParameter[this.lambdaParameter.length - 1]); }, parseName: function () { //bnf: Name : (WORD | UNDERSCORE) *(WORD | UNDERSCORE | DIGIT) //HACK: this and getNextNamePart are not a clean parser function var token = this.lexer.token; if (token.tokenType != TokenType.WORD && token.value != ASCII.UNDERSCORE && token.value != ASCII.ASTERISK) return null; var name = token.toString() + this.getNextNamePart(); this.lexer.nextToken(); return name; }, getNextNamePart: function () { var part = ""; var c = this.lexer.currentChar; var ct = this.lexer.currentCharType; while (ct == CharType.DIGIT || ct == CharType.ALPHA || c == ASCII.UNDERSCORE) { part += String.fromCharCode(c); this.lexer.nextChar(); c = this.lexer.currentChar; ct = this.lexer.currentCharType; } return part; }, //===================================================================================================================== parseOrderBy: function () { //bnf: OrderByExpr: Expr [ "asc" | "desc"] *( "," Expr [ "asc" | "desc"] ) var expr = this.parseExpr(); if (!expr) return null; var items = []; // [{expr:expr, dir:"asc"}] var dir = "asc"; var token = this.lexer.token; if (token.value == "asc" || token.value == "desc") { dir = token.value; this.lexer.nextToken(); token = this.lexer.token; } items.push({ expr: expr, dir: dir }); while (token.value == ASCII.COMMA) { this.lexer.nextToken(); token = this.lexer.token; expr = this.parseExpr(); if (!expr) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: expr", "parseOrderBy"); dir = "asc"; token = this.lexer.token; if (token.value == "asc" || token.value == "desc") { dir = token.value; this.lexer.nextToken(); token = this.lexer.token; } items.push({ expr: expr, dir: dir }); } return this.builder.buildOrderBy(items); }, //===================================================================================================================== parseExpand: function () { //bnf: OrderByExpr: MemberPath *( "," MemberPath) var expr = this.parseMemberPath(); if (!expr) return null; var items = []; var token = this.lexer.token; items.push(expr); while (token.value == ASCII.COMMA) { this.lexer.nextToken(); token = this.lexer.token; expr = this.parseMemberPath(); if (!expr) $data.oDataParser.RequestParser.SyntaxError.call(this, "Expected: expr", "parseExpand"); items.push(expr); token = this.lexer.token; } return items; } }, { SyntaxError: function (reason, caller) { var src = this.lexer.src; var token = this.lexer.token; var line = " Source:" + src.substr(0, token.column) + ">>>>" + src.substr(token.column, 1) + "<<<<" + src.substr(token.column + 1); var msg = "Syntax error at line " + token.line + ", char " + token.column + ". " + reason + line; if (caller) msg += ". Caller: " + caller; throw { message: msg, reason: reason, source: caller, line: token.line, column: token.column }; } }); })();