@@ -152,6 +152,29 @@ class ExpressionParser {
152152 }
153153 return node;
154154 }
155+ case TokenType::TK_ARRAY: {
156+ tok_.skip ();
157+ return parse_array_constructor ();
158+ }
159+ case TokenType::TK_ROW: {
160+ // ROW(expr, expr, ...) — explicit row constructor
161+ tok_.skip ();
162+ if (tok_.peek ().type == TokenType::TK_LPAREN) {
163+ tok_.skip ();
164+ AstNode* tuple = make_node (arena_, NodeType::NODE_TUPLE, t.text );
165+ if (tok_.peek ().type != TokenType::TK_RPAREN) {
166+ while (true ) {
167+ AstNode* elem = parse ();
168+ if (elem) tuple->add_child (elem);
169+ if (tok_.peek ().type == TokenType::TK_COMMA) tok_.skip ();
170+ else break ;
171+ }
172+ }
173+ if (tok_.peek ().type == TokenType::TK_RPAREN) tok_.skip ();
174+ return parse_postfix (tuple);
175+ }
176+ return make_node (arena_, NodeType::NODE_IDENTIFIER, t.text );
177+ }
155178 case TokenType::TK_CASE: {
156179 tok_.skip ();
157180 return parse_case ();
@@ -160,16 +183,34 @@ class ExpressionParser {
160183 tok_.skip ();
161184 // Could be subquery: (SELECT ...)
162185 if (tok_.peek ().type == TokenType::TK_SELECT) {
163- // Subquery — for now, skip to matching paren
164186 AstNode* node = make_node (arena_, NodeType::NODE_SUBQUERY);
165187 skip_to_matching_paren ();
166- return node;
188+ return parse_postfix (node);
189+ }
190+ // Empty tuple: ()
191+ if (tok_.peek ().type == TokenType::TK_RPAREN) {
192+ tok_.skip ();
193+ AstNode* tuple = make_node (arena_, NodeType::NODE_TUPLE);
194+ return parse_postfix (tuple);
167195 }
168196 AstNode* expr = parse ();
197+ if (tok_.peek ().type == TokenType::TK_COMMA) {
198+ // Tuple: (expr, expr, ...)
199+ AstNode* tuple = make_node (arena_, NodeType::NODE_TUPLE);
200+ if (expr) tuple->add_child (expr);
201+ while (tok_.peek ().type == TokenType::TK_COMMA) {
202+ tok_.skip ();
203+ AstNode* elem = parse ();
204+ if (elem) tuple->add_child (elem);
205+ }
206+ if (tok_.peek ().type == TokenType::TK_RPAREN) tok_.skip ();
207+ return parse_postfix (tuple);
208+ }
169209 if (tok_.peek ().type == TokenType::TK_RPAREN) {
170210 tok_.skip ();
171211 }
172- return expr;
212+ // Check for postfix: (expr).field or (expr)[index]
213+ return parse_postfix (expr);
173214 }
174215 case TokenType::TK_IDENTIFIER: {
175216 tok_.skip ();
@@ -394,6 +435,52 @@ class ExpressionParser {
394435 return node;
395436 }
396437
438+ // ARRAY[val, val, ...] constructor
439+ AstNode* parse_array_constructor () {
440+ AstNode* arr = make_node (arena_, NodeType::NODE_ARRAY_CONSTRUCTOR);
441+ if (tok_.peek ().type == TokenType::TK_LBRACKET) {
442+ tok_.skip ();
443+ if (tok_.peek ().type != TokenType::TK_RBRACKET) {
444+ while (true ) {
445+ AstNode* elem = parse ();
446+ if (elem) arr->add_child (elem);
447+ if (tok_.peek ().type == TokenType::TK_COMMA) tok_.skip ();
448+ else break ;
449+ }
450+ }
451+ if (tok_.peek ().type == TokenType::TK_RBRACKET) tok_.skip ();
452+ }
453+ return parse_postfix (arr);
454+ }
455+
456+ // Handle postfix operators: .field, [index]
457+ AstNode* parse_postfix (AstNode* expr) {
458+ while (true ) {
459+ Token t = tok_.peek ();
460+ if (t.type == TokenType::TK_DOT) {
461+ // Field access: (expr).field or (expr).*
462+ tok_.skip ();
463+ Token field = tok_.next_token ();
464+ AstNode* access = make_node (arena_, NodeType::NODE_FIELD_ACCESS);
465+ access->add_child (expr);
466+ access->add_child (make_node (arena_, NodeType::NODE_IDENTIFIER, field.text ));
467+ expr = access;
468+ } else if (t.type == TokenType::TK_LBRACKET) {
469+ // Array subscript: expr[index]
470+ tok_.skip ();
471+ AstNode* index = parse ();
472+ if (tok_.peek ().type == TokenType::TK_RBRACKET) tok_.skip ();
473+ AstNode* subscript = make_node (arena_, NodeType::NODE_ARRAY_SUBSCRIPT);
474+ subscript->add_child (expr);
475+ if (index) subscript->add_child (index);
476+ expr = subscript;
477+ } else {
478+ break ;
479+ }
480+ }
481+ return expr;
482+ }
483+
397484 // Skip tokens until matching closing paren (handles nesting)
398485 void skip_to_matching_paren () {
399486 int depth = 1 ;
@@ -473,6 +560,8 @@ class ExpressionParser {
473560 case TokenType::TK_COLUMNS:
474561 case TokenType::TK_FIELDS:
475562 case TokenType::TK_ROWS:
563+ case TokenType::TK_ARRAY:
564+ case TokenType::TK_ROW:
476565 return true ;
477566 default :
478567 return false ;
0 commit comments