@rsql/parser
Advanced tools
Comparing version 0.0.1-next.2 to 0.0.1-next.3
@@ -5,7 +5,5 @@ // Generated by dts-bundle v0.7.3 | ||
declare module '@rsql/parser' { | ||
import { ExpressionNode } from "@rsql/ast"; | ||
function parse(source: string): ExpressionNode; | ||
export { parse }; | ||
} | ||
import { ExpressionNode } from "@rsql/ast"; | ||
function parse(source: string): ExpressionNode; | ||
export { parse }; |
@@ -1,142 +0,203 @@ | ||
import { isComparisonOperator, isLogicOperator, OR, AND, ComparisonOperators, VerboseLogicOperators, ReservedChars, createSelectorNode, createValueNode, createComparisonNode, createLogicNode, isSelectorNode, isValueNode, isExpressionNode } from '@rsql/ast'; | ||
import { | ||
isComparisonOperator, | ||
isLogicOperator, | ||
OR, | ||
AND, | ||
ComparisonOperators, | ||
VerboseLogicOperators, | ||
ReservedChars, | ||
createSelectorNode, | ||
createValueNode, | ||
createComparisonNode, | ||
createLogicNode, | ||
isSelectorNode, | ||
isValueNode, | ||
isExpressionNode, | ||
} from "@rsql/ast"; | ||
var TokenType = { | ||
UNQUOTED: "UNQUOTED", | ||
QUOTED: "QUOTED", | ||
PARENTHESIS: "PARENTHESIS", | ||
OPERATOR: "OPERATOR", | ||
END: "END", | ||
UNQUOTED: "UNQUOTED", | ||
QUOTED: "QUOTED", | ||
PARENTHESIS: "PARENTHESIS", | ||
OPERATOR: "OPERATOR", | ||
END: "END", | ||
}; | ||
function createNamedToken(token, toString) { | ||
Object.defineProperty(token, "toString", { | ||
value: toString, | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
}); | ||
return token; | ||
Object.defineProperty(token, "toString", { | ||
value: toString, | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
}); | ||
return token; | ||
} | ||
function createUnquotedToken(value, position) { | ||
return createNamedToken({ | ||
type: TokenType.UNQUOTED, | ||
value: value, | ||
position: position, | ||
}, function () { return "UnquotedToken(" + value + ")"; }); | ||
return createNamedToken( | ||
{ | ||
type: TokenType.UNQUOTED, | ||
value: value, | ||
position: position, | ||
}, | ||
function () { | ||
return "UnquotedToken(" + value + ")"; | ||
} | ||
); | ||
} | ||
function createQuotedToken(value, position) { | ||
return createNamedToken({ | ||
type: TokenType.QUOTED, | ||
value: value, | ||
position: position, | ||
}, function () { return "QuotedToken(" + value + ")"; }); | ||
return createNamedToken( | ||
{ | ||
type: TokenType.QUOTED, | ||
value: value, | ||
position: position, | ||
}, | ||
function () { | ||
return "QuotedToken(" + value + ")"; | ||
} | ||
); | ||
} | ||
function createParenthesisToken(value, position) { | ||
return createNamedToken({ | ||
type: TokenType.PARENTHESIS, | ||
value: value, | ||
position: position, | ||
}, function () { return "ParenthesisToken(" + value + ")"; }); | ||
return createNamedToken( | ||
{ | ||
type: TokenType.PARENTHESIS, | ||
value: value, | ||
position: position, | ||
}, | ||
function () { | ||
return "ParenthesisToken(" + value + ")"; | ||
} | ||
); | ||
} | ||
function createOperatorToken(value, position) { | ||
return createNamedToken({ | ||
type: TokenType.OPERATOR, | ||
value: value, | ||
position: position, | ||
}, function () { return "OperatorToken(" + value + ")"; }); | ||
return createNamedToken( | ||
{ | ||
type: TokenType.OPERATOR, | ||
value: value, | ||
position: position, | ||
}, | ||
function () { | ||
return "OperatorToken(" + value + ")"; | ||
} | ||
); | ||
} | ||
function createEndToken(position) { | ||
return createNamedToken({ | ||
type: TokenType.END, | ||
value: "END", | ||
position: position, | ||
}, function () { return "EndToken"; }); | ||
return createNamedToken( | ||
{ | ||
type: TokenType.END, | ||
value: "END", | ||
position: position, | ||
}, | ||
function () { | ||
return "EndToken"; | ||
} | ||
); | ||
} | ||
function isToken(candidate) { | ||
return (Object.prototype.hasOwnProperty.call(candidate, "type") && | ||
Object.prototype.hasOwnProperty.call(candidate, "value") && | ||
Object.prototype.hasOwnProperty.call(candidate, "position")); | ||
return ( | ||
Object.prototype.hasOwnProperty.call(candidate, "type") && | ||
Object.prototype.hasOwnProperty.call(candidate, "value") && | ||
Object.prototype.hasOwnProperty.call(candidate, "position") | ||
); | ||
} | ||
function isUnquotedToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.UNQUOTED; | ||
return isToken(candidate) && candidate.type === TokenType.UNQUOTED; | ||
} | ||
function isQuotedToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.QUOTED; | ||
return isToken(candidate) && candidate.type === TokenType.QUOTED; | ||
} | ||
function isParenthesisToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.PARENTHESIS; | ||
return isToken(candidate) && candidate.type === TokenType.PARENTHESIS; | ||
} | ||
function isOpenParenthesisToken(candidate) { | ||
return isParenthesisToken(candidate) && candidate.value === "("; | ||
return isParenthesisToken(candidate) && candidate.value === "("; | ||
} | ||
function isCloseParenthesisToken(candidate) { | ||
return isParenthesisToken(candidate) && candidate.value === ")"; | ||
return isParenthesisToken(candidate) && candidate.value === ")"; | ||
} | ||
function isOperatorToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.OPERATOR; | ||
return isToken(candidate) && candidate.type === TokenType.OPERATOR; | ||
} | ||
function isComparisonOperatorToken(candidate) { | ||
return isOperatorToken(candidate) && isComparisonOperator(candidate.value); | ||
return isOperatorToken(candidate) && isComparisonOperator(candidate.value); | ||
} | ||
function isOrOperatorToken(candidate) { | ||
return isOperatorToken(candidate) && isLogicOperator(candidate.value, OR); | ||
return isOperatorToken(candidate) && isLogicOperator(candidate.value, OR); | ||
} | ||
function isAndOperatorToken(candidate) { | ||
return isOperatorToken(candidate) && isLogicOperator(candidate.value, AND); | ||
return isOperatorToken(candidate) && isLogicOperator(candidate.value, AND); | ||
} | ||
function isEndToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.END; | ||
return isToken(candidate) && candidate.type === TokenType.END; | ||
} | ||
function createErrorForUnexpectedCharacter(position, source) { | ||
var character = source[position]; | ||
return new SyntaxError("Unexpected character '" + character + "' at position " + (position + 1) + " in \"" + source + "\"."); | ||
var character = source[position]; | ||
return new SyntaxError( | ||
"Unexpected character '" + character + "' at position " + (position + 1) + ' in "' + source + '".' | ||
); | ||
} | ||
function createErrorForUnclosedQuote(position, source) { | ||
var character = source[position]; | ||
return new SyntaxError("Unclosed quote '" + character + "' at position " + (position + 1) + " in \"" + source + "\"."); | ||
var character = source[position]; | ||
return new SyntaxError("Unclosed quote '" + character + "' at position " + (position + 1) + ' in "' + source + '".'); | ||
} | ||
function createErrorForUnexpectedToken(token, source) { | ||
return new SyntaxError(isEndToken(token) | ||
? "Unexpected end in \"" + source + "\"." | ||
: "Unexpected " + (token.value.length > 1 ? "string" : "character") + " '" + token.value + "' at position " + (token.position + 1) + " in \"" + source + "\"."); | ||
return new SyntaxError( | ||
isEndToken(token) | ||
? 'Unexpected end in "' + source + '".' | ||
: "Unexpected " + | ||
(token.value.length > 1 ? "string" : "character") + | ||
" '" + | ||
token.value + | ||
"' at position " + | ||
(token.position + 1) + | ||
' in "' + | ||
source + | ||
'".' | ||
); | ||
} | ||
function createErrorForUnclosedParenthesis(token, source, parentPosition) { | ||
return new SyntaxError("Unexpected end in \"" + source + "\". Did you forget to close parenthesis at position " + (parentPosition + 1) + "?"); | ||
return new SyntaxError( | ||
'Unexpected end in "' + source + '". Did you forget to close parenthesis at position ' + (parentPosition + 1) + "?" | ||
); | ||
} | ||
function createErrorForEmptyInput(token, source) { | ||
return new SyntaxError("Unexpected end in \"" + source + "\". Cannot parse empty string."); | ||
return new SyntaxError('Unexpected end in "' + source + '". Cannot parse empty string.'); | ||
} | ||
function createLexerContext(input) { | ||
return { | ||
position: 0, | ||
buffer: input, | ||
length: input.length, | ||
}; | ||
return { | ||
position: 0, | ||
buffer: input, | ||
length: input.length, | ||
}; | ||
} | ||
var seekComparisonCustomOperatorToken = function (context) { | ||
// scan for FIQL custom operators: =[a-z]= | ||
// assume that context.buffer[context.position] === '=' | ||
var endPosition = context.position + 1; | ||
// scan for chars from a to z | ||
while (endPosition < context.length && | ||
context.buffer.charCodeAt(endPosition) >= 97 && // a | ||
context.buffer.charCodeAt(endPosition) <= 122 // z | ||
) { | ||
endPosition++; | ||
} | ||
if (context.buffer[endPosition] === "=") { | ||
var operator = context.buffer.slice(context.position, endPosition + 1); | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
// scan for FIQL custom operators: =[a-z]= | ||
// assume that context.buffer[context.position] === '=' | ||
var endPosition = context.position + 1; | ||
// scan for chars from a to z | ||
while ( | ||
endPosition < context.length && | ||
context.buffer.charCodeAt(endPosition) >= 97 && // a | ||
context.buffer.charCodeAt(endPosition) <= 122 // z | ||
) { | ||
endPosition++; | ||
} | ||
if (context.buffer[endPosition] === "=") { | ||
var operator = context.buffer.slice(context.position, endPosition + 1); | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
}; | ||
function createScanSymbol(symbols) { | ||
return function scanSymbol(context) { | ||
return symbols.find(function (symbol) { return context.buffer.substr(context.position, symbol.length) === symbol; }) || null; | ||
}; | ||
return function scanSymbol(context) { | ||
return ( | ||
symbols.find(function (symbol) { | ||
return context.buffer.substr(context.position, symbol.length) === symbol; | ||
}) || null | ||
); | ||
}; | ||
} | ||
@@ -146,26 +207,30 @@ | ||
var seekComparisonOperatorToken = function (context) { | ||
var operator = scanAnyComparisonOperator(context); | ||
if (operator) { | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
var operator = scanAnyComparisonOperator(context); | ||
if (operator) { | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
}; | ||
var seekLogicCanonicalOperatorToken = function (context) { | ||
// we assume that symbol is a valid LogicOperator | ||
var operator = context.buffer.charAt(context.position); | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += 1; | ||
return token; | ||
// we assume that symbol is a valid LogicOperator | ||
var operator = context.buffer.charAt(context.position); | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += 1; | ||
return token; | ||
}; | ||
function createScanNonReservedSymbol(symbols) { | ||
return function scanNonReservedSymbol(context) { | ||
return (symbols.find(function (symbol) { | ||
return context.buffer.substr(context.position, symbol.length) === symbol && | ||
context.buffer[context.position + symbol.length] === " "; | ||
}) || null); | ||
}; | ||
return function scanNonReservedSymbol(context) { | ||
return ( | ||
symbols.find(function (symbol) { | ||
return ( | ||
context.buffer.substr(context.position, symbol.length) === symbol && | ||
context.buffer[context.position + symbol.length] === " " | ||
); | ||
}) || null | ||
); | ||
}; | ||
} | ||
@@ -175,53 +240,56 @@ | ||
var seekLogicVerboseOperatorToken = function (context) { | ||
var operator = scanLogicVerboseOperator(context); | ||
if (operator) { | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
var operator = scanLogicVerboseOperator(context); | ||
if (operator) { | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
}; | ||
var seekParenthesisToken = function (context) { | ||
// we assume that parenthesis is a valid Parenthesis | ||
var parenthesis = context.buffer.charAt(context.position); | ||
var token = createParenthesisToken(parenthesis, context.position); | ||
context.position += 1; | ||
return token; | ||
// we assume that parenthesis is a valid Parenthesis | ||
var parenthesis = context.buffer.charAt(context.position); | ||
var token = createParenthesisToken(parenthesis, context.position); | ||
context.position += 1; | ||
return token; | ||
}; | ||
var seekQuotedToken = function (context) { | ||
// we assume that quote is a valid QuoteSymbol | ||
var quote = context.buffer.charAt(context.position); | ||
var endPosition = context.position; | ||
while (endPosition < context.length) { | ||
endPosition = context.buffer.indexOf(quote, endPosition + 1); | ||
if (endPosition === -1) { | ||
throw createErrorForUnclosedQuote(context.position, context.buffer); | ||
} | ||
// scan back for escape characters | ||
var escaped = false; | ||
for (var scanPosition = endPosition - 1; context.buffer[scanPosition] === "\\" && scanPosition > context.position; scanPosition--) { | ||
escaped = !escaped; | ||
} | ||
if (!escaped) { | ||
// it's not escaped quote - we've found terminating quote | ||
break; | ||
} | ||
// we assume that quote is a valid QuoteSymbol | ||
var quote = context.buffer.charAt(context.position); | ||
var endPosition = context.position; | ||
while (endPosition < context.length) { | ||
endPosition = context.buffer.indexOf(quote, endPosition + 1); | ||
if (endPosition === -1) { | ||
throw createErrorForUnclosedQuote(context.position, context.buffer); | ||
} | ||
var value = context.buffer.substring(context.position, endPosition + 1); | ||
var token = createQuotedToken(value, context.position); | ||
context.position = endPosition + 1; | ||
return token; | ||
// scan back for escape characters | ||
var escaped = false; | ||
for ( | ||
var scanPosition = endPosition - 1; | ||
context.buffer[scanPosition] === "\\" && scanPosition > context.position; | ||
scanPosition-- | ||
) { | ||
escaped = !escaped; | ||
} | ||
if (!escaped) { | ||
// it's not escaped quote - we've found terminating quote | ||
break; | ||
} | ||
} | ||
var value = context.buffer.substring(context.position, endPosition + 1); | ||
var token = createQuotedToken(value, context.position); | ||
context.position = endPosition + 1; | ||
return token; | ||
}; | ||
var seekUnquotedToken = function (context) { | ||
var endPosition = context.position + 1; | ||
while (endPosition < context.length && | ||
ReservedChars.indexOf(context.buffer.charAt(endPosition)) === -1) { | ||
endPosition++; | ||
} | ||
var token = createUnquotedToken(context.buffer.substring(context.position, endPosition), context.position); | ||
context.position = endPosition; | ||
return token; | ||
var endPosition = context.position + 1; | ||
while (endPosition < context.length && ReservedChars.indexOf(context.buffer.charAt(endPosition)) === -1) { | ||
endPosition++; | ||
} | ||
var token = createUnquotedToken(context.buffer.substring(context.position, endPosition), context.position); | ||
context.position = endPosition; | ||
return token; | ||
}; | ||
@@ -231,129 +299,129 @@ | ||
var skipWhitespace = function (context) { | ||
while (context.position < context.length && WhitespaceChars.indexOf(context.buffer.charAt(context.position)) !== -1) { | ||
context.position++; | ||
} | ||
while (context.position < context.length && WhitespaceChars.indexOf(context.buffer.charAt(context.position)) !== -1) { | ||
context.position++; | ||
} | ||
}; | ||
var seekAnyToken = function (context) { | ||
// first skip all whitespace chars | ||
skipWhitespace(context); | ||
if (context.position >= context.length) { | ||
return null; | ||
} | ||
// then decide what to do based on the current char | ||
var char = context.buffer.charAt(context.position); | ||
var token = null; | ||
switch (char) { | ||
// single char symbols | ||
case "'": | ||
case '"': | ||
token = seekQuotedToken(context); | ||
break; | ||
// single char symbols | ||
case "(": | ||
case ")": | ||
token = seekParenthesisToken(context); | ||
break; | ||
// single char symbols | ||
case ",": | ||
case ";": | ||
token = seekLogicCanonicalOperatorToken(context); | ||
break; | ||
// multi char symbols for comparison operator | ||
case "=": | ||
case "!": | ||
case "~": | ||
case "<": | ||
case ">": | ||
token = seekComparisonOperatorToken(context); | ||
if (!token && char === "=") { | ||
token = seekComparisonCustomOperatorToken(context); | ||
} | ||
break; | ||
// unreserved char | ||
default: | ||
// there are VerboseLogicOperators (and, or) that uses not reserved chars | ||
token = seekLogicVerboseOperatorToken(context); | ||
// if it's not an OperatorToken, process UnquotedToken | ||
if (!token) { | ||
token = seekUnquotedToken(context); | ||
} | ||
} | ||
if (!token) { | ||
throw createErrorForUnexpectedCharacter(context.position, context.buffer); | ||
} | ||
return token; | ||
// first skip all whitespace chars | ||
skipWhitespace(context); | ||
if (context.position >= context.length) { | ||
return null; | ||
} | ||
// then decide what to do based on the current char | ||
var char = context.buffer.charAt(context.position); | ||
var token = null; | ||
switch (char) { | ||
// single char symbols | ||
case "'": | ||
case '"': | ||
token = seekQuotedToken(context); | ||
break; | ||
// single char symbols | ||
case "(": | ||
case ")": | ||
token = seekParenthesisToken(context); | ||
break; | ||
// single char symbols | ||
case ",": | ||
case ";": | ||
token = seekLogicCanonicalOperatorToken(context); | ||
break; | ||
// multi char symbols for comparison operator | ||
case "=": | ||
case "!": | ||
case "~": | ||
case "<": | ||
case ">": | ||
token = seekComparisonOperatorToken(context); | ||
if (!token && char === "=") { | ||
token = seekComparisonCustomOperatorToken(context); | ||
} | ||
break; | ||
// unreserved char | ||
default: | ||
// there are VerboseLogicOperators (and, or) that uses not reserved chars | ||
token = seekLogicVerboseOperatorToken(context); | ||
// if it's not an OperatorToken, process UnquotedToken | ||
if (!token) { | ||
token = seekUnquotedToken(context); | ||
} | ||
} | ||
if (!token) { | ||
throw createErrorForUnexpectedCharacter(context.position, context.buffer); | ||
} | ||
return token; | ||
}; | ||
function lex(input) { | ||
var context = createLexerContext(input); | ||
var tokens = []; | ||
for (var token = seekAnyToken(context); token !== null; token = seekAnyToken(context)) { | ||
tokens.push(token); | ||
} | ||
tokens.push(createEndToken(context.position)); | ||
return tokens; | ||
var context = createLexerContext(input); | ||
var tokens = []; | ||
for (var token = seekAnyToken(context); token !== null; token = seekAnyToken(context)) { | ||
tokens.push(token); | ||
} | ||
tokens.push(createEndToken(context.position)); | ||
return tokens; | ||
} | ||
function getParserContextState(context) { | ||
return context.state[context.state.length - 1]; | ||
return context.state[context.state.length - 1]; | ||
} | ||
function getParserContextToken(context) { | ||
return context.tokens[context.position]; | ||
return context.tokens[context.position]; | ||
} | ||
function getParserContextHead(context) { | ||
return context.stack[context.stack.length - 1]; | ||
return context.stack[context.stack.length - 1]; | ||
} | ||
function createParserContext(tokens) { | ||
return { | ||
position: 0, | ||
tokens: tokens, | ||
stack: [], | ||
state: [0], | ||
parent: null, | ||
}; | ||
return { | ||
position: 0, | ||
tokens: tokens, | ||
stack: [], | ||
state: [0], | ||
parent: null, | ||
}; | ||
} | ||
var OperationType = { | ||
SHIFT: 0, | ||
PUSH: 1, | ||
REDUCE: 2, | ||
POP: 3, | ||
GOTO: 4, | ||
ACCEPT: 5, | ||
SHIFT: 0, | ||
PUSH: 1, | ||
REDUCE: 2, | ||
POP: 3, | ||
GOTO: 4, | ||
ACCEPT: 5, | ||
}; | ||
function shift(state) { | ||
return { | ||
type: OperationType.SHIFT, | ||
state: state, | ||
}; | ||
return { | ||
type: OperationType.SHIFT, | ||
state: state, | ||
}; | ||
} | ||
function reduce(production) { | ||
return { | ||
type: OperationType.REDUCE, | ||
production: production, | ||
}; | ||
return { | ||
type: OperationType.REDUCE, | ||
production: production, | ||
}; | ||
} | ||
function push(state) { | ||
return { | ||
type: OperationType.PUSH, | ||
state: state, | ||
}; | ||
return { | ||
type: OperationType.PUSH, | ||
state: state, | ||
}; | ||
} | ||
function pop(production) { | ||
return { | ||
type: OperationType.POP, | ||
production: production, | ||
}; | ||
return { | ||
type: OperationType.POP, | ||
production: production, | ||
}; | ||
} | ||
function goto(state) { | ||
return { | ||
type: OperationType.GOTO, | ||
state: state, | ||
}; | ||
return { | ||
type: OperationType.GOTO, | ||
state: state, | ||
}; | ||
} | ||
function accept() { | ||
return { | ||
type: OperationType.ACCEPT, | ||
}; | ||
return { | ||
type: OperationType.ACCEPT, | ||
}; | ||
} | ||
@@ -363,76 +431,81 @@ var noop = undefined; | ||
var selectorProduction = function (stack) { | ||
var token = stack[stack.length - 1]; | ||
return { | ||
consumed: 1, | ||
produced: createSelectorNode(token.value, true), | ||
}; | ||
var token = stack[stack.length - 1]; | ||
return { | ||
consumed: 1, | ||
produced: createSelectorNode(token.value, true), | ||
}; | ||
}; | ||
var singleValueProduction = function (stack) { | ||
var token = stack[stack.length - 1]; | ||
var value = isQuotedToken(token) ? token.value.slice(1, -1) : token.value; | ||
return { | ||
consumed: 1, | ||
produced: createValueNode(value, true), | ||
}; | ||
var token = stack[stack.length - 1]; | ||
var value = isQuotedToken(token) ? token.value.slice(1, -1) : token.value; | ||
return { | ||
consumed: 1, | ||
produced: createValueNode(value, true), | ||
}; | ||
}; | ||
var multiValueProduction = function (stack) { | ||
var closeParenthesisIndex = stack.length - 1; | ||
var openParenthesisIndex = stack.map(function (item) { return isOpenParenthesisToken(item); }).lastIndexOf(true); | ||
var valueTokens = stack | ||
.slice(openParenthesisIndex, closeParenthesisIndex) | ||
.filter(function (item) { return isUnquotedToken(item) || isQuotedToken(item); }); | ||
return { | ||
consumed: closeParenthesisIndex - openParenthesisIndex + 1, | ||
produced: createValueNode(valueTokens.map(function (valueToken) { return (isQuotedToken(valueToken) ? valueToken.value.slice(1, -1) : valueToken.value); }), true), | ||
}; | ||
var closeParenthesisIndex = stack.length - 1; | ||
var openParenthesisIndex = stack | ||
.map(function (item) { | ||
return isOpenParenthesisToken(item); | ||
}) | ||
.lastIndexOf(true); | ||
var valueTokens = stack.slice(openParenthesisIndex, closeParenthesisIndex).filter(function (item) { | ||
return isUnquotedToken(item) || isQuotedToken(item); | ||
}); | ||
return { | ||
consumed: closeParenthesisIndex - openParenthesisIndex + 1, | ||
produced: createValueNode( | ||
valueTokens.map(function (valueToken) { | ||
return isQuotedToken(valueToken) ? valueToken.value.slice(1, -1) : valueToken.value; | ||
}), | ||
true | ||
), | ||
}; | ||
}; | ||
var comparisonExpressionProduction = function (stack) { | ||
var selector = stack[stack.length - 3]; | ||
var operator = stack[stack.length - 2]; | ||
var value = stack[stack.length - 1]; | ||
return { | ||
consumed: 3, | ||
produced: createComparisonNode(selector, operator.value, value, true), | ||
}; | ||
var selector = stack[stack.length - 3]; | ||
var operator = stack[stack.length - 2]; | ||
var value = stack[stack.length - 1]; | ||
return { | ||
consumed: 3, | ||
produced: createComparisonNode(selector, operator.value, value, true), | ||
}; | ||
}; | ||
var logicalExpressionProduction = function (stack) { | ||
var left = stack[stack.length - 3]; | ||
var operator = stack[stack.length - 2]; | ||
var right = stack[stack.length - 1]; | ||
return { | ||
consumed: 3, | ||
produced: createLogicNode(left, operator.value, right, true), | ||
}; | ||
var left = stack[stack.length - 3]; | ||
var operator = stack[stack.length - 2]; | ||
var right = stack[stack.length - 1]; | ||
return { | ||
consumed: 3, | ||
produced: createLogicNode(left, operator.value, right, true), | ||
}; | ||
}; | ||
var groupExpressionProduction = function (stack) { | ||
var expression = stack[stack.length - 2]; | ||
return { | ||
consumed: 3, | ||
produced: expression, | ||
}; | ||
var expression = stack[stack.length - 2]; | ||
return { | ||
consumed: 3, | ||
produced: expression, | ||
}; | ||
}; | ||
var productions = [ | ||
/* 0 */ selectorProduction, | ||
/* 1 */ singleValueProduction, | ||
/* 2 */ multiValueProduction, | ||
/* 3 */ comparisonExpressionProduction, | ||
/* 4 */ logicalExpressionProduction, | ||
/* 5 */ groupExpressionProduction, | ||
/* 0 */ selectorProduction, | ||
/* 1 */ singleValueProduction, | ||
/* 2 */ multiValueProduction, | ||
/* 3 */ comparisonExpressionProduction, | ||
/* 4 */ logicalExpressionProduction, | ||
/* 5 */ groupExpressionProduction, | ||
]; | ||
var tokenMatchers = [ | ||
/* 0 */ isOpenParenthesisToken, | ||
/* 1 */ isCloseParenthesisToken, | ||
/* 2 */ isUnquotedToken, | ||
/* 3 */ isQuotedToken, | ||
/* 4 */ isComparisonOperatorToken, | ||
/* 5 */ isOrOperatorToken, | ||
/* 6 */ isAndOperatorToken, | ||
/* 7 */ isEndToken, | ||
/* 0 */ isOpenParenthesisToken, | ||
/* 1 */ isCloseParenthesisToken, | ||
/* 2 */ isUnquotedToken, | ||
/* 3 */ isQuotedToken, | ||
/* 4 */ isComparisonOperatorToken, | ||
/* 5 */ isOrOperatorToken, | ||
/* 6 */ isAndOperatorToken, | ||
/* 7 */ isEndToken, | ||
]; | ||
var nodeMatchers = [ | ||
/* 0 */ isSelectorNode, | ||
/* 1 */ isValueNode, | ||
/* 2 */ isExpressionNode, | ||
]; | ||
var nodeMatchers = [/* 0 */ isSelectorNode, /* 1 */ isValueNode, /* 2 */ isExpressionNode]; | ||
// prettier-ignore | ||
@@ -459,118 +532,128 @@ var table = [ | ||
function getParserTokenOperation(state, token) { | ||
return table[state][0][tokenMatchers.findIndex(function (matcher) { return matcher(token); })]; | ||
return table[state][0][ | ||
tokenMatchers.findIndex(function (matcher) { | ||
return matcher(token); | ||
}) | ||
]; | ||
} | ||
function getParserNodeOperation(state, node) { | ||
return table[state][1][nodeMatchers.findIndex(function (matcher) { return matcher(node); })]; | ||
return table[state][1][ | ||
nodeMatchers.findIndex(function (matcher) { | ||
return matcher(node); | ||
}) | ||
]; | ||
} | ||
function getMostMeaningfulInvalidToken(context) { | ||
if (context.position > 0 && | ||
isCloseParenthesisToken(context.tokens[context.position - 1]) && | ||
context.parent === null) { | ||
// in this case we were not able to pop CLOSE_PARENTHESIS token, which was invalid in the first place | ||
return context.tokens[context.position - 1]; | ||
} | ||
return context.tokens[context.position]; | ||
if ( | ||
context.position > 0 && | ||
isCloseParenthesisToken(context.tokens[context.position - 1]) && | ||
context.parent === null | ||
) { | ||
// in this case we were not able to pop CLOSE_PARENTHESIS token, which was invalid in the first place | ||
return context.tokens[context.position - 1]; | ||
} | ||
return context.tokens[context.position]; | ||
} | ||
function handleShift(context, shiftOperation) { | ||
// we can perform side-effects on shift operation to reduce memory usage | ||
context.stack.push(context.tokens[context.position]); | ||
context.state.push(shiftOperation.state); | ||
context.position = context.position + 1; | ||
return context; | ||
// we can perform side-effects on shift operation to reduce memory usage | ||
context.stack.push(context.tokens[context.position]); | ||
context.state.push(shiftOperation.state); | ||
context.position = context.position + 1; | ||
return context; | ||
} | ||
function handlePush(context, pushOperation) { | ||
return { | ||
position: context.position + 1, | ||
tokens: context.tokens, | ||
stack: [context.tokens[context.position]], | ||
state: [pushOperation.state], | ||
parent: context, | ||
}; | ||
return { | ||
position: context.position + 1, | ||
tokens: context.tokens, | ||
stack: [context.tokens[context.position]], | ||
state: [pushOperation.state], | ||
parent: context, | ||
}; | ||
} | ||
function handleGoTo(context, gotoOperation) { | ||
// we can perform side-effects on goto operation to reduce memory usage | ||
context.state.push(gotoOperation.state); | ||
return context; | ||
// we can perform side-effects on goto operation to reduce memory usage | ||
context.state.push(gotoOperation.state); | ||
return context; | ||
} | ||
function handleReduce(context, reduceOperation, input) { | ||
var _a = productions[reduceOperation.production](context.stack), consumed = _a.consumed, produced = _a.produced; | ||
// we can perform side-effects on reduce operation to reduce memory usage | ||
for (var i = 0; i < consumed; ++i) { | ||
context.stack.pop(); | ||
context.state.pop(); | ||
} | ||
context.stack.push(produced); | ||
var stateAfterReduce = getParserContextState(context); | ||
var nodeAfterReduce = getParserContextHead(context); | ||
var gotoOperation = getParserNodeOperation(stateAfterReduce, nodeAfterReduce); | ||
if (gotoOperation) { | ||
context = handleGoTo(context, gotoOperation); | ||
} | ||
else { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
return context; | ||
var _a = productions[reduceOperation.production](context.stack), | ||
consumed = _a.consumed, | ||
produced = _a.produced; | ||
// we can perform side-effects on reduce operation to reduce memory usage | ||
for (var i = 0; i < consumed; ++i) { | ||
context.stack.pop(); | ||
context.state.pop(); | ||
} | ||
context.stack.push(produced); | ||
var stateAfterReduce = getParserContextState(context); | ||
var nodeAfterReduce = getParserContextHead(context); | ||
var gotoOperation = getParserNodeOperation(stateAfterReduce, nodeAfterReduce); | ||
if (gotoOperation) { | ||
context = handleGoTo(context, gotoOperation); | ||
} else { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
return context; | ||
} | ||
function handlePop(context, popOperation, input) { | ||
if (!context.parent) { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
var produced = productions[popOperation.production](context.stack).produced; | ||
// we can perform side-effects on pop operation to reduce memory usage (as child context will not be used anymore) | ||
context.parent.position = context.position; | ||
context.parent.stack.push(produced); | ||
context = context.parent; | ||
var stateAfterPop = getParserContextState(context); | ||
var nodeAfterPop = getParserContextHead(context); | ||
var gotoOperation = getParserNodeOperation(stateAfterPop, nodeAfterPop); | ||
if (gotoOperation) { | ||
context = handleGoTo(context, gotoOperation); | ||
} | ||
else { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
return context; | ||
if (!context.parent) { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
var produced = productions[popOperation.production](context.stack).produced; | ||
// we can perform side-effects on pop operation to reduce memory usage (as child context will not be used anymore) | ||
context.parent.position = context.position; | ||
context.parent.stack.push(produced); | ||
context = context.parent; | ||
var stateAfterPop = getParserContextState(context); | ||
var nodeAfterPop = getParserContextHead(context); | ||
var gotoOperation = getParserNodeOperation(stateAfterPop, nodeAfterPop); | ||
if (gotoOperation) { | ||
context = handleGoTo(context, gotoOperation); | ||
} else { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
return context; | ||
} | ||
function handleAccept(context, input) { | ||
if (context.parent !== null) { | ||
throw createErrorForUnclosedParenthesis(getMostMeaningfulInvalidToken(context), input, context.parent.position); | ||
} | ||
return getParserContextHead(context); | ||
if (context.parent !== null) { | ||
throw createErrorForUnclosedParenthesis(getMostMeaningfulInvalidToken(context), input, context.parent.position); | ||
} | ||
return getParserContextHead(context); | ||
} | ||
function parse(source) { | ||
if (typeof source !== "string") { | ||
throw new TypeError("The argument passed to the \"parse\" function has to be a string, \"" + source + "\" passed."); | ||
if (typeof source !== "string") { | ||
throw new TypeError('The argument passed to the "parse" function has to be a string, "' + source + '" passed.'); | ||
} | ||
var tokens = lex(source); | ||
if (tokens.length === 1 && tokens[0].type === "END") { | ||
throw createErrorForEmptyInput(tokens[0], source); | ||
} | ||
var context = createParserContext(tokens); | ||
while (context.position < context.tokens.length) { | ||
var state = getParserContextState(context); | ||
var token = getParserContextToken(context); | ||
var operation = getParserTokenOperation(state, token); | ||
if (!operation) { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), source); | ||
} | ||
var tokens = lex(source); | ||
if (tokens.length === 1 && tokens[0].type === "END") { | ||
throw createErrorForEmptyInput(tokens[0], source); | ||
switch (operation.type) { | ||
case OperationType.SHIFT: | ||
context = handleShift(context, operation); | ||
break; | ||
case OperationType.PUSH: | ||
context = handlePush(context, operation); | ||
break; | ||
case OperationType.REDUCE: | ||
context = handleReduce(context, operation, source); | ||
break; | ||
case OperationType.POP: | ||
context = handlePop(context, operation, source); | ||
break; | ||
case OperationType.ACCEPT: | ||
return handleAccept(context, source); | ||
} | ||
var context = createParserContext(tokens); | ||
while (context.position < context.tokens.length) { | ||
var state = getParserContextState(context); | ||
var token = getParserContextToken(context); | ||
var operation = getParserTokenOperation(state, token); | ||
if (!operation) { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), source); | ||
} | ||
switch (operation.type) { | ||
case OperationType.SHIFT: | ||
context = handleShift(context, operation); | ||
break; | ||
case OperationType.PUSH: | ||
context = handlePush(context, operation); | ||
break; | ||
case OperationType.REDUCE: | ||
context = handleReduce(context, operation, source); | ||
break; | ||
case OperationType.POP: | ||
context = handlePop(context, operation, source); | ||
break; | ||
case OperationType.ACCEPT: | ||
return handleAccept(context, source); | ||
} | ||
} | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), source); | ||
} | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), source); | ||
} | ||
export { parse }; |
@@ -1,146 +0,192 @@ | ||
'use strict'; | ||
"use strict"; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var ast = require('@rsql/ast'); | ||
var ast = require("@rsql/ast"); | ||
var TokenType = { | ||
UNQUOTED: "UNQUOTED", | ||
QUOTED: "QUOTED", | ||
PARENTHESIS: "PARENTHESIS", | ||
OPERATOR: "OPERATOR", | ||
END: "END", | ||
UNQUOTED: "UNQUOTED", | ||
QUOTED: "QUOTED", | ||
PARENTHESIS: "PARENTHESIS", | ||
OPERATOR: "OPERATOR", | ||
END: "END", | ||
}; | ||
function createNamedToken(token, toString) { | ||
Object.defineProperty(token, "toString", { | ||
value: toString, | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
}); | ||
return token; | ||
Object.defineProperty(token, "toString", { | ||
value: toString, | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
}); | ||
return token; | ||
} | ||
function createUnquotedToken(value, position) { | ||
return createNamedToken({ | ||
type: TokenType.UNQUOTED, | ||
value: value, | ||
position: position, | ||
}, function () { return "UnquotedToken(" + value + ")"; }); | ||
return createNamedToken( | ||
{ | ||
type: TokenType.UNQUOTED, | ||
value: value, | ||
position: position, | ||
}, | ||
function () { | ||
return "UnquotedToken(" + value + ")"; | ||
} | ||
); | ||
} | ||
function createQuotedToken(value, position) { | ||
return createNamedToken({ | ||
type: TokenType.QUOTED, | ||
value: value, | ||
position: position, | ||
}, function () { return "QuotedToken(" + value + ")"; }); | ||
return createNamedToken( | ||
{ | ||
type: TokenType.QUOTED, | ||
value: value, | ||
position: position, | ||
}, | ||
function () { | ||
return "QuotedToken(" + value + ")"; | ||
} | ||
); | ||
} | ||
function createParenthesisToken(value, position) { | ||
return createNamedToken({ | ||
type: TokenType.PARENTHESIS, | ||
value: value, | ||
position: position, | ||
}, function () { return "ParenthesisToken(" + value + ")"; }); | ||
return createNamedToken( | ||
{ | ||
type: TokenType.PARENTHESIS, | ||
value: value, | ||
position: position, | ||
}, | ||
function () { | ||
return "ParenthesisToken(" + value + ")"; | ||
} | ||
); | ||
} | ||
function createOperatorToken(value, position) { | ||
return createNamedToken({ | ||
type: TokenType.OPERATOR, | ||
value: value, | ||
position: position, | ||
}, function () { return "OperatorToken(" + value + ")"; }); | ||
return createNamedToken( | ||
{ | ||
type: TokenType.OPERATOR, | ||
value: value, | ||
position: position, | ||
}, | ||
function () { | ||
return "OperatorToken(" + value + ")"; | ||
} | ||
); | ||
} | ||
function createEndToken(position) { | ||
return createNamedToken({ | ||
type: TokenType.END, | ||
value: "END", | ||
position: position, | ||
}, function () { return "EndToken"; }); | ||
return createNamedToken( | ||
{ | ||
type: TokenType.END, | ||
value: "END", | ||
position: position, | ||
}, | ||
function () { | ||
return "EndToken"; | ||
} | ||
); | ||
} | ||
function isToken(candidate) { | ||
return (Object.prototype.hasOwnProperty.call(candidate, "type") && | ||
Object.prototype.hasOwnProperty.call(candidate, "value") && | ||
Object.prototype.hasOwnProperty.call(candidate, "position")); | ||
return ( | ||
Object.prototype.hasOwnProperty.call(candidate, "type") && | ||
Object.prototype.hasOwnProperty.call(candidate, "value") && | ||
Object.prototype.hasOwnProperty.call(candidate, "position") | ||
); | ||
} | ||
function isUnquotedToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.UNQUOTED; | ||
return isToken(candidate) && candidate.type === TokenType.UNQUOTED; | ||
} | ||
function isQuotedToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.QUOTED; | ||
return isToken(candidate) && candidate.type === TokenType.QUOTED; | ||
} | ||
function isParenthesisToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.PARENTHESIS; | ||
return isToken(candidate) && candidate.type === TokenType.PARENTHESIS; | ||
} | ||
function isOpenParenthesisToken(candidate) { | ||
return isParenthesisToken(candidate) && candidate.value === "("; | ||
return isParenthesisToken(candidate) && candidate.value === "("; | ||
} | ||
function isCloseParenthesisToken(candidate) { | ||
return isParenthesisToken(candidate) && candidate.value === ")"; | ||
return isParenthesisToken(candidate) && candidate.value === ")"; | ||
} | ||
function isOperatorToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.OPERATOR; | ||
return isToken(candidate) && candidate.type === TokenType.OPERATOR; | ||
} | ||
function isComparisonOperatorToken(candidate) { | ||
return isOperatorToken(candidate) && ast.isComparisonOperator(candidate.value); | ||
return isOperatorToken(candidate) && ast.isComparisonOperator(candidate.value); | ||
} | ||
function isOrOperatorToken(candidate) { | ||
return isOperatorToken(candidate) && ast.isLogicOperator(candidate.value, ast.OR); | ||
return isOperatorToken(candidate) && ast.isLogicOperator(candidate.value, ast.OR); | ||
} | ||
function isAndOperatorToken(candidate) { | ||
return isOperatorToken(candidate) && ast.isLogicOperator(candidate.value, ast.AND); | ||
return isOperatorToken(candidate) && ast.isLogicOperator(candidate.value, ast.AND); | ||
} | ||
function isEndToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.END; | ||
return isToken(candidate) && candidate.type === TokenType.END; | ||
} | ||
function createErrorForUnexpectedCharacter(position, source) { | ||
var character = source[position]; | ||
return new SyntaxError("Unexpected character '" + character + "' at position " + (position + 1) + " in \"" + source + "\"."); | ||
var character = source[position]; | ||
return new SyntaxError( | ||
"Unexpected character '" + character + "' at position " + (position + 1) + ' in "' + source + '".' | ||
); | ||
} | ||
function createErrorForUnclosedQuote(position, source) { | ||
var character = source[position]; | ||
return new SyntaxError("Unclosed quote '" + character + "' at position " + (position + 1) + " in \"" + source + "\"."); | ||
var character = source[position]; | ||
return new SyntaxError("Unclosed quote '" + character + "' at position " + (position + 1) + ' in "' + source + '".'); | ||
} | ||
function createErrorForUnexpectedToken(token, source) { | ||
return new SyntaxError(isEndToken(token) | ||
? "Unexpected end in \"" + source + "\"." | ||
: "Unexpected " + (token.value.length > 1 ? "string" : "character") + " '" + token.value + "' at position " + (token.position + 1) + " in \"" + source + "\"."); | ||
return new SyntaxError( | ||
isEndToken(token) | ||
? 'Unexpected end in "' + source + '".' | ||
: "Unexpected " + | ||
(token.value.length > 1 ? "string" : "character") + | ||
" '" + | ||
token.value + | ||
"' at position " + | ||
(token.position + 1) + | ||
' in "' + | ||
source + | ||
'".' | ||
); | ||
} | ||
function createErrorForUnclosedParenthesis(token, source, parentPosition) { | ||
return new SyntaxError("Unexpected end in \"" + source + "\". Did you forget to close parenthesis at position " + (parentPosition + 1) + "?"); | ||
return new SyntaxError( | ||
'Unexpected end in "' + source + '". Did you forget to close parenthesis at position ' + (parentPosition + 1) + "?" | ||
); | ||
} | ||
function createErrorForEmptyInput(token, source) { | ||
return new SyntaxError("Unexpected end in \"" + source + "\". Cannot parse empty string."); | ||
return new SyntaxError('Unexpected end in "' + source + '". Cannot parse empty string.'); | ||
} | ||
function createLexerContext(input) { | ||
return { | ||
position: 0, | ||
buffer: input, | ||
length: input.length, | ||
}; | ||
return { | ||
position: 0, | ||
buffer: input, | ||
length: input.length, | ||
}; | ||
} | ||
var seekComparisonCustomOperatorToken = function (context) { | ||
// scan for FIQL custom operators: =[a-z]= | ||
// assume that context.buffer[context.position] === '=' | ||
var endPosition = context.position + 1; | ||
// scan for chars from a to z | ||
while (endPosition < context.length && | ||
context.buffer.charCodeAt(endPosition) >= 97 && // a | ||
context.buffer.charCodeAt(endPosition) <= 122 // z | ||
) { | ||
endPosition++; | ||
} | ||
if (context.buffer[endPosition] === "=") { | ||
var operator = context.buffer.slice(context.position, endPosition + 1); | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
// scan for FIQL custom operators: =[a-z]= | ||
// assume that context.buffer[context.position] === '=' | ||
var endPosition = context.position + 1; | ||
// scan for chars from a to z | ||
while ( | ||
endPosition < context.length && | ||
context.buffer.charCodeAt(endPosition) >= 97 && // a | ||
context.buffer.charCodeAt(endPosition) <= 122 // z | ||
) { | ||
endPosition++; | ||
} | ||
if (context.buffer[endPosition] === "=") { | ||
var operator = context.buffer.slice(context.position, endPosition + 1); | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
}; | ||
function createScanSymbol(symbols) { | ||
return function scanSymbol(context) { | ||
return symbols.find(function (symbol) { return context.buffer.substr(context.position, symbol.length) === symbol; }) || null; | ||
}; | ||
return function scanSymbol(context) { | ||
return ( | ||
symbols.find(function (symbol) { | ||
return context.buffer.substr(context.position, symbol.length) === symbol; | ||
}) || null | ||
); | ||
}; | ||
} | ||
@@ -150,26 +196,30 @@ | ||
var seekComparisonOperatorToken = function (context) { | ||
var operator = scanAnyComparisonOperator(context); | ||
if (operator) { | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
var operator = scanAnyComparisonOperator(context); | ||
if (operator) { | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
}; | ||
var seekLogicCanonicalOperatorToken = function (context) { | ||
// we assume that symbol is a valid LogicOperator | ||
var operator = context.buffer.charAt(context.position); | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += 1; | ||
return token; | ||
// we assume that symbol is a valid LogicOperator | ||
var operator = context.buffer.charAt(context.position); | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += 1; | ||
return token; | ||
}; | ||
function createScanNonReservedSymbol(symbols) { | ||
return function scanNonReservedSymbol(context) { | ||
return (symbols.find(function (symbol) { | ||
return context.buffer.substr(context.position, symbol.length) === symbol && | ||
context.buffer[context.position + symbol.length] === " "; | ||
}) || null); | ||
}; | ||
return function scanNonReservedSymbol(context) { | ||
return ( | ||
symbols.find(function (symbol) { | ||
return ( | ||
context.buffer.substr(context.position, symbol.length) === symbol && | ||
context.buffer[context.position + symbol.length] === " " | ||
); | ||
}) || null | ||
); | ||
}; | ||
} | ||
@@ -179,53 +229,56 @@ | ||
var seekLogicVerboseOperatorToken = function (context) { | ||
var operator = scanLogicVerboseOperator(context); | ||
if (operator) { | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
var operator = scanLogicVerboseOperator(context); | ||
if (operator) { | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
}; | ||
var seekParenthesisToken = function (context) { | ||
// we assume that parenthesis is a valid Parenthesis | ||
var parenthesis = context.buffer.charAt(context.position); | ||
var token = createParenthesisToken(parenthesis, context.position); | ||
context.position += 1; | ||
return token; | ||
// we assume that parenthesis is a valid Parenthesis | ||
var parenthesis = context.buffer.charAt(context.position); | ||
var token = createParenthesisToken(parenthesis, context.position); | ||
context.position += 1; | ||
return token; | ||
}; | ||
var seekQuotedToken = function (context) { | ||
// we assume that quote is a valid QuoteSymbol | ||
var quote = context.buffer.charAt(context.position); | ||
var endPosition = context.position; | ||
while (endPosition < context.length) { | ||
endPosition = context.buffer.indexOf(quote, endPosition + 1); | ||
if (endPosition === -1) { | ||
throw createErrorForUnclosedQuote(context.position, context.buffer); | ||
} | ||
// scan back for escape characters | ||
var escaped = false; | ||
for (var scanPosition = endPosition - 1; context.buffer[scanPosition] === "\\" && scanPosition > context.position; scanPosition--) { | ||
escaped = !escaped; | ||
} | ||
if (!escaped) { | ||
// it's not escaped quote - we've found terminating quote | ||
break; | ||
} | ||
// we assume that quote is a valid QuoteSymbol | ||
var quote = context.buffer.charAt(context.position); | ||
var endPosition = context.position; | ||
while (endPosition < context.length) { | ||
endPosition = context.buffer.indexOf(quote, endPosition + 1); | ||
if (endPosition === -1) { | ||
throw createErrorForUnclosedQuote(context.position, context.buffer); | ||
} | ||
var value = context.buffer.substring(context.position, endPosition + 1); | ||
var token = createQuotedToken(value, context.position); | ||
context.position = endPosition + 1; | ||
return token; | ||
// scan back for escape characters | ||
var escaped = false; | ||
for ( | ||
var scanPosition = endPosition - 1; | ||
context.buffer[scanPosition] === "\\" && scanPosition > context.position; | ||
scanPosition-- | ||
) { | ||
escaped = !escaped; | ||
} | ||
if (!escaped) { | ||
// it's not escaped quote - we've found terminating quote | ||
break; | ||
} | ||
} | ||
var value = context.buffer.substring(context.position, endPosition + 1); | ||
var token = createQuotedToken(value, context.position); | ||
context.position = endPosition + 1; | ||
return token; | ||
}; | ||
var seekUnquotedToken = function (context) { | ||
var endPosition = context.position + 1; | ||
while (endPosition < context.length && | ||
ast.ReservedChars.indexOf(context.buffer.charAt(endPosition)) === -1) { | ||
endPosition++; | ||
} | ||
var token = createUnquotedToken(context.buffer.substring(context.position, endPosition), context.position); | ||
context.position = endPosition; | ||
return token; | ||
var endPosition = context.position + 1; | ||
while (endPosition < context.length && ast.ReservedChars.indexOf(context.buffer.charAt(endPosition)) === -1) { | ||
endPosition++; | ||
} | ||
var token = createUnquotedToken(context.buffer.substring(context.position, endPosition), context.position); | ||
context.position = endPosition; | ||
return token; | ||
}; | ||
@@ -235,129 +288,129 @@ | ||
var skipWhitespace = function (context) { | ||
while (context.position < context.length && WhitespaceChars.indexOf(context.buffer.charAt(context.position)) !== -1) { | ||
context.position++; | ||
} | ||
while (context.position < context.length && WhitespaceChars.indexOf(context.buffer.charAt(context.position)) !== -1) { | ||
context.position++; | ||
} | ||
}; | ||
var seekAnyToken = function (context) { | ||
// first skip all whitespace chars | ||
skipWhitespace(context); | ||
if (context.position >= context.length) { | ||
return null; | ||
} | ||
// then decide what to do based on the current char | ||
var char = context.buffer.charAt(context.position); | ||
var token = null; | ||
switch (char) { | ||
// single char symbols | ||
case "'": | ||
case '"': | ||
token = seekQuotedToken(context); | ||
break; | ||
// single char symbols | ||
case "(": | ||
case ")": | ||
token = seekParenthesisToken(context); | ||
break; | ||
// single char symbols | ||
case ",": | ||
case ";": | ||
token = seekLogicCanonicalOperatorToken(context); | ||
break; | ||
// multi char symbols for comparison operator | ||
case "=": | ||
case "!": | ||
case "~": | ||
case "<": | ||
case ">": | ||
token = seekComparisonOperatorToken(context); | ||
if (!token && char === "=") { | ||
token = seekComparisonCustomOperatorToken(context); | ||
} | ||
break; | ||
// unreserved char | ||
default: | ||
// there are VerboseLogicOperators (and, or) that uses not reserved chars | ||
token = seekLogicVerboseOperatorToken(context); | ||
// if it's not an OperatorToken, process UnquotedToken | ||
if (!token) { | ||
token = seekUnquotedToken(context); | ||
} | ||
} | ||
if (!token) { | ||
throw createErrorForUnexpectedCharacter(context.position, context.buffer); | ||
} | ||
return token; | ||
// first skip all whitespace chars | ||
skipWhitespace(context); | ||
if (context.position >= context.length) { | ||
return null; | ||
} | ||
// then decide what to do based on the current char | ||
var char = context.buffer.charAt(context.position); | ||
var token = null; | ||
switch (char) { | ||
// single char symbols | ||
case "'": | ||
case '"': | ||
token = seekQuotedToken(context); | ||
break; | ||
// single char symbols | ||
case "(": | ||
case ")": | ||
token = seekParenthesisToken(context); | ||
break; | ||
// single char symbols | ||
case ",": | ||
case ";": | ||
token = seekLogicCanonicalOperatorToken(context); | ||
break; | ||
// multi char symbols for comparison operator | ||
case "=": | ||
case "!": | ||
case "~": | ||
case "<": | ||
case ">": | ||
token = seekComparisonOperatorToken(context); | ||
if (!token && char === "=") { | ||
token = seekComparisonCustomOperatorToken(context); | ||
} | ||
break; | ||
// unreserved char | ||
default: | ||
// there are VerboseLogicOperators (and, or) that uses not reserved chars | ||
token = seekLogicVerboseOperatorToken(context); | ||
// if it's not an OperatorToken, process UnquotedToken | ||
if (!token) { | ||
token = seekUnquotedToken(context); | ||
} | ||
} | ||
if (!token) { | ||
throw createErrorForUnexpectedCharacter(context.position, context.buffer); | ||
} | ||
return token; | ||
}; | ||
function lex(input) { | ||
var context = createLexerContext(input); | ||
var tokens = []; | ||
for (var token = seekAnyToken(context); token !== null; token = seekAnyToken(context)) { | ||
tokens.push(token); | ||
} | ||
tokens.push(createEndToken(context.position)); | ||
return tokens; | ||
var context = createLexerContext(input); | ||
var tokens = []; | ||
for (var token = seekAnyToken(context); token !== null; token = seekAnyToken(context)) { | ||
tokens.push(token); | ||
} | ||
tokens.push(createEndToken(context.position)); | ||
return tokens; | ||
} | ||
function getParserContextState(context) { | ||
return context.state[context.state.length - 1]; | ||
return context.state[context.state.length - 1]; | ||
} | ||
function getParserContextToken(context) { | ||
return context.tokens[context.position]; | ||
return context.tokens[context.position]; | ||
} | ||
function getParserContextHead(context) { | ||
return context.stack[context.stack.length - 1]; | ||
return context.stack[context.stack.length - 1]; | ||
} | ||
function createParserContext(tokens) { | ||
return { | ||
position: 0, | ||
tokens: tokens, | ||
stack: [], | ||
state: [0], | ||
parent: null, | ||
}; | ||
return { | ||
position: 0, | ||
tokens: tokens, | ||
stack: [], | ||
state: [0], | ||
parent: null, | ||
}; | ||
} | ||
var OperationType = { | ||
SHIFT: 0, | ||
PUSH: 1, | ||
REDUCE: 2, | ||
POP: 3, | ||
GOTO: 4, | ||
ACCEPT: 5, | ||
SHIFT: 0, | ||
PUSH: 1, | ||
REDUCE: 2, | ||
POP: 3, | ||
GOTO: 4, | ||
ACCEPT: 5, | ||
}; | ||
function shift(state) { | ||
return { | ||
type: OperationType.SHIFT, | ||
state: state, | ||
}; | ||
return { | ||
type: OperationType.SHIFT, | ||
state: state, | ||
}; | ||
} | ||
function reduce(production) { | ||
return { | ||
type: OperationType.REDUCE, | ||
production: production, | ||
}; | ||
return { | ||
type: OperationType.REDUCE, | ||
production: production, | ||
}; | ||
} | ||
function push(state) { | ||
return { | ||
type: OperationType.PUSH, | ||
state: state, | ||
}; | ||
return { | ||
type: OperationType.PUSH, | ||
state: state, | ||
}; | ||
} | ||
function pop(production) { | ||
return { | ||
type: OperationType.POP, | ||
production: production, | ||
}; | ||
return { | ||
type: OperationType.POP, | ||
production: production, | ||
}; | ||
} | ||
function goto(state) { | ||
return { | ||
type: OperationType.GOTO, | ||
state: state, | ||
}; | ||
return { | ||
type: OperationType.GOTO, | ||
state: state, | ||
}; | ||
} | ||
function accept() { | ||
return { | ||
type: OperationType.ACCEPT, | ||
}; | ||
return { | ||
type: OperationType.ACCEPT, | ||
}; | ||
} | ||
@@ -367,76 +420,81 @@ var noop = undefined; | ||
var selectorProduction = function (stack) { | ||
var token = stack[stack.length - 1]; | ||
return { | ||
consumed: 1, | ||
produced: ast.createSelectorNode(token.value, true), | ||
}; | ||
var token = stack[stack.length - 1]; | ||
return { | ||
consumed: 1, | ||
produced: ast.createSelectorNode(token.value, true), | ||
}; | ||
}; | ||
var singleValueProduction = function (stack) { | ||
var token = stack[stack.length - 1]; | ||
var value = isQuotedToken(token) ? token.value.slice(1, -1) : token.value; | ||
return { | ||
consumed: 1, | ||
produced: ast.createValueNode(value, true), | ||
}; | ||
var token = stack[stack.length - 1]; | ||
var value = isQuotedToken(token) ? token.value.slice(1, -1) : token.value; | ||
return { | ||
consumed: 1, | ||
produced: ast.createValueNode(value, true), | ||
}; | ||
}; | ||
var multiValueProduction = function (stack) { | ||
var closeParenthesisIndex = stack.length - 1; | ||
var openParenthesisIndex = stack.map(function (item) { return isOpenParenthesisToken(item); }).lastIndexOf(true); | ||
var valueTokens = stack | ||
.slice(openParenthesisIndex, closeParenthesisIndex) | ||
.filter(function (item) { return isUnquotedToken(item) || isQuotedToken(item); }); | ||
return { | ||
consumed: closeParenthesisIndex - openParenthesisIndex + 1, | ||
produced: ast.createValueNode(valueTokens.map(function (valueToken) { return (isQuotedToken(valueToken) ? valueToken.value.slice(1, -1) : valueToken.value); }), true), | ||
}; | ||
var closeParenthesisIndex = stack.length - 1; | ||
var openParenthesisIndex = stack | ||
.map(function (item) { | ||
return isOpenParenthesisToken(item); | ||
}) | ||
.lastIndexOf(true); | ||
var valueTokens = stack.slice(openParenthesisIndex, closeParenthesisIndex).filter(function (item) { | ||
return isUnquotedToken(item) || isQuotedToken(item); | ||
}); | ||
return { | ||
consumed: closeParenthesisIndex - openParenthesisIndex + 1, | ||
produced: ast.createValueNode( | ||
valueTokens.map(function (valueToken) { | ||
return isQuotedToken(valueToken) ? valueToken.value.slice(1, -1) : valueToken.value; | ||
}), | ||
true | ||
), | ||
}; | ||
}; | ||
var comparisonExpressionProduction = function (stack) { | ||
var selector = stack[stack.length - 3]; | ||
var operator = stack[stack.length - 2]; | ||
var value = stack[stack.length - 1]; | ||
return { | ||
consumed: 3, | ||
produced: ast.createComparisonNode(selector, operator.value, value, true), | ||
}; | ||
var selector = stack[stack.length - 3]; | ||
var operator = stack[stack.length - 2]; | ||
var value = stack[stack.length - 1]; | ||
return { | ||
consumed: 3, | ||
produced: ast.createComparisonNode(selector, operator.value, value, true), | ||
}; | ||
}; | ||
var logicalExpressionProduction = function (stack) { | ||
var left = stack[stack.length - 3]; | ||
var operator = stack[stack.length - 2]; | ||
var right = stack[stack.length - 1]; | ||
return { | ||
consumed: 3, | ||
produced: ast.createLogicNode(left, operator.value, right, true), | ||
}; | ||
var left = stack[stack.length - 3]; | ||
var operator = stack[stack.length - 2]; | ||
var right = stack[stack.length - 1]; | ||
return { | ||
consumed: 3, | ||
produced: ast.createLogicNode(left, operator.value, right, true), | ||
}; | ||
}; | ||
var groupExpressionProduction = function (stack) { | ||
var expression = stack[stack.length - 2]; | ||
return { | ||
consumed: 3, | ||
produced: expression, | ||
}; | ||
var expression = stack[stack.length - 2]; | ||
return { | ||
consumed: 3, | ||
produced: expression, | ||
}; | ||
}; | ||
var productions = [ | ||
/* 0 */ selectorProduction, | ||
/* 1 */ singleValueProduction, | ||
/* 2 */ multiValueProduction, | ||
/* 3 */ comparisonExpressionProduction, | ||
/* 4 */ logicalExpressionProduction, | ||
/* 5 */ groupExpressionProduction, | ||
/* 0 */ selectorProduction, | ||
/* 1 */ singleValueProduction, | ||
/* 2 */ multiValueProduction, | ||
/* 3 */ comparisonExpressionProduction, | ||
/* 4 */ logicalExpressionProduction, | ||
/* 5 */ groupExpressionProduction, | ||
]; | ||
var tokenMatchers = [ | ||
/* 0 */ isOpenParenthesisToken, | ||
/* 1 */ isCloseParenthesisToken, | ||
/* 2 */ isUnquotedToken, | ||
/* 3 */ isQuotedToken, | ||
/* 4 */ isComparisonOperatorToken, | ||
/* 5 */ isOrOperatorToken, | ||
/* 6 */ isAndOperatorToken, | ||
/* 7 */ isEndToken, | ||
/* 0 */ isOpenParenthesisToken, | ||
/* 1 */ isCloseParenthesisToken, | ||
/* 2 */ isUnquotedToken, | ||
/* 3 */ isQuotedToken, | ||
/* 4 */ isComparisonOperatorToken, | ||
/* 5 */ isOrOperatorToken, | ||
/* 6 */ isAndOperatorToken, | ||
/* 7 */ isEndToken, | ||
]; | ||
var nodeMatchers = [ | ||
/* 0 */ ast.isSelectorNode, | ||
/* 1 */ ast.isValueNode, | ||
/* 2 */ ast.isExpressionNode, | ||
]; | ||
var nodeMatchers = [/* 0 */ ast.isSelectorNode, /* 1 */ ast.isValueNode, /* 2 */ ast.isExpressionNode]; | ||
// prettier-ignore | ||
@@ -463,118 +521,128 @@ var table = [ | ||
function getParserTokenOperation(state, token) { | ||
return table[state][0][tokenMatchers.findIndex(function (matcher) { return matcher(token); })]; | ||
return table[state][0][ | ||
tokenMatchers.findIndex(function (matcher) { | ||
return matcher(token); | ||
}) | ||
]; | ||
} | ||
function getParserNodeOperation(state, node) { | ||
return table[state][1][nodeMatchers.findIndex(function (matcher) { return matcher(node); })]; | ||
return table[state][1][ | ||
nodeMatchers.findIndex(function (matcher) { | ||
return matcher(node); | ||
}) | ||
]; | ||
} | ||
function getMostMeaningfulInvalidToken(context) { | ||
if (context.position > 0 && | ||
isCloseParenthesisToken(context.tokens[context.position - 1]) && | ||
context.parent === null) { | ||
// in this case we were not able to pop CLOSE_PARENTHESIS token, which was invalid in the first place | ||
return context.tokens[context.position - 1]; | ||
} | ||
return context.tokens[context.position]; | ||
if ( | ||
context.position > 0 && | ||
isCloseParenthesisToken(context.tokens[context.position - 1]) && | ||
context.parent === null | ||
) { | ||
// in this case we were not able to pop CLOSE_PARENTHESIS token, which was invalid in the first place | ||
return context.tokens[context.position - 1]; | ||
} | ||
return context.tokens[context.position]; | ||
} | ||
function handleShift(context, shiftOperation) { | ||
// we can perform side-effects on shift operation to reduce memory usage | ||
context.stack.push(context.tokens[context.position]); | ||
context.state.push(shiftOperation.state); | ||
context.position = context.position + 1; | ||
return context; | ||
// we can perform side-effects on shift operation to reduce memory usage | ||
context.stack.push(context.tokens[context.position]); | ||
context.state.push(shiftOperation.state); | ||
context.position = context.position + 1; | ||
return context; | ||
} | ||
function handlePush(context, pushOperation) { | ||
return { | ||
position: context.position + 1, | ||
tokens: context.tokens, | ||
stack: [context.tokens[context.position]], | ||
state: [pushOperation.state], | ||
parent: context, | ||
}; | ||
return { | ||
position: context.position + 1, | ||
tokens: context.tokens, | ||
stack: [context.tokens[context.position]], | ||
state: [pushOperation.state], | ||
parent: context, | ||
}; | ||
} | ||
function handleGoTo(context, gotoOperation) { | ||
// we can perform side-effects on goto operation to reduce memory usage | ||
context.state.push(gotoOperation.state); | ||
return context; | ||
// we can perform side-effects on goto operation to reduce memory usage | ||
context.state.push(gotoOperation.state); | ||
return context; | ||
} | ||
function handleReduce(context, reduceOperation, input) { | ||
var _a = productions[reduceOperation.production](context.stack), consumed = _a.consumed, produced = _a.produced; | ||
// we can perform side-effects on reduce operation to reduce memory usage | ||
for (var i = 0; i < consumed; ++i) { | ||
context.stack.pop(); | ||
context.state.pop(); | ||
} | ||
context.stack.push(produced); | ||
var stateAfterReduce = getParserContextState(context); | ||
var nodeAfterReduce = getParserContextHead(context); | ||
var gotoOperation = getParserNodeOperation(stateAfterReduce, nodeAfterReduce); | ||
if (gotoOperation) { | ||
context = handleGoTo(context, gotoOperation); | ||
} | ||
else { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
return context; | ||
var _a = productions[reduceOperation.production](context.stack), | ||
consumed = _a.consumed, | ||
produced = _a.produced; | ||
// we can perform side-effects on reduce operation to reduce memory usage | ||
for (var i = 0; i < consumed; ++i) { | ||
context.stack.pop(); | ||
context.state.pop(); | ||
} | ||
context.stack.push(produced); | ||
var stateAfterReduce = getParserContextState(context); | ||
var nodeAfterReduce = getParserContextHead(context); | ||
var gotoOperation = getParserNodeOperation(stateAfterReduce, nodeAfterReduce); | ||
if (gotoOperation) { | ||
context = handleGoTo(context, gotoOperation); | ||
} else { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
return context; | ||
} | ||
function handlePop(context, popOperation, input) { | ||
if (!context.parent) { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
var produced = productions[popOperation.production](context.stack).produced; | ||
// we can perform side-effects on pop operation to reduce memory usage (as child context will not be used anymore) | ||
context.parent.position = context.position; | ||
context.parent.stack.push(produced); | ||
context = context.parent; | ||
var stateAfterPop = getParserContextState(context); | ||
var nodeAfterPop = getParserContextHead(context); | ||
var gotoOperation = getParserNodeOperation(stateAfterPop, nodeAfterPop); | ||
if (gotoOperation) { | ||
context = handleGoTo(context, gotoOperation); | ||
} | ||
else { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
return context; | ||
if (!context.parent) { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
var produced = productions[popOperation.production](context.stack).produced; | ||
// we can perform side-effects on pop operation to reduce memory usage (as child context will not be used anymore) | ||
context.parent.position = context.position; | ||
context.parent.stack.push(produced); | ||
context = context.parent; | ||
var stateAfterPop = getParserContextState(context); | ||
var nodeAfterPop = getParserContextHead(context); | ||
var gotoOperation = getParserNodeOperation(stateAfterPop, nodeAfterPop); | ||
if (gotoOperation) { | ||
context = handleGoTo(context, gotoOperation); | ||
} else { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
return context; | ||
} | ||
function handleAccept(context, input) { | ||
if (context.parent !== null) { | ||
throw createErrorForUnclosedParenthesis(getMostMeaningfulInvalidToken(context), input, context.parent.position); | ||
} | ||
return getParserContextHead(context); | ||
if (context.parent !== null) { | ||
throw createErrorForUnclosedParenthesis(getMostMeaningfulInvalidToken(context), input, context.parent.position); | ||
} | ||
return getParserContextHead(context); | ||
} | ||
function parse(source) { | ||
if (typeof source !== "string") { | ||
throw new TypeError("The argument passed to the \"parse\" function has to be a string, \"" + source + "\" passed."); | ||
if (typeof source !== "string") { | ||
throw new TypeError('The argument passed to the "parse" function has to be a string, "' + source + '" passed.'); | ||
} | ||
var tokens = lex(source); | ||
if (tokens.length === 1 && tokens[0].type === "END") { | ||
throw createErrorForEmptyInput(tokens[0], source); | ||
} | ||
var context = createParserContext(tokens); | ||
while (context.position < context.tokens.length) { | ||
var state = getParserContextState(context); | ||
var token = getParserContextToken(context); | ||
var operation = getParserTokenOperation(state, token); | ||
if (!operation) { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), source); | ||
} | ||
var tokens = lex(source); | ||
if (tokens.length === 1 && tokens[0].type === "END") { | ||
throw createErrorForEmptyInput(tokens[0], source); | ||
switch (operation.type) { | ||
case OperationType.SHIFT: | ||
context = handleShift(context, operation); | ||
break; | ||
case OperationType.PUSH: | ||
context = handlePush(context, operation); | ||
break; | ||
case OperationType.REDUCE: | ||
context = handleReduce(context, operation, source); | ||
break; | ||
case OperationType.POP: | ||
context = handlePop(context, operation, source); | ||
break; | ||
case OperationType.ACCEPT: | ||
return handleAccept(context, source); | ||
} | ||
var context = createParserContext(tokens); | ||
while (context.position < context.tokens.length) { | ||
var state = getParserContextState(context); | ||
var token = getParserContextToken(context); | ||
var operation = getParserTokenOperation(state, token); | ||
if (!operation) { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), source); | ||
} | ||
switch (operation.type) { | ||
case OperationType.SHIFT: | ||
context = handleShift(context, operation); | ||
break; | ||
case OperationType.PUSH: | ||
context = handlePush(context, operation); | ||
break; | ||
case OperationType.REDUCE: | ||
context = handleReduce(context, operation, source); | ||
break; | ||
case OperationType.POP: | ||
context = handlePop(context, operation, source); | ||
break; | ||
case OperationType.ACCEPT: | ||
return handleAccept(context, source); | ||
} | ||
} | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), source); | ||
} | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), source); | ||
} | ||
exports.parse = parse; |
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(global = global || self, factory(global.RSQLParser = {})); | ||
}(this, (function (exports) { 'use strict'; | ||
typeof exports === "object" && typeof module !== "undefined" | ||
? factory(exports) | ||
: typeof define === "function" && define.amd | ||
? define(["exports"], factory) | ||
: ((global = global || self), factory((global.RSQLParser = {}))); | ||
})(this, function (exports) { | ||
"use strict"; | ||
var EQ = "=="; | ||
var NEQ = "!="; | ||
var LE = "<="; | ||
var GE = ">="; | ||
var LT = "<"; | ||
var GT = ">"; | ||
var IN = "=in="; | ||
var OUT = "=out="; | ||
var LE_VERBOSE = "=le="; | ||
var GE_VERBOSE = "=ge="; | ||
var LT_VERBOSE = "=lt="; | ||
var GT_VERBOSE = "=gt="; | ||
var ComparisonOperators = [EQ, NEQ, LE, GE, LT, GT, IN, OUT, LE_VERBOSE, GE_VERBOSE, LT_VERBOSE, GT_VERBOSE]; | ||
function mapToCanonicalComparisonOperator(operator) { | ||
switch (operator) { | ||
case LE_VERBOSE: | ||
return LE; | ||
case LT_VERBOSE: | ||
return LT; | ||
case GE_VERBOSE: | ||
return GE; | ||
case GT_VERBOSE: | ||
return GT; | ||
default: | ||
return operator; | ||
} | ||
var EQ = "=="; | ||
var NEQ = "!="; | ||
var LE = "<="; | ||
var GE = ">="; | ||
var LT = "<"; | ||
var GT = ">"; | ||
var IN = "=in="; | ||
var OUT = "=out="; | ||
var LE_VERBOSE = "=le="; | ||
var GE_VERBOSE = "=ge="; | ||
var LT_VERBOSE = "=lt="; | ||
var GT_VERBOSE = "=gt="; | ||
var ComparisonOperators = [EQ, NEQ, LE, GE, LT, GT, IN, OUT, LE_VERBOSE, GE_VERBOSE, LT_VERBOSE, GT_VERBOSE]; | ||
function mapToCanonicalComparisonOperator(operator) { | ||
switch (operator) { | ||
case LE_VERBOSE: | ||
return LE; | ||
case LT_VERBOSE: | ||
return LT; | ||
case GE_VERBOSE: | ||
return GE; | ||
case GT_VERBOSE: | ||
return GT; | ||
default: | ||
return operator; | ||
} | ||
var CUSTOM_OPERATOR_REGEXP = /^=[a-z]+=$/; | ||
function isCustomComparisonOperator(candidate) { | ||
return candidate.length > 2 && CUSTOM_OPERATOR_REGEXP.test(candidate); | ||
} | ||
function isComparisonOperator(candidate, operator) { | ||
switch (candidate) { | ||
case EQ: | ||
case NEQ: | ||
case LE: | ||
case GE: | ||
case LT: | ||
case GT: | ||
case IN: | ||
case OUT: | ||
case LE_VERBOSE: | ||
case GE_VERBOSE: | ||
case LT_VERBOSE: | ||
case GT_VERBOSE: | ||
return (operator === undefined || | ||
mapToCanonicalComparisonOperator(candidate) === mapToCanonicalComparisonOperator(operator)); | ||
default: | ||
if (isCustomComparisonOperator(candidate)) { | ||
return operator === undefined || candidate === operator; | ||
} | ||
else { | ||
return false; | ||
} | ||
} | ||
var CUSTOM_OPERATOR_REGEXP = /^=[a-z]+=$/; | ||
function isCustomComparisonOperator(candidate) { | ||
return candidate.length > 2 && CUSTOM_OPERATOR_REGEXP.test(candidate); | ||
} | ||
function isComparisonOperator(candidate, operator) { | ||
switch (candidate) { | ||
case EQ: | ||
case NEQ: | ||
case LE: | ||
case GE: | ||
case LT: | ||
case GT: | ||
case IN: | ||
case OUT: | ||
case LE_VERBOSE: | ||
case GE_VERBOSE: | ||
case LT_VERBOSE: | ||
case GT_VERBOSE: | ||
return ( | ||
operator === undefined || | ||
mapToCanonicalComparisonOperator(candidate) === mapToCanonicalComparisonOperator(operator) | ||
); | ||
default: | ||
if (isCustomComparisonOperator(candidate)) { | ||
return operator === undefined || candidate === operator; | ||
} else { | ||
return false; | ||
} | ||
} | ||
} | ||
var AND = ";"; | ||
var OR = ","; | ||
var AND_VERBOSE = "and"; | ||
var OR_VERBOSE = "or"; | ||
var VerboseLogicOperators = [AND_VERBOSE, OR_VERBOSE]; | ||
function mapToCanonicalLogicOperator(operator) { | ||
switch (operator) { | ||
case AND_VERBOSE: | ||
return AND; | ||
case OR_VERBOSE: | ||
return OR; | ||
default: | ||
return operator; | ||
} | ||
var AND = ";"; | ||
var OR = ","; | ||
var AND_VERBOSE = "and"; | ||
var OR_VERBOSE = "or"; | ||
var VerboseLogicOperators = [AND_VERBOSE, OR_VERBOSE]; | ||
function mapToCanonicalLogicOperator(operator) { | ||
switch (operator) { | ||
case AND_VERBOSE: | ||
return AND; | ||
case OR_VERBOSE: | ||
return OR; | ||
default: | ||
return operator; | ||
} | ||
function isLogicOperator(candidate, operator) { | ||
switch (candidate) { | ||
case AND: | ||
case OR: | ||
case AND_VERBOSE: | ||
case OR_VERBOSE: | ||
return operator === undefined || mapToCanonicalLogicOperator(candidate) === mapToCanonicalLogicOperator(operator); | ||
default: | ||
return false; | ||
} | ||
} | ||
function isLogicOperator(candidate, operator) { | ||
switch (candidate) { | ||
case AND: | ||
case OR: | ||
case AND_VERBOSE: | ||
case OR_VERBOSE: | ||
return ( | ||
operator === undefined || mapToCanonicalLogicOperator(candidate) === mapToCanonicalLogicOperator(operator) | ||
); | ||
default: | ||
return false; | ||
} | ||
} | ||
var ReservedChars = ['"', "'", "(", ")", ";", ",", "=", "!", "~", "<", ">", " ", "\n", "\t", "\r"]; | ||
var ReservedChars = ['"', "'", "(", ")", ";", ",", "=", "!", "~", "<", ">", " ", "\n", "\t", "\r"]; | ||
var NodeType = { | ||
SELECTOR: "SELECTOR", | ||
VALUE: "VALUE", | ||
COMPARISON: "COMPARISON", | ||
LOGIC: "LOGIC", | ||
}; | ||
function createNamedNode(node, toString) { | ||
Object.defineProperty(node, "toString", { | ||
value: toString, | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
}); | ||
return node; | ||
var NodeType = { | ||
SELECTOR: "SELECTOR", | ||
VALUE: "VALUE", | ||
COMPARISON: "COMPARISON", | ||
LOGIC: "LOGIC", | ||
}; | ||
function createNamedNode(node, toString) { | ||
Object.defineProperty(node, "toString", { | ||
value: toString, | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
}); | ||
return node; | ||
} | ||
function createSelectorNode(selector, skipChecks) { | ||
if (skipChecks === void 0) { | ||
skipChecks = false; | ||
} | ||
function createSelectorNode(selector, skipChecks) { | ||
if (skipChecks === void 0) { skipChecks = false; } | ||
if (!skipChecks) { | ||
if (typeof selector !== "string") { | ||
throw new TypeError("The \"selector\" has to be a \"string\", \"" + String(selector) + "\" passed."); | ||
} | ||
if (!selector || selector.length === 0) { | ||
throw new Error('The "selector" cannot be an empty string.'); | ||
} | ||
var reservedChar = ReservedChars.find(function (reservedChar) { return selector.indexOf(reservedChar) !== -1; }); | ||
if (reservedChar) { | ||
var position = selector.indexOf(reservedChar) + 1; | ||
throw new Error("The \"selector\" contains reserved character '" + reservedChar + "' at position " + position + " in \"" + selector + "\"."); | ||
} | ||
} | ||
return createNamedNode({ | ||
type: NodeType.SELECTOR, | ||
selector: selector, | ||
}, function () { return "SelectorNode(\"" + selector + "\")"; }); | ||
if (!skipChecks) { | ||
if (typeof selector !== "string") { | ||
throw new TypeError('The "selector" has to be a "string", "' + String(selector) + '" passed.'); | ||
} | ||
if (!selector || selector.length === 0) { | ||
throw new Error('The "selector" cannot be an empty string.'); | ||
} | ||
var reservedChar = ReservedChars.find(function (reservedChar) { | ||
return selector.indexOf(reservedChar) !== -1; | ||
}); | ||
if (reservedChar) { | ||
var position = selector.indexOf(reservedChar) + 1; | ||
throw new Error( | ||
'The "selector" contains reserved character \'' + | ||
reservedChar + | ||
"' at position " + | ||
position + | ||
' in "' + | ||
selector + | ||
'".' | ||
); | ||
} | ||
} | ||
function createValueNode(value, skipChecks) { | ||
if (skipChecks === void 0) { skipChecks = false; } | ||
if (!skipChecks) { | ||
if (typeof value !== "string" && !Array.isArray(value)) { | ||
throw new TypeError("The \"value\" has to be a \"string | string[]\", \"" + String(value) + "\" passed."); | ||
} | ||
if (Array.isArray(value) && value.length === 0) { | ||
throw new Error('The "value" cannot be an empty array.'); | ||
} | ||
} | ||
return createNamedNode({ | ||
type: NodeType.VALUE, | ||
value: value, | ||
}, function () { return "ValueNode(" + JSON.stringify(value) + ")"; }); | ||
return createNamedNode( | ||
{ | ||
type: NodeType.SELECTOR, | ||
selector: selector, | ||
}, | ||
function () { | ||
return 'SelectorNode("' + selector + '")'; | ||
} | ||
); | ||
} | ||
function createValueNode(value, skipChecks) { | ||
if (skipChecks === void 0) { | ||
skipChecks = false; | ||
} | ||
function createComparisonNode(selector, operator, value, skipChecks) { | ||
if (skipChecks === void 0) { skipChecks = false; } | ||
if (!skipChecks) { | ||
if (!isSelectorNode(selector)) { | ||
throw new TypeError("The \"selector\" has to be a \"SelectorNode\", \"" + String(selector) + "\" passed."); | ||
} | ||
if (typeof operator !== "string") { | ||
throw new TypeError("The \"operator\" has to be a \"SelectorNode\", \"" + String(operator) + "\" passed."); | ||
} | ||
if (!isComparisonOperator(operator)) { | ||
throw new TypeError("The \"operator\" has to be a valid \"ComparisonOperator\", " + String(operator) + " passed."); | ||
} | ||
if (!isValueNode(value)) { | ||
throw new TypeError("The \"value\" has to be a \"ValueNode\", \"" + String(value) + "\" passed."); | ||
} | ||
} | ||
return createNamedNode({ | ||
type: NodeType.COMPARISON, | ||
left: selector, | ||
operator: operator, | ||
right: value, | ||
}, function () { return "ComparisonNode(" + selector + "," + operator + "," + value + ")"; }); | ||
if (!skipChecks) { | ||
if (typeof value !== "string" && !Array.isArray(value)) { | ||
throw new TypeError('The "value" has to be a "string | string[]", "' + String(value) + '" passed.'); | ||
} | ||
if (Array.isArray(value) && value.length === 0) { | ||
throw new Error('The "value" cannot be an empty array.'); | ||
} | ||
} | ||
function createLogicNode(left, operator, right, skipChecks) { | ||
if (skipChecks === void 0) { skipChecks = false; } | ||
if (!skipChecks) { | ||
if (!isExpressionNode(left)) { | ||
throw new TypeError("The \"left\" has to be a \"ExpressionNode\", \"" + String(left) + "\" passed."); | ||
} | ||
if (typeof operator !== "string") { | ||
throw new TypeError("The \"operator\" has to be a \"string\", \"" + String(operator) + "\" passed."); | ||
} | ||
if (!isLogicOperator(operator)) { | ||
throw new TypeError("The \"operator\" has to be a valid \"LogicOperator\", " + String(operator) + " passed."); | ||
} | ||
if (!isExpressionNode(right)) { | ||
throw new TypeError("The \"right\" has to be a \"ExpressionNode\", \"" + String(right) + "\" passed."); | ||
} | ||
} | ||
return createNamedNode({ | ||
type: NodeType.LOGIC, | ||
left: left, | ||
operator: operator, | ||
right: right, | ||
}, function () { return "LogicNode(" + left + "," + operator + "," + right + ")"; }); | ||
return createNamedNode( | ||
{ | ||
type: NodeType.VALUE, | ||
value: value, | ||
}, | ||
function () { | ||
return "ValueNode(" + JSON.stringify(value) + ")"; | ||
} | ||
); | ||
} | ||
function createComparisonNode(selector, operator, value, skipChecks) { | ||
if (skipChecks === void 0) { | ||
skipChecks = false; | ||
} | ||
function isNode(candidate) { | ||
return candidate !== undefined && candidate !== null && Object.prototype.hasOwnProperty.call(candidate, "type"); | ||
if (!skipChecks) { | ||
if (!isSelectorNode(selector)) { | ||
throw new TypeError('The "selector" has to be a "SelectorNode", "' + String(selector) + '" passed.'); | ||
} | ||
if (typeof operator !== "string") { | ||
throw new TypeError('The "operator" has to be a "SelectorNode", "' + String(operator) + '" passed.'); | ||
} | ||
if (!isComparisonOperator(operator)) { | ||
throw new TypeError('The "operator" has to be a valid "ComparisonOperator", ' + String(operator) + " passed."); | ||
} | ||
if (!isValueNode(value)) { | ||
throw new TypeError('The "value" has to be a "ValueNode", "' + String(value) + '" passed.'); | ||
} | ||
} | ||
function isSelectorNode(candidate) { | ||
return isNode(candidate) && candidate.type === NodeType.SELECTOR; | ||
return createNamedNode( | ||
{ | ||
type: NodeType.COMPARISON, | ||
left: selector, | ||
operator: operator, | ||
right: value, | ||
}, | ||
function () { | ||
return "ComparisonNode(" + selector + "," + operator + "," + value + ")"; | ||
} | ||
); | ||
} | ||
function createLogicNode(left, operator, right, skipChecks) { | ||
if (skipChecks === void 0) { | ||
skipChecks = false; | ||
} | ||
function isValueNode(candidate) { | ||
return isNode(candidate) && candidate.type === NodeType.VALUE; | ||
if (!skipChecks) { | ||
if (!isExpressionNode(left)) { | ||
throw new TypeError('The "left" has to be a "ExpressionNode", "' + String(left) + '" passed.'); | ||
} | ||
if (typeof operator !== "string") { | ||
throw new TypeError('The "operator" has to be a "string", "' + String(operator) + '" passed.'); | ||
} | ||
if (!isLogicOperator(operator)) { | ||
throw new TypeError('The "operator" has to be a valid "LogicOperator", ' + String(operator) + " passed."); | ||
} | ||
if (!isExpressionNode(right)) { | ||
throw new TypeError('The "right" has to be a "ExpressionNode", "' + String(right) + '" passed.'); | ||
} | ||
} | ||
function isComparisonNode(candidate, operator) { | ||
return (isNode(candidate) && | ||
candidate.type === NodeType.COMPARISON && | ||
(operator === undefined || isComparisonOperator(candidate.operator, operator))); | ||
} | ||
function isLogicNode(candidate, operator) { | ||
return (isNode(candidate) && | ||
candidate.type === NodeType.LOGIC && | ||
(operator === undefined || isLogicOperator(candidate.operator, operator))); | ||
} | ||
function isExpressionNode(candidate) { | ||
return isComparisonNode(candidate) || isLogicNode(candidate); | ||
} | ||
return createNamedNode( | ||
{ | ||
type: NodeType.LOGIC, | ||
left: left, | ||
operator: operator, | ||
right: right, | ||
}, | ||
function () { | ||
return "LogicNode(" + left + "," + operator + "," + right + ")"; | ||
} | ||
); | ||
} | ||
function isNode(candidate) { | ||
return candidate !== undefined && candidate !== null && Object.prototype.hasOwnProperty.call(candidate, "type"); | ||
} | ||
function isSelectorNode(candidate) { | ||
return isNode(candidate) && candidate.type === NodeType.SELECTOR; | ||
} | ||
function isValueNode(candidate) { | ||
return isNode(candidate) && candidate.type === NodeType.VALUE; | ||
} | ||
function isComparisonNode(candidate, operator) { | ||
return ( | ||
isNode(candidate) && | ||
candidate.type === NodeType.COMPARISON && | ||
(operator === undefined || isComparisonOperator(candidate.operator, operator)) | ||
); | ||
} | ||
function isLogicNode(candidate, operator) { | ||
return ( | ||
isNode(candidate) && | ||
candidate.type === NodeType.LOGIC && | ||
(operator === undefined || isLogicOperator(candidate.operator, operator)) | ||
); | ||
} | ||
function isExpressionNode(candidate) { | ||
return isComparisonNode(candidate) || isLogicNode(candidate); | ||
} | ||
var TokenType = { | ||
UNQUOTED: "UNQUOTED", | ||
QUOTED: "QUOTED", | ||
PARENTHESIS: "PARENTHESIS", | ||
OPERATOR: "OPERATOR", | ||
END: "END", | ||
var TokenType = { | ||
UNQUOTED: "UNQUOTED", | ||
QUOTED: "QUOTED", | ||
PARENTHESIS: "PARENTHESIS", | ||
OPERATOR: "OPERATOR", | ||
END: "END", | ||
}; | ||
function createNamedToken(token, toString) { | ||
Object.defineProperty(token, "toString", { | ||
value: toString, | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
}); | ||
return token; | ||
} | ||
function createUnquotedToken(value, position) { | ||
return createNamedToken( | ||
{ | ||
type: TokenType.UNQUOTED, | ||
value: value, | ||
position: position, | ||
}, | ||
function () { | ||
return "UnquotedToken(" + value + ")"; | ||
} | ||
); | ||
} | ||
function createQuotedToken(value, position) { | ||
return createNamedToken( | ||
{ | ||
type: TokenType.QUOTED, | ||
value: value, | ||
position: position, | ||
}, | ||
function () { | ||
return "QuotedToken(" + value + ")"; | ||
} | ||
); | ||
} | ||
function createParenthesisToken(value, position) { | ||
return createNamedToken( | ||
{ | ||
type: TokenType.PARENTHESIS, | ||
value: value, | ||
position: position, | ||
}, | ||
function () { | ||
return "ParenthesisToken(" + value + ")"; | ||
} | ||
); | ||
} | ||
function createOperatorToken(value, position) { | ||
return createNamedToken( | ||
{ | ||
type: TokenType.OPERATOR, | ||
value: value, | ||
position: position, | ||
}, | ||
function () { | ||
return "OperatorToken(" + value + ")"; | ||
} | ||
); | ||
} | ||
function createEndToken(position) { | ||
return createNamedToken( | ||
{ | ||
type: TokenType.END, | ||
value: "END", | ||
position: position, | ||
}, | ||
function () { | ||
return "EndToken"; | ||
} | ||
); | ||
} | ||
function isToken(candidate) { | ||
return ( | ||
Object.prototype.hasOwnProperty.call(candidate, "type") && | ||
Object.prototype.hasOwnProperty.call(candidate, "value") && | ||
Object.prototype.hasOwnProperty.call(candidate, "position") | ||
); | ||
} | ||
function isUnquotedToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.UNQUOTED; | ||
} | ||
function isQuotedToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.QUOTED; | ||
} | ||
function isParenthesisToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.PARENTHESIS; | ||
} | ||
function isOpenParenthesisToken(candidate) { | ||
return isParenthesisToken(candidate) && candidate.value === "("; | ||
} | ||
function isCloseParenthesisToken(candidate) { | ||
return isParenthesisToken(candidate) && candidate.value === ")"; | ||
} | ||
function isOperatorToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.OPERATOR; | ||
} | ||
function isComparisonOperatorToken(candidate) { | ||
return isOperatorToken(candidate) && isComparisonOperator(candidate.value); | ||
} | ||
function isOrOperatorToken(candidate) { | ||
return isOperatorToken(candidate) && isLogicOperator(candidate.value, OR); | ||
} | ||
function isAndOperatorToken(candidate) { | ||
return isOperatorToken(candidate) && isLogicOperator(candidate.value, AND); | ||
} | ||
function isEndToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.END; | ||
} | ||
function createErrorForUnexpectedCharacter(position, source) { | ||
var character = source[position]; | ||
return new SyntaxError( | ||
"Unexpected character '" + character + "' at position " + (position + 1) + ' in "' + source + '".' | ||
); | ||
} | ||
function createErrorForUnclosedQuote(position, source) { | ||
var character = source[position]; | ||
return new SyntaxError( | ||
"Unclosed quote '" + character + "' at position " + (position + 1) + ' in "' + source + '".' | ||
); | ||
} | ||
function createErrorForUnexpectedToken(token, source) { | ||
return new SyntaxError( | ||
isEndToken(token) | ||
? 'Unexpected end in "' + source + '".' | ||
: "Unexpected " + | ||
(token.value.length > 1 ? "string" : "character") + | ||
" '" + | ||
token.value + | ||
"' at position " + | ||
(token.position + 1) + | ||
' in "' + | ||
source + | ||
'".' | ||
); | ||
} | ||
function createErrorForUnclosedParenthesis(token, source, parentPosition) { | ||
return new SyntaxError( | ||
'Unexpected end in "' + | ||
source + | ||
'". Did you forget to close parenthesis at position ' + | ||
(parentPosition + 1) + | ||
"?" | ||
); | ||
} | ||
function createErrorForEmptyInput(token, source) { | ||
return new SyntaxError('Unexpected end in "' + source + '". Cannot parse empty string.'); | ||
} | ||
function createLexerContext(input) { | ||
return { | ||
position: 0, | ||
buffer: input, | ||
length: input.length, | ||
}; | ||
function createNamedToken(token, toString) { | ||
Object.defineProperty(token, "toString", { | ||
value: toString, | ||
enumerable: false, | ||
configurable: false, | ||
writable: false, | ||
}); | ||
return token; | ||
} | ||
function createUnquotedToken(value, position) { | ||
return createNamedToken({ | ||
type: TokenType.UNQUOTED, | ||
value: value, | ||
position: position, | ||
}, function () { return "UnquotedToken(" + value + ")"; }); | ||
} | ||
function createQuotedToken(value, position) { | ||
return createNamedToken({ | ||
type: TokenType.QUOTED, | ||
value: value, | ||
position: position, | ||
}, function () { return "QuotedToken(" + value + ")"; }); | ||
} | ||
function createParenthesisToken(value, position) { | ||
return createNamedToken({ | ||
type: TokenType.PARENTHESIS, | ||
value: value, | ||
position: position, | ||
}, function () { return "ParenthesisToken(" + value + ")"; }); | ||
} | ||
function createOperatorToken(value, position) { | ||
return createNamedToken({ | ||
type: TokenType.OPERATOR, | ||
value: value, | ||
position: position, | ||
}, function () { return "OperatorToken(" + value + ")"; }); | ||
} | ||
function createEndToken(position) { | ||
return createNamedToken({ | ||
type: TokenType.END, | ||
value: "END", | ||
position: position, | ||
}, function () { return "EndToken"; }); | ||
} | ||
function isToken(candidate) { | ||
return (Object.prototype.hasOwnProperty.call(candidate, "type") && | ||
Object.prototype.hasOwnProperty.call(candidate, "value") && | ||
Object.prototype.hasOwnProperty.call(candidate, "position")); | ||
} | ||
function isUnquotedToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.UNQUOTED; | ||
} | ||
function isQuotedToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.QUOTED; | ||
} | ||
function isParenthesisToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.PARENTHESIS; | ||
} | ||
function isOpenParenthesisToken(candidate) { | ||
return isParenthesisToken(candidate) && candidate.value === "("; | ||
} | ||
function isCloseParenthesisToken(candidate) { | ||
return isParenthesisToken(candidate) && candidate.value === ")"; | ||
} | ||
function isOperatorToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.OPERATOR; | ||
} | ||
function isComparisonOperatorToken(candidate) { | ||
return isOperatorToken(candidate) && isComparisonOperator(candidate.value); | ||
} | ||
function isOrOperatorToken(candidate) { | ||
return isOperatorToken(candidate) && isLogicOperator(candidate.value, OR); | ||
} | ||
function isAndOperatorToken(candidate) { | ||
return isOperatorToken(candidate) && isLogicOperator(candidate.value, AND); | ||
} | ||
function isEndToken(candidate) { | ||
return isToken(candidate) && candidate.type === TokenType.END; | ||
} | ||
} | ||
function createErrorForUnexpectedCharacter(position, source) { | ||
var character = source[position]; | ||
return new SyntaxError("Unexpected character '" + character + "' at position " + (position + 1) + " in \"" + source + "\"."); | ||
var seekComparisonCustomOperatorToken = function (context) { | ||
// scan for FIQL custom operators: =[a-z]= | ||
// assume that context.buffer[context.position] === '=' | ||
var endPosition = context.position + 1; | ||
// scan for chars from a to z | ||
while ( | ||
endPosition < context.length && | ||
context.buffer.charCodeAt(endPosition) >= 97 && // a | ||
context.buffer.charCodeAt(endPosition) <= 122 // z | ||
) { | ||
endPosition++; | ||
} | ||
function createErrorForUnclosedQuote(position, source) { | ||
var character = source[position]; | ||
return new SyntaxError("Unclosed quote '" + character + "' at position " + (position + 1) + " in \"" + source + "\"."); | ||
if (context.buffer[endPosition] === "=") { | ||
var operator = context.buffer.slice(context.position, endPosition + 1); | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
function createErrorForUnexpectedToken(token, source) { | ||
return new SyntaxError(isEndToken(token) | ||
? "Unexpected end in \"" + source + "\"." | ||
: "Unexpected " + (token.value.length > 1 ? "string" : "character") + " '" + token.value + "' at position " + (token.position + 1) + " in \"" + source + "\"."); | ||
} | ||
function createErrorForUnclosedParenthesis(token, source, parentPosition) { | ||
return new SyntaxError("Unexpected end in \"" + source + "\". Did you forget to close parenthesis at position " + (parentPosition + 1) + "?"); | ||
} | ||
function createErrorForEmptyInput(token, source) { | ||
return new SyntaxError("Unexpected end in \"" + source + "\". Cannot parse empty string."); | ||
} | ||
return null; | ||
}; | ||
function createLexerContext(input) { | ||
return { | ||
position: 0, | ||
buffer: input, | ||
length: input.length, | ||
}; | ||
} | ||
var seekComparisonCustomOperatorToken = function (context) { | ||
// scan for FIQL custom operators: =[a-z]= | ||
// assume that context.buffer[context.position] === '=' | ||
var endPosition = context.position + 1; | ||
// scan for chars from a to z | ||
while (endPosition < context.length && | ||
context.buffer.charCodeAt(endPosition) >= 97 && // a | ||
context.buffer.charCodeAt(endPosition) <= 122 // z | ||
) { | ||
endPosition++; | ||
} | ||
if (context.buffer[endPosition] === "=") { | ||
var operator = context.buffer.slice(context.position, endPosition + 1); | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
function createScanSymbol(symbols) { | ||
return function scanSymbol(context) { | ||
return ( | ||
symbols.find(function (symbol) { | ||
return context.buffer.substr(context.position, symbol.length) === symbol; | ||
}) || null | ||
); | ||
}; | ||
} | ||
function createScanSymbol(symbols) { | ||
return function scanSymbol(context) { | ||
return symbols.find(function (symbol) { return context.buffer.substr(context.position, symbol.length) === symbol; }) || null; | ||
}; | ||
var scanAnyComparisonOperator = createScanSymbol(ComparisonOperators); | ||
var seekComparisonOperatorToken = function (context) { | ||
var operator = scanAnyComparisonOperator(context); | ||
if (operator) { | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
}; | ||
var scanAnyComparisonOperator = createScanSymbol(ComparisonOperators); | ||
var seekComparisonOperatorToken = function (context) { | ||
var operator = scanAnyComparisonOperator(context); | ||
if (operator) { | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
}; | ||
var seekLogicCanonicalOperatorToken = function (context) { | ||
// we assume that symbol is a valid LogicOperator | ||
var operator = context.buffer.charAt(context.position); | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += 1; | ||
return token; | ||
}; | ||
var seekLogicCanonicalOperatorToken = function (context) { | ||
// we assume that symbol is a valid LogicOperator | ||
var operator = context.buffer.charAt(context.position); | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += 1; | ||
return token; | ||
function createScanNonReservedSymbol(symbols) { | ||
return function scanNonReservedSymbol(context) { | ||
return ( | ||
symbols.find(function (symbol) { | ||
return ( | ||
context.buffer.substr(context.position, symbol.length) === symbol && | ||
context.buffer[context.position + symbol.length] === " " | ||
); | ||
}) || null | ||
); | ||
}; | ||
} | ||
function createScanNonReservedSymbol(symbols) { | ||
return function scanNonReservedSymbol(context) { | ||
return (symbols.find(function (symbol) { | ||
return context.buffer.substr(context.position, symbol.length) === symbol && | ||
context.buffer[context.position + symbol.length] === " "; | ||
}) || null); | ||
}; | ||
var scanLogicVerboseOperator = createScanNonReservedSymbol(VerboseLogicOperators); | ||
var seekLogicVerboseOperatorToken = function (context) { | ||
var operator = scanLogicVerboseOperator(context); | ||
if (operator) { | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
}; | ||
var scanLogicVerboseOperator = createScanNonReservedSymbol(VerboseLogicOperators); | ||
var seekLogicVerboseOperatorToken = function (context) { | ||
var operator = scanLogicVerboseOperator(context); | ||
if (operator) { | ||
var token = createOperatorToken(operator, context.position); | ||
context.position += operator.length; | ||
return token; | ||
} | ||
return null; | ||
}; | ||
var seekParenthesisToken = function (context) { | ||
// we assume that parenthesis is a valid Parenthesis | ||
var parenthesis = context.buffer.charAt(context.position); | ||
var token = createParenthesisToken(parenthesis, context.position); | ||
context.position += 1; | ||
return token; | ||
}; | ||
var seekParenthesisToken = function (context) { | ||
// we assume that parenthesis is a valid Parenthesis | ||
var parenthesis = context.buffer.charAt(context.position); | ||
var token = createParenthesisToken(parenthesis, context.position); | ||
context.position += 1; | ||
return token; | ||
}; | ||
var seekQuotedToken = function (context) { | ||
// we assume that quote is a valid QuoteSymbol | ||
var quote = context.buffer.charAt(context.position); | ||
var endPosition = context.position; | ||
while (endPosition < context.length) { | ||
endPosition = context.buffer.indexOf(quote, endPosition + 1); | ||
if (endPosition === -1) { | ||
throw createErrorForUnclosedQuote(context.position, context.buffer); | ||
} | ||
// scan back for escape characters | ||
var escaped = false; | ||
for ( | ||
var scanPosition = endPosition - 1; | ||
context.buffer[scanPosition] === "\\" && scanPosition > context.position; | ||
scanPosition-- | ||
) { | ||
escaped = !escaped; | ||
} | ||
if (!escaped) { | ||
// it's not escaped quote - we've found terminating quote | ||
break; | ||
} | ||
} | ||
var value = context.buffer.substring(context.position, endPosition + 1); | ||
var token = createQuotedToken(value, context.position); | ||
context.position = endPosition + 1; | ||
return token; | ||
}; | ||
var seekQuotedToken = function (context) { | ||
// we assume that quote is a valid QuoteSymbol | ||
var quote = context.buffer.charAt(context.position); | ||
var endPosition = context.position; | ||
while (endPosition < context.length) { | ||
endPosition = context.buffer.indexOf(quote, endPosition + 1); | ||
if (endPosition === -1) { | ||
throw createErrorForUnclosedQuote(context.position, context.buffer); | ||
} | ||
// scan back for escape characters | ||
var escaped = false; | ||
for (var scanPosition = endPosition - 1; context.buffer[scanPosition] === "\\" && scanPosition > context.position; scanPosition--) { | ||
escaped = !escaped; | ||
} | ||
if (!escaped) { | ||
// it's not escaped quote - we've found terminating quote | ||
break; | ||
} | ||
} | ||
var value = context.buffer.substring(context.position, endPosition + 1); | ||
var token = createQuotedToken(value, context.position); | ||
context.position = endPosition + 1; | ||
return token; | ||
}; | ||
var seekUnquotedToken = function (context) { | ||
var endPosition = context.position + 1; | ||
while (endPosition < context.length && ReservedChars.indexOf(context.buffer.charAt(endPosition)) === -1) { | ||
endPosition++; | ||
} | ||
var token = createUnquotedToken(context.buffer.substring(context.position, endPosition), context.position); | ||
context.position = endPosition; | ||
return token; | ||
}; | ||
var seekUnquotedToken = function (context) { | ||
var endPosition = context.position + 1; | ||
while (endPosition < context.length && | ||
ReservedChars.indexOf(context.buffer.charAt(endPosition)) === -1) { | ||
endPosition++; | ||
} | ||
var token = createUnquotedToken(context.buffer.substring(context.position, endPosition), context.position); | ||
context.position = endPosition; | ||
return token; | ||
}; | ||
var WhitespaceChars = [" ", "\n", "\t", "\r"]; | ||
var skipWhitespace = function (context) { | ||
while ( | ||
context.position < context.length && | ||
WhitespaceChars.indexOf(context.buffer.charAt(context.position)) !== -1 | ||
) { | ||
context.position++; | ||
} | ||
}; | ||
var WhitespaceChars = [" ", "\n", "\t", "\r"]; | ||
var skipWhitespace = function (context) { | ||
while (context.position < context.length && WhitespaceChars.indexOf(context.buffer.charAt(context.position)) !== -1) { | ||
context.position++; | ||
var seekAnyToken = function (context) { | ||
// first skip all whitespace chars | ||
skipWhitespace(context); | ||
if (context.position >= context.length) { | ||
return null; | ||
} | ||
// then decide what to do based on the current char | ||
var char = context.buffer.charAt(context.position); | ||
var token = null; | ||
switch (char) { | ||
// single char symbols | ||
case "'": | ||
case '"': | ||
token = seekQuotedToken(context); | ||
break; | ||
// single char symbols | ||
case "(": | ||
case ")": | ||
token = seekParenthesisToken(context); | ||
break; | ||
// single char symbols | ||
case ",": | ||
case ";": | ||
token = seekLogicCanonicalOperatorToken(context); | ||
break; | ||
// multi char symbols for comparison operator | ||
case "=": | ||
case "!": | ||
case "~": | ||
case "<": | ||
case ">": | ||
token = seekComparisonOperatorToken(context); | ||
if (!token && char === "=") { | ||
token = seekComparisonCustomOperatorToken(context); | ||
} | ||
}; | ||
var seekAnyToken = function (context) { | ||
// first skip all whitespace chars | ||
skipWhitespace(context); | ||
if (context.position >= context.length) { | ||
return null; | ||
} | ||
// then decide what to do based on the current char | ||
var char = context.buffer.charAt(context.position); | ||
var token = null; | ||
switch (char) { | ||
// single char symbols | ||
case "'": | ||
case '"': | ||
token = seekQuotedToken(context); | ||
break; | ||
// single char symbols | ||
case "(": | ||
case ")": | ||
token = seekParenthesisToken(context); | ||
break; | ||
// single char symbols | ||
case ",": | ||
case ";": | ||
token = seekLogicCanonicalOperatorToken(context); | ||
break; | ||
// multi char symbols for comparison operator | ||
case "=": | ||
case "!": | ||
case "~": | ||
case "<": | ||
case ">": | ||
token = seekComparisonOperatorToken(context); | ||
if (!token && char === "=") { | ||
token = seekComparisonCustomOperatorToken(context); | ||
} | ||
break; | ||
// unreserved char | ||
default: | ||
// there are VerboseLogicOperators (and, or) that uses not reserved chars | ||
token = seekLogicVerboseOperatorToken(context); | ||
// if it's not an OperatorToken, process UnquotedToken | ||
if (!token) { | ||
token = seekUnquotedToken(context); | ||
} | ||
} | ||
break; | ||
// unreserved char | ||
default: | ||
// there are VerboseLogicOperators (and, or) that uses not reserved chars | ||
token = seekLogicVerboseOperatorToken(context); | ||
// if it's not an OperatorToken, process UnquotedToken | ||
if (!token) { | ||
throw createErrorForUnexpectedCharacter(context.position, context.buffer); | ||
token = seekUnquotedToken(context); | ||
} | ||
return token; | ||
}; | ||
function lex(input) { | ||
var context = createLexerContext(input); | ||
var tokens = []; | ||
for (var token = seekAnyToken(context); token !== null; token = seekAnyToken(context)) { | ||
tokens.push(token); | ||
} | ||
tokens.push(createEndToken(context.position)); | ||
return tokens; | ||
} | ||
if (!token) { | ||
throw createErrorForUnexpectedCharacter(context.position, context.buffer); | ||
} | ||
return token; | ||
}; | ||
function getParserContextState(context) { | ||
return context.state[context.state.length - 1]; | ||
function lex(input) { | ||
var context = createLexerContext(input); | ||
var tokens = []; | ||
for (var token = seekAnyToken(context); token !== null; token = seekAnyToken(context)) { | ||
tokens.push(token); | ||
} | ||
function getParserContextToken(context) { | ||
return context.tokens[context.position]; | ||
} | ||
function getParserContextHead(context) { | ||
return context.stack[context.stack.length - 1]; | ||
} | ||
function createParserContext(tokens) { | ||
return { | ||
position: 0, | ||
tokens: tokens, | ||
stack: [], | ||
state: [0], | ||
parent: null, | ||
}; | ||
} | ||
tokens.push(createEndToken(context.position)); | ||
return tokens; | ||
} | ||
var OperationType = { | ||
SHIFT: 0, | ||
PUSH: 1, | ||
REDUCE: 2, | ||
POP: 3, | ||
GOTO: 4, | ||
ACCEPT: 5, | ||
function getParserContextState(context) { | ||
return context.state[context.state.length - 1]; | ||
} | ||
function getParserContextToken(context) { | ||
return context.tokens[context.position]; | ||
} | ||
function getParserContextHead(context) { | ||
return context.stack[context.stack.length - 1]; | ||
} | ||
function createParserContext(tokens) { | ||
return { | ||
position: 0, | ||
tokens: tokens, | ||
stack: [], | ||
state: [0], | ||
parent: null, | ||
}; | ||
function shift(state) { | ||
return { | ||
type: OperationType.SHIFT, | ||
state: state, | ||
}; | ||
} | ||
function reduce(production) { | ||
return { | ||
type: OperationType.REDUCE, | ||
production: production, | ||
}; | ||
} | ||
function push(state) { | ||
return { | ||
type: OperationType.PUSH, | ||
state: state, | ||
}; | ||
} | ||
function pop(production) { | ||
return { | ||
type: OperationType.POP, | ||
production: production, | ||
}; | ||
} | ||
function goto(state) { | ||
return { | ||
type: OperationType.GOTO, | ||
state: state, | ||
}; | ||
} | ||
function accept() { | ||
return { | ||
type: OperationType.ACCEPT, | ||
}; | ||
} | ||
var noop = undefined; | ||
} | ||
var selectorProduction = function (stack) { | ||
var token = stack[stack.length - 1]; | ||
return { | ||
consumed: 1, | ||
produced: createSelectorNode(token.value, true), | ||
}; | ||
var OperationType = { | ||
SHIFT: 0, | ||
PUSH: 1, | ||
REDUCE: 2, | ||
POP: 3, | ||
GOTO: 4, | ||
ACCEPT: 5, | ||
}; | ||
function shift(state) { | ||
return { | ||
type: OperationType.SHIFT, | ||
state: state, | ||
}; | ||
var singleValueProduction = function (stack) { | ||
var token = stack[stack.length - 1]; | ||
var value = isQuotedToken(token) ? token.value.slice(1, -1) : token.value; | ||
return { | ||
consumed: 1, | ||
produced: createValueNode(value, true), | ||
}; | ||
} | ||
function reduce(production) { | ||
return { | ||
type: OperationType.REDUCE, | ||
production: production, | ||
}; | ||
var multiValueProduction = function (stack) { | ||
var closeParenthesisIndex = stack.length - 1; | ||
var openParenthesisIndex = stack.map(function (item) { return isOpenParenthesisToken(item); }).lastIndexOf(true); | ||
var valueTokens = stack | ||
.slice(openParenthesisIndex, closeParenthesisIndex) | ||
.filter(function (item) { return isUnquotedToken(item) || isQuotedToken(item); }); | ||
return { | ||
consumed: closeParenthesisIndex - openParenthesisIndex + 1, | ||
produced: createValueNode(valueTokens.map(function (valueToken) { return (isQuotedToken(valueToken) ? valueToken.value.slice(1, -1) : valueToken.value); }), true), | ||
}; | ||
} | ||
function push(state) { | ||
return { | ||
type: OperationType.PUSH, | ||
state: state, | ||
}; | ||
var comparisonExpressionProduction = function (stack) { | ||
var selector = stack[stack.length - 3]; | ||
var operator = stack[stack.length - 2]; | ||
var value = stack[stack.length - 1]; | ||
return { | ||
consumed: 3, | ||
produced: createComparisonNode(selector, operator.value, value, true), | ||
}; | ||
} | ||
function pop(production) { | ||
return { | ||
type: OperationType.POP, | ||
production: production, | ||
}; | ||
var logicalExpressionProduction = function (stack) { | ||
var left = stack[stack.length - 3]; | ||
var operator = stack[stack.length - 2]; | ||
var right = stack[stack.length - 1]; | ||
return { | ||
consumed: 3, | ||
produced: createLogicNode(left, operator.value, right, true), | ||
}; | ||
} | ||
function goto(state) { | ||
return { | ||
type: OperationType.GOTO, | ||
state: state, | ||
}; | ||
var groupExpressionProduction = function (stack) { | ||
var expression = stack[stack.length - 2]; | ||
return { | ||
consumed: 3, | ||
produced: expression, | ||
}; | ||
} | ||
function accept() { | ||
return { | ||
type: OperationType.ACCEPT, | ||
}; | ||
} | ||
var noop = undefined; | ||
var productions = [ | ||
/* 0 */ selectorProduction, | ||
/* 1 */ singleValueProduction, | ||
/* 2 */ multiValueProduction, | ||
/* 3 */ comparisonExpressionProduction, | ||
/* 4 */ logicalExpressionProduction, | ||
/* 5 */ groupExpressionProduction, | ||
var selectorProduction = function (stack) { | ||
var token = stack[stack.length - 1]; | ||
return { | ||
consumed: 1, | ||
produced: createSelectorNode(token.value, true), | ||
}; | ||
}; | ||
var singleValueProduction = function (stack) { | ||
var token = stack[stack.length - 1]; | ||
var value = isQuotedToken(token) ? token.value.slice(1, -1) : token.value; | ||
return { | ||
consumed: 1, | ||
produced: createValueNode(value, true), | ||
}; | ||
}; | ||
var multiValueProduction = function (stack) { | ||
var closeParenthesisIndex = stack.length - 1; | ||
var openParenthesisIndex = stack | ||
.map(function (item) { | ||
return isOpenParenthesisToken(item); | ||
}) | ||
.lastIndexOf(true); | ||
var valueTokens = stack.slice(openParenthesisIndex, closeParenthesisIndex).filter(function (item) { | ||
return isUnquotedToken(item) || isQuotedToken(item); | ||
}); | ||
return { | ||
consumed: closeParenthesisIndex - openParenthesisIndex + 1, | ||
produced: createValueNode( | ||
valueTokens.map(function (valueToken) { | ||
return isQuotedToken(valueToken) ? valueToken.value.slice(1, -1) : valueToken.value; | ||
}), | ||
true | ||
), | ||
}; | ||
}; | ||
var comparisonExpressionProduction = function (stack) { | ||
var selector = stack[stack.length - 3]; | ||
var operator = stack[stack.length - 2]; | ||
var value = stack[stack.length - 1]; | ||
return { | ||
consumed: 3, | ||
produced: createComparisonNode(selector, operator.value, value, true), | ||
}; | ||
}; | ||
var logicalExpressionProduction = function (stack) { | ||
var left = stack[stack.length - 3]; | ||
var operator = stack[stack.length - 2]; | ||
var right = stack[stack.length - 1]; | ||
return { | ||
consumed: 3, | ||
produced: createLogicNode(left, operator.value, right, true), | ||
}; | ||
}; | ||
var groupExpressionProduction = function (stack) { | ||
var expression = stack[stack.length - 2]; | ||
return { | ||
consumed: 3, | ||
produced: expression, | ||
}; | ||
}; | ||
var productions = [ | ||
/* 0 */ selectorProduction, | ||
/* 1 */ singleValueProduction, | ||
/* 2 */ multiValueProduction, | ||
/* 3 */ comparisonExpressionProduction, | ||
/* 4 */ logicalExpressionProduction, | ||
/* 5 */ groupExpressionProduction, | ||
]; | ||
var tokenMatchers = [ | ||
/* 0 */ isOpenParenthesisToken, | ||
/* 1 */ isCloseParenthesisToken, | ||
/* 2 */ isUnquotedToken, | ||
/* 3 */ isQuotedToken, | ||
/* 4 */ isComparisonOperatorToken, | ||
/* 5 */ isOrOperatorToken, | ||
/* 6 */ isAndOperatorToken, | ||
/* 7 */ isEndToken, | ||
]; | ||
var nodeMatchers = [/* 0 */ isSelectorNode, /* 1 */ isValueNode, /* 2 */ isExpressionNode]; | ||
// prettier-ignore | ||
var table = [ | ||
/* st | token (terminal) | node (non-terminal) */ | ||
/* O_PAREN, C_PAREN, UNQUOTED, QUOTED, C_OP, OR_OP, AND_OP, END SELECT, VALUE, EXPR */ | ||
/* 0 */ [[push(0), noop, shift(8), noop, noop, noop, noop, noop], [goto(3), noop, goto(7)]], | ||
/* 1 */ [[push(0), noop, shift(8), noop, noop, noop, noop, noop], [goto(3), noop, goto(12),]], | ||
/* 2 */ [[push(0), noop, shift(8), noop, noop, noop, noop, noop], [goto(3), noop, goto(13),]], | ||
/* 3 */ [[noop, noop, noop, noop, shift(4), noop, noop, noop], [noop, noop, noop,]], | ||
/* 4 */ [[shift(5), noop, shift(9), shift(9), noop, noop, noop, noop], [noop, goto(11), noop,]], | ||
/* 5 */ [[noop, noop, shift(6), shift(6), noop, noop, noop, noop], [noop, noop, noop,]], | ||
/* 6 */ [[noop, shift(10), noop, noop, noop, shift(5), noop, noop], [noop, noop, noop,]], | ||
/* 7 */ [[noop, shift(14), noop, noop, noop, shift(1), shift(2), accept()], [noop, noop, noop,]], | ||
/* 8 */ [[noop, noop, noop, noop, reduce(0), noop, noop, noop], [noop, noop, noop,]], | ||
/* 9 */ [[noop, reduce(1), noop, noop, noop, reduce(1), reduce(1), reduce(1)], [noop, noop, noop,]], | ||
/* 10 */ [[noop, reduce(2), noop, noop, noop, reduce(2), reduce(2), reduce(2)], [noop, noop, noop,]], | ||
/* 11 */ [[noop, reduce(3), noop, noop, noop, reduce(3), reduce(3), reduce(3)], [noop, noop, noop,]], | ||
/* 12 */ [[noop, reduce(4), noop, noop, noop, reduce(4), shift(2), reduce(4)], [noop, noop, noop,]], | ||
/* 13 */ [[noop, reduce(4), noop, noop, noop, reduce(4), reduce(4), reduce(4)], [noop, noop, noop,]], | ||
/* 14 */ [[noop, pop(5), noop, noop, noop, pop(5), pop(5), pop(5)], [noop, noop, noop,]], | ||
]; | ||
function getParserTokenOperation(state, token) { | ||
return table[state][0][ | ||
tokenMatchers.findIndex(function (matcher) { | ||
return matcher(token); | ||
}) | ||
]; | ||
var tokenMatchers = [ | ||
/* 0 */ isOpenParenthesisToken, | ||
/* 1 */ isCloseParenthesisToken, | ||
/* 2 */ isUnquotedToken, | ||
/* 3 */ isQuotedToken, | ||
/* 4 */ isComparisonOperatorToken, | ||
/* 5 */ isOrOperatorToken, | ||
/* 6 */ isAndOperatorToken, | ||
/* 7 */ isEndToken, | ||
} | ||
function getParserNodeOperation(state, node) { | ||
return table[state][1][ | ||
nodeMatchers.findIndex(function (matcher) { | ||
return matcher(node); | ||
}) | ||
]; | ||
var nodeMatchers = [ | ||
/* 0 */ isSelectorNode, | ||
/* 1 */ isValueNode, | ||
/* 2 */ isExpressionNode, | ||
]; | ||
// prettier-ignore | ||
var table = [ | ||
/* st | token (terminal) | node (non-terminal) */ | ||
/* O_PAREN, C_PAREN, UNQUOTED, QUOTED, C_OP, OR_OP, AND_OP, END SELECT, VALUE, EXPR */ | ||
/* 0 */ [[push(0), noop, shift(8), noop, noop, noop, noop, noop], [goto(3), noop, goto(7)]], | ||
/* 1 */ [[push(0), noop, shift(8), noop, noop, noop, noop, noop], [goto(3), noop, goto(12),]], | ||
/* 2 */ [[push(0), noop, shift(8), noop, noop, noop, noop, noop], [goto(3), noop, goto(13),]], | ||
/* 3 */ [[noop, noop, noop, noop, shift(4), noop, noop, noop], [noop, noop, noop,]], | ||
/* 4 */ [[shift(5), noop, shift(9), shift(9), noop, noop, noop, noop], [noop, goto(11), noop,]], | ||
/* 5 */ [[noop, noop, shift(6), shift(6), noop, noop, noop, noop], [noop, noop, noop,]], | ||
/* 6 */ [[noop, shift(10), noop, noop, noop, shift(5), noop, noop], [noop, noop, noop,]], | ||
/* 7 */ [[noop, shift(14), noop, noop, noop, shift(1), shift(2), accept()], [noop, noop, noop,]], | ||
/* 8 */ [[noop, noop, noop, noop, reduce(0), noop, noop, noop], [noop, noop, noop,]], | ||
/* 9 */ [[noop, reduce(1), noop, noop, noop, reduce(1), reduce(1), reduce(1)], [noop, noop, noop,]], | ||
/* 10 */ [[noop, reduce(2), noop, noop, noop, reduce(2), reduce(2), reduce(2)], [noop, noop, noop,]], | ||
/* 11 */ [[noop, reduce(3), noop, noop, noop, reduce(3), reduce(3), reduce(3)], [noop, noop, noop,]], | ||
/* 12 */ [[noop, reduce(4), noop, noop, noop, reduce(4), shift(2), reduce(4)], [noop, noop, noop,]], | ||
/* 13 */ [[noop, reduce(4), noop, noop, noop, reduce(4), reduce(4), reduce(4)], [noop, noop, noop,]], | ||
/* 14 */ [[noop, pop(5), noop, noop, noop, pop(5), pop(5), pop(5)], [noop, noop, noop,]], | ||
]; | ||
function getParserTokenOperation(state, token) { | ||
return table[state][0][tokenMatchers.findIndex(function (matcher) { return matcher(token); })]; | ||
} | ||
function getMostMeaningfulInvalidToken(context) { | ||
if ( | ||
context.position > 0 && | ||
isCloseParenthesisToken(context.tokens[context.position - 1]) && | ||
context.parent === null | ||
) { | ||
// in this case we were not able to pop CLOSE_PARENTHESIS token, which was invalid in the first place | ||
return context.tokens[context.position - 1]; | ||
} | ||
function getParserNodeOperation(state, node) { | ||
return table[state][1][nodeMatchers.findIndex(function (matcher) { return matcher(node); })]; | ||
return context.tokens[context.position]; | ||
} | ||
function handleShift(context, shiftOperation) { | ||
// we can perform side-effects on shift operation to reduce memory usage | ||
context.stack.push(context.tokens[context.position]); | ||
context.state.push(shiftOperation.state); | ||
context.position = context.position + 1; | ||
return context; | ||
} | ||
function handlePush(context, pushOperation) { | ||
return { | ||
position: context.position + 1, | ||
tokens: context.tokens, | ||
stack: [context.tokens[context.position]], | ||
state: [pushOperation.state], | ||
parent: context, | ||
}; | ||
} | ||
function handleGoTo(context, gotoOperation) { | ||
// we can perform side-effects on goto operation to reduce memory usage | ||
context.state.push(gotoOperation.state); | ||
return context; | ||
} | ||
function handleReduce(context, reduceOperation, input) { | ||
var _a = productions[reduceOperation.production](context.stack), | ||
consumed = _a.consumed, | ||
produced = _a.produced; | ||
// we can perform side-effects on reduce operation to reduce memory usage | ||
for (var i = 0; i < consumed; ++i) { | ||
context.stack.pop(); | ||
context.state.pop(); | ||
} | ||
function getMostMeaningfulInvalidToken(context) { | ||
if (context.position > 0 && | ||
isCloseParenthesisToken(context.tokens[context.position - 1]) && | ||
context.parent === null) { | ||
// in this case we were not able to pop CLOSE_PARENTHESIS token, which was invalid in the first place | ||
return context.tokens[context.position - 1]; | ||
} | ||
return context.tokens[context.position]; | ||
context.stack.push(produced); | ||
var stateAfterReduce = getParserContextState(context); | ||
var nodeAfterReduce = getParserContextHead(context); | ||
var gotoOperation = getParserNodeOperation(stateAfterReduce, nodeAfterReduce); | ||
if (gotoOperation) { | ||
context = handleGoTo(context, gotoOperation); | ||
} else { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
function handleShift(context, shiftOperation) { | ||
// we can perform side-effects on shift operation to reduce memory usage | ||
context.stack.push(context.tokens[context.position]); | ||
context.state.push(shiftOperation.state); | ||
context.position = context.position + 1; | ||
return context; | ||
return context; | ||
} | ||
function handlePop(context, popOperation, input) { | ||
if (!context.parent) { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
function handlePush(context, pushOperation) { | ||
return { | ||
position: context.position + 1, | ||
tokens: context.tokens, | ||
stack: [context.tokens[context.position]], | ||
state: [pushOperation.state], | ||
parent: context, | ||
}; | ||
var produced = productions[popOperation.production](context.stack).produced; | ||
// we can perform side-effects on pop operation to reduce memory usage (as child context will not be used anymore) | ||
context.parent.position = context.position; | ||
context.parent.stack.push(produced); | ||
context = context.parent; | ||
var stateAfterPop = getParserContextState(context); | ||
var nodeAfterPop = getParserContextHead(context); | ||
var gotoOperation = getParserNodeOperation(stateAfterPop, nodeAfterPop); | ||
if (gotoOperation) { | ||
context = handleGoTo(context, gotoOperation); | ||
} else { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
function handleGoTo(context, gotoOperation) { | ||
// we can perform side-effects on goto operation to reduce memory usage | ||
context.state.push(gotoOperation.state); | ||
return context; | ||
return context; | ||
} | ||
function handleAccept(context, input) { | ||
if (context.parent !== null) { | ||
throw createErrorForUnclosedParenthesis(getMostMeaningfulInvalidToken(context), input, context.parent.position); | ||
} | ||
function handleReduce(context, reduceOperation, input) { | ||
var _a = productions[reduceOperation.production](context.stack), consumed = _a.consumed, produced = _a.produced; | ||
// we can perform side-effects on reduce operation to reduce memory usage | ||
for (var i = 0; i < consumed; ++i) { | ||
context.stack.pop(); | ||
context.state.pop(); | ||
} | ||
context.stack.push(produced); | ||
var stateAfterReduce = getParserContextState(context); | ||
var nodeAfterReduce = getParserContextHead(context); | ||
var gotoOperation = getParserNodeOperation(stateAfterReduce, nodeAfterReduce); | ||
if (gotoOperation) { | ||
context = handleGoTo(context, gotoOperation); | ||
} | ||
else { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
return context; | ||
return getParserContextHead(context); | ||
} | ||
function parse(source) { | ||
if (typeof source !== "string") { | ||
throw new TypeError('The argument passed to the "parse" function has to be a string, "' + source + '" passed.'); | ||
} | ||
function handlePop(context, popOperation, input) { | ||
if (!context.parent) { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
var produced = productions[popOperation.production](context.stack).produced; | ||
// we can perform side-effects on pop operation to reduce memory usage (as child context will not be used anymore) | ||
context.parent.position = context.position; | ||
context.parent.stack.push(produced); | ||
context = context.parent; | ||
var stateAfterPop = getParserContextState(context); | ||
var nodeAfterPop = getParserContextHead(context); | ||
var gotoOperation = getParserNodeOperation(stateAfterPop, nodeAfterPop); | ||
if (gotoOperation) { | ||
context = handleGoTo(context, gotoOperation); | ||
} | ||
else { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), input); | ||
} | ||
return context; | ||
var tokens = lex(source); | ||
if (tokens.length === 1 && tokens[0].type === "END") { | ||
throw createErrorForEmptyInput(tokens[0], source); | ||
} | ||
function handleAccept(context, input) { | ||
if (context.parent !== null) { | ||
throw createErrorForUnclosedParenthesis(getMostMeaningfulInvalidToken(context), input, context.parent.position); | ||
} | ||
return getParserContextHead(context); | ||
} | ||
function parse(source) { | ||
if (typeof source !== "string") { | ||
throw new TypeError("The argument passed to the \"parse\" function has to be a string, \"" + source + "\" passed."); | ||
} | ||
var tokens = lex(source); | ||
if (tokens.length === 1 && tokens[0].type === "END") { | ||
throw createErrorForEmptyInput(tokens[0], source); | ||
} | ||
var context = createParserContext(tokens); | ||
while (context.position < context.tokens.length) { | ||
var state = getParserContextState(context); | ||
var token = getParserContextToken(context); | ||
var operation = getParserTokenOperation(state, token); | ||
if (!operation) { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), source); | ||
} | ||
switch (operation.type) { | ||
case OperationType.SHIFT: | ||
context = handleShift(context, operation); | ||
break; | ||
case OperationType.PUSH: | ||
context = handlePush(context, operation); | ||
break; | ||
case OperationType.REDUCE: | ||
context = handleReduce(context, operation, source); | ||
break; | ||
case OperationType.POP: | ||
context = handlePop(context, operation, source); | ||
break; | ||
case OperationType.ACCEPT: | ||
return handleAccept(context, source); | ||
} | ||
} | ||
var context = createParserContext(tokens); | ||
while (context.position < context.tokens.length) { | ||
var state = getParserContextState(context); | ||
var token = getParserContextToken(context); | ||
var operation = getParserTokenOperation(state, token); | ||
if (!operation) { | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), source); | ||
} | ||
switch (operation.type) { | ||
case OperationType.SHIFT: | ||
context = handleShift(context, operation); | ||
break; | ||
case OperationType.PUSH: | ||
context = handlePush(context, operation); | ||
break; | ||
case OperationType.REDUCE: | ||
context = handleReduce(context, operation, source); | ||
break; | ||
case OperationType.POP: | ||
context = handlePop(context, operation, source); | ||
break; | ||
case OperationType.ACCEPT: | ||
return handleAccept(context, source); | ||
} | ||
} | ||
throw createErrorForUnexpectedToken(getMostMeaningfulInvalidToken(context), source); | ||
} | ||
exports.parse = parse; | ||
exports.parse = parse; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
}))); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
}); |
@@ -7,3 +7,3 @@ { | ||
"author": "Piotr Oleś <piotrek.oles@gmail.com>", | ||
"version": "0.0.1-next.2", | ||
"version": "0.0.1-next.3", | ||
"sideEffects": false, | ||
@@ -23,5 +23,6 @@ "main": "dist/index.js", | ||
"scripts": { | ||
"clean": "rm -rf ./lib && rm -f tsconfig.tsbuildinfo && rm -rf ./dist", | ||
"compile": "tsc -b tsconfig.json && rollup -c && dts-bundle --name \"@rsql/parser\" --main lib/index.d.ts --out ../dist/index.d.ts", | ||
"build": "yarn clean && yarn compile" | ||
"build:clean": "rm -rf ./lib && rm -f tsconfig.tsbuildinfo && rm -rf ./dist", | ||
"build:lib": "tsc -b tsconfig.json", | ||
"build:dist": "rollup -c && dts-bundle --name \"@rsql/parser\" --main lib/index.d.ts --out ../dist/index.d.ts --outputAsModuleFolder && prettier --write ./dist", | ||
"build": "yarn build:clean && yarn build:lib && yarn build:dist" | ||
}, | ||
@@ -32,3 +33,3 @@ "engines": { | ||
"dependencies": { | ||
"@rsql/ast": "0.0.1-next.2" | ||
"@rsql/ast": "0.0.1-next.3" | ||
}, | ||
@@ -43,3 +44,3 @@ "jest": { | ||
}, | ||
"gitHead": "1a690bf021f57c9201d1e02d263f170a419f5c6c" | ||
"gitHead": "501c3c00630ce8668f5d9caf4b79ef17d3555419" | ||
} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
2144
0
73053
+ Added@rsql/ast@0.0.1-next.3(transitive)
- Removed@rsql/ast@0.0.1-next.2(transitive)
Updated@rsql/ast@0.0.1-next.3