From d5d542d1deea00738b604ec3fa4ae0a8b4327352 Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Fri, 20 Feb 2026 21:43:42 -0500 Subject: [PATCH] parser: improve create function table return type parsing --- crates/squawk_parser/src/grammar.rs | 23 +++------- .../snapshots/tests__create_function_err.snap | 41 ++++++++++-------- .../snapshots/tests__create_function_ok.snap | 43 ++++++++++--------- .../squawk_syntax/src/ast/generated/nodes.rs | 12 ++++++ crates/squawk_syntax/src/ast/node_ext.rs | 4 ++ crates/squawk_syntax/src/postgresql.ungram | 4 +- 6 files changed, 68 insertions(+), 59 deletions(-) diff --git a/crates/squawk_parser/src/grammar.rs b/crates/squawk_parser/src/grammar.rs index a6901be6..84a3fa46 100644 --- a/crates/squawk_parser/src/grammar.rs +++ b/crates/squawk_parser/src/grammar.rs @@ -13290,24 +13290,11 @@ fn opt_ret_type(p: &mut Parser<'_>) { let m = p.start(); if p.eat(RETURNS_KW) { if p.eat(TABLE_KW) { - delimited( - p, - L_PAREN, - R_PAREN, - COMMA, - || "unexpected comma".to_string(), - NAME_REF_FIRST, - |p| { - // TODO: should this be the column def name? - // column_name - if opt_name_ref(p).is_none() { - return false; - } - // column_type - type_name(p); - true - }, - ); + if p.at(L_PAREN) { + table_arg_list(p); + } else { + p.error("expected table arg list"); + } } else { p.eat(SETOF_KW); type_name(p); diff --git a/crates/squawk_parser/tests/snapshots/tests__create_function_err.snap b/crates/squawk_parser/tests/snapshots/tests__create_function_err.snap index 58dbe804..82ee3742 100644 --- a/crates/squawk_parser/tests/snapshots/tests__create_function_err.snap +++ b/crates/squawk_parser/tests/snapshots/tests__create_function_err.snap @@ -23,25 +23,28 @@ SOURCE_FILE WHITESPACE " " TABLE_KW "table" WHITESPACE " " - L_PAREN "(" - NAME_REF - IDENT "a" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - TEXT_KW "text" - WHITESPACE " " - NAME_REF - IDENT "b" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - INT_KW "int" - R_PAREN ")" + TABLE_ARG_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "a" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + TEXT_KW "text" + WHITESPACE " " + COLUMN + NAME + IDENT "b" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + INT_KW "int" + R_PAREN ")" WHITESPACE "\n" FUNC_OPTION_LIST AS_FUNC_OPTION diff --git a/crates/squawk_parser/tests/snapshots/tests__create_function_ok.snap b/crates/squawk_parser/tests/snapshots/tests__create_function_ok.snap index b0bd4a1e..40c1d68e 100644 --- a/crates/squawk_parser/tests/snapshots/tests__create_function_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__create_function_ok.snap @@ -494,26 +494,29 @@ SOURCE_FILE WHITESPACE " " TABLE_KW "table" WHITESPACE " " - L_PAREN "(" - NAME_REF - IDENT "a" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - TEXT_KW "text" - COMMA "," - WHITESPACE " " - NAME_REF - IDENT "b" - WHITESPACE " " - PATH_TYPE - PATH - PATH_SEGMENT - NAME_REF - INT_KW "int" - R_PAREN ")" + TABLE_ARG_LIST + L_PAREN "(" + COLUMN + NAME + IDENT "a" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + TEXT_KW "text" + COMMA "," + WHITESPACE " " + COLUMN + NAME + IDENT "b" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + INT_KW "int" + R_PAREN ")" WHITESPACE "\n" FUNC_OPTION_LIST AS_FUNC_OPTION diff --git a/crates/squawk_syntax/src/ast/generated/nodes.rs b/crates/squawk_syntax/src/ast/generated/nodes.rs index 3e317b09..8ef2dc87 100644 --- a/crates/squawk_syntax/src/ast/generated/nodes.rs +++ b/crates/squawk_syntax/src/ast/generated/nodes.rs @@ -11441,6 +11441,10 @@ impl Op { support::child(&self.syntax) } #[inline] + pub fn is_not_json_object(&self) -> Option { + support::child(&self.syntax) + } + #[inline] pub fn is_not_json_scalar(&self) -> Option { support::child(&self.syntax) } @@ -13204,6 +13208,10 @@ pub struct RetType { pub(crate) syntax: SyntaxNode, } impl RetType { + #[inline] + pub fn table_arg_list(&self) -> Option { + support::child(&self.syntax) + } #[inline] pub fn ty(&self) -> Option { support::child(&self.syntax) @@ -13212,6 +13220,10 @@ impl RetType { pub fn returns_token(&self) -> Option { support::token(&self.syntax, SyntaxKind::RETURNS_KW) } + #[inline] + pub fn table_token(&self) -> Option { + support::token(&self.syntax, SyntaxKind::TABLE_KW) + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/crates/squawk_syntax/src/ast/node_ext.rs b/crates/squawk_syntax/src/ast/node_ext.rs index 4d886ec5..7940847a 100644 --- a/crates/squawk_syntax/src/ast/node_ext.rs +++ b/crates/squawk_syntax/src/ast/node_ext.rs @@ -106,6 +106,7 @@ pub enum BinOp { IsNotDistinctFrom(ast::IsNotDistinctFrom), IsNotJson(ast::IsNotJson), IsNotJsonArray(ast::IsNotJsonArray), + IsNotJsonObject(ast::IsNotJsonObject), IsNotJsonScalar(ast::IsNotJsonScalar), IsNotJsonValue(ast::IsNotJsonValue), LAngle(SyntaxToken), @@ -205,6 +206,9 @@ impl ast::BinExpr { SyntaxKind::IS_NOT_JSON_ARRAY => { BinOp::IsNotJsonArray(ast::IsNotJsonArray { syntax: node }) } + SyntaxKind::IS_NOT_JSON_OBJECT => { + BinOp::IsNotJsonObject(ast::IsNotJsonObject { syntax: node }) + } SyntaxKind::IS_NOT_JSON_SCALAR => { BinOp::IsNotJsonScalar(ast::IsNotJsonScalar { syntax: node }) } diff --git a/crates/squawk_syntax/src/postgresql.ungram b/crates/squawk_syntax/src/postgresql.ungram index 8995a015..b6497bfe 100644 --- a/crates/squawk_syntax/src/postgresql.ungram +++ b/crates/squawk_syntax/src/postgresql.ungram @@ -431,7 +431,7 @@ IsNot = 'is' 'not' Op = -'or' | Gteq | '<' | '>' | FatArrow | '=' | 'in' | Neqb | Lteq | '+' | 'overlaps' | 'like' | 'ilike' | NotLike | NotIlike | NotIn | CustomOp | IsDistinctFrom | IsNotDistinctFrom | OperatorCall | 'is' | '^' | '%' | 'and' | '/' | Neq | 'collate' | '-' | ColonEq | ColonColon | 'value' | ':' | IsNot | SimilarTo | NotSimilarTo | AtTimeZone | IsJson | IsJsonValue | IsNotJson | IsJsonObject | IsJsonArray |IsJsonScalar | IsNotJsonValue | IsJsonObject | IsNotJsonArray | IsNotJsonScalar +'or' | Gteq | '<' | '>' | FatArrow | '=' | 'in' | Neqb | Lteq | '+' | 'overlaps' | 'like' | 'ilike' | NotLike | NotIlike | NotIn | CustomOp | IsDistinctFrom | IsNotDistinctFrom | OperatorCall | 'is' | '^' | '%' | 'and' | '/' | Neq | 'collate' | '-' | ColonEq | ColonColon | 'value' | ':' | IsNot | SimilarTo | NotSimilarTo | AtTimeZone | IsJson | IsJsonValue | IsNotJson | IsJsonObject | IsJsonArray |IsJsonScalar | IsNotJsonValue | IsNotJsonObject | IsNotJsonArray | IsNotJsonScalar IsJson = 'is' 'json' JsonKeysUniqueClause? @@ -1139,7 +1139,7 @@ OrReplace = 'or' 'replace' RetType = - 'returns' Type + 'returns' ('table' TableArgList | Type) BeginFuncOptionList = 'begin' 'atomic'