UNPKG

@sap/odata-v4

Version:

OData V4.0 server library

118 lines (108 loc) 4.29 kB
'use strict'; const TokenKind = require('./UriTokenizer').TokenKind; const BinaryExpression = require('./BinaryExpression'); const BinaryOperatorKind = BinaryExpression.OperatorKind; const UnaryExpression = require('./UnaryExpression'); const UnaryOperatorKind = UnaryExpression.OperatorKind; const LiteralExpression = require('./LiteralExpression'); const EdmPrimitiveTypeKind = require('../edm/EdmPrimitiveTypeKind'); const UriSyntaxError = require('../errors/UriSyntaxError'); /** * Parses search expressions according to the following (rewritten) grammar: * <pre> * SearchExpression ::= OrExpression * OrExpression ::= AndExpression ('OR' AndExpression)* * AndExpression ::= Term ('AND'? Term)* * Term ::= ('NOT'? (Word | Phrase)) | ('(' SearchExpression ')') * </pre> */ class SearchParser { /** * Parse search expression. * @param {UriTokenizer} tokenizer tokenizer * @returns {Expression} the root of the search-expression tree */ parse(tokenizer) { return this._parseOrExpression(tokenizer); } /** * Parse search expression with zero or more 'OR' operators. * @param {UriTokenizer} tokenizer tokenizer * @returns {Expression} expression * @private */ _parseOrExpression(tokenizer) { let left = this._parseAndExpression(tokenizer); while (tokenizer.next(TokenKind.OrOperatorSearch)) { let right = this._parseAndExpression(tokenizer); left = new BinaryExpression(left, BinaryOperatorKind.OR, right, EdmPrimitiveTypeKind.Boolean); } return left; } /** * Parse search expression with zero or more 'AND' operators. * @param {UriTokenizer} tokenizer tokenizer * @returns {Expression} expression * @private */ _parseAndExpression(tokenizer) { let left = this._parseTerm(tokenizer); while (tokenizer.next(TokenKind.AndOperatorSearch)) { // Could be whitespace or whitespace-surrounded 'AND'. let right = this._parseTerm(tokenizer); left = new BinaryExpression(left, BinaryOperatorKind.AND, right, EdmPrimitiveTypeKind.Boolean); } return left; } /** * Parse search term. * @param {UriTokenizer} tokenizer tokenizer * @returns {Expression} expression * @private */ _parseTerm(tokenizer) { if (tokenizer.next(TokenKind.OPEN)) { let expression = this._parseOrExpression(tokenizer); tokenizer.requireNext(TokenKind.CLOSE); return expression; } else if (tokenizer.next(TokenKind.NotOperatorSearch)) { return this._parseNot(tokenizer); } else if (tokenizer.next(TokenKind.Word)) { return new LiteralExpression(tokenizer.getText(), undefined); } else if (tokenizer.next(TokenKind.Phrase)) { return this._parsePhrase(tokenizer); } throw new UriSyntaxError(UriSyntaxError.Message.PHRASE_OR_WORD_EXPECTED, tokenizer.getParseString(), tokenizer.getPosition()); } /** * Parse search expression after the 'NOT' operator. * @param {UriTokenizer} tokenizer tokenizer * @returns {UnaryExpression} unary expression with operator 'NOT' * @private */ _parseNot(tokenizer) { if (tokenizer.next(TokenKind.Word)) { return new UnaryExpression(UnaryOperatorKind.NOT, new LiteralExpression(tokenizer.getText(), undefined)); } else if (tokenizer.next(TokenKind.Phrase)) { return new UnaryExpression(UnaryOperatorKind.NOT, this._parsePhrase(tokenizer)); } throw new UriSyntaxError(UriSyntaxError.Message.SEARCH_NOT_MUST_BE_FOLLOWED_BY_A_TERM, tokenizer.getParseString(), tokenizer.getPosition()); } /** * Parse search phrase. * @param {UriTokenizer} tokenizer tokenizer * @returns {LiteralExpression} literal expression * @private */ _parsePhrase(tokenizer) { const literal = tokenizer.getText(); return new LiteralExpression( literal.substring(1, literal.length - 1) .replace('\\"', '"') .replace('\\\\', '\\'), undefined); } } module.exports = SearchParser;