[rust] SWC->ESTree conversion

Start of converting swc's representation into our estree format.
This commit is contained in:
Joe Savona
2023-07-06 09:24:38 +09:00
parent 4230eb63d8
commit 2b168ad673
2 changed files with 390 additions and 0 deletions
@@ -0,0 +1,11 @@
[package]
name = "estree-swc"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
swc_ecma_ast = { version = "0.106.6", features = ["serde", "serde-impl"] }
estree = { path = "../estree" }
swc_common = "0.31.16"
@@ -0,0 +1,379 @@
use std::num::NonZeroU32;
use swc_common::{source_map::Pos, Span};
use swc_ecma_ast::{
AssignOp, BinaryOp, BlockStmt, Decl, Expr, Ident, Lit, ModuleItem, Pat, PatOrExpr, Program,
Stmt, UnaryOp, VarDeclKind, VarDeclOrExpr,
};
pub fn convert_program(program: &Program) -> estree::Program {
let mut program_items: Vec<estree::ModuleItem>;
match program {
Program::Module(program) => {
let body = &program.body;
program_items = Vec::with_capacity(body.len());
for item in body {
program_items.push(convert_module_item(item));
}
}
Program::Script(program) => {
let body = &program.body;
program_items = Vec::with_capacity(body.len());
for item in body {
program_items.push(estree::ModuleItem::Statement(Box::new(convert_statement(
item,
))));
}
}
};
estree::Program {
source_type: if program.is_script() {
estree::SourceType::Script
} else {
estree::SourceType::Module
},
body: program_items,
comments: None,
loc: None,
range: None,
}
}
fn convert_module_item(item: &ModuleItem) -> estree::ModuleItem {
match item {
ModuleItem::Stmt(item) => estree::ModuleItem::Statement(Box::new(convert_statement(item))),
_ => todo!(),
}
}
fn convert_span(span: &Span) -> Option<estree::SourceRange> {
Some(estree::SourceRange {
start: span.lo().to_u32(),
end: NonZeroU32::new(span.hi().to_u32())?,
})
}
fn convert_decl_kind(kind: &VarDeclKind) -> estree::VariableDeclarationKind {
match kind {
VarDeclKind::Const => estree::VariableDeclarationKind::Const,
VarDeclKind::Let => estree::VariableDeclarationKind::Let,
VarDeclKind::Var => estree::VariableDeclarationKind::Var,
}
}
fn convert_block_statement(stmt: &BlockStmt) -> estree::BlockStatement {
let mut body: Vec<estree::Statement> = Vec::with_capacity(stmt.stmts.len());
for stmt in &stmt.stmts {
body.push(convert_statement(stmt));
}
estree::BlockStatement {
body,
loc: None,
range: convert_span(&stmt.span),
}
}
fn convert_statement(stmt: &Stmt) -> estree::Statement {
match stmt {
Stmt::Decl(Decl::Fn(item)) => {
let name = item.ident.sym.to_string();
estree::Statement::FunctionDeclaration(Box::new(estree::FunctionDeclaration {
id: Some(estree::Identifier {
name,
loc: None,
range: convert_span(&item.ident.span),
}),
params: item
.function
.params
.iter()
.map(|param| convert_pattern(&param.pat))
.collect(),
body: item.function.body.as_ref().map(|body| {
estree::Statement::BlockStatement(Box::new(convert_block_statement(body)))
}),
is_async: item.function.is_async,
is_generator: item.function.is_generator,
loc: None,
range: convert_span(&item.function.span),
}))
}
Stmt::Block(item) => {
estree::Statement::BlockStatement(Box::new(convert_block_statement(item)))
}
Stmt::Break(item) => estree::Statement::BreakStatement(Box::new(estree::BreakStatement {
label: item.label.as_ref().map(|label| convert_identifier(label)),
loc: None,
range: convert_span(&item.span),
})),
Stmt::Continue(item) => {
estree::Statement::ContinueStatement(Box::new(estree::ContinueStatement {
label: item.label.as_ref().map(|label| convert_identifier(label)),
loc: None,
range: convert_span(&item.span),
}))
}
Stmt::Debugger(item) => {
estree::Statement::DebuggerStatement(Box::new(estree::DebuggerStatement {
loc: None,
range: convert_span(&item.span),
}))
}
Stmt::DoWhile(item) => {
estree::Statement::DoWhileStatement(Box::new(estree::DoWhileStatement {
body: convert_statement(&item.body),
test: convert_expression(&item.test),
loc: None,
range: convert_span(&item.span),
}))
}
Stmt::Empty(item) => estree::Statement::EmptyStatement(Box::new(estree::EmptyStatement {
loc: None,
range: convert_span(&item.span),
})),
Stmt::Expr(item) => {
let expression = convert_expression(&item.expr);
estree::Statement::ExpressionStatement(Box::new(estree::ExpressionStatement {
expression,
directive: None,
loc: None,
range: convert_span(&item.span),
}))
}
Stmt::For(item) => estree::Statement::ForStatement(Box::new(estree::ForStatement {
init: item.init.as_ref().map(|init| match init {
VarDeclOrExpr::Expr(init) => {
estree::ForInit::Expression(Box::new(convert_expression(init)))
}
VarDeclOrExpr::VarDecl(init) => {
assert_eq!(init.decls.len(), 1);
let decl = &init.decls[0];
estree::ForInit::VariableDeclaration(Box::new(estree::VariableDeclaration {
kind: convert_decl_kind(&init.kind),
declarations: vec![estree::VariableDeclarator {
id: convert_pattern(&decl.name),
init: decl.init.as_ref().map(|init| convert_expression(init)),
loc: None,
range: convert_span(&decl.span),
}],
loc: None,
range: convert_span(&init.span),
}))
}
}),
test: item.test.as_ref().map(|test| convert_expression(test)),
update: item
.update
.as_ref()
.map(|update| convert_expression(update)),
body: convert_statement(&item.body),
loc: None,
range: convert_span(&item.span),
})),
Stmt::Return(item) => {
estree::Statement::ReturnStatement(Box::new(estree::ReturnStatement {
argument: item.arg.as_ref().map(|arg| convert_expression(arg)),
loc: None,
range: convert_span(&item.span),
}))
}
Stmt::Throw(item) => estree::Statement::ThrowStatement(Box::new(estree::ThrowStatement {
argument: convert_expression(&item.arg),
loc: None,
range: convert_span(&item.span),
})),
Stmt::If(item) => estree::Statement::IfStatement(Box::new(estree::IfStatement {
test: convert_expression(&item.test),
consequent: convert_statement(&item.cons),
alternate: item.alt.as_ref().map(|alt| convert_statement(alt)),
loc: None,
range: convert_span(&item.span),
})),
_ => todo!(),
}
}
fn convert_expression(expr: &Expr) -> estree::ExpressionLike {
match expr {
Expr::Ident(expr) => estree::ExpressionLike::Identifier(Box::new(convert_identifier(expr))),
Expr::Array(expr) => {
estree::ExpressionLike::ArrayExpression(Box::new(estree::ArrayExpression {
elements: expr
.elems
.iter()
.map(|item| {
// TODO: represent holes in array expressions
let value = item.as_ref().unwrap();
match value.spread {
Some(spread) => estree::ExpressionLike::SpreadElement(Box::new(
estree::SpreadElement {
argument: convert_expression(&value.expr),
loc: None,
range: convert_span(&spread),
},
)),
None => convert_expression(&value.expr),
}
})
.collect(),
loc: None,
range: convert_span(&expr.span),
}))
}
Expr::Await(expr) => {
estree::ExpressionLike::AwaitExpression(Box::new(estree::AwaitExpression {
argument: convert_expression(&expr.arg),
loc: None,
range: convert_span(&expr.span),
}))
}
Expr::Unary(expr) => {
estree::ExpressionLike::UnaryExpression(Box::new(estree::UnaryExpression {
operator: convert_unary_operator(expr.op),
is_prefix: false,
argument: convert_expression(&expr.arg),
loc: None,
range: convert_span(&expr.span),
}))
}
Expr::Bin(expr) => match convert_binary_operator(expr.op) {
Operator::Binary(op) => {
estree::ExpressionLike::BinaryExpression(Box::new(estree::BinaryExpression {
operator: op,
left: convert_expression(&expr.left),
right: convert_expression(&expr.right),
loc: None,
range: convert_span(&expr.span),
}))
}
Operator::Logical(op) => {
estree::ExpressionLike::LogicalExpression(Box::new(estree::LogicalExpression {
operator: op,
left: convert_expression(&expr.left),
right: convert_expression(&expr.right),
loc: None,
range: convert_span(&expr.span),
}))
}
},
Expr::Lit(expr) => {
let (value, range) = match expr {
Lit::Bool(expr) => (
estree::LiteralValue::Boolean(expr.value),
convert_span(&expr.span),
),
Lit::Num(expr) => (
estree::LiteralValue::Number(expr.value.into()),
convert_span(&expr.span),
),
Lit::Str(expr) => (
estree::LiteralValue::String(expr.value.to_string()),
convert_span(&expr.span),
),
Lit::Null(expr) => (estree::LiteralValue::Null, convert_span(&expr.span)),
_ => todo!(),
};
estree::ExpressionLike::Literal(Box::new(estree::Literal {
value,
raw: None,
loc: None,
range,
}))
}
Expr::Assign(expr) => {
estree::ExpressionLike::AssignmentExpression(Box::new(estree::AssignmentExpression {
operator: convert_assignment_operator(expr.op),
left: convert_assignment_target(&expr.left),
right: convert_expression(&expr.right),
loc: None,
range: convert_span(&expr.span),
}))
}
_ => todo!(),
}
}
fn convert_assignment_target(_target: &PatOrExpr) -> estree::AssignmentTarget {
todo!()
}
fn convert_unary_operator(op: UnaryOp) -> estree::UnaryOperator {
match op {
UnaryOp::Bang => estree::UnaryOperator::Exclamation,
UnaryOp::Delete => estree::UnaryOperator::Delete,
UnaryOp::Minus => estree::UnaryOperator::Minus,
UnaryOp::Plus => estree::UnaryOperator::Plus,
UnaryOp::Tilde => estree::UnaryOperator::Tilde,
UnaryOp::TypeOf => estree::UnaryOperator::Typeof,
UnaryOp::Void => estree::UnaryOperator::Void,
}
}
fn convert_assignment_operator(op: AssignOp) -> estree::AssignmentOperator {
match op {
AssignOp::AddAssign => estree::AssignmentOperator::PlusEquals,
_ => todo!(),
}
}
enum Operator {
Binary(estree::BinaryOperator),
Logical(estree::LogicalOperator),
}
fn convert_binary_operator(op: BinaryOp) -> Operator {
match op {
BinaryOp::Add => Operator::Binary(estree::BinaryOperator::Plus),
BinaryOp::BitAnd => Operator::Binary(estree::BinaryOperator::Ampersand),
BinaryOp::BitOr => Operator::Binary(estree::BinaryOperator::Pipe),
BinaryOp::BitXor => Operator::Binary(estree::BinaryOperator::Caret),
BinaryOp::Div => Operator::Binary(estree::BinaryOperator::Slash),
BinaryOp::EqEq => Operator::Binary(estree::BinaryOperator::EqualsEquals),
BinaryOp::EqEqEq => Operator::Binary(estree::BinaryOperator::TripleEquals),
BinaryOp::Exp => Operator::Binary(estree::BinaryOperator::AsteriskAsterisk),
BinaryOp::Gt => Operator::Binary(estree::BinaryOperator::GreaterThan),
BinaryOp::GtEq => Operator::Binary(estree::BinaryOperator::GreaterThanEquals),
BinaryOp::In => Operator::Binary(estree::BinaryOperator::In),
BinaryOp::InstanceOf => Operator::Binary(estree::BinaryOperator::Instanceof),
BinaryOp::LShift => Operator::Binary(estree::BinaryOperator::LtLt),
BinaryOp::Lt => Operator::Binary(estree::BinaryOperator::LessThan),
BinaryOp::LtEq => Operator::Binary(estree::BinaryOperator::LessThanEquals),
BinaryOp::Mod => Operator::Binary(estree::BinaryOperator::Percent),
BinaryOp::Mul => Operator::Binary(estree::BinaryOperator::Asterisk),
BinaryOp::NotEq => Operator::Binary(estree::BinaryOperator::NotEquals),
BinaryOp::NotEqEq => Operator::Binary(estree::BinaryOperator::NotTripleEquals),
BinaryOp::RShift => Operator::Binary(estree::BinaryOperator::GtGt),
BinaryOp::Sub => Operator::Binary(estree::BinaryOperator::Minus),
BinaryOp::ZeroFillRShift => Operator::Binary(estree::BinaryOperator::GtGtGt),
BinaryOp::LogicalAnd => Operator::Logical(estree::LogicalOperator::AmpersandAmpersand),
BinaryOp::LogicalOr => Operator::Logical(estree::LogicalOperator::PipePipe),
BinaryOp::NullishCoalescing => Operator::Logical(estree::LogicalOperator::QuestionQuestion),
}
}
fn convert_pattern(pat: &Pat) -> estree::Pattern {
match pat {
Pat::Ident(pat) => estree::Pattern::Identifier(Box::new(estree::Identifier {
name: pat.id.sym.to_string(),
loc: None,
range: convert_span(&pat.span),
})),
_ => todo!(),
}
}
fn convert_identifier(identifier: &Ident) -> estree::Identifier {
let name = identifier.sym.as_ref().to_string();
estree::Identifier {
name,
loc: None,
range: convert_span(&identifier.span),
}
}
#[cfg(test)]
mod tests {
#[test]
fn it_works() {}
}