jora
Version:
JavaScript object query engine
341 lines (318 loc) • 9.56 kB
JavaScript
'use strict';
// Token type constants - using numeric codes for better performance
const TOKEN_NUMBER = 0;
const TOKEN_STRING = 1;
const TOKEN_REGEXP = 2;
const TOKEN_LITERAL = 3;
const TOKEN_IDENT = 4;
const TOKEN_$IDENT = 5;
// Special references
const TOKEN_AT = 6; // DATA
const TOKEN_HASH = 7; // CONTEXT
const TOKEN_$ = 8; // CURRENT
const TOKEN_$$ = 9; // ARG1
// Keywords
const TOKEN_AND = 10;
const TOKEN_OR = 11;
const TOKEN_NOT = 12;
const TOKEN_NO = 13;
const TOKEN_IS = 14;
const TOKEN_IN = 15;
const TOKEN_NOTIN = 16;
const TOKEN_HAS = 17;
const TOKEN_HASNO = 18;
const TOKEN_ORDER = 19;
// Method calls
const TOKEN_METHOD_OPEN = 20; // METHOD(
const TOKEN_$METHOD_OPEN = 21; // $METHOD(
// Template tokens
const TOKEN_TEMPLATE = 22;
const TOKEN_TPL_START = 23;
const TOKEN_TPL_CONTINUE = 24;
const TOKEN_TPL_END = 25;
// Operators
const TOKEN_DOT = 26;
const TOKEN_DOT_DOT = 27;
const TOKEN_DOT_DOT_DOT = 28;
const TOKEN_DOT_OPEN_PAREN = 29;
const TOKEN_DOT_OPEN_BRACKET = 30;
const TOKEN_DOT_DOT_OPEN_PAREN = 31;
const TOKEN_PIPE = 32;
const TOKEN_ARROW = 33; // =>
const TOKEN_EQUALS = 34;
const TOKEN_NOT_EQUALS = 35; // !=
const TOKEN_MATCH = 36; // ~=
const TOKEN_LESS_THAN = 37;
const TOKEN_LESS_THAN_EQUALS = 38;
const TOKEN_GREATER_THAN = 39;
const TOKEN_GREATER_THAN_EQUALS = 40;
const TOKEN_PLUS = 41;
const TOKEN_MINUS = 42;
const TOKEN_MULTIPLY = 43;
const TOKEN_DIVIDE = 44;
const TOKEN_MODULO = 45;
const TOKEN_NULLISH_COALESCING = 46; // ??
const TOKEN_QUESTION = 47;
// Punctuation
const TOKEN_OPEN_PAREN = 48;
const TOKEN_CLOSE_PAREN = 49;
const TOKEN_OPEN_BRACKET = 50;
const TOKEN_CLOSE_BRACKET = 51;
const TOKEN_OPEN_BRACE = 52;
const TOKEN_CLOSE_BRACE = 53;
const TOKEN_COMMA = 54;
const TOKEN_COLON = 55;
const TOKEN_SEMICOLON = 56;
// Special - must be last
const TOKEN_BAD = 57;
const TOKEN_EOF = 58;
const TOKEN_MAX = 59;
// Token names for debugging and error messages
const tokenNames = {
[TOKEN_NUMBER]: 'NUMBER',
[TOKEN_STRING]: 'STRING',
[TOKEN_REGEXP]: 'REGEXP',
[TOKEN_LITERAL]: 'LITERAL',
[TOKEN_IDENT]: 'IDENT',
[TOKEN_$IDENT]: '$IDENT',
[TOKEN_AT]: '@',
[TOKEN_HASH]: '#',
[TOKEN_$]: '$',
[TOKEN_$$]: '$$',
[TOKEN_AND]: 'AND',
[TOKEN_OR]: 'OR',
[TOKEN_NOT]: 'NOT',
[TOKEN_NO]: 'NO',
[TOKEN_IS]: 'IS',
[TOKEN_IN]: 'IN',
[TOKEN_NOTIN]: 'NOTIN',
[TOKEN_HAS]: 'HAS',
[TOKEN_HASNO]: 'HASNO',
[TOKEN_ORDER]: 'ORDER',
[TOKEN_METHOD_OPEN]: 'METHOD(',
[TOKEN_$METHOD_OPEN]: '$METHOD(',
[TOKEN_TEMPLATE]: 'TEMPLATE',
[TOKEN_TPL_START]: 'TPL_START',
[TOKEN_TPL_CONTINUE]: 'TPL_CONTINUE',
[TOKEN_TPL_END]: 'TPL_END',
[TOKEN_DOT]: '.',
[TOKEN_DOT_DOT]: '..',
[TOKEN_DOT_DOT_DOT]: '...',
[TOKEN_DOT_OPEN_PAREN]: '.(',
[TOKEN_DOT_OPEN_BRACKET]: '.[',
[TOKEN_DOT_DOT_OPEN_PAREN]: '..(',
[TOKEN_PIPE]: '|',
[TOKEN_ARROW]: '=>',
[TOKEN_EQUALS]: '=',
[TOKEN_NOT_EQUALS]: '!=',
[TOKEN_MATCH]: '~=',
[TOKEN_LESS_THAN]: '<',
[TOKEN_LESS_THAN_EQUALS]: '<=',
[TOKEN_GREATER_THAN]: '>',
[TOKEN_GREATER_THAN_EQUALS]: '>=',
[TOKEN_PLUS]: '+',
[TOKEN_MINUS]: '-',
[TOKEN_MULTIPLY]: '*',
[TOKEN_DIVIDE]: '/',
[TOKEN_MODULO]: '%',
[TOKEN_NULLISH_COALESCING]: '??',
[TOKEN_QUESTION]: '?',
[TOKEN_OPEN_PAREN]: '(',
[TOKEN_CLOSE_PAREN]: ')',
[TOKEN_OPEN_BRACKET]: '[',
[TOKEN_CLOSE_BRACKET]: ']',
[TOKEN_OPEN_BRACE]: '{',
[TOKEN_CLOSE_BRACE]: '}',
[TOKEN_COMMA]: ',',
[TOKEN_COLON]: ':',
[TOKEN_SEMICOLON]: ';',
[TOKEN_BAD]: 'BAD_TOKEN',
[TOKEN_EOF]: 'EOF'
};
// Bracket balance mappings
const BALANCE_TOKEN_PAIR = createTokenTable(
TOKEN_OPEN_PAREN, TOKEN_CLOSE_PAREN, // "(" -> ")"
TOKEN_DOT_OPEN_PAREN, TOKEN_CLOSE_PAREN, // ".(" -> ")"
TOKEN_DOT_DOT_OPEN_PAREN, TOKEN_CLOSE_PAREN, // "..(" -> ")"
TOKEN_METHOD_OPEN, TOKEN_CLOSE_PAREN, // "METHOD(" -> ")"
TOKEN_$METHOD_OPEN, TOKEN_CLOSE_PAREN, // "$METHOD(" -> ")"
TOKEN_OPEN_BRACKET, TOKEN_CLOSE_BRACKET, // "[" -> "]"
TOKEN_DOT_OPEN_BRACKET, TOKEN_CLOSE_BRACKET, // ".[" -> "]"
TOKEN_OPEN_BRACE, TOKEN_CLOSE_BRACE, // "{" -> "}"
TOKEN_TPL_START, TOKEN_TPL_END // "`{" -> "}`"
);
// Keyword lookup
const KEYWORDS = {
'has no': TOKEN_HASNO,
'not in': TOKEN_NOTIN,
'and': TOKEN_AND,
'or': TOKEN_OR,
'not': TOKEN_NOT,
'has': TOKEN_HAS,
'is': TOKEN_IS,
'in': TOKEN_IN,
'no': TOKEN_NO,
'asc': TOKEN_ORDER,
'ascN': TOKEN_ORDER,
'ascA': TOKEN_ORDER,
'ascNA': TOKEN_ORDER,
'ascAN': TOKEN_ORDER,
'desc': TOKEN_ORDER,
'descN': TOKEN_ORDER,
'descA': TOKEN_ORDER,
'descNA': TOKEN_ORDER,
'descAN': TOKEN_ORDER
};
// String to token mapping
const STR_TO_TOKEN = {
'...': TOKEN_DOT_DOT_DOT,
'..(': TOKEN_DOT_DOT_OPEN_PAREN,
'..': TOKEN_DOT_DOT,
'.(': TOKEN_DOT_OPEN_PAREN,
'.[': TOKEN_DOT_OPEN_BRACKET,
'=>': TOKEN_ARROW,
'!=': TOKEN_NOT_EQUALS,
'~=': TOKEN_MATCH,
'<=': TOKEN_LESS_THAN_EQUALS,
'>=': TOKEN_GREATER_THAN_EQUALS,
'??': TOKEN_NULLISH_COALESCING,
'@': TOKEN_AT,
'#': TOKEN_HASH,
'.': TOKEN_DOT,
'|': TOKEN_PIPE,
'=': TOKEN_EQUALS,
'<': TOKEN_LESS_THAN,
'>': TOKEN_GREATER_THAN,
'+': TOKEN_PLUS,
'-': TOKEN_MINUS,
'*': TOKEN_MULTIPLY,
'/': TOKEN_DIVIDE,
'%': TOKEN_MODULO,
'?': TOKEN_QUESTION,
'(': TOKEN_OPEN_PAREN,
')': TOKEN_CLOSE_PAREN,
'[': TOKEN_OPEN_BRACKET,
']': TOKEN_CLOSE_BRACKET,
'{': TOKEN_OPEN_BRACE,
'}': TOKEN_CLOSE_BRACE,
',': TOKEN_COMMA,
':': TOKEN_COLON,
';': TOKEN_SEMICOLON
};
// Keyword tokens — used to determine empty ident range behavior in tolerant mode
const KEYWORD_TOKENS = createTokenSet(
TOKEN_AND,
TOKEN_OR,
TOKEN_NOT,
TOKEN_NO,
TOKEN_IN,
TOKEN_NOTIN,
TOKEN_HAS,
TOKEN_HASNO,
TOKEN_IS,
TOKEN_ORDER
);
const PREVENT_KEYWORD = createTokenSet(
TOKEN_DOT,
TOKEN_DOT_DOT
);
const PREVENT_PRIMITIVE = createTokenSet(
TOKEN_NUMBER,
TOKEN_STRING,
TOKEN_TEMPLATE,
TOKEN_TPL_END,
TOKEN_REGEXP,
TOKEN_LITERAL,
TOKEN_IDENT,
TOKEN_$$,
TOKEN_$IDENT,
TOKEN_$,
TOKEN_AT,
TOKEN_HASH,
TOKEN_CLOSE_PAREN,
TOKEN_CLOSE_BRACKET,
TOKEN_CLOSE_BRACE,
TOKEN_DOT
);
function createTokenSet(...tokens) {
const set = new Int8Array(TOKEN_MAX);
for (const token of tokens) {
set[token] = 1;
}
return set;
}
function createTokenTable(...pairs) {
const table = new Int8Array(TOKEN_MAX).fill(-1);
for (let i = 0; i < pairs.length; i += 2) {
table[pairs[i]] = pairs[i + 1];
}
return table;
}
exports.BALANCE_TOKEN_PAIR = BALANCE_TOKEN_PAIR;
exports.KEYWORDS = KEYWORDS;
exports.KEYWORD_TOKENS = KEYWORD_TOKENS;
exports.PREVENT_KEYWORD = PREVENT_KEYWORD;
exports.PREVENT_PRIMITIVE = PREVENT_PRIMITIVE;
exports.STR_TO_TOKEN = STR_TO_TOKEN;
exports.TOKEN_$ = TOKEN_$;
exports.TOKEN_$$ = TOKEN_$$;
exports.TOKEN_$IDENT = TOKEN_$IDENT;
exports.TOKEN_$METHOD_OPEN = TOKEN_$METHOD_OPEN;
exports.TOKEN_AND = TOKEN_AND;
exports.TOKEN_ARROW = TOKEN_ARROW;
exports.TOKEN_AT = TOKEN_AT;
exports.TOKEN_BAD = TOKEN_BAD;
exports.TOKEN_CLOSE_BRACE = TOKEN_CLOSE_BRACE;
exports.TOKEN_CLOSE_BRACKET = TOKEN_CLOSE_BRACKET;
exports.TOKEN_CLOSE_PAREN = TOKEN_CLOSE_PAREN;
exports.TOKEN_COLON = TOKEN_COLON;
exports.TOKEN_COMMA = TOKEN_COMMA;
exports.TOKEN_DIVIDE = TOKEN_DIVIDE;
exports.TOKEN_DOT = TOKEN_DOT;
exports.TOKEN_DOT_DOT = TOKEN_DOT_DOT;
exports.TOKEN_DOT_DOT_DOT = TOKEN_DOT_DOT_DOT;
exports.TOKEN_DOT_DOT_OPEN_PAREN = TOKEN_DOT_DOT_OPEN_PAREN;
exports.TOKEN_DOT_OPEN_BRACKET = TOKEN_DOT_OPEN_BRACKET;
exports.TOKEN_DOT_OPEN_PAREN = TOKEN_DOT_OPEN_PAREN;
exports.TOKEN_EOF = TOKEN_EOF;
exports.TOKEN_EQUALS = TOKEN_EQUALS;
exports.TOKEN_GREATER_THAN = TOKEN_GREATER_THAN;
exports.TOKEN_GREATER_THAN_EQUALS = TOKEN_GREATER_THAN_EQUALS;
exports.TOKEN_HAS = TOKEN_HAS;
exports.TOKEN_HASH = TOKEN_HASH;
exports.TOKEN_HASNO = TOKEN_HASNO;
exports.TOKEN_IDENT = TOKEN_IDENT;
exports.TOKEN_IN = TOKEN_IN;
exports.TOKEN_IS = TOKEN_IS;
exports.TOKEN_LESS_THAN = TOKEN_LESS_THAN;
exports.TOKEN_LESS_THAN_EQUALS = TOKEN_LESS_THAN_EQUALS;
exports.TOKEN_LITERAL = TOKEN_LITERAL;
exports.TOKEN_MATCH = TOKEN_MATCH;
exports.TOKEN_METHOD_OPEN = TOKEN_METHOD_OPEN;
exports.TOKEN_MINUS = TOKEN_MINUS;
exports.TOKEN_MODULO = TOKEN_MODULO;
exports.TOKEN_MULTIPLY = TOKEN_MULTIPLY;
exports.TOKEN_NO = TOKEN_NO;
exports.TOKEN_NOT = TOKEN_NOT;
exports.TOKEN_NOTIN = TOKEN_NOTIN;
exports.TOKEN_NOT_EQUALS = TOKEN_NOT_EQUALS;
exports.TOKEN_NULLISH_COALESCING = TOKEN_NULLISH_COALESCING;
exports.TOKEN_NUMBER = TOKEN_NUMBER;
exports.TOKEN_OPEN_BRACE = TOKEN_OPEN_BRACE;
exports.TOKEN_OPEN_BRACKET = TOKEN_OPEN_BRACKET;
exports.TOKEN_OPEN_PAREN = TOKEN_OPEN_PAREN;
exports.TOKEN_OR = TOKEN_OR;
exports.TOKEN_ORDER = TOKEN_ORDER;
exports.TOKEN_PIPE = TOKEN_PIPE;
exports.TOKEN_PLUS = TOKEN_PLUS;
exports.TOKEN_QUESTION = TOKEN_QUESTION;
exports.TOKEN_REGEXP = TOKEN_REGEXP;
exports.TOKEN_SEMICOLON = TOKEN_SEMICOLON;
exports.TOKEN_STRING = TOKEN_STRING;
exports.TOKEN_TEMPLATE = TOKEN_TEMPLATE;
exports.TOKEN_TPL_CONTINUE = TOKEN_TPL_CONTINUE;
exports.TOKEN_TPL_END = TOKEN_TPL_END;
exports.TOKEN_TPL_START = TOKEN_TPL_START;
exports.createTokenSet = createTokenSet;
exports.createTokenTable = createTokenTable;
exports.tokenNames = tokenNames;