@filtron/core
Advanced tools
| // Traversing the AST to extract all field names used in a query. | ||
| // Run with: bun run examples/ast-inspection.ts | ||
| import { parseOrThrow, type ASTNode } from "../src/index"; | ||
| function collectFields(node: ASTNode): string[] { | ||
| switch (node.type) { | ||
| case "or": | ||
| case "and": | ||
| return [...collectFields(node.left), ...collectFields(node.right)]; | ||
| case "not": | ||
| return collectFields(node.expression); | ||
| case "comparison": | ||
| case "oneOf": | ||
| case "notOneOf": | ||
| case "exists": | ||
| case "booleanField": | ||
| case "range": | ||
| return [node.field]; | ||
| } | ||
| } | ||
| const ast = parseOrThrow('(role = "admin" OR role = "user") AND verified AND age >= 21'); | ||
| const fields = [...new Set(collectFields(ast))]; | ||
| console.log("Fields:", fields); | ||
| // Output: Fields: [ "role", "verified", "age" ] |
| // Handling parse errors with both parse() and parseOrThrow(). | ||
| // Run with: bun run examples/error-handling.ts | ||
| import { parse, parseOrThrow, FiltronParseError } from "../src/index"; | ||
| // Using parse() - returns a result object with success/error | ||
| const result = parse("invalid === query"); | ||
| if (!result.success) { | ||
| console.log("Parse failed:", result.error); | ||
| if (result.position !== undefined) { | ||
| console.log("Error position:", result.position); | ||
| } | ||
| } | ||
| // Using parseOrThrow() - throws FiltronParseError on failure | ||
| try { | ||
| parseOrThrow("another bad >> query"); | ||
| } catch (error) { | ||
| if (error instanceof FiltronParseError) { | ||
| console.log("Caught FiltronParseError:", error.message); | ||
| if (error.position !== undefined) { | ||
| console.log("Error position:", error.position); | ||
| } | ||
| } | ||
| } |
| // Overview of Filtron query syntax and supported operators. | ||
| // Run with: bun run examples/query-syntax.ts | ||
| import { parseOrThrow } from "../src/index"; | ||
| // Comparison operators: =, !=, >, >=, <, <= | ||
| parseOrThrow('status = "active"'); | ||
| parseOrThrow("age >= 18"); | ||
| parseOrThrow("price < 100"); | ||
| // Contains operator (~) for substring matching | ||
| parseOrThrow('name ~ "john"'); | ||
| // Boolean field shorthand (field is truthy) | ||
| parseOrThrow("verified"); | ||
| parseOrThrow("premium AND verified"); | ||
| // Field existence check (?) | ||
| parseOrThrow("email?"); | ||
| // One-of operator (:) for matching against a list | ||
| parseOrThrow('role : ["admin", "moderator", "user"]'); | ||
| // Not-one-of operator (!:) | ||
| parseOrThrow('status !: ["deleted", "banned"]'); | ||
| // Range syntax for numeric ranges (inclusive) | ||
| parseOrThrow("age = 18..65"); | ||
| // Logical operators: AND, OR, NOT | ||
| parseOrThrow('active AND role = "admin"'); | ||
| parseOrThrow('role = "admin" OR role = "moderator"'); | ||
| parseOrThrow("NOT suspended"); | ||
| // Parentheses for grouping | ||
| parseOrThrow('(role = "admin" OR role = "moderator") AND active'); | ||
| // Dotted field names for nested access | ||
| parseOrThrow("user.profile.age >= 18"); | ||
| console.log("All syntax examples parsed successfully"); |
| // Minimal example of parsing a Filtron query. | ||
| // Run with: bun run examples/simple.ts | ||
| import { parse } from "../src/index"; | ||
| const result = parse('status = "active" AND age >= 18'); | ||
| if (result.success) { | ||
| console.log(result.ast); | ||
| } else { | ||
| console.error(result.error); | ||
| } |
| { | ||
| "name": "Filtron", | ||
| "scopeName": "source.filtron", | ||
| "patterns": [ | ||
| { | ||
| "include": "#keywords" | ||
| }, | ||
| { | ||
| "include": "#operators" | ||
| }, | ||
| { | ||
| "include": "#strings" | ||
| }, | ||
| { | ||
| "include": "#numbers" | ||
| }, | ||
| { | ||
| "include": "#fields" | ||
| }, | ||
| { | ||
| "include": "#brackets" | ||
| } | ||
| ], | ||
| "repository": { | ||
| "keywords": { | ||
| "match": "\\b(AND|OR|NOT|EXISTS|TRUE|FALSE)\\b", | ||
| "name": "keyword.control.filtron" | ||
| }, | ||
| "operators": { | ||
| "match": "(>=|<=|!=|!:|:|=|>|<|~|\\?|\\.\\.)", | ||
| "name": "keyword.operator.filtron" | ||
| }, | ||
| "strings": { | ||
| "begin": "\"", | ||
| "end": "\"", | ||
| "name": "string.quoted.double.filtron", | ||
| "patterns": [ | ||
| { | ||
| "match": "\\\\.", | ||
| "name": "constant.character.escape.filtron" | ||
| } | ||
| ] | ||
| }, | ||
| "numbers": { | ||
| "match": "-?\\d+(\\.\\d+)?", | ||
| "name": "constant.numeric.filtron" | ||
| }, | ||
| "fields": { | ||
| "match": "[a-zA-Z_][a-zA-Z0-9_.]*", | ||
| "name": "variable.other.filtron" | ||
| }, | ||
| "brackets": { | ||
| "match": "[\\[\\]\\(\\)]", | ||
| "name": "punctuation.bracket.filtron" | ||
| } | ||
| } | ||
| } |
+3
-3
@@ -1,5 +0,5 @@ | ||
| var A={Tab:9,Newline:10,CarriageReturn:13,Space:32,Bang:33,Quote:34,LParen:40,RParen:41,Comma:44,Minus:45,Dot:46,Slash:47,Zero:48,Nine:57,Colon:58,LessThan:60,Equals:61,GreaterThan:62,Question:63,UpperA:65,UpperZ:90,LBracket:91,Backslash:92,RBracket:93,Underscore:95,LowerA:97,LowerN:110,LowerR:114,LowerT:116,LowerZ:122,Tilde:126};class z extends Error{position;constructor(V,B){super(V);this.position=B;this.name="LexerError"}}class G{pos=0;input;length;constructor(V){this.input=V,this.length=V.length}readString(){let V=this.input,B=this.length,O=this.pos,N=this.pos+1,R=!1,M=N;while(N<B){let b=V.charCodeAt(N);if(b===A.Quote)return this.pos=N+1,{type:"STRING",value:V.slice(M,N),start:O,end:this.pos};if(b===A.Backslash){R=!0;break}N++}if(R){N=M;let b="",D=N;while(N<B){let F=V.charCodeAt(N);if(F===A.Quote)return b+=V.slice(D,N),this.pos=N+1,{type:"STRING",value:b,start:O,end:this.pos};if(F===A.Backslash){b+=V.slice(D,N),N++;let H=V.charCodeAt(N);switch(N++,H){case A.LowerN:b+=` | ||
| `;break;case A.LowerT:b+="\t";break;case A.LowerR:b+="\r";break;case A.Backslash:b+="\\";break;case A.Quote:b+='"';break;default:b+=String.fromCharCode(H)}D=N;continue}N++}}throw new z("Unterminated string literal",O)}readNumber(){let V=this.input,B=this.length,O=this.pos,N=this.pos;if(V.charCodeAt(N)===A.Minus)N++;while(N<B){let R=V.charCodeAt(N);if(R<A.Zero||R>A.Nine)break;N++}if(N<B&&V.charCodeAt(N)===A.Dot&&N+1<B){let R=V.charCodeAt(N+1);if(R>=A.Zero&&R<=A.Nine){N++;while(N<B){let M=V.charCodeAt(N);if(M<A.Zero||M>A.Nine)break;N++}return this.pos=N,{type:"NUMBER",value:parseFloat(V.slice(O,N)),start:O,end:N}}}return this.pos=N,{type:"NUMBER",value:parseInt(V.slice(O,N),10),start:O,end:N}}readIdentifier(){let V=this.input,B=this.length,O=this.pos,N=this.pos;while(N<B){let M=V.charCodeAt(N);if(M>=A.LowerA&&M<=A.LowerZ||M>=A.UpperA&&M<=A.UpperZ||M>=A.Zero&&M<=A.Nine||M===A.Underscore)N++;else break}this.pos=N;let R=N-O;if(R>=2&&R<=6){let M=V.charCodeAt(O)|32;if(R===2){if(M===111&&(V.charCodeAt(O+1)|32)===114)return{type:"OR",value:"or",start:O,end:N}}else if(R===3){if(M===97){if((V.charCodeAt(O+1)|32)===110&&(V.charCodeAt(O+2)|32)===100)return{type:"AND",value:"and",start:O,end:N}}else if(M===110){if((V.charCodeAt(O+1)|32)===111&&(V.charCodeAt(O+2)|32)===116)return{type:"NOT",value:"not",start:O,end:N}}}else if(R===4){if(M===116){if((V.charCodeAt(O+1)|32)===114&&(V.charCodeAt(O+2)|32)===117&&(V.charCodeAt(O+3)|32)===101)return{type:"TRUE",value:!0,start:O,end:N}}}else if(R===5){if(M===102){if((V.charCodeAt(O+1)|32)===97&&(V.charCodeAt(O+2)|32)===108&&(V.charCodeAt(O+3)|32)===115&&(V.charCodeAt(O+4)|32)===101)return{type:"FALSE",value:!1,start:O,end:N}}}else if(R===6){if(M===101){if((V.charCodeAt(O+1)|32)===120&&(V.charCodeAt(O+2)|32)===105&&(V.charCodeAt(O+3)|32)===115&&(V.charCodeAt(O+4)|32)===116&&(V.charCodeAt(O+5)|32)===115)return{type:"EXISTS",value:"exists",start:O,end:N}}}}return{type:"IDENT",value:V.slice(O,N),start:O,end:N}}next(){let V=this.input,B=this.length,O=this.pos;while(O<B){let M=V.charCodeAt(O);if(M===A.Space||M===A.Tab||M===A.Newline||M===A.CarriageReturn){O++;continue}if(M===A.Slash&&O+1<B&&V.charCodeAt(O+1)===A.Slash){O+=2;while(O<B&&V.charCodeAt(O)!==A.Newline)O++;continue}break}if(this.pos=O,O>=this.length)return{type:"EOF",value:"",start:O,end:O};let N=V.charCodeAt(O);switch(N){case A.LParen:return this.pos=O+1,{type:"LPAREN",value:"(",start:O,end:O+1};case A.RParen:return this.pos=O+1,{type:"RPAREN",value:")",start:O,end:O+1};case A.LBracket:return this.pos=O+1,{type:"LBRACKET",value:"[",start:O,end:O+1};case A.RBracket:return this.pos=O+1,{type:"RBRACKET",value:"]",start:O,end:O+1};case A.Comma:return this.pos=O+1,{type:"COMMA",value:",",start:O,end:O+1};case A.Question:return this.pos=O+1,{type:"QUESTION",value:"?",start:O,end:O+1};case A.Equals:return this.pos=O+1,{type:"EQ",value:"=",start:O,end:O+1};case A.Tilde:return this.pos=O+1,{type:"LIKE",value:"~",start:O,end:O+1};case A.Colon:return this.pos=O+1,{type:"COLON",value:":",start:O,end:O+1}}let R=O+1<this.length?V.charCodeAt(O+1):0;if(N===A.Bang){if(R===A.Equals)return this.pos=O+2,{type:"NEQ",value:"!=",start:O,end:O+2};if(R===A.Colon)return this.pos=O+2,{type:"NOT_COLON",value:"!:",start:O,end:O+2}}if(N===A.GreaterThan){if(R===A.Equals)return this.pos=O+2,{type:"GTE",value:">=",start:O,end:O+2};return this.pos=O+1,{type:"GT",value:">",start:O,end:O+1}}if(N===A.LessThan){if(R===A.Equals)return this.pos=O+2,{type:"LTE",value:"<=",start:O,end:O+2};return this.pos=O+1,{type:"LT",value:"<",start:O,end:O+1}}if(N===A.Dot){if(R===A.Dot)return this.pos=O+2,{type:"DOTDOT",value:"..",start:O,end:O+2};return this.pos=O+1,{type:"DOT",value:".",start:O,end:O+1}}if(N===A.Quote)return this.readString();if(N>=A.Zero&&N<=A.Nine)return this.readNumber();if(N===A.Minus&&R>=A.Zero&&R<=A.Nine)return this.readNumber();if(N>=A.LowerA&&N<=A.LowerZ||N>=A.UpperA&&N<=A.UpperZ||N===A.Underscore)return this.readIdentifier();throw new z(`Unexpected character: '${V[O]}'`,O)}}class j extends Error{position;constructor(V,B){super(V);this.position=B;this.name="ParseError"}}class I{lexer;current;nextToken=null;constructor(V){this.lexer=new G(V),this.current=this.lexer.next()}advance(){let V=this.current;if(this.nextToken)this.current=this.nextToken,this.nextToken=null;else this.current=this.lexer.next();return V}check(V){return this.current.type===V}expect(V,B){if(this.current.type!==V){let O=B??`Expected ${V}, got ${this.current.type}`;throw new j(O,this.current.start)}return this.advance()}parse(){if(this.check("EOF"))throw new j("Empty query",0);let V=this.parseOrExpression();if(!this.check("EOF"))throw new j(`Unexpected token: ${this.current.type}`,this.current.start);return V}parseOrExpression(){let V=this.parseAndExpression();while(this.check("OR")){this.advance();let B=this.parseAndExpression();V={type:"or",left:V,right:B}}return V}parseAndExpression(){let V=this.parseNotExpression();while(this.check("AND")){this.advance();let B=this.parseNotExpression();V={type:"and",left:V,right:B}}return V}parseNotExpression(){if(this.check("NOT"))return this.advance(),{type:"not",expression:this.parseNotExpression()};return this.parsePrimaryExpression()}parsePrimaryExpression(){if(this.check("LPAREN")){this.advance();let V=this.parseOrExpression();return this.expect("RPAREN","Expected closing parenthesis"),V}return this.parseFieldExpression()}parseFieldExpression(){let V=this.parseFieldName(),B=this.current.type;if(B==="QUESTION"||B==="EXISTS")return this.advance(),{type:"exists",field:V};if(B==="COLON"&&this.peekNextIsLBracket())return this.advance(),this.parseOneOfArray(V,"oneOf");if(B==="NOT_COLON")return this.advance(),this.parseOneOfArray(V,"notOneOf");if(B==="EQ"||B==="NEQ"||B==="GT"||B==="GTE"||B==="LT"||B==="LTE"||B==="LIKE"||B==="COLON"){let O=this.advance(),N=this.tokenToOperator(O);if(N==="="&&this.check("NUMBER")){let b=this.advance().value;if(this.check("DOTDOT")){this.advance();let F=this.expect("NUMBER","Expected number after '..'").value;return{type:"range",field:V,min:b,max:F}}return{type:"comparison",field:V,operator:N,value:{type:"number",value:b}}}let R=this.parseValue();return{type:"comparison",field:V,operator:N,value:R}}return{type:"booleanField",field:V}}peekNextIsLBracket(){if(!this.nextToken)this.nextToken=this.lexer.next();return this.nextToken.type==="LBRACKET"}tokenToOperator(V){switch(V.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 j(`Invalid operator: ${V.type}`,V.start)}}parseFieldName(){let B=this.expect("IDENT","Expected field name").value;while(this.check("DOT")){this.advance();let O=this.expect("IDENT","Expected identifier after '.'");B+="."+O.value}return B}parseOneOfArray(V,B){this.expect("LBRACKET","Expected '[' after operator");let O=[];if(!this.check("RBRACKET")){O.push(this.parseValue());while(this.check("COMMA"))this.advance(),O.push(this.parseValue())}if(this.expect("RBRACKET","Expected ']' to close array"),O.length===0)throw new j("Array cannot be empty",this.current.start);return{type:B,field:V,values:O}}parseValue(){let V=this.current.type;if(V==="STRING")return{type:"string",value:this.advance().value};if(V==="NUMBER")return{type:"number",value:this.advance().value};if(V==="TRUE")return this.advance(),{type:"boolean",value:!0};if(V==="FALSE")return this.advance(),{type:"boolean",value:!1};if(V==="IDENT"){let O=this.advance().value;while(this.current.type==="DOT"){this.advance();let N=this.expect("IDENT","Expected identifier after '.'");O+="."+N.value}return{type:"identifier",value:O}}throw new j(`Expected value, got ${V}`,this.current.start)}}function J(V){return new I(V).parse()}var K=(V)=>{try{return{success:!0,ast:J(V)}}catch(B){let O;if(B instanceof j||B instanceof z)O=B.message;else if(B instanceof Error)O=B.message;else O=String(B);return{success:!1,error:O,message:O}}},W=(V)=>{let B=K(V);if(B.success)return B.ast;throw Error(`Failed to parse Filtron query: ${B.error}`)};export{W as parseOrThrow,K as parse}; | ||
| var V={Tab:9,Newline:10,CarriageReturn:13,Space:32,Bang:33,Quote:34,LParen:40,RParen:41,Comma:44,Minus:45,Dot:46,Slash:47,Zero:48,Nine:57,Colon:58,LessThan:60,Equals:61,GreaterThan:62,Question:63,UpperA:65,UpperZ:90,LBracket:91,Backslash:92,RBracket:93,Underscore:95,LowerA:97,LowerN:110,LowerR:114,LowerT:116,LowerZ:122,Tilde:126};class G extends Error{position;constructor(N,A){super(N);this.position=A;this.name="LexerError"}}class I{pos=0;input;length;constructor(N){this.input=N,this.length=N.length}readString(){let N=this.input,A=this.length,O=this.pos,B=this.pos+1,M=!1,b=B;while(B<A){let R=N.charCodeAt(B);if(R===V.Quote)return this.pos=B+1,{type:"STRING",value:N.slice(b,B),start:O,end:this.pos};if(R===V.Backslash){M=!0;break}B++}if(M){B=b;let R="",z=B;while(B<A){let D=N.charCodeAt(B);if(D===V.Quote)return R+=N.slice(z,B),this.pos=B+1,{type:"STRING",value:R,start:O,end:this.pos};if(D===V.Backslash){R+=N.slice(z,B),B++;let H=N.charCodeAt(B);switch(B++,H){case V.LowerN:R+=` | ||
| `;break;case V.LowerT:R+="\t";break;case V.LowerR:R+="\r";break;case V.Backslash:R+="\\";break;case V.Quote:R+='"';break;default:R+=String.fromCharCode(H)}z=B;continue}B++}}throw new G("Unterminated string literal",O)}readNumber(){let N=this.input,A=this.length,O=this.pos,B=this.pos,M=!1;if(N.charCodeAt(B)===V.Minus)M=!0,B++;let b=0;while(B<A){let R=N.charCodeAt(B);if(R<V.Zero||R>V.Nine)break;b=b*10+(R-V.Zero),B++}if(B<A&&N.charCodeAt(B)===V.Dot&&B+1<A){let R=N.charCodeAt(B+1);if(R>=V.Zero&&R<=V.Nine){B++;let z=0,D=1;while(B<A){let J=N.charCodeAt(B);if(J<V.Zero||J>V.Nine)break;z=z*10+(J-V.Zero),D*=10,B++}this.pos=B;let H=b+z/D;return{type:"NUMBER",value:M?-H:H,start:O,end:B}}}return this.pos=B,{type:"NUMBER",value:M?-b:b,start:O,end:B}}readIdentifier(){let N=this.input,A=this.length,O=this.pos,B=this.pos;while(B<A){let b=N.charCodeAt(B);if(b>=V.LowerA&&b<=V.LowerZ||b>=V.UpperA&&b<=V.UpperZ||b>=V.Zero&&b<=V.Nine||b===V.Underscore)B++;else break}this.pos=B;let M=B-O;if(M>=2&&M<=6){let b=N.charCodeAt(O)|32;if(M===2){if(b===111&&(N.charCodeAt(O+1)|32)===114)return{type:"OR",value:"or",start:O,end:B}}else if(M===3){if(b===97){if((N.charCodeAt(O+1)|32)===110&&(N.charCodeAt(O+2)|32)===100)return{type:"AND",value:"and",start:O,end:B}}else if(b===110){if((N.charCodeAt(O+1)|32)===111&&(N.charCodeAt(O+2)|32)===116)return{type:"NOT",value:"not",start:O,end:B}}}else if(M===4){if(b===116){if((N.charCodeAt(O+1)|32)===114&&(N.charCodeAt(O+2)|32)===117&&(N.charCodeAt(O+3)|32)===101)return{type:"TRUE",value:!0,start:O,end:B}}}else if(M===5){if(b===102){if((N.charCodeAt(O+1)|32)===97&&(N.charCodeAt(O+2)|32)===108&&(N.charCodeAt(O+3)|32)===115&&(N.charCodeAt(O+4)|32)===101)return{type:"FALSE",value:!1,start:O,end:B}}}else if(M===6){if(b===101){if((N.charCodeAt(O+1)|32)===120&&(N.charCodeAt(O+2)|32)===105&&(N.charCodeAt(O+3)|32)===115&&(N.charCodeAt(O+4)|32)===116&&(N.charCodeAt(O+5)|32)===115)return{type:"EXISTS",value:"exists",start:O,end:B}}}}return{type:"IDENT",value:N.slice(O,B),start:O,end:B}}next(){let N=this.input,A=this.length,O=this.pos;while(O<A){let b=N.charCodeAt(O);if(b===V.Space||b===V.Tab||b===V.Newline||b===V.CarriageReturn){O++;continue}if(b===V.Slash&&O+1<A&&N.charCodeAt(O+1)===V.Slash){O+=2;while(O<A&&N.charCodeAt(O)!==V.Newline)O++;continue}break}if(this.pos=O,O>=this.length)return{type:"EOF",value:"",start:O,end:O};let B=N.charCodeAt(O);switch(B){case V.LParen:return this.pos=O+1,{type:"LPAREN",value:"(",start:O,end:O+1};case V.RParen:return this.pos=O+1,{type:"RPAREN",value:")",start:O,end:O+1};case V.LBracket:return this.pos=O+1,{type:"LBRACKET",value:"[",start:O,end:O+1};case V.RBracket:return this.pos=O+1,{type:"RBRACKET",value:"]",start:O,end:O+1};case V.Comma:return this.pos=O+1,{type:"COMMA",value:",",start:O,end:O+1};case V.Question:return this.pos=O+1,{type:"QUESTION",value:"?",start:O,end:O+1};case V.Equals:return this.pos=O+1,{type:"EQ",value:"=",start:O,end:O+1};case V.Tilde:return this.pos=O+1,{type:"LIKE",value:"~",start:O,end:O+1};case V.Colon:return this.pos=O+1,{type:"COLON",value:":",start:O,end:O+1}}let M=O+1<this.length?N.charCodeAt(O+1):0;if(B===V.Bang){if(M===V.Equals)return this.pos=O+2,{type:"NEQ",value:"!=",start:O,end:O+2};if(M===V.Colon)return this.pos=O+2,{type:"NOT_COLON",value:"!:",start:O,end:O+2}}if(B===V.GreaterThan){if(M===V.Equals)return this.pos=O+2,{type:"GTE",value:">=",start:O,end:O+2};return this.pos=O+1,{type:"GT",value:">",start:O,end:O+1}}if(B===V.LessThan){if(M===V.Equals)return this.pos=O+2,{type:"LTE",value:"<=",start:O,end:O+2};return this.pos=O+1,{type:"LT",value:"<",start:O,end:O+1}}if(B===V.Dot){if(M===V.Dot)return this.pos=O+2,{type:"DOTDOT",value:"..",start:O,end:O+2};return this.pos=O+1,{type:"DOT",value:".",start:O,end:O+1}}if(B===V.Quote)return this.readString();if(B>=V.Zero&&B<=V.Nine)return this.readNumber();if(B===V.Minus&&M>=V.Zero&&M<=V.Nine)return this.readNumber();if(B>=V.LowerA&&B<=V.LowerZ||B>=V.UpperA&&B<=V.UpperZ||B===V.Underscore)return this.readIdentifier();throw new G(`Unexpected character: '${N[O]}'`,O)}}class j extends Error{position;constructor(N,A){super(N);this.position=A;this.name="ParseError"}}class W{lexer;current;nextToken=null;constructor(N){this.lexer=new I(N),this.current=this.lexer.next()}advance(){let N=this.current;if(this.nextToken)this.current=this.nextToken,this.nextToken=null;else this.current=this.lexer.next();return N}check(N){return this.current.type===N}expect(N,A){if(this.current.type!==N){let O=A??`Expected ${N}, got ${this.current.type}`;throw new j(O,this.current.start)}return this.advance()}parse(){if(this.check("EOF"))throw new j("Empty query",0);let N=this.parseOrExpression();if(!this.check("EOF"))throw new j(`Unexpected token: ${this.current.type}`,this.current.start);return N}parseOrExpression(){let N=this.parseAndExpression();while(this.check("OR")){this.advance();let A=this.parseAndExpression();N={type:"or",left:N,right:A}}return N}parseAndExpression(){let N=this.parseNotExpression();while(this.check("AND")){this.advance();let A=this.parseNotExpression();N={type:"and",left:N,right:A}}return N}parseNotExpression(){if(this.check("NOT"))return this.advance(),{type:"not",expression:this.parseNotExpression()};return this.parsePrimaryExpression()}parsePrimaryExpression(){if(this.check("LPAREN")){this.advance();let N=this.parseOrExpression();return this.expect("RPAREN","Expected closing parenthesis"),N}return this.parseFieldExpression()}parseFieldExpression(){let N=this.parseFieldName(),A=this.current.type;if(A==="QUESTION"||A==="EXISTS")return this.advance(),{type:"exists",field:N};if(A==="COLON"&&this.peekNextIsLBracket())return this.advance(),this.parseOneOfArray(N,"oneOf");if(A==="NOT_COLON")return this.advance(),this.parseOneOfArray(N,"notOneOf");if(A==="EQ"||A==="NEQ"||A==="GT"||A==="GTE"||A==="LT"||A==="LTE"||A==="LIKE"||A==="COLON"){let O=this.advance(),B=this.tokenToOperator(O);if(B==="="&&this.check("NUMBER")){let R=this.advance().value;if(this.check("DOTDOT")){this.advance();let D=this.expect("NUMBER","Expected number after '..'").value;return{type:"range",field:N,min:R,max:D}}return{type:"comparison",field:N,operator:B,value:{type:"number",value:R}}}let M=this.parseValue();return{type:"comparison",field:N,operator:B,value:M}}return{type:"booleanField",field:N}}peekNextIsLBracket(){if(!this.nextToken)this.nextToken=this.lexer.next();return this.nextToken.type==="LBRACKET"}tokenToOperator(N){switch(N.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 j(`Invalid operator: ${N.type}`,N.start)}}parseFieldName(){let A=this.expect("IDENT","Expected field name").value;while(this.check("DOT")){this.advance();let O=this.expect("IDENT","Expected identifier after '.'");A+="."+O.value}return A}parseOneOfArray(N,A){this.expect("LBRACKET","Expected '[' after operator");let O=[];if(!this.check("RBRACKET")){O.push(this.parseValue());while(this.check("COMMA"))this.advance(),O.push(this.parseValue())}if(this.expect("RBRACKET","Expected ']' to close array"),O.length===0)throw new j("Array cannot be empty",this.current.start);return{type:A,field:N,values:O}}parseValue(){let N=this.current.type;if(N==="STRING")return{type:"string",value:this.advance().value};if(N==="NUMBER")return{type:"number",value:this.advance().value};if(N==="TRUE")return this.advance(),{type:"boolean",value:!0};if(N==="FALSE")return this.advance(),{type:"boolean",value:!1};if(N==="IDENT"){let O=this.advance().value;while(this.current.type==="DOT"){this.advance();let B=this.expect("IDENT","Expected identifier after '.'");O+="."+B.value}return{type:"identifier",value:O}}throw new j(`Expected value, got ${N}`,this.current.start)}}function X(N){return new W(N).parse()}class K extends Error{position;constructor(N,A){super(N);this.position=A;this.name="FiltronParseError"}}var Y=(N)=>{try{return{success:!0,ast:X(N)}}catch(A){let O,B;if(A instanceof j||A instanceof G)O=A.message,B=A.position;else if(A instanceof Error)O=A.message;else O=String(A);return{success:!1,error:O,message:O,position:B}}},Z=(N)=>{let A=Y(N);if(A.success)return A.ast;throw new K(`Failed to parse Filtron query: ${A.error}`,A.position)};export{Z as parseOrThrow,Y as parse,G as LexerError,I as Lexer,K as FiltronParseError}; | ||
| //# debugId=839D03E8D259923E64756E2164756E21 | ||
| //# debugId=55F7033D8A7F4FE964756E2164756E21 | ||
| //# sourceMappingURL=index.js.map |
@@ -5,9 +5,9 @@ { | ||
| "sourcesContent": [ | ||
| "/**\n * A Lexer for the Filtron query language\n */\n\n/**\n * Token types produced by the lexer\n */\nexport type TokenType =\n\t| \"LPAREN\"\n\t| \"RPAREN\"\n\t| \"LBRACKET\"\n\t| \"RBRACKET\"\n\t| \"COMMA\"\n\t| \"QUESTION\"\n\t| \"DOT\"\n\t| \"DOTDOT\"\n\t| \"AND\"\n\t| \"OR\"\n\t| \"NOT\"\n\t| \"EXISTS\"\n\t| \"TRUE\"\n\t| \"FALSE\"\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| \"STRING\"\n\t| \"NUMBER\"\n\t| \"IDENT\"\n\t| \"EOF\";\n\n/** Base token properties */\ninterface TokenBase {\n\tstart: number;\n\tend: number;\n}\n\n/** Tokens with string values (punctuation, operators, keywords, identifiers) */\nexport interface StringToken extends TokenBase {\n\ttype:\n\t\t| \"LPAREN\"\n\t\t| \"RPAREN\"\n\t\t| \"LBRACKET\"\n\t\t| \"RBRACKET\"\n\t\t| \"COMMA\"\n\t\t| \"QUESTION\"\n\t\t| \"DOT\"\n\t\t| \"DOTDOT\"\n\t\t| \"AND\"\n\t\t| \"OR\"\n\t\t| \"NOT\"\n\t\t| \"EXISTS\"\n\t\t| \"EQ\"\n\t\t| \"NEQ\"\n\t\t| \"GT\"\n\t\t| \"GTE\"\n\t\t| \"LT\"\n\t\t| \"LTE\"\n\t\t| \"LIKE\"\n\t\t| \"COLON\"\n\t\t| \"NOT_COLON\"\n\t\t| \"STRING\"\n\t\t| \"IDENT\"\n\t\t| \"EOF\";\n\tvalue: string;\n}\n\n/** Tokens with number values */\nexport interface NumberToken extends TokenBase {\n\ttype: \"NUMBER\";\n\tvalue: number;\n}\n\n/** Tokens with boolean values */\nexport interface BooleanToken extends TokenBase {\n\ttype: \"TRUE\" | \"FALSE\";\n\tvalue: boolean;\n}\n\n/**\n * Discriminated union of all token types for proper type narrowing\n */\nexport type Token = StringToken | NumberToken | BooleanToken;\n\n// Character codes\nconst C = {\n\tTab: 9,\n\tNewline: 10,\n\tCarriageReturn: 13,\n\tSpace: 32,\n\tBang: 33,\n\tQuote: 34,\n\tLParen: 40,\n\tRParen: 41,\n\tComma: 44,\n\tMinus: 45,\n\tDot: 46,\n\tSlash: 47,\n\tZero: 48,\n\tNine: 57,\n\tColon: 58,\n\tLessThan: 60,\n\tEquals: 61,\n\tGreaterThan: 62,\n\tQuestion: 63,\n\tUpperA: 65,\n\tUpperZ: 90,\n\tLBracket: 91,\n\tBackslash: 92,\n\tRBracket: 93,\n\tUnderscore: 95,\n\tLowerA: 97,\n\tLowerN: 110,\n\tLowerR: 114,\n\tLowerT: 116,\n\tLowerZ: 122,\n\tTilde: 126,\n} as const;\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 * Read a string literal - fast path for no escapes\n\t */\n\tprivate readString(): Token {\n\t\tconst input = this.input;\n\t\tconst length = this.length;\n\t\tconst start = this.pos;\n\t\tlet pos = this.pos + 1; // skip opening quote\n\n\t\t// Fast path: scan for closing quote or backslash\n\t\tlet hasEscape = false;\n\t\tconst scanStart = pos;\n\t\twhile (pos < length) {\n\t\t\tconst code = input.charCodeAt(pos);\n\t\t\tif (code === C.Quote) {\n\t\t\t\t// No escapes - just slice\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"STRING\",\n\t\t\t\t\tvalue: input.slice(scanStart, pos),\n\t\t\t\t\tstart,\n\t\t\t\t\tend: this.pos,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (code === C.Backslash) {\n\t\t\t\thasEscape = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tpos++;\n\t\t}\n\n\t\t// Slow path: handle escapes\n\t\tif (hasEscape) {\n\t\t\tpos = scanStart;\n\t\t\tlet result = \"\";\n\t\t\tlet chunkStart = pos;\n\n\t\t\twhile (pos < length) {\n\t\t\t\tconst code = input.charCodeAt(pos);\n\n\t\t\t\tif (code === C.Quote) {\n\t\t\t\t\tresult += input.slice(chunkStart, pos);\n\t\t\t\t\tthis.pos = pos + 1;\n\t\t\t\t\treturn { type: \"STRING\", value: result, start, end: this.pos };\n\t\t\t\t}\n\n\t\t\t\tif (code === C.Backslash) {\n\t\t\t\t\tresult += input.slice(chunkStart, pos);\n\t\t\t\t\tpos++;\n\t\t\t\t\tconst escaped = input.charCodeAt(pos);\n\t\t\t\t\tpos++;\n\t\t\t\t\tswitch (escaped) {\n\t\t\t\t\t\tcase C.LowerN:\n\t\t\t\t\t\t\tresult += \"\\n\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase C.LowerT:\n\t\t\t\t\t\t\tresult += \"\\t\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase C.LowerR:\n\t\t\t\t\t\t\tresult += \"\\r\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase C.Backslash:\n\t\t\t\t\t\t\tresult += \"\\\\\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase C.Quote:\n\t\t\t\t\t\t\tresult += '\"';\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tresult += String.fromCharCode(escaped);\n\t\t\t\t\t}\n\t\t\t\t\tchunkStart = pos;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tpos++;\n\t\t\t}\n\t\t}\n\n\t\tthrow new LexerError(\"Unterminated string literal\", start);\n\t}\n\n\t/**\n\t * Read a number literal\n\t */\n\tprivate readNumber(): Token {\n\t\tconst input = this.input;\n\t\tconst length = this.length;\n\t\tconst start = this.pos;\n\t\tlet pos = this.pos;\n\n\t\t// Handle negative sign\n\t\tif (input.charCodeAt(pos) === C.Minus) {\n\t\t\tpos++;\n\t\t}\n\n\t\t// Read integer part\n\t\twhile (pos < length) {\n\t\t\tconst code = input.charCodeAt(pos);\n\t\t\tif (code < C.Zero || code > C.Nine) break;\n\t\t\tpos++;\n\t\t}\n\n\t\t// Check for decimal\n\t\tif (pos < length && input.charCodeAt(pos) === C.Dot && pos + 1 < length) {\n\t\t\tconst nextCode = input.charCodeAt(pos + 1);\n\t\t\tif (nextCode >= C.Zero && nextCode <= C.Nine) {\n\t\t\t\tpos++; // skip dot\n\t\t\t\twhile (pos < length) {\n\t\t\t\t\tconst code = input.charCodeAt(pos);\n\t\t\t\t\tif (code < C.Zero || code > C.Nine) break;\n\t\t\t\t\tpos++;\n\t\t\t\t}\n\t\t\t\tthis.pos = pos;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"NUMBER\",\n\t\t\t\t\tvalue: parseFloat(input.slice(start, pos)),\n\t\t\t\t\tstart,\n\t\t\t\t\tend: pos,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tthis.pos = pos;\n\t\treturn {\n\t\t\ttype: \"NUMBER\",\n\t\t\tvalue: parseInt(input.slice(start, pos), 10),\n\t\t\tstart,\n\t\t\tend: pos,\n\t\t};\n\t}\n\n\t/**\n\t * Read identifier with fast keyword detection\n\t */\n\tprivate readIdentifier(): Token {\n\t\tconst input = this.input;\n\t\tconst length = this.length;\n\t\tconst start = this.pos;\n\t\tlet pos = this.pos;\n\n\t\t// Read all alphanumeric characters\n\t\twhile (pos < length) {\n\t\t\tconst code = input.charCodeAt(pos);\n\t\t\tif (\n\t\t\t\t(code >= C.LowerA && code <= C.LowerZ) ||\n\t\t\t\t(code >= C.UpperA && code <= C.UpperZ) ||\n\t\t\t\t(code >= C.Zero && code <= C.Nine) ||\n\t\t\t\tcode === C.Underscore\n\t\t\t) {\n\t\t\t\tpos++;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tthis.pos = pos;\n\t\tconst len = pos - start;\n\n\t\t// Keyword detection based on length and first char\n\t\tif (len >= 2 && len <= 6) {\n\t\t\tconst firstCode = input.charCodeAt(start) | 0x20; // lowercase\n\n\t\t\tif (len === 2) {\n\t\t\t\t// \"or\"\n\t\t\t\tif (firstCode === 111 && (input.charCodeAt(start + 1) | 0x20) === 114) {\n\t\t\t\t\treturn { type: \"OR\", value: \"or\", start, end: pos };\n\t\t\t\t}\n\t\t\t} else if (len === 3) {\n\t\t\t\t// \"and\", \"not\"\n\t\t\t\tif (firstCode === 97) {\n\t\t\t\t\t// 'a'\n\t\t\t\t\tif (\n\t\t\t\t\t\t(input.charCodeAt(start + 1) | 0x20) === 110 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 2) | 0x20) === 100\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn { type: \"AND\", value: \"and\", start, end: pos };\n\t\t\t\t\t}\n\t\t\t\t} else if (firstCode === 110) {\n\t\t\t\t\t// 'n'\n\t\t\t\t\tif (\n\t\t\t\t\t\t(input.charCodeAt(start + 1) | 0x20) === 111 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 2) | 0x20) === 116\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn { type: \"NOT\", value: \"not\", start, end: pos };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (len === 4) {\n\t\t\t\t// \"true\"\n\t\t\t\tif (firstCode === 116) {\n\t\t\t\t\t// 't'\n\t\t\t\t\tif (\n\t\t\t\t\t\t(input.charCodeAt(start + 1) | 0x20) === 114 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 2) | 0x20) === 117 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 3) | 0x20) === 101\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn { type: \"TRUE\", value: true, start, end: pos };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (len === 5) {\n\t\t\t\t// \"false\"\n\t\t\t\tif (firstCode === 102) {\n\t\t\t\t\t// 'f'\n\t\t\t\t\tif (\n\t\t\t\t\t\t(input.charCodeAt(start + 1) | 0x20) === 97 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 2) | 0x20) === 108 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 3) | 0x20) === 115 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 4) | 0x20) === 101\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn { type: \"FALSE\", value: false, start, end: pos };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (len === 6) {\n\t\t\t\t// \"exists\"\n\t\t\t\tif (firstCode === 101) {\n\t\t\t\t\t// 'e'\n\t\t\t\t\tif (\n\t\t\t\t\t\t(input.charCodeAt(start + 1) | 0x20) === 120 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 2) | 0x20) === 105 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 3) | 0x20) === 115 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 4) | 0x20) === 116 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 5) | 0x20) === 115\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn { type: \"EXISTS\", value: \"exists\", start, end: pos };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { type: \"IDENT\", value: input.slice(start, pos), start, end: pos };\n\t}\n\n\t/**\n\t * Get the next token\n\t */\n\tnext(): Token {\n\t\tconst input = this.input;\n\t\tconst length = this.length;\n\n\t\tlet pos = this.pos;\n\t\twhile (pos < length) {\n\t\t\tconst c = input.charCodeAt(pos);\n\t\t\tif (c === C.Space || c === C.Tab || c === C.Newline || c === C.CarriageReturn) {\n\t\t\t\tpos++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (c === C.Slash && pos + 1 < length && input.charCodeAt(pos + 1) === C.Slash) {\n\t\t\t\tpos += 2;\n\t\t\t\twhile (pos < length && input.charCodeAt(pos) !== C.Newline) {\n\t\t\t\t\tpos++;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tthis.pos = pos;\n\n\t\tif (pos >= this.length) {\n\t\t\treturn { type: \"EOF\", value: \"\", start: pos, end: pos };\n\t\t}\n\n\t\tconst code = input.charCodeAt(pos);\n\n\t\t// Single character tokens (most common first)\n\t\tswitch (code) {\n\t\t\tcase C.LParen:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"LPAREN\", value: \"(\", start: pos, end: pos + 1 };\n\t\t\tcase C.RParen:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"RPAREN\", value: \")\", start: pos, end: pos + 1 };\n\t\t\tcase C.LBracket:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"LBRACKET\", value: \"[\", start: pos, end: pos + 1 };\n\t\t\tcase C.RBracket:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"RBRACKET\", value: \"]\", start: pos, end: pos + 1 };\n\t\t\tcase C.Comma:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"COMMA\", value: \",\", start: pos, end: pos + 1 };\n\t\t\tcase C.Question:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"QUESTION\", value: \"?\", start: pos, end: pos + 1 };\n\t\t\tcase C.Equals:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"EQ\", value: \"=\", start: pos, end: pos + 1 };\n\t\t\tcase C.Tilde:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"LIKE\", value: \"~\", start: pos, end: pos + 1 };\n\t\t\tcase C.Colon:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"COLON\", value: \":\", start: pos, end: pos + 1 };\n\t\t}\n\n\t\t// Two-character operators\n\t\tconst nextCode = pos + 1 < this.length ? input.charCodeAt(pos + 1) : 0;\n\n\t\tif (code === C.Bang) {\n\t\t\tif (nextCode === C.Equals) {\n\t\t\t\tthis.pos = pos + 2;\n\t\t\t\treturn { type: \"NEQ\", value: \"!=\", start: pos, end: pos + 2 };\n\t\t\t}\n\t\t\tif (nextCode === C.Colon) {\n\t\t\t\tthis.pos = pos + 2;\n\t\t\t\treturn { type: \"NOT_COLON\", value: \"!:\", start: pos, end: pos + 2 };\n\t\t\t}\n\t\t}\n\n\t\tif (code === C.GreaterThan) {\n\t\t\tif (nextCode === C.Equals) {\n\t\t\t\tthis.pos = pos + 2;\n\t\t\t\treturn { type: \"GTE\", value: \">=\", start: pos, end: pos + 2 };\n\t\t\t}\n\t\t\tthis.pos = pos + 1;\n\t\t\treturn { type: \"GT\", value: \">\", start: pos, end: pos + 1 };\n\t\t}\n\n\t\tif (code === C.LessThan) {\n\t\t\tif (nextCode === C.Equals) {\n\t\t\t\tthis.pos = pos + 2;\n\t\t\t\treturn { type: \"LTE\", value: \"<=\", start: pos, end: pos + 2 };\n\t\t\t}\n\t\t\tthis.pos = pos + 1;\n\t\t\treturn { type: \"LT\", value: \"<\", start: pos, end: pos + 1 };\n\t\t}\n\n\t\tif (code === C.Dot) {\n\t\t\tif (nextCode === C.Dot) {\n\t\t\t\tthis.pos = pos + 2;\n\t\t\t\treturn { type: \"DOTDOT\", value: \"..\", start: pos, end: pos + 2 };\n\t\t\t}\n\t\t\tthis.pos = pos + 1;\n\t\t\treturn { type: \"DOT\", value: \".\", start: pos, end: pos + 1 };\n\t\t}\n\n\t\t// String literal\n\t\tif (code === C.Quote) {\n\t\t\treturn this.readString();\n\t\t}\n\n\t\t// Number\n\t\tif (code >= C.Zero && code <= C.Nine) {\n\t\t\treturn this.readNumber();\n\t\t}\n\t\tif (code === C.Minus && nextCode >= C.Zero && nextCode <= C.Nine) {\n\t\t\treturn this.readNumber();\n\t\t}\n\n\t\t// Identifier or keyword\n\t\tif (\n\t\t\t(code >= C.LowerA && code <= C.LowerZ) ||\n\t\t\t(code >= C.UpperA && code <= C.UpperZ) ||\n\t\t\tcode === C.Underscore\n\t\t) {\n\t\t\treturn this.readIdentifier();\n\t\t}\n\n\t\tthrow new LexerError(`Unexpected character: '${input[pos]}'`, pos);\n\t}\n}\n", | ||
| "/**\n * A Lexer for the Filtron query language\n */\n\n/**\n * Token types produced by the lexer\n */\nexport type TokenType =\n\t| \"LPAREN\"\n\t| \"RPAREN\"\n\t| \"LBRACKET\"\n\t| \"RBRACKET\"\n\t| \"COMMA\"\n\t| \"QUESTION\"\n\t| \"DOT\"\n\t| \"DOTDOT\"\n\t| \"AND\"\n\t| \"OR\"\n\t| \"NOT\"\n\t| \"EXISTS\"\n\t| \"TRUE\"\n\t| \"FALSE\"\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| \"STRING\"\n\t| \"NUMBER\"\n\t| \"IDENT\"\n\t| \"EOF\";\n\n/** Base token properties */\ninterface TokenBase {\n\tstart: number;\n\tend: number;\n}\n\n/** Tokens with string values (punctuation, operators, keywords, identifiers) */\nexport interface StringToken extends TokenBase {\n\ttype:\n\t\t| \"LPAREN\"\n\t\t| \"RPAREN\"\n\t\t| \"LBRACKET\"\n\t\t| \"RBRACKET\"\n\t\t| \"COMMA\"\n\t\t| \"QUESTION\"\n\t\t| \"DOT\"\n\t\t| \"DOTDOT\"\n\t\t| \"AND\"\n\t\t| \"OR\"\n\t\t| \"NOT\"\n\t\t| \"EXISTS\"\n\t\t| \"EQ\"\n\t\t| \"NEQ\"\n\t\t| \"GT\"\n\t\t| \"GTE\"\n\t\t| \"LT\"\n\t\t| \"LTE\"\n\t\t| \"LIKE\"\n\t\t| \"COLON\"\n\t\t| \"NOT_COLON\"\n\t\t| \"STRING\"\n\t\t| \"IDENT\"\n\t\t| \"EOF\";\n\tvalue: string;\n}\n\n/** Tokens with number values */\nexport interface NumberToken extends TokenBase {\n\ttype: \"NUMBER\";\n\tvalue: number;\n}\n\n/** Tokens with boolean values */\nexport interface BooleanToken extends TokenBase {\n\ttype: \"TRUE\" | \"FALSE\";\n\tvalue: boolean;\n}\n\n/**\n * Discriminated union of all token types for proper type narrowing\n */\nexport type Token = StringToken | NumberToken | BooleanToken;\n\n// Character codes\nconst C = {\n\tTab: 9,\n\tNewline: 10,\n\tCarriageReturn: 13,\n\tSpace: 32,\n\tBang: 33,\n\tQuote: 34,\n\tLParen: 40,\n\tRParen: 41,\n\tComma: 44,\n\tMinus: 45,\n\tDot: 46,\n\tSlash: 47,\n\tZero: 48,\n\tNine: 57,\n\tColon: 58,\n\tLessThan: 60,\n\tEquals: 61,\n\tGreaterThan: 62,\n\tQuestion: 63,\n\tUpperA: 65,\n\tUpperZ: 90,\n\tLBracket: 91,\n\tBackslash: 92,\n\tRBracket: 93,\n\tUnderscore: 95,\n\tLowerA: 97,\n\tLowerN: 110,\n\tLowerR: 114,\n\tLowerT: 116,\n\tLowerZ: 122,\n\tTilde: 126,\n} as const;\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 * Read a string literal - fast path for no escapes\n\t */\n\tprivate readString(): Token {\n\t\tconst input = this.input;\n\t\tconst length = this.length;\n\t\tconst start = this.pos;\n\t\tlet pos = this.pos + 1; // skip opening quote\n\n\t\t// Fast path: scan for closing quote or backslash\n\t\tlet hasEscape = false;\n\t\tconst scanStart = pos;\n\t\twhile (pos < length) {\n\t\t\tconst code = input.charCodeAt(pos);\n\t\t\tif (code === C.Quote) {\n\t\t\t\t// No escapes - just slice\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"STRING\",\n\t\t\t\t\tvalue: input.slice(scanStart, pos),\n\t\t\t\t\tstart,\n\t\t\t\t\tend: this.pos,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (code === C.Backslash) {\n\t\t\t\thasEscape = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tpos++;\n\t\t}\n\n\t\t// Slow path: handle escapes\n\t\tif (hasEscape) {\n\t\t\tpos = scanStart;\n\t\t\tlet result = \"\";\n\t\t\tlet chunkStart = pos;\n\n\t\t\twhile (pos < length) {\n\t\t\t\tconst code = input.charCodeAt(pos);\n\n\t\t\t\tif (code === C.Quote) {\n\t\t\t\t\tresult += input.slice(chunkStart, pos);\n\t\t\t\t\tthis.pos = pos + 1;\n\t\t\t\t\treturn { type: \"STRING\", value: result, start, end: this.pos };\n\t\t\t\t}\n\n\t\t\t\tif (code === C.Backslash) {\n\t\t\t\t\tresult += input.slice(chunkStart, pos);\n\t\t\t\t\tpos++;\n\t\t\t\t\tconst escaped = input.charCodeAt(pos);\n\t\t\t\t\tpos++;\n\t\t\t\t\tswitch (escaped) {\n\t\t\t\t\t\tcase C.LowerN:\n\t\t\t\t\t\t\tresult += \"\\n\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase C.LowerT:\n\t\t\t\t\t\t\tresult += \"\\t\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase C.LowerR:\n\t\t\t\t\t\t\tresult += \"\\r\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase C.Backslash:\n\t\t\t\t\t\t\tresult += \"\\\\\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase C.Quote:\n\t\t\t\t\t\t\tresult += '\"';\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tresult += String.fromCharCode(escaped);\n\t\t\t\t\t}\n\t\t\t\t\tchunkStart = pos;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tpos++;\n\t\t\t}\n\t\t}\n\n\t\tthrow new LexerError(\"Unterminated string literal\", start);\n\t}\n\n\t/**\n\t * Read a number literal\n\t */\n\tprivate readNumber(): Token {\n\t\tconst input = this.input;\n\t\tconst length = this.length;\n\t\tconst start = this.pos;\n\t\tlet pos = this.pos;\n\n\t\t// Handle negative sign\n\t\tlet negative = false;\n\t\tif (input.charCodeAt(pos) === C.Minus) {\n\t\t\tnegative = true;\n\t\t\tpos++;\n\t\t}\n\n\t\t// Read integer part directly\n\t\tlet value = 0;\n\t\twhile (pos < length) {\n\t\t\tconst code = input.charCodeAt(pos);\n\t\t\tif (code < C.Zero || code > C.Nine) break;\n\t\t\tvalue = value * 10 + (code - C.Zero);\n\t\t\tpos++;\n\t\t}\n\n\t\t// Check for decimal\n\t\tif (pos < length && input.charCodeAt(pos) === C.Dot && pos + 1 < length) {\n\t\t\tconst nextCode = input.charCodeAt(pos + 1);\n\t\t\tif (nextCode >= C.Zero && nextCode <= C.Nine) {\n\t\t\t\tpos++; // skip dot\n\t\t\t\tlet fraction = 0;\n\t\t\t\tlet divisor = 1;\n\t\t\t\twhile (pos < length) {\n\t\t\t\t\tconst code = input.charCodeAt(pos);\n\t\t\t\t\tif (code < C.Zero || code > C.Nine) break;\n\t\t\t\t\tfraction = fraction * 10 + (code - C.Zero);\n\t\t\t\t\tdivisor *= 10;\n\t\t\t\t\tpos++;\n\t\t\t\t}\n\t\t\t\tthis.pos = pos;\n\t\t\t\tconst floatValue = value + fraction / divisor;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"NUMBER\",\n\t\t\t\t\tvalue: negative ? -floatValue : floatValue,\n\t\t\t\t\tstart,\n\t\t\t\t\tend: pos,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tthis.pos = pos;\n\t\treturn {\n\t\t\ttype: \"NUMBER\",\n\t\t\tvalue: negative ? -value : value,\n\t\t\tstart,\n\t\t\tend: pos,\n\t\t};\n\t}\n\n\t/**\n\t * Read identifier with fast keyword detection\n\t */\n\tprivate readIdentifier(): Token {\n\t\tconst input = this.input;\n\t\tconst length = this.length;\n\t\tconst start = this.pos;\n\t\tlet pos = this.pos;\n\n\t\t// Read all alphanumeric characters\n\t\twhile (pos < length) {\n\t\t\tconst code = input.charCodeAt(pos);\n\t\t\tif (\n\t\t\t\t(code >= C.LowerA && code <= C.LowerZ) ||\n\t\t\t\t(code >= C.UpperA && code <= C.UpperZ) ||\n\t\t\t\t(code >= C.Zero && code <= C.Nine) ||\n\t\t\t\tcode === C.Underscore\n\t\t\t) {\n\t\t\t\tpos++;\n\t\t\t} else {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tthis.pos = pos;\n\t\tconst len = pos - start;\n\n\t\t// Keyword detection based on length and first char\n\t\tif (len >= 2 && len <= 6) {\n\t\t\tconst firstCode = input.charCodeAt(start) | 0x20; // lowercase\n\n\t\t\tif (len === 2) {\n\t\t\t\t// \"or\"\n\t\t\t\tif (firstCode === 111 && (input.charCodeAt(start + 1) | 0x20) === 114) {\n\t\t\t\t\treturn { type: \"OR\", value: \"or\", start, end: pos };\n\t\t\t\t}\n\t\t\t} else if (len === 3) {\n\t\t\t\t// \"and\", \"not\"\n\t\t\t\tif (firstCode === 97) {\n\t\t\t\t\t// 'a'\n\t\t\t\t\tif (\n\t\t\t\t\t\t(input.charCodeAt(start + 1) | 0x20) === 110 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 2) | 0x20) === 100\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn { type: \"AND\", value: \"and\", start, end: pos };\n\t\t\t\t\t}\n\t\t\t\t} else if (firstCode === 110) {\n\t\t\t\t\t// 'n'\n\t\t\t\t\tif (\n\t\t\t\t\t\t(input.charCodeAt(start + 1) | 0x20) === 111 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 2) | 0x20) === 116\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn { type: \"NOT\", value: \"not\", start, end: pos };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (len === 4) {\n\t\t\t\t// \"true\"\n\t\t\t\tif (firstCode === 116) {\n\t\t\t\t\t// 't'\n\t\t\t\t\tif (\n\t\t\t\t\t\t(input.charCodeAt(start + 1) | 0x20) === 114 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 2) | 0x20) === 117 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 3) | 0x20) === 101\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn { type: \"TRUE\", value: true, start, end: pos };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (len === 5) {\n\t\t\t\t// \"false\"\n\t\t\t\tif (firstCode === 102) {\n\t\t\t\t\t// 'f'\n\t\t\t\t\tif (\n\t\t\t\t\t\t(input.charCodeAt(start + 1) | 0x20) === 97 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 2) | 0x20) === 108 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 3) | 0x20) === 115 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 4) | 0x20) === 101\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn { type: \"FALSE\", value: false, start, end: pos };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (len === 6) {\n\t\t\t\t// \"exists\"\n\t\t\t\tif (firstCode === 101) {\n\t\t\t\t\t// 'e'\n\t\t\t\t\tif (\n\t\t\t\t\t\t(input.charCodeAt(start + 1) | 0x20) === 120 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 2) | 0x20) === 105 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 3) | 0x20) === 115 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 4) | 0x20) === 116 &&\n\t\t\t\t\t\t(input.charCodeAt(start + 5) | 0x20) === 115\n\t\t\t\t\t) {\n\t\t\t\t\t\treturn { type: \"EXISTS\", value: \"exists\", start, end: pos };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { type: \"IDENT\", value: input.slice(start, pos), start, end: pos };\n\t}\n\n\t/**\n\t * Get the next token\n\t */\n\tnext(): Token {\n\t\tconst input = this.input;\n\t\tconst length = this.length;\n\n\t\tlet pos = this.pos;\n\t\twhile (pos < length) {\n\t\t\tconst c = input.charCodeAt(pos);\n\t\t\tif (c === C.Space || c === C.Tab || c === C.Newline || c === C.CarriageReturn) {\n\t\t\t\tpos++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (c === C.Slash && pos + 1 < length && input.charCodeAt(pos + 1) === C.Slash) {\n\t\t\t\tpos += 2;\n\t\t\t\twhile (pos < length && input.charCodeAt(pos) !== C.Newline) {\n\t\t\t\t\tpos++;\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tthis.pos = pos;\n\n\t\tif (pos >= this.length) {\n\t\t\treturn { type: \"EOF\", value: \"\", start: pos, end: pos };\n\t\t}\n\n\t\tconst code = input.charCodeAt(pos);\n\n\t\t// Single character tokens (most common first)\n\t\tswitch (code) {\n\t\t\tcase C.LParen:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"LPAREN\", value: \"(\", start: pos, end: pos + 1 };\n\t\t\tcase C.RParen:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"RPAREN\", value: \")\", start: pos, end: pos + 1 };\n\t\t\tcase C.LBracket:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"LBRACKET\", value: \"[\", start: pos, end: pos + 1 };\n\t\t\tcase C.RBracket:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"RBRACKET\", value: \"]\", start: pos, end: pos + 1 };\n\t\t\tcase C.Comma:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"COMMA\", value: \",\", start: pos, end: pos + 1 };\n\t\t\tcase C.Question:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"QUESTION\", value: \"?\", start: pos, end: pos + 1 };\n\t\t\tcase C.Equals:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"EQ\", value: \"=\", start: pos, end: pos + 1 };\n\t\t\tcase C.Tilde:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"LIKE\", value: \"~\", start: pos, end: pos + 1 };\n\t\t\tcase C.Colon:\n\t\t\t\tthis.pos = pos + 1;\n\t\t\t\treturn { type: \"COLON\", value: \":\", start: pos, end: pos + 1 };\n\t\t}\n\n\t\t// Two-character operators\n\t\tconst nextCode = pos + 1 < this.length ? input.charCodeAt(pos + 1) : 0;\n\n\t\tif (code === C.Bang) {\n\t\t\tif (nextCode === C.Equals) {\n\t\t\t\tthis.pos = pos + 2;\n\t\t\t\treturn { type: \"NEQ\", value: \"!=\", start: pos, end: pos + 2 };\n\t\t\t}\n\t\t\tif (nextCode === C.Colon) {\n\t\t\t\tthis.pos = pos + 2;\n\t\t\t\treturn { type: \"NOT_COLON\", value: \"!:\", start: pos, end: pos + 2 };\n\t\t\t}\n\t\t}\n\n\t\tif (code === C.GreaterThan) {\n\t\t\tif (nextCode === C.Equals) {\n\t\t\t\tthis.pos = pos + 2;\n\t\t\t\treturn { type: \"GTE\", value: \">=\", start: pos, end: pos + 2 };\n\t\t\t}\n\t\t\tthis.pos = pos + 1;\n\t\t\treturn { type: \"GT\", value: \">\", start: pos, end: pos + 1 };\n\t\t}\n\n\t\tif (code === C.LessThan) {\n\t\t\tif (nextCode === C.Equals) {\n\t\t\t\tthis.pos = pos + 2;\n\t\t\t\treturn { type: \"LTE\", value: \"<=\", start: pos, end: pos + 2 };\n\t\t\t}\n\t\t\tthis.pos = pos + 1;\n\t\t\treturn { type: \"LT\", value: \"<\", start: pos, end: pos + 1 };\n\t\t}\n\n\t\tif (code === C.Dot) {\n\t\t\tif (nextCode === C.Dot) {\n\t\t\t\tthis.pos = pos + 2;\n\t\t\t\treturn { type: \"DOTDOT\", value: \"..\", start: pos, end: pos + 2 };\n\t\t\t}\n\t\t\tthis.pos = pos + 1;\n\t\t\treturn { type: \"DOT\", value: \".\", start: pos, end: pos + 1 };\n\t\t}\n\n\t\t// String literal\n\t\tif (code === C.Quote) {\n\t\t\treturn this.readString();\n\t\t}\n\n\t\t// Number\n\t\tif (code >= C.Zero && code <= C.Nine) {\n\t\t\treturn this.readNumber();\n\t\t}\n\t\tif (code === C.Minus && nextCode >= C.Zero && nextCode <= C.Nine) {\n\t\t\treturn this.readNumber();\n\t\t}\n\n\t\t// Identifier or keyword\n\t\tif (\n\t\t\t(code >= C.LowerA && code <= C.LowerZ) ||\n\t\t\t(code >= C.UpperA && code <= C.UpperZ) ||\n\t\t\tcode === C.Underscore\n\t\t) {\n\t\t\treturn this.readIdentifier();\n\t\t}\n\n\t\tthrow new LexerError(`Unexpected character: '${input[pos]}'`, pos);\n\t}\n}\n", | ||
| "/**\n * Recursive Descent Parser for Filtron\n * https://en.wikipedia.org/wiki/Recursive_descent_parser\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 * Parser for Filtron queries\n */\nclass Parser {\n\tprivate lexer: Lexer;\n\tprivate current: Token;\n\tprivate nextToken: Token | null = null;\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\tif (this.nextToken) {\n\t\t\tthis.current = this.nextToken;\n\t\t\tthis.nextToken = null;\n\t\t} else {\n\t\t\tthis.current = this.lexer.next();\n\t\t}\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 * 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\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();\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();\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();\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();\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\t\tconst t = this.current.type;\n\n\t\t// Exists check with ? or EXISTS\n\t\tif (t === \"QUESTION\" || t === \"EXISTS\") {\n\t\t\tthis.advance();\n\t\t\treturn { type: \"exists\", field } as ExistsExpression;\n\t\t}\n\n\t\t// OneOf: field : [values]\n\t\tif (t === \"COLON\" && this.peekNextIsLBracket()) {\n\t\t\tthis.advance();\n\t\t\treturn this.parseOneOfArray(field, \"oneOf\");\n\t\t}\n\n\t\t// NotOneOf: field !: [values]\n\t\tif (t === \"NOT_COLON\") {\n\t\t\tthis.advance();\n\t\t\treturn this.parseOneOfArray(field, \"notOneOf\");\n\t\t}\n\n\t\t// Comparison with operator\n\t\tif (\n\t\t\tt === \"EQ\" ||\n\t\t\tt === \"NEQ\" ||\n\t\t\tt === \"GT\" ||\n\t\t\tt === \"GTE\" ||\n\t\t\tt === \"LT\" ||\n\t\t\tt === \"LTE\" ||\n\t\t\tt === \"LIKE\" ||\n\t\t\tt === \"COLON\"\n\t\t) {\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();\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 {\n\t\t\t\ttype: \"comparison\",\n\t\t\t\tfield,\n\t\t\t\toperator,\n\t\t\t\tvalue,\n\t\t\t} 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\tprivate peekNextIsLBracket(): boolean {\n\t\tif (!this.nextToken) {\n\t\t\tthis.nextToken = this.lexer.next();\n\t\t}\n\t\treturn this.nextToken.type === \"LBRACKET\";\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\tconst t = this.current.type;\n\n\t\t// String literal\n\t\tif (t === \"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 (t === \"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 (t === \"TRUE\") {\n\t\t\tthis.advance();\n\t\t\treturn { type: \"boolean\", value: true };\n\t\t}\n\t\tif (t === \"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 (t === \"IDENT\") {\n\t\t\tconst first = this.advance();\n\t\t\tlet value = first.value as string;\n\n\t\t\twhile (this.current.type === \"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 ${t}`, 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 * The Filtron parser public API\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 * The generic parse result\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" | ||
| "/**\n * The Filtron parser public API\n */\n\nimport type { ASTNode } from \"./types\";\nimport { LexerError } from \"./lexer\";\nimport { parseQuery, ParseError as RDParseError } from \"./rd-parser\";\n\n/**\n * Error thrown by parseOrThrow when parsing fails.\n * Includes the position in the query where the error occurred.\n */\nexport class FiltronParseError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic position?: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"FiltronParseError\";\n\t}\n}\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\tposition?: number;\n}\n\n/**\n * The generic parse result\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\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\t\tlet position: number | undefined;\n\n\t\tif (error instanceof RDParseError || error instanceof LexerError) {\n\t\t\tmessage = error.message;\n\t\t\tposition = error.position;\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\tposition,\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 FiltronParseError if parsing fails, with position information\n *\n * @example\n * ```typescript\n * try {\n * const ast = parseOrThrow('age > 18');\n * console.log(ast);\n * } catch (error) {\n * if (error instanceof FiltronParseError) {\n * console.error('Parse failed at position', error.position, ':', error.message);\n * }\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 FiltronParseError(`Failed to parse Filtron query: ${result.error}`, result.position);\n};\n" | ||
| ], | ||
| "mappings": "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", | ||
| "debugId": "839D03E8D259923E64756E2164756E21", | ||
| "mappings": "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", | ||
| "debugId": "55F7033D8A7F4FE964756E2164756E21", | ||
| "names": [] | ||
| } |
@@ -14,5 +14,7 @@ /** | ||
| */ | ||
| export { parse, parseOrThrow } from "./parser"; | ||
| export { parse, parseOrThrow, FiltronParseError } from "./parser"; | ||
| export type { ParseResult, ParseSuccess, ParseError } from "./parser"; | ||
| export { Lexer, LexerError } from "./lexer"; | ||
| export type { Token, TokenType, StringToken, NumberToken, BooleanToken } from "./lexer"; | ||
| 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,eAAe,EACf,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,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAClE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAGtE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC5C,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAGxF,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"} |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"lexer.d.ts","sourceRoot":"","sources":["../../src/lexer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,MAAM,SAAS,GAClB,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,UAAU,GACV,OAAO,GACP,UAAU,GACV,KAAK,GACL,QAAQ,GACR,KAAK,GACL,IAAI,GACJ,KAAK,GACL,QAAQ,GACR,MAAM,GACN,OAAO,GACP,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,KAAK,GACL,MAAM,GACN,OAAO,GACP,WAAW,GACX,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,KAAK,CAAC;AAET,4BAA4B;AAC5B,UAAU,SAAS;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,gFAAgF;AAChF,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC7C,IAAI,EACD,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,UAAU,GACV,OAAO,GACP,UAAU,GACV,KAAK,GACL,QAAQ,GACR,KAAK,GACL,IAAI,GACJ,KAAK,GACL,QAAQ,GACR,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,KAAK,GACL,MAAM,GACN,OAAO,GACP,WAAW,GACX,QAAQ,GACR,OAAO,GACP,KAAK,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;CACd;AAED,gCAAgC;AAChC,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC7C,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACd;AAED,iCAAiC;AACjC,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC9C,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC;AAqC7D;;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,UAAU;IA8ElB;;OAEG;IACH,OAAO,CAAC,UAAU;IA+ClB;;OAEG;IACH,OAAO,CAAC,cAAc;IAiGtB;;OAEG;IACH,IAAI,IAAI,KAAK;CA4Hb"} | ||
| {"version":3,"file":"lexer.d.ts","sourceRoot":"","sources":["../../src/lexer.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,MAAM,SAAS,GAClB,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,UAAU,GACV,OAAO,GACP,UAAU,GACV,KAAK,GACL,QAAQ,GACR,KAAK,GACL,IAAI,GACJ,KAAK,GACL,QAAQ,GACR,MAAM,GACN,OAAO,GACP,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,KAAK,GACL,MAAM,GACN,OAAO,GACP,WAAW,GACX,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,KAAK,CAAC;AAET,4BAA4B;AAC5B,UAAU,SAAS;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,gFAAgF;AAChF,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC7C,IAAI,EACD,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,UAAU,GACV,OAAO,GACP,UAAU,GACV,KAAK,GACL,QAAQ,GACR,KAAK,GACL,IAAI,GACJ,KAAK,GACL,QAAQ,GACR,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,KAAK,GACL,IAAI,GACJ,KAAK,GACL,MAAM,GACN,OAAO,GACP,WAAW,GACX,QAAQ,GACR,OAAO,GACP,KAAK,CAAC;IACT,KAAK,EAAE,MAAM,CAAC;CACd;AAED,gCAAgC;AAChC,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC7C,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACd;AAED,iCAAiC;AACjC,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC9C,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,KAAK,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,MAAM,MAAM,KAAK,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC;AAqC7D;;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,UAAU;IA8ElB;;OAEG;IACH,OAAO,CAAC,UAAU;IAwDlB;;OAEG;IACH,OAAO,CAAC,cAAc;IAiGtB;;OAEG;IACH,IAAI,IAAI,KAAK;CA4Hb"} |
+14
-3
@@ -6,2 +6,10 @@ /** | ||
| /** | ||
| * Error thrown by parseOrThrow when parsing fails. | ||
| * Includes the position in the query where the error occurred. | ||
| */ | ||
| export declare class FiltronParseError extends Error { | ||
| position?: number | undefined; | ||
| constructor(message: string, position?: number | undefined); | ||
| } | ||
| /** | ||
| * Result of a successful parse operation | ||
@@ -20,2 +28,3 @@ */ | ||
| message: string; | ||
| position?: number; | ||
| } | ||
@@ -30,3 +39,3 @@ /** | ||
| * @param query - The Filtron query string to parse | ||
| * @returns A ParseResult containing either the AST or an error message | ||
| * @returns A ParseResult containing either the AST or an error | ||
| * | ||
@@ -50,3 +59,3 @@ * @example | ||
| * @returns The parsed AST | ||
| * @throws Error if parsing fails | ||
| * @throws FiltronParseError if parsing fails, with position information | ||
| * | ||
@@ -59,3 +68,5 @@ * @example | ||
| * } catch (error) { | ||
| * console.error('Parse failed:', error.message); | ||
| * if (error instanceof FiltronParseError) { | ||
| * console.error('Parse failed at position', error.position, ':', error.message); | ||
| * } | ||
| * } | ||
@@ -62,0 +73,0 @@ * ``` |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/parser.ts"],"names":[],"mappings":"AAAA;;GAEG;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"} | ||
| {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/parser.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAIvC;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAGnC,QAAQ,CAAC,EAAE,MAAM;gBADxB,OAAO,EAAE,MAAM,EACR,QAAQ,CAAC,EAAE,MAAM,YAAA;CAKzB;AAED;;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;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,UAAU,CAAC;AAEpD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,KAAK,GAAI,OAAO,MAAM,KAAG,WA2BrC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,OAQ5C,CAAC"} |
+7
-4
| { | ||
| "$schema": "https://json.schemastore.org/package.json", | ||
| "name": "@filtron/core", | ||
| "version": "1.3.2", | ||
| "version": "1.4.0", | ||
| "description": "Filtron parses human-friendly filter strings into structured queries you can use anywhere — SQL databases, in-memory arrays, or your own custom backend.", | ||
@@ -30,2 +30,4 @@ "keywords": [ | ||
| "dist", | ||
| "examples", | ||
| "filtron.tmLanguage.json", | ||
| "LICENSE", | ||
@@ -43,3 +45,4 @@ "README.md" | ||
| "default": "./dist/index.js" | ||
| } | ||
| }, | ||
| "./filtron.tmLanguage.json": "./filtron.tmLanguage.json" | ||
| }, | ||
@@ -55,3 +58,3 @@ "scripts": { | ||
| "@filtron/benchmark": "1.0.0", | ||
| "@types/bun": "1.3.5", | ||
| "@types/bun": "1.3.6", | ||
| "typescript": "5.9.3" | ||
@@ -71,3 +74,3 @@ }, | ||
| }, | ||
| "packageManager": "bun@1.3.3" | ||
| "packageManager": "bun@1.3.6" | ||
| } |
+53
-39
@@ -11,3 +11,3 @@ # @filtron/core | ||
| Let users filter data with readable expressions instead of building complex query UIs: | ||
| Let users filter data with readable expressions: | ||
@@ -20,3 +20,3 @@ ``` | ||
| **Use cases:** Search UIs, API query parameters, admin dashboards, real-time data filtering. | ||
| Filtron works best when your data has dynamic or user-defined fields that aren't part of your type system: e-commerce catalogs, log aggregation, CMS taxonomies, or multi-tenant platforms with custom metadata. | ||
@@ -42,5 +42,5 @@ ## Installation | ||
| if (result.success) { | ||
| console.log(result.ast); | ||
| console.log(result.ast); | ||
| } else { | ||
| console.error(result.error); | ||
| console.error(result.error); | ||
| } | ||
@@ -53,3 +53,3 @@ ``` | ||
| // Comparisons | ||
| parse('age > 18'); | ||
| parse("age > 18"); | ||
| parse('status = "active"'); | ||
@@ -59,10 +59,10 @@ parse('role != "guest"'); | ||
| // Boolean logic | ||
| parse('age > 18 AND verified'); | ||
| parse('admin OR moderator'); | ||
| parse('NOT suspended'); | ||
| parse('(admin OR mod) AND active'); | ||
| parse("age > 18 AND verified"); | ||
| parse("admin OR moderator"); | ||
| parse("NOT suspended"); | ||
| parse("(admin OR mod) AND active"); | ||
| // Field existence | ||
| parse('email?'); | ||
| parse('profile EXISTS'); | ||
| parse("email?"); | ||
| parse("profile EXISTS"); | ||
@@ -76,7 +76,7 @@ // Contains (substring) | ||
| // Ranges | ||
| parse('age = 18..65'); | ||
| parse('price = 9.99..99.99'); | ||
| parse("age = 18..65"); | ||
| parse("price = 9.99..99.99"); | ||
| // Nested fields | ||
| parse('user.profile.age >= 18'); | ||
| parse("user.profile.age >= 18"); | ||
| ``` | ||
@@ -104,8 +104,9 @@ | ||
| ```typescript | ||
| const result = parse('age > 18'); | ||
| const result = parse("age > 18"); | ||
| if (result.success) { | ||
| result.ast; // ASTNode | ||
| result.ast; // ASTNode | ||
| } else { | ||
| result.error; // string | ||
| result.error; // string - error message | ||
| result.position; // number - position in input where error occurred | ||
| } | ||
@@ -116,9 +117,14 @@ ``` | ||
| Parses a filter expression, throwing on invalid input. | ||
| Parses a filter expression, throwing `FiltronParseError` on invalid input. | ||
| ```typescript | ||
| import { parseOrThrow, FiltronParseError } from "@filtron/core"; | ||
| try { | ||
| const ast = parseOrThrow('age > 18'); | ||
| const ast = parseOrThrow("age > 18"); | ||
| } catch (error) { | ||
| console.error(error.message); | ||
| if (error instanceof FiltronParseError) { | ||
| console.error(error.message); // error description | ||
| console.error(error.position); // position in input where error occurred | ||
| } | ||
| } | ||
@@ -132,23 +138,31 @@ ``` | ||
| ```typescript | ||
| import type { | ||
| ParseResult, | ||
| ASTNode, | ||
| AndExpression, | ||
| OrExpression, | ||
| NotExpression, | ||
| ComparisonExpression, | ||
| ExistsExpression, | ||
| BooleanFieldExpression, | ||
| OneOfExpression, | ||
| NotOneOfExpression, | ||
| RangeExpression, | ||
| Value, | ||
| ComparisonOperator, | ||
| StringValue, | ||
| NumberValue, | ||
| BooleanValue, | ||
| IdentifierValue, | ||
| import { | ||
| FiltronParseError, // Error class thrown by parseOrThrow | ||
| type ParseResult, | ||
| type ASTNode, | ||
| type AndExpression, | ||
| type OrExpression, | ||
| type NotExpression, | ||
| type ComparisonExpression, | ||
| type ExistsExpression, | ||
| type BooleanFieldExpression, | ||
| type OneOfExpression, | ||
| type NotOneOfExpression, | ||
| type RangeExpression, | ||
| type Value, | ||
| type ComparisonOperator, | ||
| type StringValue, | ||
| type NumberValue, | ||
| type BooleanValue, | ||
| type IdentifierValue, | ||
| } from "@filtron/core"; | ||
| ``` | ||
| The Lexer types are also available if you want to use them for syntax highlighting or other purposes: | ||
| ```typescript | ||
| import { Lexer, LexerError } from "@filtron/core"; | ||
| import type { Token, TokenType, StringToken, NumberToken, BooleanToken } from "@filtron/core"; | ||
| ``` | ||
| ### AST structure | ||
@@ -196,3 +210,3 @@ | ||
| Hand-written recursive descent parser. ~9 KB minified, zero dependencies. | ||
| Recursive descent parser. ~9 KB minified, zero dependencies. | ||
@@ -199,0 +213,0 @@ | Query complexity | Parse time | Throughput | |
-21
| MIT License | ||
| Copyright (c) 2026 Johan Bergström | ||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
72375
9.14%21
23.53%490
45.83%212
7.07%