1- import { LITERALS , OPERATOR_PRECEDENCE , UNSUPPORTED_PATTERN } from "../constants.js" ;
1+ import { LITERALS , LOGICAL_OPERATORS , OPERATOR_PRECEDENCE , UNSUPPORTED_PATTERN } from "../constants.js" ;
22import { Tokenizer } from "./tokenizer.js" ;
33
44
@@ -78,38 +78,43 @@ export function parse(input, variables = []) {
7878 }
7979
8080 function parseFunction ( ) {
81- const funcName = currentToken . value . toUpperCase ( ) ;
81+ const functionName = currentToken . value . toUpperCase ( ) ;
8282 next ( ) ;
8383
84- expectedToken ( currentToken , "(" , `Expected ( after ${ funcName } ` ) ;
84+ expectedToken ( currentToken , "(" , `Expected ( after ${ functionName } ` ) ;
8585
8686 next ( ) ;
8787
88- const args = [ ] ;
88+ const functionArgs = [ ] ;
8989 while ( currentToken && currentToken . value !== ")" ) {
90- args . push ( parseExpression ( ) ) ;
90+ functionArgs . push ( parseExpression ( ) ) ;
9191 if ( currentToken && currentToken . value === "," ) next ( ) ;
9292 }
9393
94- expectedToken ( currentToken , ")" , `Expected ) after ${ funcName } ` ) ;
94+ expectedToken ( currentToken , ")" , `Expected ) after ${ functionName } ` ) ;
9595
9696 next ( ) ; // Consume the closing parenthesis
9797
9898 // Check if the next token is an operator and process it
9999 if ( currentToken && currentToken . type === "operator" ) {
100100 const operator = currentToken . value ;
101101 next ( ) ; // Move to the next token after the operator
102- const value = parseValue ( ) ; // Parse the value after the operator
102+ const rightOperand = parseValue ( ) ; // Parse the value after the operator
103+ const nodeType = LOGICAL_OPERATORS . includes ( operator . toLowerCase ( ) ) ? "logical" : "comparison" ;
104+
105+ if ( nodeType === "logical" ) {
106+ return { type : "logical" , operator, left : { type : "function" , name : functionName , args : functionArgs } , right : rightOperand } ;
107+ }
103108
104109 return {
105110 type : "comparison" ,
106- left : { type : "function" , name : funcName , args } ,
111+ left : { type : "function" , name : functionName , args : functionArgs } ,
107112 operator,
108- value
113+ value : rightOperand
109114 } ;
110115 }
111116
112- return { type : "function" , name : funcName , args } ;
117+ return { type : "function" , name : functionName , args : functionArgs } ;
113118 }
114119
115120 // Parses logical expressions using operator precedence
@@ -165,6 +170,21 @@ export function parse(input, variables = []) {
165170
166171 if ( operator === "between" ) return parseBetweenComparison ( field , operator ) ;
167172
173+ if ( currentToken . type === "function" ) {
174+ const functionNode = parseFunction ( ) ;
175+
176+ // Wrap the function inside a comparison if it's directly after an operator
177+ const leftComparison = {
178+ type : "comparison" ,
179+ field,
180+ operator,
181+ value : functionNode . left
182+ } ;
183+
184+ functionNode . left = leftComparison ;
185+ return functionNode ;
186+ }
187+
168188 // For other comparison operators, parse a single right-hand value
169189 const valueType = currentToken . type ;
170190 const value = parseValue ( operator ) ;
@@ -184,30 +204,59 @@ export function parse(input, variables = []) {
184204 function parseValue ( operatorToken ) {
185205 if ( ! currentToken ) throw new Error ( "Unexpected end of input" ) ;
186206
207+ // Handle function without consuming the token
208+ if ( currentToken . type === "function" ) {
209+ return parseFunction ( ) ;
210+ }
211+
187212 const token = currentToken ;
188213 next ( ) ; // Move to the next token
189214
190- if ( token . type === "number" ) return Number ( token . value ) ;
191- if ( token . type === "string" ) return token . value . slice ( 1 , - 1 ) . replace ( / ' ' / g, "" ) ;
192- if ( token . type === "identifier" ) return token . value ;
193- if ( token . type === "null" ) return null ;
215+ switch ( token . type ) {
216+ case "number" :
217+ return Number ( token . value ) ;
194218
195- // Handle placeholders like `{VariableName}`
196- if ( token . type === "placeholder" ) {
197- const val = token . value . slice ( 1 , - 1 ) ;
198- if ( ! variables . includes ( val ) ) variables . push ( val ) ;
199- return { type : "placeholder" , value : val } ;
200- }
219+ case "string" :
220+ return token . value . slice ( 1 , - 1 ) . replace ( / ' ' / g, "" ) ;
201221
202- operatorToken = operatorToken . toUpperCase ( ) ;
222+ case "identifier" :
223+ return token . value ;
203224
204- // Handle IN operator which requires a list of values
205- if ( operatorToken && ( operatorToken === "IN" || operatorToken === "NOT IN" ) ) return parseInList ( token ) ;
225+ case "null" :
226+ return null ;
227+
228+ case "placeholder" : {
229+ const val = token . value . slice ( 1 , - 1 ) ;
230+ if ( ! variables . includes ( val ) ) variables . push ( val ) ;
231+ return { type : "placeholder" , value : val } ;
232+ }
233+
234+ case "paren" : {
235+ if ( currentToken . type === "function" ) {
236+ return parseFunction ( ) ;
237+ }
238+ // Handle ({Placeholder}) syntax for placeholders inside parentheses
239+ const nextToken = tokenizer . peekNextToken ( ) ;
240+ if ( currentToken && currentToken . type === "placeholder" &&
241+ nextToken && nextToken . type === "paren" ) {
242+ const val = parseValue ( ) ;
243+ return { type : "placeholder" , value : val } ;
244+ }
245+ break ;
246+ }
247+ }
248+
249+ // Handle IN or NOT IN operator (outside switch as intended)
250+ operatorToken = operatorToken ?. toUpperCase ( ) ;
251+ if ( operatorToken === "IN" || operatorToken === "NOT IN" ) {
252+ return parseInList ( token ) ;
253+ }
206254
207255 throw new Error ( `Unexpected value: ${ token . value } ` ) ;
208256 }
209257
210258
259+
211260 // Start parsing and return the AST with extracted variables
212261 return { ast : parseExpression ( ) , variables } ;
213262}
0 commit comments