Replies: 3 comments 25 replies
-
babel-plugin-transform-typescript: |
Beta Was this translation helpful? Give feedback.
-
List of APIs needed for implementing transformers(WIP)
Since I want to list high-level concepts, I didn't link to their corresponding implementations in Babel or ESbuild. We might want to base them on either one of them or even write something unique to oxc, The thing is that we just need a way to do these operations. Feel free to edit this comment and help make it more organized and complete. |
Beta Was this translation helpful? Give feedback.
-
Here's my notes and thoughts on this matter. Requirements
Nice to haves
Different patterns for mutatingSome ideas I had while trying to think of a new transformer implementation. Immutable AST builds a mutable AST on-demandWith this pattern, we would have 2 ASTs, the 1st is the original immutable AST, and the 2nd is a new mutable AST (starts at Pros
Cons
impl Transformer for X {
fn transform(&mut self, old_node: &AstNode) -> Result<AstNode> {
let mut new_node = old_node.clone();
// Mutate node
Ok(new_node)
}
} Enter/Leave specific transform methodsInstead of having a single Pros
Cons
impl Transformer for X {
fn transform(&mut self, node: &AstNode) -> Result<Option<AstNode>> {
match node.kind() {
SomeNode => {
if self.ctx.is_within_function(...) {
// Flag parent changes within child
self.in_function_body = true;
}
}
};
Ok(None)
}
fn transform_on_leave(&mut self, node: &AstNode) -> Result<Option<AstNode>> {
if self.in_function_body && node.kind() == Function {
let mut node = node.clone();
// Mutate parent and return a replacement node
return Ok(Some(node));
}
Ok(None)
}
} No child -> parent traversal, instead parent -> child visit pre-checks
This pattern goes against our requirement of "traversing up to the parent from the child", but instead offers an alternative. If we have situations where a child needs to detect if it's within a certain parent, or that parent needs to be mutated base on the status of a child, we can solve these by implementing a custom Pros
Cons
struct IsChildInParent {
pub yes: bool,
}
impl Visit for IsChildInParent {
fn enter_node(&mut self, kind: &AstKind) {
match kind {
Child1 | Child2 => {
self.yes = true;
}
_ => {}
};
}
}
impl Transformer for X {
fn transform(&mut self, node: &AstNode) -> Result<Option<AstNode>> {
match node.kind() {
ParentNode => {
let visitor = IsChildInParent { yes: false };
visitor.visit_parent_node(&ast.node);
if visitor.yes {
// Child exists within this parent,
// so mutate the parent from this context
}
}
ChildNode => {
// Mutate child if need be
}
};
Ok(None)
}
} Open questionsBased on all of this, the big problem I keep coming back to, is how do we keep the semantic tables in sync? Especially when we need to add/remove/rename/move scopes/bindings/symbols/etc. This is further made complicated by the fact that |
Beta Was this translation helpful? Give feedback.
-
The current top-to-bottom porting strategy is not working right now. I get depressed whenever I want to implement a plugin due to so many obstacles.
We are going to try another strategy, by doing research and layout all the dependencies of the plugins. After all the dependencies of each plugins are collected, we are going to implement all the utilities from the bottom-up.
Dependencies are anything that we haven't implemented yet.
Beta Was this translation helpful? Give feedback.
All reactions