oxc-project / oxc-project/backlog
AST: Encode Template Literals as a Head and Spans
Nobody has claimed this yet.
- Dominant language
- No language data
- Stars
- 7
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
Parent: oxc-project/backlog#210
Summary
Replace the parallel vectors in TemplateLiteral and TSTemplateLiteralType with a required head quasi followed by a vector of interpolations. Each interpolation pairs an expression or type with the quasi that follows it.
Also remove TemplateElement::tail, which is derivable from the element's position.
Motivation
The current AST stores static chunks and interpolations independently:
template.quasis.len() == template.expressions.len() + 1
template_type.quasis.len() == template_type.types.len() + 1
These relationships are required by the grammar but are not enforced by the types. The AST can represent:
- a template with no quasis;
- unpaired expressions, types, or quasis;
- a non-final quasi marked as
tail; - multiple tail quasis or no tail quasi.
Consumers assume the valid shape. JavaScript codegen, for example, unwraps the first quasi and then
uses zip, which truncates to the shorter vector in release builds:
debug_assert_eq!(self.quasis.len(), self.expressions.len() + 1);
let (first_quasi, remaining_quasis) = self.quasis.split_first().unwrap();
for (expr, quasi) in self.expressions.iter().zip(remaining_quasis) {
// ...
}
Invalid ASTs can therefore panic or produce incomplete output. The independent tail boolean duplicates positional information and can contradict the surrounding template.
Current AST Shape
pub struct TemplateLiteral<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub quasis: Vec<'a, TemplateElement<'a>>,
pub expressions: Vec<'a, Expression<'a>>,
}
pub struct TSTemplateLiteralType<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub quasis: Vec<'a, TemplateElement<'a>>,
pub types: Vec<'a, TSType<'a>>,
}
pub struct TemplateElement<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub value: TemplateElementValue<'a>,
pub tail: bool,
pub lone_surrogates: bool,
}
Proposed AST Shape
Represent the first quasi separately and pair every interpolation with its following quasi:
pub struct TemplateLiteral<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub head: TemplateElement<'a>,
pub interpolations: Vec<'a, TemplateInterpolation<'a>>,
}
pub struct TemplateInterpolation<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub expression: Expression<'a>,
pub quasi: TemplateElement<'a>,
}
pub struct TSTemplateLiteralType<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub head: TemplateElement<'a>,
pub interpolations: Vec<'a, TSTemplateInterpolation<'a>>,
}
pub struct TSTemplateInterpolation<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub ty: TSType<'a>,
pub quasi: TemplateElement<'a>,
}
pub struct TemplateElement<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub value: TemplateElementValue<'a>,
pub lone_surrogates: bool,
}
The exact span, node ID, and boxing layout of the new interpolation types should be confirmed during
implementation.
Syntax Mapping
| Source form | Proposed representation |
|---|---|
`hello` |
head: "hello", interpolations: [] |
`a${b}c` |
head: "a", interpolations: [{ expression: b, quasi: "c" }] |
`a${b}c${d}e` |
head: "a", two interpolations pairing b with "c" and d with "e" |
`a${string}b` as a type |
head: "a", interpolations: [{ ty: string, quasi: "b" }] |
A no-substitution template is always represented by head and an empty interpolations vector.
The head is the tail in that case; otherwise, the final interpolation's quasi is the tail.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by locating the TemplateLiteral, TSTemplateLiteralType, and TemplateElement definitions and the JavaScript codegen consumer described in the issue. Confirm the interpolation span, node ID, and boxing layout, then update affected consumers so valid templates use a head plus paired interpolations and no tail field remains.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100