Python: Support template strings in rest of extractor

Adds three new AST nodes to the mix:

- `TemplateString` represents a t-string in Python 3.14
- `TemplateStringPart` represents one of the string constituents of a
t-string. (The interpolated expressions are represented as `Expr` nodes,
just like f-strings.)
- `JoinedTemplateString` represents an implicit concatenation of
template strings.

Importantly, we _completely avoid_ the complicated construction we
currently do for format strings (as well as the confusing nomenclature).
No extra injection of empty strings (so that a template string is a
strict alternation of strings and expressions). A `JoinedTemplateString`
simply has a list of template string children, and a `TemplateString`
has a list of "values" which may be either `Expr` or
`TemplateStringPart` nodes.

If we ever find that we actually want the more complicated interface for
these strings, then I would much rather we reconstruct this inside of QL
rather than in the parser.
This commit is contained in:
Taus
2025-10-28 22:05:53 +00:00
parent cd7ae34380
commit 28e733e335
7 changed files with 323 additions and 4 deletions

View File

@@ -117,6 +117,9 @@
(string string_content: (_) @part)
{ let @part.node = (ast-node @part "StringPart") }
(template_string string_content: (_) @part)
{ let @part.node = (ast-node @part "TemplateStringPart") }
; A string concatenation that contains no interpolated expressions is just a `Str` (and its children
; will be `StringPart`s). A string concatenation that contains interpolated expressions is a
; `JoinedStr`, however.
@@ -142,6 +145,12 @@
}
}
(template_string) @tstring
{ let @tstring.node = (ast-node @tstring "TemplateString") }
(concatenated_template_string) @tstrings
{ let @tstrings.node = (ast-node @tstrings "JoinedTemplateString") }
(pair) @kvpair
{ let @kvpair.node = (ast-node @kvpair "KeyValuePair") }
@@ -2052,6 +2061,44 @@
;;;;;; End of JoinedStr (`f"foo"`)
;;;;;; JoinedTemplateString / TemplateString (`t"foo"`)
; Record the prefix of the template string.
(template_string) @tstring
{
attr (@tstring.node) prefix = (string-prefix @tstring)
}
; Attach raw children (string parts and interpolations) to the template string node.
(template_string (string_content) @part) @tmpl_any
{
edge @tmpl_any.node -> @part.node
attr (@tmpl_any.node -> @part.node) values = (named-child-index @part)
attr (@part.node) ctx = "load"
let safe_string = (concatenate-strings (string-safe-prefix @tmpl_any) (source-text @part) (string-quotes @tmpl_any))
attr (@part.node) s = safe_string
attr (@part.node) text = safe_string
}
(template_string (interpolation expression: (_) @part) @interp) @tmpl_any
{
edge @tmpl_any.node -> @part.node
attr (@tmpl_any.node -> @part.node) values = (named-child-index @interp)
attr (@part.node) ctx = "load"
}
; Concatenated template strings simply have a list-like field containing the template strings that
; are concatenated together.
(concatenated_template_string (template_string) @tstring) @tmpl_concat
{
edge @tmpl_concat.node -> @tstring.node
attr (@tmpl_concat.node -> @tstring.node) strings = (named-child-index @tstring)
attr (@tstring.node) ctx = "load"
}
;;;;;; End of JoinedTemplateString / TemplateString (`t"foo"`)
;;;;;; List (`[...]`)