diff --git a/ada/nodes.lkt b/ada/nodes.lkt index 96934edc2..4ddeb5230 100644 --- a/ada/nodes.lkt +++ b/ada/nodes.lkt @@ -12080,13 +12080,22 @@ class DeclExpr: Expr { class FormatStringLiteral: Expr { @parse_field opening_chunk: FormatStringTokStart @parse_field mid_exprs: ASTList[FormatStringChunk] - @parse_field trailing_expr: FormatStringChunk + + # This field is null when no expressions to expand are given in the + # interpolated string, for example with `f"a dummy interpolated string"`. + @parse_field @nullable trailing_expr: FormatStringChunk @with_dynvars(env, origin, entry_point) fun xref_equation(): Equation = ( - ( - %predicate(BaseTypeDecl.allows_string_literal, node.expected_type_var(), error_location=node) and %eq(node.expected_type_var(), node.type_var()) - ) and self.trailing_expr.sub_equation() + %predicate( + BaseTypeDecl.allows_string_literal, + node.expected_type_var(), + error_location=node + ) and %eq(node.expected_type_var(), node.type_var()) + and self.trailing_expr.do( + (te) => te.sub_equation(), + default_val=%true + ) ) and self.mid_exprs.logic_all((m) => m.expr.sub_equation()) } @@ -15801,6 +15810,11 @@ class FormatStringTokMid: FormatStringTokNode implements TokenNode { class FormatStringTokStart: FormatStringTokNode implements TokenNode { } +|" Node holding a formatting "string" token. This token is used when the +|" corresponding interpolated string doesn't have any expression to expand. +class FormatStringTokString: FormatStringTokStart implements TokenNode { +} + |" List of statements, with optional exception handlers (:rmlink:`11.2`). @snaps class HandledStmts: AdaNode { diff --git a/ada/parser.lkt b/ada/parser.lkt index cdec09a60..5b16d2046 100644 --- a/ada/parser.lkt +++ b/ada/parser.lkt @@ -898,11 +898,19 @@ grammar ada_grammar { identifier <- Identifier(@Identifier) char_literal <- CharLiteral(@Char) string_literal <- StringLiteral(@String) - format_string_literal <- FormatStringLiteral( - FormatStringTokStart(@FormatStringStart) - list*(FormatStringChunk(expr - FormatStringTokMid(@FormatStringMid))) - FormatStringChunk(expr FormatStringTokEnd(@FormatStringEnd)) + format_string_literal <- or( + # String interpolation with interpolated expressions + FormatStringLiteral( + FormatStringTokStart(@FormatStringStart) + list*(FormatStringChunk(expr FormatStringTokMid(@FormatStringMid))) + FormatStringChunk(expr FormatStringTokEnd(@FormatStringEnd)) + ) + + # String interpolation without interpolated expressions + | FormatStringLiteral( + FormatStringTokString(@FormatStringString) + null(ASTList[FormatStringChunk]) null(FormatStringChunk) + ) ) defining_id <- DefiningName(identifier) dec_literal <- RealLiteral(@Decimal) diff --git a/ada/tokens.lkt b/ada/tokens.lkt index aac03f077..34e4d124c 100644 --- a/ada/tokens.lkt +++ b/ada/tokens.lkt @@ -4,6 +4,10 @@ lexer ada_lexer { val p_format_string_start = p"f\\\"(\\\"\\\"|{bracket_char}|[^\\n\\\"\\{])*\\{" val p_format_string_mid = p"\\}(\\\"\\\"|{bracket_char}|[^\\n\\\"\\{])*\\{" val p_format_string_end = p"\\}(\\\"\\\"|{bracket_char}|[^\\n\\\"\\{])*\\\"" + + # Simple format string literal without expressions to expand + val p_format_string_string = p"f\\\"(\\\"\\\"|{bracket_char}|[^\\n\\\"\\{])*\\\"" + val p_percent_string = p"%(%%|{bracket_char}|[^\\n%])*%" val digit = p"[0-9]" val extended_digit = p"[0-9a-zA-Z]" @@ -161,6 +165,7 @@ lexer ada_lexer { } String <- or(p"{p_string}" | p"{p_percent_string}") + FormatStringString <- p"{p_format_string_string}" FormatStringStart <- p"{p_format_string_start}" FormatStringMid <- p"{p_format_string_mid}" FormatStringEnd <- p"{p_format_string_end}" diff --git a/contrib/highlight/highlighter.adb b/contrib/highlight/highlighter.adb index de169b2e2..57673f581 100644 --- a/contrib/highlight/highlighter.adb +++ b/contrib/highlight/highlighter.adb @@ -27,6 +27,7 @@ package body Highlighter is Ada_Format_String_End | Ada_Format_String_Mid | Ada_Format_String_Start + | Ada_Format_String_String | Ada_String => String_Literal, diff --git a/testsuite/tests/parser/interpolated_string_0/input b/testsuite/tests/parser/interpolated_string_0/input new file mode 100644 index 000000000..a49f628ef --- /dev/null +++ b/testsuite/tests/parser/interpolated_string_0/input @@ -0,0 +1 @@ +f"a{A}B{B}C{C}D{D}" diff --git a/testsuite/tests/parser/interpolated_string_0/test.out b/testsuite/tests/parser/interpolated_string_0/test.out new file mode 100644 index 000000000..9cba04f01 --- /dev/null +++ b/testsuite/tests/parser/interpolated_string_0/test.out @@ -0,0 +1,26 @@ +FormatStringLiteral[1:1-1:20] +|f_opening_chunk: +| FormatStringTokStart[1:1-1:5]: f"a{ +|f_mid_exprs: +| FormatStringChunkList[1:5-1:17] +| | FormatStringChunk[1:5-1:9] +| | |f_expr: +| | | Id[1:5-1:6]: A +| | |f_string_tok: +| | | FormatStringTokMid[1:6-1:9]: }B{ +| | FormatStringChunk[1:9-1:13] +| | |f_expr: +| | | Id[1:9-1:10]: B +| | |f_string_tok: +| | | FormatStringTokMid[1:10-1:13]: }C{ +| | FormatStringChunk[1:13-1:17] +| | |f_expr: +| | | Id[1:13-1:14]: C +| | |f_string_tok: +| | | FormatStringTokMid[1:14-1:17]: }D{ +|f_trailing_expr: +| FormatStringChunk[1:17-1:20] +| |f_expr: +| | Id[1:17-1:18]: D +| |f_string_tok: +| | FormatStringTokEnd[1:18-1:20]: }" diff --git a/testsuite/tests/parser/interpolated_string_0/test.yaml b/testsuite/tests/parser/interpolated_string_0/test.yaml new file mode 100644 index 000000000..1ee35e9e6 --- /dev/null +++ b/testsuite/tests/parser/interpolated_string_0/test.yaml @@ -0,0 +1,2 @@ +driver: parser +rule: format_string_literal diff --git a/testsuite/tests/parser/interpolated_string_1/input b/testsuite/tests/parser/interpolated_string_1/input new file mode 100644 index 000000000..17d978eec --- /dev/null +++ b/testsuite/tests/parser/interpolated_string_1/input @@ -0,0 +1 @@ +f"lol" diff --git a/testsuite/tests/parser/interpolated_string_1/test.out b/testsuite/tests/parser/interpolated_string_1/test.out new file mode 100644 index 000000000..33df98e7e --- /dev/null +++ b/testsuite/tests/parser/interpolated_string_1/test.out @@ -0,0 +1,6 @@ +FormatStringLiteral[1:1-1:7] +|f_opening_chunk: +| FormatStringTokString[1:1-1:7]: f"lol" +|f_mid_exprs: +| FormatStringChunkList[1:7-1:7]: +|f_trailing_expr: diff --git a/testsuite/tests/parser/interpolated_string_1/test.yaml b/testsuite/tests/parser/interpolated_string_1/test.yaml new file mode 100644 index 000000000..1ee35e9e6 --- /dev/null +++ b/testsuite/tests/parser/interpolated_string_1/test.yaml @@ -0,0 +1,2 @@ +driver: parser +rule: format_string_literal