@filtron/core
Advanced tools
| { | ||
| "version": 3, | ||
| "sources": ["../src/lexer.ts", "../src/rd-parser.ts", "../src/parser.ts"], | ||
| "sourcesContent": [ | ||
| "/**\n * Lexer for Filtron query language\n *\n * Tokenizes input strings into a stream of tokens for the parser.\n */\n\n/**\n * Token types produced by the lexer\n */\nexport type TokenType =\n\t// Grouping\n\t| \"LPAREN\"\n\t| \"RPAREN\"\n\t| \"LBRACKET\"\n\t| \"RBRACKET\"\n\t// Punctuation\n\t| \"COMMA\"\n\t| \"QUESTION\"\n\t| \"DOT\"\n\t| \"DOTDOT\"\n\t// Keywords (case-insensitive)\n\t| \"AND\"\n\t| \"OR\"\n\t| \"NOT\"\n\t| \"EXISTS\"\n\t| \"TRUE\"\n\t| \"FALSE\"\n\t// Operators\n\t| \"EQ\" // =\n\t| \"NEQ\" // !=\n\t| \"GT\" // >\n\t| \"GTE\" // >=\n\t| \"LT\" // <\n\t| \"LTE\" // <=\n\t| \"LIKE\" // ~\n\t| \"COLON\" // :\n\t| \"NOT_COLON\" // !:\n\t// Literals\n\t| \"STRING\"\n\t| \"NUMBER\"\n\t| \"IDENT\"\n\t// End of input\n\t| \"EOF\";\n\n/**\n * A token produced by the lexer\n */\nexport interface Token {\n\ttype: TokenType;\n\tvalue: string | number | boolean;\n\tstart: number;\n\tend: number;\n}\n\n/**\n * Keywords mapped to their token types\n */\nconst KEYWORDS: Record<string, TokenType> = {\n\tand: \"AND\",\n\tor: \"OR\",\n\tnot: \"NOT\",\n\texists: \"EXISTS\",\n\ttrue: \"TRUE\",\n\tfalse: \"FALSE\",\n};\n\n/**\n * Lexer error with position information\n */\nexport class LexerError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic position: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"LexerError\";\n\t}\n}\n\n/**\n * Lexer for tokenizing Filtron query strings\n */\nexport class Lexer {\n\tprivate pos = 0;\n\tprivate readonly input: string;\n\tprivate readonly length: number;\n\n\tconstructor(input: string) {\n\t\tthis.input = input;\n\t\tthis.length = input.length;\n\t}\n\n\t/**\n\t * Get the current character without advancing\n\t */\n\tprivate peek(): string {\n\t\treturn this.pos < this.length ? this.input[this.pos] : \"\";\n\t}\n\n\t/**\n\t * Get the next character without advancing\n\t */\n\tprivate peekNext(): string {\n\t\treturn this.pos + 1 < this.length ? this.input[this.pos + 1] : \"\";\n\t}\n\n\t/**\n\t * Advance and return the current character\n\t */\n\tprivate advance(): string {\n\t\treturn this.pos < this.length ? this.input[this.pos++] : \"\";\n\t}\n\n\t/**\n\t * Skip whitespace and comments\n\t */\n\tprivate skipWhitespaceAndComments(): void {\n\t\twhile (this.pos < this.length) {\n\t\t\tconst char = this.peek();\n\n\t\t\t// Skip whitespace\n\t\t\tif (char === \" \" || char === \"\\t\" || char === \"\\n\" || char === \"\\r\") {\n\t\t\t\tthis.advance();\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Skip single-line comments: // ...\n\t\t\tif (char === \"/\" && this.peekNext() === \"/\") {\n\t\t\t\tthis.advance(); // skip /\n\t\t\t\tthis.advance(); // skip /\n\t\t\t\twhile (this.pos < this.length && this.peek() !== \"\\n\") {\n\t\t\t\t\tthis.advance();\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t/**\n\t * Read a string literal (double-quoted)\n\t */\n\tprivate readString(): Token {\n\t\tconst start = this.pos;\n\t\tthis.advance(); // skip opening quote\n\n\t\tlet value = \"\";\n\t\twhile (this.pos < this.length) {\n\t\t\tconst char = this.peek();\n\n\t\t\tif (char === '\"') {\n\t\t\t\tthis.advance(); // skip closing quote\n\t\t\t\treturn { type: \"STRING\", value, start, end: this.pos };\n\t\t\t}\n\n\t\t\tif (char === \"\\\\\") {\n\t\t\t\tthis.advance(); // skip backslash\n\t\t\t\tconst escaped = this.advance();\n\t\t\t\tswitch (escaped) {\n\t\t\t\t\tcase \"n\":\n\t\t\t\t\t\tvalue += \"\\n\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"t\":\n\t\t\t\t\t\tvalue += \"\\t\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"r\":\n\t\t\t\t\t\tvalue += \"\\r\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"\\\\\":\n\t\t\t\t\t\tvalue += \"\\\\\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase '\"':\n\t\t\t\t\t\tvalue += '\"';\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t// Keep unknown escapes as-is\n\t\t\t\t\t\tvalue += escaped;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tvalue += this.advance();\n\t\t}\n\n\t\tthrow new LexerError(\"Unterminated string literal\", start);\n\t}\n\n\t/**\n\t * Read a number literal (integer or float, possibly negative)\n\t */\n\tprivate readNumber(): Token {\n\t\tconst start = this.pos;\n\t\tlet numStr = \"\";\n\n\t\t// Handle negative sign\n\t\tif (this.peek() === \"-\") {\n\t\t\tnumStr += this.advance();\n\t\t}\n\n\t\t// Read integer part\n\t\twhile (/[0-9]/.test(this.peek())) {\n\t\t\tnumStr += this.advance();\n\t\t}\n\n\t\t// Check for decimal part\n\t\tif (this.peek() === \".\" && /[0-9]/.test(this.peekNext())) {\n\t\t\tnumStr += this.advance(); // the dot\n\t\t\twhile (/[0-9]/.test(this.peek())) {\n\t\t\t\tnumStr += this.advance();\n\t\t\t}\n\t\t\treturn { type: \"NUMBER\", value: parseFloat(numStr), start, end: this.pos };\n\t\t}\n\n\t\treturn { type: \"NUMBER\", value: parseInt(numStr, 10), start, end: this.pos };\n\t}\n\n\t/**\n\t * Read an identifier or keyword\n\t */\n\tprivate readIdentifier(): Token {\n\t\tconst start = this.pos;\n\t\tlet ident = \"\";\n\n\t\twhile (this.pos < this.length) {\n\t\t\tconst char = this.peek();\n\t\t\tif (/[a-zA-Z0-9_]/.test(char)) {\n\t\t\t\tident += this.advance();\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Check if it's a keyword\n\t\tconst lower = ident.toLowerCase();\n\t\tconst keywordType = KEYWORDS[lower];\n\t\tif (keywordType) {\n\t\t\tif (keywordType === \"TRUE\") {\n\t\t\t\treturn { type: \"TRUE\", value: true, start, end: this.pos };\n\t\t\t}\n\t\t\tif (keywordType === \"FALSE\") {\n\t\t\t\treturn { type: \"FALSE\", value: false, start, end: this.pos };\n\t\t\t}\n\t\t\treturn { type: keywordType, value: lower, start, end: this.pos };\n\t\t}\n\n\t\treturn { type: \"IDENT\", value: ident, start, end: this.pos };\n\t}\n\n\t/**\n\t * Get the next token\n\t */\n\tnext(): Token {\n\t\tthis.skipWhitespaceAndComments();\n\n\t\tif (this.pos >= this.length) {\n\t\t\treturn { type: \"EOF\", value: \"\", start: this.pos, end: this.pos };\n\t\t}\n\n\t\tconst start = this.pos;\n\t\tconst char = this.peek();\n\n\t\t// Single character tokens\n\t\tswitch (char) {\n\t\t\tcase \"(\":\n\t\t\t\tthis.advance();\n\t\t\t\treturn { type: \"LPAREN\", value: \"(\", start, end: this.pos };\n\t\t\tcase \")\":\n\t\t\t\tthis.advance();\n\t\t\t\treturn { type: \"RPAREN\", value: \")\", start, end: this.pos };\n\t\t\tcase \"[\":\n\t\t\t\tthis.advance();\n\t\t\t\treturn { type: \"LBRACKET\", value: \"[\", start, end: this.pos };\n\t\t\tcase \"]\":\n\t\t\t\tthis.advance();\n\t\t\t\treturn { type: \"RBRACKET\", value: \"]\", start, end: this.pos };\n\t\t\tcase \",\":\n\t\t\t\tthis.advance();\n\t\t\t\treturn { type: \"COMMA\", value: \",\", start, end: this.pos };\n\t\t\tcase \"?\":\n\t\t\t\tthis.advance();\n\t\t\t\treturn { type: \"QUESTION\", value: \"?\", start, end: this.pos };\n\t\t}\n\n\t\t// Two-character operators (check these before single-char versions)\n\t\tif (char === \"!\" && this.peekNext() === \"=\") {\n\t\t\tthis.advance();\n\t\t\tthis.advance();\n\t\t\treturn { type: \"NEQ\", value: \"!=\", start, end: this.pos };\n\t\t}\n\t\tif (char === \"!\" && this.peekNext() === \":\") {\n\t\t\tthis.advance();\n\t\t\tthis.advance();\n\t\t\treturn { type: \"NOT_COLON\", value: \"!:\", start, end: this.pos };\n\t\t}\n\t\tif (char === \">\" && this.peekNext() === \"=\") {\n\t\t\tthis.advance();\n\t\t\tthis.advance();\n\t\t\treturn { type: \"GTE\", value: \">=\", start, end: this.pos };\n\t\t}\n\t\tif (char === \"<\" && this.peekNext() === \"=\") {\n\t\t\tthis.advance();\n\t\t\tthis.advance();\n\t\t\treturn { type: \"LTE\", value: \"<=\", start, end: this.pos };\n\t\t}\n\t\tif (char === \".\" && this.peekNext() === \".\") {\n\t\t\tthis.advance();\n\t\t\tthis.advance();\n\t\t\treturn { type: \"DOTDOT\", value: \"..\", start, end: this.pos };\n\t\t}\n\n\t\t// Single character operators\n\t\tswitch (char) {\n\t\t\tcase \".\":\n\t\t\t\tthis.advance();\n\t\t\t\treturn { type: \"DOT\", value: \".\", start, end: this.pos };\n\t\t\tcase \"=\":\n\t\t\t\tthis.advance();\n\t\t\t\treturn { type: \"EQ\", value: \"=\", start, end: this.pos };\n\t\t\tcase \">\":\n\t\t\t\tthis.advance();\n\t\t\t\treturn { type: \"GT\", value: \">\", start, end: this.pos };\n\t\t\tcase \"<\":\n\t\t\t\tthis.advance();\n\t\t\t\treturn { type: \"LT\", value: \"<\", start, end: this.pos };\n\t\t\tcase \"~\":\n\t\t\t\tthis.advance();\n\t\t\t\treturn { type: \"LIKE\", value: \"~\", start, end: this.pos };\n\t\t\tcase \":\":\n\t\t\t\tthis.advance();\n\t\t\t\treturn { type: \"COLON\", value: \":\", start, end: this.pos };\n\t\t}\n\n\t\t// String literal\n\t\tif (char === '\"') {\n\t\t\treturn this.readString();\n\t\t}\n\n\t\t// Number (including negative numbers)\n\t\tif (/[0-9]/.test(char) || (char === \"-\" && /[0-9]/.test(this.peekNext()))) {\n\t\t\treturn this.readNumber();\n\t\t}\n\n\t\t// Identifier or keyword\n\t\tif (/[a-zA-Z_]/.test(char)) {\n\t\t\treturn this.readIdentifier();\n\t\t}\n\n\t\tthrow new LexerError(`Unexpected character: '${char}'`, this.pos);\n\t}\n}\n", | ||
| "/**\n * Recursive Descent Parser for Filtron\n *\n * Grammar (in order of precedence, lowest to highest):\n *\n * Query = OrExpression\n * OrExpression = AndExpression (OR AndExpression)*\n * AndExpression = NotExpression (AND NotExpression)*\n * NotExpression = NOT NotExpression | PrimaryExpression\n * PrimaryExpression = '(' OrExpression ')' | FieldExpression\n * FieldExpression = FieldName ('?' | EXISTS | ComparisonOp Value RangeSuffix? | OneOfOp '[' Values ']')?\n * FieldName = IDENT ('.' IDENT)*\n * Value = STRING | NUMBER | BOOLEAN | DottedIdent\n * Values = Value (',' Value)*\n * RangeSuffix = '..' NUMBER\n */\n\nimport type {\n\tASTNode,\n\tValue,\n\tComparisonOperator,\n\tComparisonExpression,\n\tOneOfExpression,\n\tNotOneOfExpression,\n\tExistsExpression,\n\tBooleanFieldExpression,\n\tRangeExpression,\n} from \"./types\";\nimport { Lexer, type Token, type TokenType } from \"./lexer\";\n\n/**\n * Parser error with position information\n */\nexport class ParseError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic position: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"ParseError\";\n\t}\n}\n\n/**\n * Recursive descent parser for Filtron queries\n */\nclass Parser {\n\tprivate lexer: Lexer;\n\tprivate current: Token;\n\n\tconstructor(input: string) {\n\t\tthis.lexer = new Lexer(input);\n\t\tthis.current = this.lexer.next();\n\t}\n\n\t/**\n\t * Advance to the next token and return the previous one\n\t */\n\tprivate advance(): Token {\n\t\tconst prev = this.current;\n\t\tthis.current = this.lexer.next();\n\t\treturn prev;\n\t}\n\n\t/**\n\t * Check if current token matches the given type\n\t */\n\tprivate check(type: TokenType): boolean {\n\t\treturn this.current.type === type;\n\t}\n\n\t/**\n\t * Check if current token matches any of the given types\n\t */\n\tprivate checkAny(...types: TokenType[]): boolean {\n\t\treturn types.includes(this.current.type);\n\t}\n\n\t/**\n\t * Consume a token of the expected type, or throw an error\n\t */\n\tprivate expect(type: TokenType, message?: string): Token {\n\t\tif (this.current.type !== type) {\n\t\t\tconst msg = message ?? `Expected ${type}, got ${this.current.type}`;\n\t\t\tthrow new ParseError(msg, this.current.start);\n\t\t}\n\t\treturn this.advance();\n\t}\n\n\t/**\n\t * Parse a complete query\n\t */\n\tparse(): ASTNode {\n\t\tif (this.check(\"EOF\")) {\n\t\t\tthrow new ParseError(\"Empty query\", 0);\n\t\t}\n\n\t\tconst result = this.parseOrExpression();\n\n\t\tif (!this.check(\"EOF\")) {\n\t\t\tthrow new ParseError(`Unexpected token: ${this.current.type}`, this.current.start);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Parse OR expression (lowest precedence)\n\t * OrExpression = AndExpression (OR AndExpression)*\n\t */\n\tprivate parseOrExpression(): ASTNode {\n\t\tlet left = this.parseAndExpression();\n\n\t\twhile (this.check(\"OR\")) {\n\t\t\tthis.advance(); // consume OR\n\t\t\tconst right = this.parseAndExpression();\n\t\t\tleft = { type: \"or\", left, right };\n\t\t}\n\n\t\treturn left;\n\t}\n\n\t/**\n\t * Parse AND expression\n\t * AndExpression = NotExpression (AND NotExpression)*\n\t */\n\tprivate parseAndExpression(): ASTNode {\n\t\tlet left = this.parseNotExpression();\n\n\t\twhile (this.check(\"AND\")) {\n\t\t\tthis.advance(); // consume AND\n\t\t\tconst right = this.parseNotExpression();\n\t\t\tleft = { type: \"and\", left, right };\n\t\t}\n\n\t\treturn left;\n\t}\n\n\t/**\n\t * Parse NOT expression\n\t * NotExpression = NOT NotExpression | PrimaryExpression\n\t */\n\tprivate parseNotExpression(): ASTNode {\n\t\tif (this.check(\"NOT\")) {\n\t\t\tthis.advance(); // consume NOT\n\t\t\tconst expression = this.parseNotExpression();\n\t\t\treturn { type: \"not\", expression };\n\t\t}\n\n\t\treturn this.parsePrimaryExpression();\n\t}\n\n\t/**\n\t * Parse primary expression (highest precedence)\n\t * PrimaryExpression = '(' OrExpression ')' | FieldExpression\n\t */\n\tprivate parsePrimaryExpression(): ASTNode {\n\t\t// Parenthesized expression\n\t\tif (this.check(\"LPAREN\")) {\n\t\t\tthis.advance(); // consume (\n\t\t\tconst expr = this.parseOrExpression();\n\t\t\tthis.expect(\"RPAREN\", \"Expected closing parenthesis\");\n\t\t\treturn expr;\n\t\t}\n\n\t\treturn this.parseFieldExpression();\n\t}\n\n\t/**\n\t * Parse field expression\n\t * FieldExpression = FieldName ('?' | EXISTS | ComparisonOp Value RangeSuffix? | OneOfOp '[' Values ']')?\n\t */\n\tprivate parseFieldExpression(): ASTNode {\n\t\tconst field = this.parseFieldName();\n\n\t\t// Exists check with ?\n\t\tif (this.check(\"QUESTION\")) {\n\t\t\tthis.advance(); // consume ?\n\t\t\treturn { type: \"exists\", field } as ExistsExpression;\n\t\t}\n\n\t\t// Exists check with EXISTS keyword\n\t\tif (this.check(\"EXISTS\")) {\n\t\t\tthis.advance(); // consume EXISTS\n\t\t\treturn { type: \"exists\", field } as ExistsExpression;\n\t\t}\n\n\t\t// OneOf: field : [values]\n\t\tif (this.check(\"COLON\") && this.peekNextIsLBracket()) {\n\t\t\tthis.advance(); // consume :\n\t\t\treturn this.parseOneOfArray(field, \"oneOf\");\n\t\t}\n\n\t\t// NotOneOf: field !: [values]\n\t\tif (this.check(\"NOT_COLON\")) {\n\t\t\tthis.advance(); // consume !:\n\t\t\treturn this.parseOneOfArray(field, \"notOneOf\");\n\t\t}\n\n\t\t// Comparison with operator\n\t\tif (this.isComparisonOperator()) {\n\t\t\tconst opToken = this.advance();\n\t\t\tconst operator = this.tokenToOperator(opToken);\n\n\t\t\t// Check for range expression: field = min..max\n\t\t\tif (operator === \"=\" && this.check(\"NUMBER\")) {\n\t\t\t\tconst minToken = this.advance();\n\t\t\t\tconst min = minToken.value as number;\n\n\t\t\t\tif (this.check(\"DOTDOT\")) {\n\t\t\t\t\tthis.advance(); // consume ..\n\t\t\t\t\tconst maxToken = this.expect(\"NUMBER\", \"Expected number after '..'\");\n\t\t\t\t\tconst max = maxToken.value as number;\n\t\t\t\t\treturn { type: \"range\", field, min, max } as RangeExpression;\n\t\t\t\t}\n\n\t\t\t\t// Not a range, just a regular comparison with a number\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"comparison\",\n\t\t\t\t\tfield,\n\t\t\t\t\toperator,\n\t\t\t\t\tvalue: { type: \"number\", value: min },\n\t\t\t\t} as ComparisonExpression;\n\t\t\t}\n\n\t\t\tconst value = this.parseValue();\n\t\t\treturn { type: \"comparison\", field, operator, value } as ComparisonExpression;\n\t\t}\n\n\t\t// Boolean field shorthand (just the field name)\n\t\treturn { type: \"booleanField\", field } as BooleanFieldExpression;\n\t}\n\n\t/**\n\t * Check if the token after the current one is LBRACKET\n\t * (used to distinguish : as oneOf vs : as comparison operator)\n\t *\n\t * Note: We manually save/restore lexer state because this.current is already\n\t * one token ahead of the lexer's position, and we need to peek further ahead.\n\t */\n\tprivate peekNextIsLBracket(): boolean {\n\t\tconst savedPos = this.lexer[\"pos\"];\n\t\tconst savedCurrent = this.current;\n\t\tconst next = this.lexer.next();\n\t\tthis.lexer[\"pos\"] = savedPos;\n\t\tthis.current = savedCurrent;\n\t\treturn next.type === \"LBRACKET\";\n\t}\n\n\t/**\n\t * Check if current token is a comparison operator\n\t */\n\tprivate isComparisonOperator(): boolean {\n\t\treturn this.checkAny(\"EQ\", \"NEQ\", \"GT\", \"GTE\", \"LT\", \"LTE\", \"LIKE\", \"COLON\");\n\t}\n\n\t/**\n\t * Convert a token to its corresponding comparison operator\n\t */\n\tprivate tokenToOperator(token: Token): ComparisonOperator {\n\t\tswitch (token.type) {\n\t\t\tcase \"EQ\":\n\t\t\t\treturn \"=\";\n\t\t\tcase \"NEQ\":\n\t\t\t\treturn \"!=\";\n\t\t\tcase \"GT\":\n\t\t\t\treturn \">\";\n\t\t\tcase \"GTE\":\n\t\t\t\treturn \">=\";\n\t\t\tcase \"LT\":\n\t\t\t\treturn \"<\";\n\t\t\tcase \"LTE\":\n\t\t\t\treturn \"<=\";\n\t\t\tcase \"LIKE\":\n\t\t\t\treturn \"~\";\n\t\t\tcase \"COLON\":\n\t\t\t\treturn \":\";\n\t\t\tdefault:\n\t\t\t\tthrow new ParseError(`Invalid operator: ${token.type}`, token.start);\n\t\t}\n\t}\n\n\t/**\n\t * Parse a field name (possibly dotted)\n\t * FieldName = IDENT ('.' IDENT)*\n\t */\n\tprivate parseFieldName(): string {\n\t\tconst first = this.expect(\"IDENT\", \"Expected field name\");\n\t\tlet name = first.value as string;\n\n\t\twhile (this.check(\"DOT\")) {\n\t\t\tthis.advance(); // consume .\n\t\t\tconst next = this.expect(\"IDENT\", \"Expected identifier after '.'\");\n\t\t\tname += \".\" + next.value;\n\t\t}\n\n\t\treturn name;\n\t}\n\n\t/**\n\t * Parse oneOf/notOneOf array\n\t * '[' Values ']'\n\t */\n\tprivate parseOneOfArray(\n\t\tfield: string,\n\t\ttype: \"oneOf\" | \"notOneOf\",\n\t): OneOfExpression | NotOneOfExpression {\n\t\tthis.expect(\"LBRACKET\", \"Expected '[' after operator\");\n\n\t\tconst values: Value[] = [];\n\n\t\t// Handle non-empty array\n\t\tif (!this.check(\"RBRACKET\")) {\n\t\t\tvalues.push(this.parseValue());\n\n\t\t\twhile (this.check(\"COMMA\")) {\n\t\t\t\tthis.advance(); // consume ,\n\t\t\t\tvalues.push(this.parseValue());\n\t\t\t}\n\t\t}\n\n\t\tthis.expect(\"RBRACKET\", \"Expected ']' to close array\");\n\n\t\tif (values.length === 0) {\n\t\t\tthrow new ParseError(\"Array cannot be empty\", this.current.start);\n\t\t}\n\n\t\treturn { type, field, values };\n\t}\n\n\t/**\n\t * Parse a value\n\t * Value = STRING | NUMBER | BOOLEAN | DottedIdent\n\t */\n\tprivate parseValue(): Value {\n\t\t// String literal\n\t\tif (this.check(\"STRING\")) {\n\t\t\tconst token = this.advance();\n\t\t\treturn { type: \"string\", value: token.value as string };\n\t\t}\n\n\t\t// Number literal\n\t\tif (this.check(\"NUMBER\")) {\n\t\t\tconst token = this.advance();\n\t\t\treturn { type: \"number\", value: token.value as number };\n\t\t}\n\n\t\t// Boolean literal\n\t\tif (this.check(\"TRUE\")) {\n\t\t\tthis.advance();\n\t\t\treturn { type: \"boolean\", value: true };\n\t\t}\n\t\tif (this.check(\"FALSE\")) {\n\t\t\tthis.advance();\n\t\t\treturn { type: \"boolean\", value: false };\n\t\t}\n\n\t\t// Identifier (possibly dotted)\n\t\tif (this.check(\"IDENT\")) {\n\t\t\tconst first = this.advance();\n\t\t\tlet value = first.value as string;\n\n\t\t\twhile (this.check(\"DOT\")) {\n\t\t\t\tthis.advance(); // consume .\n\t\t\t\tconst next = this.expect(\"IDENT\", \"Expected identifier after '.'\");\n\t\t\t\tvalue += \".\" + next.value;\n\t\t\t}\n\n\t\t\treturn { type: \"identifier\", value };\n\t\t}\n\n\t\tthrow new ParseError(`Expected value, got ${this.current.type}`, this.current.start);\n\t}\n}\n\n/**\n * Parse a Filtron query string into an AST\n *\n * @param input - The query string to parse\n * @returns The parsed AST\n * @throws ParseError if the query is invalid\n */\nexport function parseQuery(input: string): ASTNode {\n\tconst parser = new Parser(input);\n\treturn parser.parse();\n}\n", | ||
| "/**\n * Filtron Parser\n *\n * High-performance recursive descent parser for Filtron query language.\n */\n\nimport type { ASTNode } from \"./types\";\nimport { LexerError } from \"./lexer\";\nimport { parseQuery, ParseError as RDParseError } from \"./rd-parser\";\n\n/**\n * Result of a successful parse operation\n */\nexport interface ParseSuccess {\n\tsuccess: true;\n\tast: ASTNode;\n}\n\n/**\n * Result of a failed parse operation\n */\nexport interface ParseError {\n\tsuccess: false;\n\terror: string;\n\tmessage: string;\n}\n\n/**\n * Result of a parse operation - either success or error\n */\nexport type ParseResult = ParseSuccess | ParseError;\n\n/**\n * Parses a Filtron query string into an Abstract Syntax Tree (AST).\n *\n * @param query - The Filtron query string to parse\n * @returns A ParseResult containing either the AST or an error message\n *\n * @example\n * ```typescript\n * const result = parse('age > 18 AND status = \"active\"');\n * if (result.success) {\n * console.log(result.ast);\n * } else {\n * console.error(result.error);\n * }\n * ```\n */\nexport const parse = (query: string): ParseResult => {\n\ttry {\n\t\tconst ast = parseQuery(query);\n\t\treturn {\n\t\t\tsuccess: true,\n\t\t\tast,\n\t\t};\n\t} catch (error) {\n\t\tlet message: string;\n\n\t\tif (error instanceof RDParseError || error instanceof LexerError) {\n\t\t\tmessage = error.message;\n\t\t} else if (error instanceof Error) {\n\t\t\tmessage = error.message;\n\t\t} else {\n\t\t\tmessage = String(error);\n\t\t}\n\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: message,\n\t\t\tmessage,\n\t\t};\n\t}\n};\n\n/**\n * Parses a Filtron query string and throws an error if parsing fails.\n * Use this when you want to handle errors with try/catch instead of checking the result.\n *\n * @param query - The Filtron query string to parse\n * @returns The parsed AST\n * @throws Error if parsing fails\n *\n * @example\n * ```typescript\n * try {\n * const ast = parseOrThrow('age > 18');\n * console.log(ast);\n * } catch (error) {\n * console.error('Parse failed:', error.message);\n * }\n * ```\n */\nexport const parseOrThrow = (query: string): ASTNode => {\n\tconst result = parse(query);\n\n\tif (result.success) {\n\t\treturn result.ast;\n\t}\n\n\tthrow new Error(`Failed to parse Filtron query: ${result.error}`);\n};\n" | ||
| ], | ||
| "mappings": "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", | ||
| "debugId": "D0558EABEFF0FABB64756E2164756E21", | ||
| "names": [] | ||
| } |
| import type { | ||
| ASTNode, | ||
| ComparisonExpression, | ||
| BooleanFieldExpression, | ||
| AndExpression, | ||
| OrExpression, | ||
| NotExpression, | ||
| ExistsExpression, | ||
| OneOfExpression, | ||
| NotOneOfExpression, | ||
| RangeExpression, | ||
| } from "./types"; | ||
| /** | ||
| * Fast path: Parse simple comparison expression | ||
| * Handles: field = value, field > 123, nested.field != "test" | ||
| * | ||
| * Returns null if pattern doesn't match or value is too complex | ||
| */ | ||
| export declare function parseSimpleComparison(query: string): ComparisonExpression | null; | ||
| /** | ||
| * Fast path: Parse simple boolean field expression | ||
| * Handles: verified, user.premium, active | ||
| * | ||
| * Returns null if pattern doesn't match or is a keyword | ||
| */ | ||
| export declare function parseSimpleBooleanField(query: string): BooleanFieldExpression | null; | ||
| /** | ||
| * Fast path: Parse exists expression with ? operator | ||
| * Handles: email?, user.profile.avatar? | ||
| * | ||
| * Returns null if pattern doesn't match | ||
| */ | ||
| export declare function parseExistsQuestion(query: string): ExistsExpression | null; | ||
| /** | ||
| * Fast path: Parse exists expression with exists keyword | ||
| * Handles: email exists, user.profile.avatar EXISTS | ||
| * | ||
| * Returns null if pattern doesn't match | ||
| */ | ||
| export declare function parseExistsKeyword(query: string): ExistsExpression | null; | ||
| /** | ||
| * Fast path: Parse oneOf expression | ||
| * Handles: status : ["active", "pending"], role : [1, 2, 3] | ||
| * | ||
| * Returns null if pattern doesn't match or values are complex | ||
| */ | ||
| export declare function parseOneOf(query: string): OneOfExpression | null; | ||
| /** | ||
| * Fast path: Parse notOneOf expression | ||
| * Handles: status !: ["banned", "deleted"], role !: [0] | ||
| * | ||
| * Returns null if pattern doesn't match or values are complex | ||
| */ | ||
| export declare function parseNotOneOf(query: string): NotOneOfExpression | null; | ||
| /** | ||
| * Fast path: Parse range expression | ||
| * Handles: age = 18..65, price = 0..100, score = -10..10 | ||
| * | ||
| * Returns null if pattern doesn't match | ||
| */ | ||
| export declare function parseRange(query: string): RangeExpression | null; | ||
| /** | ||
| * Fast path: Parse NOT expression (single negation) | ||
| * Handles: NOT verified, NOT (field = value), NOT status : ["active"] | ||
| * | ||
| * Returns null if expression is too complex | ||
| */ | ||
| export declare function parseSimpleNot(query: string): NotExpression | null; | ||
| /** | ||
| * Fast path: Parse simple AND expression | ||
| * Handles: expr1 AND expr2 (where both are simple expressions) | ||
| * Also handles chains: expr1 AND expr2 AND expr3 (up to 5 terms for performance) | ||
| * | ||
| * Returns null if pattern doesn't match or expressions are complex | ||
| */ | ||
| export declare function parseSimpleAnd(query: string): AndExpression | null; | ||
| /** | ||
| * Fast path: Parse simple OR expression | ||
| * Handles: expr1 OR expr2 (where both are simple expressions) | ||
| * Also handles chains: expr1 OR expr2 OR expr3 (up to 5 terms for performance) | ||
| * | ||
| * Returns null if pattern doesn't match or expressions are complex | ||
| */ | ||
| export declare function parseSimpleOr(query: string): OrExpression | null; | ||
| /** | ||
| * Fast path: Try to parse query using optimized fast paths | ||
| * | ||
| * Returns null if no fast path matches (fallback to full Ohm.js parser) | ||
| * | ||
| */ | ||
| export declare function tryFastPath(query: string): ASTNode | null; | ||
| //# sourceMappingURL=fast-path.d.ts.map |
| {"version":3,"file":"fast-path.d.ts","sourceRoot":"","sources":["../../src/fast-path.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,OAAO,EACP,oBAAoB,EACpB,sBAAsB,EACtB,aAAa,EACb,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,eAAe,EAGf,MAAM,SAAS,CAAC;AAmRjB;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAgChF;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI,CAkBpF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAmB1E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAmBzE;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CA2BhE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CA2BtE;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CA2BhE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CA6BlE;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAoDlE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CA0DhE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CA0GzD"} |
| /** | ||
| * Lexer for Filtron query language | ||
| * | ||
| * Tokenizes input strings into a stream of tokens for the parser. | ||
| */ | ||
| /** | ||
| * Token types produced by the lexer | ||
| */ | ||
| export type TokenType = "LPAREN" | "RPAREN" | "LBRACKET" | "RBRACKET" | "COMMA" | "QUESTION" | "DOT" | "DOTDOT" | "AND" | "OR" | "NOT" | "EXISTS" | "TRUE" | "FALSE" | "EQ" | "NEQ" | "GT" | "GTE" | "LT" | "LTE" | "LIKE" | "COLON" | "NOT_COLON" | "STRING" | "NUMBER" | "IDENT" | "EOF"; | ||
| /** | ||
| * A token produced by the lexer | ||
| */ | ||
| export interface Token { | ||
| type: TokenType; | ||
| value: string | number | boolean; | ||
| start: number; | ||
| end: number; | ||
| } | ||
| /** | ||
| * Lexer error with position information | ||
| */ | ||
| export declare class LexerError extends Error { | ||
| position: number; | ||
| constructor(message: string, position: number); | ||
| } | ||
| /** | ||
| * Lexer for tokenizing Filtron query strings | ||
| */ | ||
| export declare class Lexer { | ||
| private pos; | ||
| private readonly input; | ||
| private readonly length; | ||
| constructor(input: string); | ||
| /** | ||
| * Get the current character without advancing | ||
| */ | ||
| private peek; | ||
| /** | ||
| * Get the next character without advancing | ||
| */ | ||
| private peekNext; | ||
| /** | ||
| * Advance and return the current character | ||
| */ | ||
| private advance; | ||
| /** | ||
| * Skip whitespace and comments | ||
| */ | ||
| private skipWhitespaceAndComments; | ||
| /** | ||
| * Read a string literal (double-quoted) | ||
| */ | ||
| private readString; | ||
| /** | ||
| * Read a number literal (integer or float, possibly negative) | ||
| */ | ||
| private readNumber; | ||
| /** | ||
| * Read an identifier or keyword | ||
| */ | ||
| private readIdentifier; | ||
| /** | ||
| * Get the next token | ||
| */ | ||
| next(): Token; | ||
| } | ||
| //# sourceMappingURL=lexer.d.ts.map |
| {"version":3,"file":"lexer.d.ts","sourceRoot":"","sources":["../../src/lexer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,MAAM,SAAS,GAElB,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,UAAU,GAEV,OAAO,GACP,UAAU,GACV,KAAK,GACL,QAAQ,GAER,KAAK,GACL,IAAI,GACJ,KAAK,GACL,QAAQ,GACR,MAAM,GACN,OAAO,GAEP,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,KAAK,GACL,MAAM,GACN,OAAO,GACP,WAAW,GAEX,QAAQ,GACR,QAAQ,GACR,OAAO,GAEP,KAAK,CAAC;AAET;;GAEG;AACH,MAAM,WAAW,KAAK;IACrB,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACZ;AAcD;;GAEG;AACH,qBAAa,UAAW,SAAQ,KAAK;IAG5B,QAAQ,EAAE,MAAM;gBADvB,OAAO,EAAE,MAAM,EACR,QAAQ,EAAE,MAAM;CAKxB;AAED;;GAEG;AACH,qBAAa,KAAK;IACjB,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,KAAK,EAAE,MAAM;IAKzB;;OAEG;IACH,OAAO,CAAC,IAAI;IAIZ;;OAEG;IACH,OAAO,CAAC,QAAQ;IAIhB;;OAEG;IACH,OAAO,CAAC,OAAO;IAIf;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAwBjC;;OAEG;IACH,OAAO,CAAC,UAAU;IA6ClB;;OAEG;IACH,OAAO,CAAC,UAAU;IA0BlB;;OAEG;IACH,OAAO,CAAC,cAAc;IA6BtB;;OAEG;IACH,IAAI,IAAI,KAAK;CAkGb"} |
| /** | ||
| * Recursive Descent Parser for Filtron | ||
| * | ||
| * Grammar (in order of precedence, lowest to highest): | ||
| * | ||
| * Query = OrExpression | ||
| * OrExpression = AndExpression (OR AndExpression)* | ||
| * AndExpression = NotExpression (AND NotExpression)* | ||
| * NotExpression = NOT NotExpression | PrimaryExpression | ||
| * PrimaryExpression = '(' OrExpression ')' | FieldExpression | ||
| * FieldExpression = FieldName ('?' | EXISTS | ComparisonOp Value RangeSuffix? | OneOfOp '[' Values ']')? | ||
| * FieldName = IDENT ('.' IDENT)* | ||
| * Value = STRING | NUMBER | BOOLEAN | DottedIdent | ||
| * Values = Value (',' Value)* | ||
| * RangeSuffix = '..' NUMBER | ||
| */ | ||
| import type { ASTNode } from "./types"; | ||
| /** | ||
| * Parser error with position information | ||
| */ | ||
| export declare class ParseError extends Error { | ||
| position: number; | ||
| constructor(message: string, position: number); | ||
| } | ||
| /** | ||
| * Parse a Filtron query string into an AST | ||
| * | ||
| * @param input - The query string to parse | ||
| * @returns The parsed AST | ||
| * @throws ParseError if the query is invalid | ||
| */ | ||
| export declare function parseQuery(input: string): ASTNode; | ||
| //# sourceMappingURL=rd-parser.d.ts.map |
| {"version":3,"file":"rd-parser.d.ts","sourceRoot":"","sources":["../../src/rd-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EACX,OAAO,EASP,MAAM,SAAS,CAAC;AAGjB;;GAEG;AACH,qBAAa,UAAW,SAAQ,KAAK;IAG5B,QAAQ,EAAE,MAAM;gBADvB,OAAO,EAAE,MAAM,EACR,QAAQ,EAAE,MAAM;CAKxB;AA8UD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGjD"} |
+6
-59
@@ -1,60 +0,7 @@ | ||
| import{makeRecipe as m}from"ohm-js";var x=m(["grammar",{source:`Filtron { | ||
| Query = OrExpression | ||
| var I={and:"AND",or:"OR",not:"NOT",exists:"EXISTS",true:"TRUE",false:"FALSE"};class x extends Error{position;constructor(O,V){super(O);this.position=V;this.name="LexerError"}}class C{pos=0;input;length;constructor(O){this.input=O,this.length=O.length}peek(){return this.pos<this.length?this.input[this.pos]:""}peekNext(){return this.pos+1<this.length?this.input[this.pos+1]:""}advance(){return this.pos<this.length?this.input[this.pos++]:""}skipWhitespaceAndComments(){while(this.pos<this.length){let O=this.peek();if(O===" "||O==="\t"||O===` | ||
| `||O==="\r"){this.advance();continue}if(O==="/"&&this.peekNext()==="/"){this.advance(),this.advance();while(this.pos<this.length&&this.peek()!==` | ||
| `)this.advance();continue}break}}readString(){let O=this.pos;this.advance();let V="";while(this.pos<this.length){let N=this.peek();if(N==='"')return this.advance(),{type:"STRING",value:V,start:O,end:this.pos};if(N==="\\"){this.advance();let A=this.advance();switch(A){case"n":V+=` | ||
| `;break;case"t":V+="\t";break;case"r":V+="\r";break;case"\\":V+="\\";break;case'"':V+='"';break;default:V+=A}continue}V+=this.advance()}throw new x("Unterminated string literal",O)}readNumber(){let O=this.pos,V="";if(this.peek()==="-")V+=this.advance();while(/[0-9]/.test(this.peek()))V+=this.advance();if(this.peek()==="."&&/[0-9]/.test(this.peekNext())){V+=this.advance();while(/[0-9]/.test(this.peek()))V+=this.advance();return{type:"NUMBER",value:parseFloat(V),start:O,end:this.pos}}return{type:"NUMBER",value:parseInt(V,10),start:O,end:this.pos}}readIdentifier(){let O=this.pos,V="";while(this.pos<this.length){let M=this.peek();if(/[a-zA-Z0-9_]/.test(M))V+=this.advance();else break}let N=V.toLowerCase(),A=I[N];if(A){if(A==="TRUE")return{type:"TRUE",value:!0,start:O,end:this.pos};if(A==="FALSE")return{type:"FALSE",value:!1,start:O,end:this.pos};return{type:A,value:N,start:O,end:this.pos}}return{type:"IDENT",value:V,start:O,end:this.pos}}next(){if(this.skipWhitespaceAndComments(),this.pos>=this.length)return{type:"EOF",value:"",start:this.pos,end:this.pos};let O=this.pos,V=this.peek();switch(V){case"(":return this.advance(),{type:"LPAREN",value:"(",start:O,end:this.pos};case")":return this.advance(),{type:"RPAREN",value:")",start:O,end:this.pos};case"[":return this.advance(),{type:"LBRACKET",value:"[",start:O,end:this.pos};case"]":return this.advance(),{type:"RBRACKET",value:"]",start:O,end:this.pos};case",":return this.advance(),{type:"COMMA",value:",",start:O,end:this.pos};case"?":return this.advance(),{type:"QUESTION",value:"?",start:O,end:this.pos}}if(V==="!"&&this.peekNext()==="=")return this.advance(),this.advance(),{type:"NEQ",value:"!=",start:O,end:this.pos};if(V==="!"&&this.peekNext()===":")return this.advance(),this.advance(),{type:"NOT_COLON",value:"!:",start:O,end:this.pos};if(V===">"&&this.peekNext()==="=")return this.advance(),this.advance(),{type:"GTE",value:">=",start:O,end:this.pos};if(V==="<"&&this.peekNext()==="=")return this.advance(),this.advance(),{type:"LTE",value:"<=",start:O,end:this.pos};if(V==="."&&this.peekNext()===".")return this.advance(),this.advance(),{type:"DOTDOT",value:"..",start:O,end:this.pos};switch(V){case".":return this.advance(),{type:"DOT",value:".",start:O,end:this.pos};case"=":return this.advance(),{type:"EQ",value:"=",start:O,end:this.pos};case">":return this.advance(),{type:"GT",value:">",start:O,end:this.pos};case"<":return this.advance(),{type:"LT",value:"<",start:O,end:this.pos};case"~":return this.advance(),{type:"LIKE",value:"~",start:O,end:this.pos};case":":return this.advance(),{type:"COLON",value:":",start:O,end:this.pos}}if(V==='"')return this.readString();if(/[0-9]/.test(V)||V==="-"&&/[0-9]/.test(this.peekNext()))return this.readNumber();if(/[a-zA-Z_]/.test(V))return this.readIdentifier();throw new x(`Unexpected character: '${V}'`,this.pos)}}class B extends Error{position;constructor(O,V){super(O);this.position=V;this.name="ParseError"}}class z{lexer;current;constructor(O){this.lexer=new C(O),this.current=this.lexer.next()}advance(){let O=this.current;return this.current=this.lexer.next(),O}check(O){return this.current.type===O}checkAny(...O){return O.includes(this.current.type)}expect(O,V){if(this.current.type!==O){let N=V??`Expected ${O}, got ${this.current.type}`;throw new B(N,this.current.start)}return this.advance()}parse(){if(this.check("EOF"))throw new B("Empty query",0);let O=this.parseOrExpression();if(!this.check("EOF"))throw new B(`Unexpected token: ${this.current.type}`,this.current.start);return O}parseOrExpression(){let O=this.parseAndExpression();while(this.check("OR")){this.advance();let V=this.parseAndExpression();O={type:"or",left:O,right:V}}return O}parseAndExpression(){let O=this.parseNotExpression();while(this.check("AND")){this.advance();let V=this.parseNotExpression();O={type:"and",left:O,right:V}}return O}parseNotExpression(){if(this.check("NOT"))return this.advance(),{type:"not",expression:this.parseNotExpression()};return this.parsePrimaryExpression()}parsePrimaryExpression(){if(this.check("LPAREN")){this.advance();let O=this.parseOrExpression();return this.expect("RPAREN","Expected closing parenthesis"),O}return this.parseFieldExpression()}parseFieldExpression(){let O=this.parseFieldName();if(this.check("QUESTION"))return this.advance(),{type:"exists",field:O};if(this.check("EXISTS"))return this.advance(),{type:"exists",field:O};if(this.check("COLON")&&this.peekNextIsLBracket())return this.advance(),this.parseOneOfArray(O,"oneOf");if(this.check("NOT_COLON"))return this.advance(),this.parseOneOfArray(O,"notOneOf");if(this.isComparisonOperator()){let V=this.advance(),N=this.tokenToOperator(V);if(N==="="&&this.check("NUMBER")){let j=this.advance().value;if(this.check("DOTDOT")){this.advance();let H=this.expect("NUMBER","Expected number after '..'").value;return{type:"range",field:O,min:j,max:H}}return{type:"comparison",field:O,operator:N,value:{type:"number",value:j}}}let A=this.parseValue();return{type:"comparison",field:O,operator:N,value:A}}return{type:"booleanField",field:O}}peekNextIsLBracket(){let O=this.lexer.pos,V=this.current,N=this.lexer.next();return this.lexer.pos=O,this.current=V,N.type==="LBRACKET"}isComparisonOperator(){return this.checkAny("EQ","NEQ","GT","GTE","LT","LTE","LIKE","COLON")}tokenToOperator(O){switch(O.type){case"EQ":return"=";case"NEQ":return"!=";case"GT":return">";case"GTE":return">=";case"LT":return"<";case"LTE":return"<=";case"LIKE":return"~";case"COLON":return":";default:throw new B(`Invalid operator: ${O.type}`,O.start)}}parseFieldName(){let V=this.expect("IDENT","Expected field name").value;while(this.check("DOT")){this.advance();let N=this.expect("IDENT","Expected identifier after '.'");V+="."+N.value}return V}parseOneOfArray(O,V){this.expect("LBRACKET","Expected '[' after operator");let N=[];if(!this.check("RBRACKET")){N.push(this.parseValue());while(this.check("COMMA"))this.advance(),N.push(this.parseValue())}if(this.expect("RBRACKET","Expected ']' to close array"),N.length===0)throw new B("Array cannot be empty",this.current.start);return{type:V,field:O,values:N}}parseValue(){if(this.check("STRING"))return{type:"string",value:this.advance().value};if(this.check("NUMBER"))return{type:"number",value:this.advance().value};if(this.check("TRUE"))return this.advance(),{type:"boolean",value:!0};if(this.check("FALSE"))return this.advance(),{type:"boolean",value:!1};if(this.check("IDENT")){let V=this.advance().value;while(this.check("DOT")){this.advance();let N=this.expect("IDENT","Expected identifier after '.'");V+="."+N.value}return{type:"identifier",value:V}}throw new B(`Expected value, got ${this.current.type}`,this.current.start)}}function F(O){return new z(O).parse()}var G=(O)=>{try{return{success:!0,ast:F(O)}}catch(V){let N;if(V instanceof B||V instanceof x)N=V.message;else if(V instanceof Error)N=V.message;else N=String(V);return{success:!1,error:N,message:N}}},J=(O)=>{let V=G(O);if(V.success)return V.ast;throw Error(`Failed to parse Filtron query: ${V.error}`)};export{J as parseOrThrow,G as parse}; | ||
| OrExpression = AndExpression (or AndExpression)* | ||
| AndExpression = NotExpression (and NotExpression)* | ||
| NotExpression = not NotExpression -- negation | ||
| | PrimaryExpression | ||
| PrimaryExpression = "(" OrExpression ")" -- parens | ||
| | FieldExpression | ||
| FieldExpression = FieldName "?" -- existsQuestion | ||
| | FieldName exists -- existsKeyword | ||
| | FieldName notOneOfOp "[" NonemptyListOf<Value, ","> "]" -- notOneOf | ||
| | FieldName oneOfOp "[" NonemptyListOf<Value, ","> "]" -- oneOf | ||
| | FieldName ComparisonOp Value -- comparison | ||
| | FieldName -- booleanField | ||
| FieldName = ident ("." ident)* | ||
| oneOfOp = ":" | ||
| notOneOfOp = "!:" | ||
| ComparisonOp = "!=" | ">=" | "<=" | "=" | "~" | ">" | "<" | ":" | ||
| Value = stringLiteral | ||
| | numberLiteral | ||
| | booleanLiteral | ||
| | ident ("." ident)* -- dottedIdent | ||
| | ident -- simpleIdent | ||
| stringLiteral = "\\"" stringChar* "\\"" | ||
| stringChar = ~("\\"" | "\\\\") any -- nonEscaped | ||
| | "\\\\" any -- escaped | ||
| numberLiteral = "-"? digit+ "." digit+ -- float | ||
| | "-"? digit+ -- int | ||
| booleanLiteral = true | false | ||
| ident = ~keyword identStart identPart* | ||
| identStart = letter | "_" | ||
| identPart = alnum | "_" | ||
| // Keywords (case-insensitive) | ||
| keyword = or | and | not | exists | true | false | ||
| or = caseInsensitive<"or"> ~identPart | ||
| and = caseInsensitive<"and"> ~identPart | ||
| not = caseInsensitive<"not"> ~identPart | ||
| exists = caseInsensitive<"exists"> ~identPart | ||
| true = caseInsensitive<"true"> ~identPart | ||
| false = caseInsensitive<"false"> ~identPart | ||
| // Whitespace | ||
| space += comment | ||
| comment = "//" (~"\\n" any)* | ||
| }`},"Filtron",null,"Query",{Query:["define",{sourceInterval:[12,32]},null,[],["app",{sourceInterval:[20,32]},"OrExpression",[]]],OrExpression:["define",{sourceInterval:[36,84]},null,[],["seq",{sourceInterval:[51,84]},["app",{sourceInterval:[51,64]},"AndExpression",[]],["star",{sourceInterval:[65,84]},["seq",{sourceInterval:[66,82]},["app",{sourceInterval:[66,68]},"or",[]],["app",{sourceInterval:[69,82]},"AndExpression",[]]]]]],AndExpression:["define",{sourceInterval:[88,138]},null,[],["seq",{sourceInterval:[104,138]},["app",{sourceInterval:[104,117]},"NotExpression",[]],["star",{sourceInterval:[118,138]},["seq",{sourceInterval:[119,136]},["app",{sourceInterval:[119,122]},"and",[]],["app",{sourceInterval:[123,136]},"NotExpression",[]]]]]],NotExpression_negation:["define",{sourceInterval:[158,187]},null,[],["seq",{sourceInterval:[158,175]},["app",{sourceInterval:[158,161]},"not",[]],["app",{sourceInterval:[162,175]},"NotExpression",[]]]],NotExpression:["define",{sourceInterval:[142,223]},null,[],["alt",{sourceInterval:[158,223]},["app",{sourceInterval:[158,175]},"NotExpression_negation",[]],["app",{sourceInterval:[206,223]},"PrimaryExpression",[]]]],PrimaryExpression_parens:["define",{sourceInterval:[247,277]},null,[],["seq",{sourceInterval:[247,267]},["terminal",{sourceInterval:[247,250]},"("],["app",{sourceInterval:[251,263]},"OrExpression",[]],["terminal",{sourceInterval:[264,267]},")"]]],PrimaryExpression:["define",{sourceInterval:[227,315]},null,[],["alt",{sourceInterval:[247,315]},["app",{sourceInterval:[247,267]},"PrimaryExpression_parens",[]],["app",{sourceInterval:[300,315]},"FieldExpression",[]]]],FieldExpression_existsQuestion:["define",{sourceInterval:[337,368]},null,[],["seq",{sourceInterval:[337,350]},["app",{sourceInterval:[337,346]},"FieldName",[]],["terminal",{sourceInterval:[347,350]},"?"]]],FieldExpression_existsKeyword:["define",{sourceInterval:[389,422]},null,[],["seq",{sourceInterval:[389,405]},["app",{sourceInterval:[389,398]},"FieldName",[]],["app",{sourceInterval:[399,405]},"exists",[]]]],FieldExpression_notOneOf:["define",{sourceInterval:[443,510]},null,[],["seq",{sourceInterval:[443,498]},["app",{sourceInterval:[443,452]},"FieldName",[]],["app",{sourceInterval:[453,463]},"notOneOfOp",[]],["terminal",{sourceInterval:[464,467]},"["],["app",{sourceInterval:[468,494]},"NonemptyListOf",[["app",{sourceInterval:[483,488]},"Value",[]],["terminal",{sourceInterval:[490,493]},","]]],["terminal",{sourceInterval:[495,498]},"]"]]],FieldExpression_oneOf:["define",{sourceInterval:[531,592]},null,[],["seq",{sourceInterval:[531,583]},["app",{sourceInterval:[531,540]},"FieldName",[]],["app",{sourceInterval:[541,548]},"oneOfOp",[]],["terminal",{sourceInterval:[549,552]},"["],["app",{sourceInterval:[553,579]},"NonemptyListOf",[["app",{sourceInterval:[568,573]},"Value",[]],["terminal",{sourceInterval:[575,578]},","]]],["terminal",{sourceInterval:[580,583]},"]"]]],FieldExpression_comparison:["define",{sourceInterval:[613,655]},null,[],["seq",{sourceInterval:[613,641]},["app",{sourceInterval:[613,622]},"FieldName",[]],["app",{sourceInterval:[623,635]},"ComparisonOp",[]],["app",{sourceInterval:[636,641]},"Value",[]]]],FieldExpression_booleanField:["define",{sourceInterval:[676,701]},null,[],["app",{sourceInterval:[676,685]},"FieldName",[]]],FieldExpression:["define",{sourceInterval:[319,701]},null,[],["alt",{sourceInterval:[337,701]},["app",{sourceInterval:[337,350]},"FieldExpression_existsQuestion",[]],["app",{sourceInterval:[389,405]},"FieldExpression_existsKeyword",[]],["app",{sourceInterval:[443,498]},"FieldExpression_notOneOf",[]],["app",{sourceInterval:[531,583]},"FieldExpression_oneOf",[]],["app",{sourceInterval:[613,641]},"FieldExpression_comparison",[]],["app",{sourceInterval:[676,685]},"FieldExpression_booleanField",[]]]],FieldName:["define",{sourceInterval:[705,735]},null,[],["seq",{sourceInterval:[717,735]},["app",{sourceInterval:[717,722]},"ident",[]],["star",{sourceInterval:[723,735]},["seq",{sourceInterval:[724,733]},["terminal",{sourceInterval:[724,727]},"."],["app",{sourceInterval:[728,733]},"ident",[]]]]]],oneOfOp:["define",{sourceInterval:[739,752]},null,[],["terminal",{sourceInterval:[749,752]},":"]],notOneOfOp:["define",{sourceInterval:[755,772]},null,[],["terminal",{sourceInterval:[768,772]},"!:"]],ComparisonOp:["define",{sourceInterval:[775,838]},null,[],["alt",{sourceInterval:[790,838]},["terminal",{sourceInterval:[790,794]},"!="],["terminal",{sourceInterval:[797,801]},">="],["terminal",{sourceInterval:[804,808]},"<="],["terminal",{sourceInterval:[811,814]},"="],["terminal",{sourceInterval:[817,820]},"~"],["terminal",{sourceInterval:[823,826]},">"],["terminal",{sourceInterval:[829,832]},"<"],["terminal",{sourceInterval:[835,838]},":"]]],Value_dottedIdent:["define",{sourceInterval:[923,956]},null,[],["seq",{sourceInterval:[923,941]},["app",{sourceInterval:[923,928]},"ident",[]],["star",{sourceInterval:[929,941]},["seq",{sourceInterval:[930,939]},["terminal",{sourceInterval:[930,933]},"."],["app",{sourceInterval:[934,939]},"ident",[]]]]]],Value_simpleIdent:["define",{sourceInterval:[967,987]},null,[],["app",{sourceInterval:[967,972]},"ident",[]]],Value:["define",{sourceInterval:[842,987]},null,[],["alt",{sourceInterval:[850,987]},["app",{sourceInterval:[850,863]},"stringLiteral",[]],["app",{sourceInterval:[874,887]},"numberLiteral",[]],["app",{sourceInterval:[898,912]},"booleanLiteral",[]],["app",{sourceInterval:[923,941]},"Value_dottedIdent",[]],["app",{sourceInterval:[967,972]},"Value_simpleIdent",[]]]],stringLiteral:["define",{sourceInterval:[991,1028]},null,[],["seq",{sourceInterval:[1007,1028]},["terminal",{sourceInterval:[1007,1011]},'"'],["star",{sourceInterval:[1012,1023]},["app",{sourceInterval:[1012,1022]},"stringChar",[]]],["terminal",{sourceInterval:[1024,1028]},'"']]],stringChar_nonEscaped:["define",{sourceInterval:[1044,1076]},null,[],["seq",{sourceInterval:[1044,1062]},["not",{sourceInterval:[1044,1058]},["alt",{sourceInterval:[1046,1057]},["terminal",{sourceInterval:[1046,1050]},'"'],["terminal",{sourceInterval:[1053,1057]},"\\"]]],["app",{sourceInterval:[1059,1062]},"any",[]]]],stringChar_escaped:["define",{sourceInterval:[1092,1111]},null,[],["seq",{sourceInterval:[1092,1100]},["terminal",{sourceInterval:[1092,1096]},"\\"],["app",{sourceInterval:[1097,1100]},"any",[]]]],stringChar:["define",{sourceInterval:[1031,1111]},null,[],["alt",{sourceInterval:[1044,1111]},["app",{sourceInterval:[1044,1062]},"stringChar_nonEscaped",[]],["app",{sourceInterval:[1092,1100]},"stringChar_escaped",[]]]],numberLiteral_float:["define",{sourceInterval:[1131,1162]},null,[],["seq",{sourceInterval:[1131,1153]},["opt",{sourceInterval:[1131,1135]},["terminal",{sourceInterval:[1131,1134]},"-"]],["plus",{sourceInterval:[1136,1142]},["app",{sourceInterval:[1136,1141]},"digit",[]]],["terminal",{sourceInterval:[1143,1146]},"."],["plus",{sourceInterval:[1147,1153]},["app",{sourceInterval:[1147,1152]},"digit",[]]]]],numberLiteral_int:["define",{sourceInterval:[1181,1199]},null,[],["seq",{sourceInterval:[1181,1192]},["opt",{sourceInterval:[1181,1185]},["terminal",{sourceInterval:[1181,1184]},"-"]],["plus",{sourceInterval:[1186,1192]},["app",{sourceInterval:[1186,1191]},"digit",[]]]]],numberLiteral:["define",{sourceInterval:[1115,1199]},null,[],["alt",{sourceInterval:[1131,1199]},["app",{sourceInterval:[1131,1153]},"numberLiteral_float",[]],["app",{sourceInterval:[1181,1192]},"numberLiteral_int",[]]]],booleanLiteral:["define",{sourceInterval:[1203,1232]},null,[],["alt",{sourceInterval:[1220,1232]},["app",{sourceInterval:[1220,1224]},"true",[]],["app",{sourceInterval:[1227,1232]},"false",[]]]],ident:["define",{sourceInterval:[1236,1274]},null,[],["seq",{sourceInterval:[1244,1274]},["not",{sourceInterval:[1244,1252]},["app",{sourceInterval:[1245,1252]},"keyword",[]]],["app",{sourceInterval:[1253,1263]},"identStart",[]],["star",{sourceInterval:[1264,1274]},["app",{sourceInterval:[1264,1273]},"identPart",[]]]]],identStart:["define",{sourceInterval:[1277,1302]},null,[],["alt",{sourceInterval:[1290,1302]},["app",{sourceInterval:[1290,1296]},"letter",[]],["terminal",{sourceInterval:[1299,1302]},"_"]]],identPart:["define",{sourceInterval:[1305,1328]},null,[],["alt",{sourceInterval:[1317,1328]},["app",{sourceInterval:[1317,1322]},"alnum",[]],["terminal",{sourceInterval:[1325,1328]},"_"]]],keyword:["define",{sourceInterval:[1365,1413]},null,[],["alt",{sourceInterval:[1375,1413]},["app",{sourceInterval:[1375,1377]},"or",[]],["app",{sourceInterval:[1380,1383]},"and",[]],["app",{sourceInterval:[1386,1389]},"not",[]],["app",{sourceInterval:[1392,1398]},"exists",[]],["app",{sourceInterval:[1401,1405]},"true",[]],["app",{sourceInterval:[1408,1413]},"false",[]]]],or:["define",{sourceInterval:[1416,1453]},null,[],["seq",{sourceInterval:[1421,1453]},["app",{sourceInterval:[1421,1442]},"caseInsensitive",[["terminal",{sourceInterval:[1437,1441]},"or"]]],["not",{sourceInterval:[1443,1453]},["app",{sourceInterval:[1444,1453]},"identPart",[]]]]],and:["define",{sourceInterval:[1456,1495]},null,[],["seq",{sourceInterval:[1462,1495]},["app",{sourceInterval:[1462,1484]},"caseInsensitive",[["terminal",{sourceInterval:[1478,1483]},"and"]]],["not",{sourceInterval:[1485,1495]},["app",{sourceInterval:[1486,1495]},"identPart",[]]]]],not:["define",{sourceInterval:[1498,1537]},null,[],["seq",{sourceInterval:[1504,1537]},["app",{sourceInterval:[1504,1526]},"caseInsensitive",[["terminal",{sourceInterval:[1520,1525]},"not"]]],["not",{sourceInterval:[1527,1537]},["app",{sourceInterval:[1528,1537]},"identPart",[]]]]],exists:["define",{sourceInterval:[1540,1585]},null,[],["seq",{sourceInterval:[1549,1585]},["app",{sourceInterval:[1549,1574]},"caseInsensitive",[["terminal",{sourceInterval:[1565,1573]},"exists"]]],["not",{sourceInterval:[1575,1585]},["app",{sourceInterval:[1576,1585]},"identPart",[]]]]],true:["define",{sourceInterval:[1588,1629]},null,[],["seq",{sourceInterval:[1595,1629]},["app",{sourceInterval:[1595,1618]},"caseInsensitive",[["terminal",{sourceInterval:[1611,1617]},"true"]]],["not",{sourceInterval:[1619,1629]},["app",{sourceInterval:[1620,1629]},"identPart",[]]]]],false:["define",{sourceInterval:[1632,1675]},null,[],["seq",{sourceInterval:[1640,1675]},["app",{sourceInterval:[1640,1664]},"caseInsensitive",[["terminal",{sourceInterval:[1656,1663]},"false"]]],["not",{sourceInterval:[1665,1675]},["app",{sourceInterval:[1666,1675]},"identPart",[]]]]],space:["extend",{sourceInterval:[1695,1711]},null,[],["app",{sourceInterval:[1704,1711]},"comment",[]]],comment:["define",{sourceInterval:[1714,1741]},null,[],["seq",{sourceInterval:[1724,1741]},["terminal",{sourceInterval:[1724,1728]},"//"],["star",{sourceInterval:[1729,1741]},["seq",{sourceInterval:[1730,1739]},["not",{sourceInterval:[1730,1735]},["terminal",{sourceInterval:[1731,1735]},` | ||
| `]],["app",{sourceInterval:[1736,1739]},"any",[]]]]]]}]),I=x;var i={Query(e){return e.toAST()},OrExpression(e,n,s){let a=e.toAST(),l=s.children,t=l.length;for(let r=0;r<t;r++)a={type:"or",left:a,right:l[r].toAST()};return a},AndExpression(e,n,s){let a=e.toAST(),l=s.children,t=l.length;for(let r=0;r<t;r++)a={type:"and",left:a,right:l[r].toAST()};return a},NotExpression_negation(e,n){return{type:"not",expression:n.toAST()}},NotExpression(e){return e.toAST()},PrimaryExpression_parens(e,n,s){return n.toAST()},PrimaryExpression(e){return e.toAST()},FieldExpression_existsQuestion(e,n){return{type:"exists",field:e.toAST()}},FieldExpression_existsKeyword(e,n){return{type:"exists",field:e.toAST()}},FieldExpression_notOneOf(e,n,s,a,l){let r=a.asIteration().children,p=r.length,u=Array.from({length:p});for(let o=0;o<p;o++)u[o]=r[o].toAST();return{type:"notOneOf",field:e.toAST(),values:u}},FieldExpression_oneOf(e,n,s,a,l){let r=a.asIteration().children,p=r.length,u=Array.from({length:p});for(let o=0;o<p;o++)u[o]=r[o].toAST();return{type:"oneOf",field:e.toAST(),values:u}},FieldExpression_comparison(e,n,s){return{type:"comparison",field:e.toAST(),operator:n.sourceString,value:s.toAST()}},FieldExpression_booleanField(e){return{type:"booleanField",field:e.toAST()}},FieldName(e,n,s){let a=s.children,l=a.length;if(l===0)return e.sourceString;let t=e.sourceString;for(let r=0;r<l;r++)t+="."+a[r].sourceString;return t},Value(e){return e.toAST()},stringLiteral(e,n,s){let a=n.children,l=a.length;if(l===0)return{type:"string",value:""};let t="";for(let r=0;r<l;r++){let u=a[r].sourceString;if(u.length>1&&u[0]==="\\"){let o=u[1];switch(o){case"n":t+=` | ||
| `;break;case"t":t+="\t";break;case"r":t+="\r";break;case"\\":t+="\\";break;case'"':t+='"';break;default:t+=o}}else t+=u}return{type:"string",value:t}},numberLiteral_float(e,n,s,a){return{type:"number",value:Number.parseFloat(e.sourceString+n.sourceString+"."+a.sourceString)}},numberLiteral_int(e,n){return{type:"number",value:Number.parseInt(e.sourceString+n.sourceString,10)}},booleanLiteral(e){return{type:"boolean",value:e.sourceString.toLowerCase()==="true"}},ident(e,n){return{type:"identifier",value:this.sourceString}},Value_dottedIdent(e,n,s){let a=s.children,l=a.length;if(l===0)return{type:"identifier",value:e.sourceString};let t=e.sourceString;for(let r=0;r<l;r++)t+="."+a[r].sourceString;return{type:"identifier",value:t}},Value_simpleIdent(e){return e.toAST()},or(e){return e},and(e){return e},not(e){return e},exists(e){return e},true(e){return e},false(e){return e},oneOfOp(e){return e},notOneOfOp(e){return e},ComparisonOp(e){return e}};var v=I,c=v.createSemantics();c.addOperation("toAST",i);var d=(e)=>{try{let n=v.match(e);if(n.failed())return{success:!1,error:n.message??"Parse error",message:n.message??"Parse error"};return{success:!0,ast:c(n).toAST()}}catch(n){let s=n instanceof Error?n.message:String(n);return{success:!1,error:s,message:s}}},E=(e)=>{let n=d(e);if(n.success)return n.ast;throw Error(`Failed to parse Filtron query: ${n.error}`)};export{E as parseOrThrow,d as parse}; | ||
| //# debugId=D0558EABEFF0FABB64756E2164756E21 | ||
| //# sourceMappingURL=index.js.map |
@@ -16,3 +16,3 @@ /** | ||
| export type { ParseResult, ParseSuccess, ParseError } from "./parser"; | ||
| export type { ASTNode, Value, ComparisonOperator, OrExpression, AndExpression, NotExpression, ComparisonExpression, OneOfExpression, NotOneOfExpression, ExistsExpression, BooleanFieldExpression, StringValue, NumberValue, BooleanValue, IdentifierValue, } from "./types"; | ||
| export type { ASTNode, Value, ComparisonOperator, OrExpression, AndExpression, NotExpression, ComparisonExpression, OneOfExpression, NotOneOfExpression, ExistsExpression, BooleanFieldExpression, RangeExpression, StringValue, NumberValue, BooleanValue, IdentifierValue, } from "./types"; | ||
| //# sourceMappingURL=index.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC/C,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtE,YAAY,EACX,OAAO,EACP,KAAK,EACL,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,WAAW,EACX,WAAW,EACX,YAAY,EACZ,eAAe,GACf,MAAM,SAAS,CAAC"} | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC/C,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtE,YAAY,EACX,OAAO,EACP,KAAK,EACL,kBAAkB,EAClB,YAAY,EACZ,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,sBAAsB,EACtB,eAAe,EACf,WAAW,EACX,WAAW,EACX,YAAY,EACZ,eAAe,GACf,MAAM,SAAS,CAAC"} |
@@ -0,1 +1,6 @@ | ||
| /** | ||
| * Filtron Parser | ||
| * | ||
| * High-performance recursive descent parser for Filtron query language. | ||
| */ | ||
| import type { ASTNode } from "./types"; | ||
@@ -2,0 +7,0 @@ /** |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAWvC;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,IAAI,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,UAAU,CAAC;AAEpD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,KAAK,GAAI,OAAO,MAAM,KAAG,WA0BrC,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,OAQ5C,CAAC"} | ||
| {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/parser.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAIvC;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,IAAI,CAAC;IACd,GAAG,EAAE,OAAO,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,OAAO,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,UAAU,CAAC;AAEpD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,KAAK,GAAI,OAAO,MAAM,KAAG,WAwBrC,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,OAQ5C,CAAC"} |
| import type { FiltronActionDict } from "./grammar.ohm-bundle.js"; | ||
| import type { ASTNode, Value } from "./types"; | ||
| type SemanticReturnType = ASTNode | Value | Value[] | string | number | [number, number]; | ||
| /** | ||
| * Semantic actions for converting Ohm parse tree to Filtron AST | ||
| */ | ||
| export declare const semanticActions: FiltronActionDict<ASTNode | Value | Value[] | string>; | ||
| //# sourceMappingURL=semantics.d.ts.map | ||
| export declare const semanticActions: FiltronActionDict<SemanticReturnType>; | ||
| export {}; | ||
| //# sourceMappingURL=semantics.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"semantics.d.ts","sourceRoot":"","sources":["../../src/semantics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9C;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,iBAAiB,CAC9C,OAAO,GAAG,KAAK,GAAG,KAAK,EAAE,GAAG,MAAM,CA6RlC,CAAC"} | ||
| {"version":3,"file":"semantics.d.ts","sourceRoot":"","sources":["../../src/semantics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,OAAO,EAAmB,KAAK,EAAE,MAAM,SAAS,CAAC;AAG/D,KAAK,kBAAkB,GAAG,OAAO,GAAG,KAAK,GAAG,KAAK,EAAE,GAAG,MAAM,GAAG,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAsBzF;;GAEG;AACH,eAAO,MAAM,eAAe,EAAE,iBAAiB,CAAC,kBAAkB,CAuRjE,CAAC"} |
+11
-1
@@ -7,3 +7,3 @@ /** | ||
| */ | ||
| export type ASTNode = OrExpression | AndExpression | NotExpression | ComparisonExpression | OneOfExpression | NotOneOfExpression | ExistsExpression | BooleanFieldExpression; | ||
| export type ASTNode = OrExpression | AndExpression | NotExpression | ComparisonExpression | OneOfExpression | NotOneOfExpression | ExistsExpression | BooleanFieldExpression | RangeExpression; | ||
| /** | ||
@@ -85,2 +85,12 @@ * Value type - represents literal values in expressions | ||
| /** | ||
| * Range expression - checks if a field value is between two numbers (inclusive) | ||
| * Example: age = 18..65, price = 0..100 | ||
| */ | ||
| export interface RangeExpression { | ||
| type: "range"; | ||
| field: string; | ||
| min: number; | ||
| max: number; | ||
| } | ||
| /** | ||
| * String value - a quoted string literal | ||
@@ -87,0 +97,0 @@ */ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,MAAM,OAAO,GAChB,YAAY,GACZ,aAAa,GACb,aAAa,GACb,oBAAoB,GACpB,eAAe,GACf,kBAAkB,GAClB,gBAAgB,GAChB,sBAAsB,CAAC;AAE1B;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,GAAG,eAAe,CAAC;AAE/E;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAC3B,GAAG,GACH,IAAI,GACJ,GAAG,GACH,GAAG,GACH,IAAI,GACJ,GAAG,GACH,IAAI,GACJ,GAAG,CAAC;AAEP;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,KAAK,CAAC;IACZ,UAAU,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,KAAK,EAAE,KAAK,CAAC;CACb;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,EAAE,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,EAAE,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,cAAc,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACd"} | ||
| {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,MAAM,OAAO,GAChB,YAAY,GACZ,aAAa,GACb,aAAa,GACb,oBAAoB,GACpB,eAAe,GACf,kBAAkB,GAClB,gBAAgB,GAChB,sBAAsB,GACtB,eAAe,CAAC;AAEnB;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,GAAG,eAAe,CAAC;AAE/E;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,GAAG,GAAG,CAAC;AAElF;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,IAAI,CAAC;IACX,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,KAAK,CAAC;IACZ,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,KAAK,CAAC;IACZ,UAAU,EAAE,OAAO,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACpC,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,KAAK,EAAE,KAAK,CAAC;CACb;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,EAAE,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IAClC,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,EAAE,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACtC,IAAI,EAAE,cAAc,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACd"} |
+8
-15
| { | ||
| "$schema": "https://json.schemastore.org/package.json", | ||
| "name": "@filtron/core", | ||
| "version": "1.0.0", | ||
| "version": "1.1.0", | ||
| "description": "A fast, type-safe query language parser for filtering data in real-time APIs", | ||
@@ -12,9 +12,6 @@ "keywords": [ | ||
| "query-language", | ||
| "ast", | ||
| "ohm", | ||
| "typescript", | ||
| "filtering", | ||
| "search", | ||
| "query-parser", | ||
| "sql-where" | ||
| "query-parser" | ||
| ], | ||
@@ -49,14 +46,10 @@ "homepage": "https://github.com/jbergstroem/filtron#readme", | ||
| "scripts": { | ||
| "bench": "bun --expose-gc run benchmark.ts", | ||
| "build": "bun run build:grammar && bun build --minify --splitting --outdir=dist --external ohm-js index.ts && tsc --emitDeclarationOnly --declaration --declarationMap --outDir dist --skipLibCheck", | ||
| "build:grammar": "bun run ohm generateBundles --withTypes --esm src/grammar.ohm", | ||
| "bench": "bun run build && bun --expose-gc run benchmark.ts", | ||
| "build": "bun build --minify --splitting --sourcemap=linked --outdir=dist index.ts && tsc --emitDeclarationOnly --declaration --declarationMap --outDir dist --skipLibCheck", | ||
| "prepublishOnly": "bun run build && bun test", | ||
| "test": "bun test" | ||
| "test": "bun test", | ||
| "typecheck": "tsc --noemit" | ||
| }, | ||
| "dependencies": { | ||
| "ohm-js": "17.2.1" | ||
| }, | ||
| "devDependencies": { | ||
| "@ohm-js/cli": "2.0.1", | ||
| "@types/bun": "1.3.2", | ||
| "@types/bun": "1.3.3", | ||
| "mitata": "1.0.34", | ||
@@ -77,3 +70,3 @@ "typescript": "5.9.3" | ||
| }, | ||
| "packageManager": "bun@1.3.2" | ||
| "packageManager": "bun@1.3.3" | ||
| } |
+17
-51
@@ -1,4 +0,4 @@ | ||
| # Filtron | ||
| # @filtron/core | ||
| Fast, type-safe query language parser for filtering data in real-time APIs. Built with [Ohm.js](https://ohmjs.org/). | ||
| Fast, type-safe query language parser for filtering data in real-time APIs. | ||
@@ -10,6 +10,5 @@ [](https://www.npmjs.com/package/@filtron/core) | ||
| - **Fast**: ~50μs parse time, 18K+ parses/sec | ||
| - **Small**: 15 KB minified, 56 KB installed | ||
| - **Fast**: High-performance recursive descent parser — 250ns-3μs per query | ||
| - **Small**: ~8 KB minified, zero runtime dependencies | ||
| - **Type-safe**: Full TypeScript support with discriminated union AST | ||
| - **Snack-sized dependencies**: Only `ohm-js` runtime required | ||
@@ -73,2 +72,6 @@ ## Installation | ||
| // Range expressions | ||
| parse("age = 18..65"); | ||
| parse("price = 9.99..99.99"); | ||
| // Complex queries | ||
@@ -90,2 +93,3 @@ parse('(role = "admin" OR role = "mod") AND status = "active"'); | ||
| | `?`, `EXISTS` | Field exists | `email?` | | ||
| | `..` | Range | `age = 18..65` | | ||
| | `AND`, `OR`, `NOT` | Boolean logic | `a AND b OR c` | | ||
@@ -148,40 +152,12 @@ | ||
| ## Packages | ||
| ## Performance | ||
| ### [@filtron/sql](./packages/sql) | ||
| Filtron uses a hand-written recursive descent parser optimized for speed: | ||
| SQL WHERE clause generator with parameterized queries for safe database filtering. | ||
| | Query Type | Parse Time | Throughput | | ||
| | ---------- | ------------- | ---------------- | | ||
| | Simple | ~250-350ns | 3-4M ops/sec | | ||
| | Medium | ~600-1700ns | 600K-1.6M ops/sec | | ||
| | Complex | ~1.5-3μs | 350-700K ops/sec | | ||
| ```typescript | ||
| import { parse } from "@filtron/core"; | ||
| import { toSQL } from "@filtron/sql"; | ||
| const result = parse('age > 18 AND status = "active"'); | ||
| if (result.success) { | ||
| const { sql, params } = toSQL(result.ast); | ||
| // sql: "(age > $1 AND status = $2)" | ||
| // params: [18, "active"] | ||
| const users = await db.query(`SELECT * FROM users WHERE ${sql}`, params); | ||
| } | ||
| ``` | ||
| **Features:** | ||
| - Parameterized queries (PostgreSQL/DuckDB `$1`, MySQL/SQLite/DuckDB `?`) | ||
| - Field name mapping and escaping | ||
| - Zero SQL injection risk | ||
| - Full TypeScript support | ||
| See the [@filtron/sql README](./packages/sql/README.md) for full documentation. | ||
| ## Performance | ||
| ``` | ||
| Parse Time: ~50μs per query | ||
| Throughput: 18,755 parses/sec | ||
| Startup: <1ms with pre-compiled grammar | ||
| Memory: Efficient GC, minimal allocation | ||
| ``` | ||
| Run benchmarks: `bun run bench` | ||
@@ -191,3 +167,3 @@ | ||
| - **[Contributing Guide](./CONTRIBUTING.md)** - Development setup and workflow | ||
| - **[Contributing Guide](../../CONTRIBUTING.md)** - Development setup and workflow | ||
@@ -197,11 +173,1 @@ ## Inspiration | ||
| The Filtron query language syntax was strongly inspired by [dumbql](https://github.com/tomakado/dumbql). | ||
| ## License | ||
| MIT - See [LICENSE](./LICENSE) | ||
| ## Links | ||
| - **GitHub**: https://github.com/jbergstroem/filtron | ||
| - **npm**: https://www.npmjs.com/package/@filtron/core | ||
| - **Issues**: https://github.com/jbergstroem/filtron/issues |
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
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
63010
97.55%1
-50%3
-25%21
50%438
49.49%0
-100%168
-16.83%1
Infinity%- Removed
- Removed