diff --git a/crates/squawk_parser/src/grammar.rs b/crates/squawk_parser/src/grammar.rs index 50b5a8c5..10fbecfa 100644 --- a/crates/squawk_parser/src/grammar.rs +++ b/crates/squawk_parser/src/grammar.rs @@ -37,7 +37,7 @@ fn literal(p: &mut Parser<'_>) -> Option { let m = p.start(); if p.eat(UNICODE_ESC_STRING) { if p.eat(UESCAPE_KW) { - p.eat(STRING); + p.expect(STRING); } } // E021-03 string continuation syntax @@ -1678,7 +1678,9 @@ fn path_segment(p: &mut Parser<'_>, kind: SyntaxKind) { // skip } else if p.at_ts(COL_LABEL_FIRST) { let m = p.start(); - p.bump_any(); + if !opt_ident(p) { + p.bump_any(); + } let kind = if p.at(DOT) { NAME_REF } else { kind }; m.complete(p, kind); } else { @@ -2250,7 +2252,9 @@ fn index_expr(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker { fn name_ref_or_index(p: &mut Parser<'_>) { assert!(p.at(IDENT) || p.at_ts(TYPE_KEYWORDS) || p.at_ts(ALL_KEYWORDS) || p.at(INT_NUMBER)); let m = p.start(); - p.bump_any(); + if !opt_ident(p) { + p.bump_any(); + } m.complete(p, NAME_REF); } @@ -14267,6 +14271,8 @@ fn create_extension(p: &mut Parser<'_>) -> CompletedMarker { } fn opt_ident(p: &mut Parser<'_>) -> bool { + // handle cases like: + // U&"!0069!006E!0074!0038" UESCAPE '!' if p.eat(IDENT) { if p.eat(UESCAPE_KW) { p.expect(STRING); diff --git a/crates/squawk_parser/tests/data/ok/select.sql b/crates/squawk_parser/tests/data/ok/select.sql index 45f6fd62..26381e9c 100644 --- a/crates/squawk_parser/tests/data/ok/select.sql +++ b/crates/squawk_parser/tests/data/ok/select.sql @@ -509,6 +509,12 @@ ORDER BY sensor_id, day; select U&"d!0061t!+000061" UESCAPE '!'; SELECT U&' \' UESCAPE '!'; +-- select with uescape cast +select 2::U&"!0069!006E!0074!0038" UESCAPE '!' from t; + +-- select with uescape on qualified field +select t.U&"!0061" UESCAPE '!' from t; + -- select_from_user_table select * from user; diff --git a/crates/squawk_parser/tests/snapshots/tests__select_ok.snap b/crates/squawk_parser/tests/snapshots/tests__select_ok.snap index ab53510c..7b808863 100644 --- a/crates/squawk_parser/tests/snapshots/tests__select_ok.snap +++ b/crates/squawk_parser/tests/snapshots/tests__select_ok.snap @@ -6277,6 +6277,65 @@ SOURCE_FILE STRING "'!'" SEMICOLON ";" WHITESPACE "\n\n" + COMMENT "-- select with uescape cast" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + CAST_EXPR + LITERAL + INT_NUMBER "2" + COLON_COLON + COLON ":" + COLON ":" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "U&\"!0069!006E!0074!0038\"" + WHITESPACE " " + UESCAPE_KW "UESCAPE" + WHITESPACE " " + STRING "'!'" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + NAME_REF + IDENT "t" + SEMICOLON ";" + WHITESPACE "\n\n" + COMMENT "-- select with uescape on qualified field" + WHITESPACE "\n" + SELECT + SELECT_CLAUSE + SELECT_KW "select" + WHITESPACE " " + TARGET_LIST + TARGET + FIELD_EXPR + NAME_REF + IDENT "t" + DOT "." + NAME_REF + IDENT "U&\"!0061\"" + WHITESPACE " " + UESCAPE_KW "UESCAPE" + WHITESPACE " " + STRING "'!'" + WHITESPACE " " + FROM_CLAUSE + FROM_KW "from" + WHITESPACE " " + FROM_ITEM + NAME_REF + IDENT "t" + SEMICOLON ";" + WHITESPACE "\n\n" COMMENT "-- select_from_user_table" WHITESPACE "\n" SELECT