mirror of
https://github.com/swc-project/swc.git
synced 2024-10-05 04:39:06 +03:00
Visitor for stable rust (#743)
This commit is contained in:
parent
c282fb3092
commit
b279e7a815
@ -1,4 +1,5 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
|
members = ["ecmascript/visit"]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "swc"
|
name = "swc"
|
||||||
|
17
ecmascript/visit/Cargo.toml
Normal file
17
ecmascript/visit/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "swc_ecma_visit"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||||
|
license = "Apache-2.0/MIT"
|
||||||
|
repository = "https://github.com/swc-project/swc.git"
|
||||||
|
documentation = "https://swc-project.github.io/rustdoc/swc_ecma_visit/"
|
||||||
|
description = "Visitors for swc ecmascript nodes which works on stable rustc"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
swc_atoms = { version = "0.2", path = "../../atoms" }
|
||||||
|
swc_common = { version = "0.5.9", path = "../../common" }
|
||||||
|
swc_ecma_ast = { version = "0.18.0", path ="../ast" }
|
||||||
|
swc_ecma_visit_macros = { version = "0.1.0", path ="./macros" }
|
||||||
|
num-bigint = { version = "0.2", features = ["serde"] }
|
16
ecmascript/visit/macros/Cargo.toml
Normal file
16
ecmascript/visit/macros/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "swc_ecma_visit_macros"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
swc_macros_common = { version = "0.3", path ="../../../macros/common" }
|
||||||
|
proc-macro2 = "1"
|
||||||
|
pmutil = "0.5.1"
|
||||||
|
quote = "1"
|
||||||
|
syn = { version = "1", features = ["parsing", "full"] }
|
||||||
|
Inflector = "0.11.4"
|
740
ecmascript/visit/macros/src/lib.rs
Normal file
740
ecmascript/visit/macros/src/lib.rs
Normal file
@ -0,0 +1,740 @@
|
|||||||
|
use inflector::Inflector;
|
||||||
|
use pmutil::{q, IdentExt};
|
||||||
|
use proc_macro2::Ident;
|
||||||
|
use std::mem::replace;
|
||||||
|
use swc_macros_common::{call_site, def_site};
|
||||||
|
use syn::{
|
||||||
|
parse_quote::parse, punctuated::Punctuated, spanned::Spanned, Arm, AttrStyle, Attribute, Block,
|
||||||
|
Expr, ExprBlock, ExprMatch, FieldValue, Fields, FnArg, GenericArgument, Index, Item, ItemTrait,
|
||||||
|
Member, Path, PathArguments, ReturnType, Signature, Stmt, Token, TraitItem, TraitItemMethod,
|
||||||
|
Type, TypePath, TypeReference, VisPublic, Visibility,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// This creates `Visit`. This is extensible visitor generator, and it
|
||||||
|
///
|
||||||
|
/// - works with stable rustc
|
||||||
|
///
|
||||||
|
/// - highly extensible and used to create Visitor for any types
|
||||||
|
///
|
||||||
|
/// If there's a need, I'll publish the macro with generic name.
|
||||||
|
///
|
||||||
|
/// - will be extended to create `VisitMut` and `Fold` in future
|
||||||
|
///
|
||||||
|
/// (If there's a request)
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn define(tts: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let block: Block = parse(tts.into());
|
||||||
|
|
||||||
|
// Required to generate specialization code.
|
||||||
|
let mut types = vec![];
|
||||||
|
let mut methods = vec![];
|
||||||
|
|
||||||
|
for stmts in block.stmts {
|
||||||
|
let item = match stmts {
|
||||||
|
Stmt::Item(item) => item,
|
||||||
|
_ => unimplemented!("error reporting for something other than Item"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mtd = make_method(item, &mut types);
|
||||||
|
methods.push(mtd);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut tokens = q!({});
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut new = vec![];
|
||||||
|
for ty in &types {
|
||||||
|
add_required(&mut new, ty);
|
||||||
|
}
|
||||||
|
types.extend(new);
|
||||||
|
}
|
||||||
|
|
||||||
|
methods.dedup_by_key(|v| v.sig.ident.to_string());
|
||||||
|
methods.sort_by_cached_key(|v| v.sig.ident.to_string());
|
||||||
|
|
||||||
|
for ty in types {
|
||||||
|
let name = method_name(&ty);
|
||||||
|
let s = name.to_string();
|
||||||
|
if methods.iter().any(|m| m.sig.ident == &*s) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
methods.push(TraitItemMethod {
|
||||||
|
attrs: vec![],
|
||||||
|
sig: create_method_sig(&ty),
|
||||||
|
default: Some(create_method_body(&ty)),
|
||||||
|
semi_token: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
methods.iter_mut().for_each(|v| {
|
||||||
|
v.attrs.push(Attribute {
|
||||||
|
pound_token: def_site(),
|
||||||
|
style: AttrStyle::Outer,
|
||||||
|
bracket_token: def_site(),
|
||||||
|
path: q!({ allow }).parse(),
|
||||||
|
tokens: q!({ (unused_variables) }).parse(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let fn_name = v.sig.ident.clone();
|
||||||
|
let default_body = replace(
|
||||||
|
&mut v.default,
|
||||||
|
Some(
|
||||||
|
q!(Vars { fn_name: &fn_name }, {
|
||||||
|
{
|
||||||
|
fn_name(self, n, _parent)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.parse(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
let arg_ty = v
|
||||||
|
.sig
|
||||||
|
.inputs
|
||||||
|
.iter()
|
||||||
|
.skip(1)
|
||||||
|
.next()
|
||||||
|
.map(|v| match *v {
|
||||||
|
FnArg::Typed(ref pat) => &pat.ty,
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
tokens.push_tokens(&q!(
|
||||||
|
Vars {
|
||||||
|
fn_name,
|
||||||
|
default_body,
|
||||||
|
Type: arg_ty,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
pub fn fn_name<V: ?Sized + Visit>(_visitor: &mut V, n: Type, _parent: &dyn Node) {
|
||||||
|
default_body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
))
|
||||||
|
});
|
||||||
|
|
||||||
|
tokens.push_tokens(&ItemTrait {
|
||||||
|
attrs: vec![],
|
||||||
|
vis: Visibility::Public(VisPublic {
|
||||||
|
pub_token: def_site(),
|
||||||
|
}),
|
||||||
|
unsafety: None,
|
||||||
|
auto_token: None,
|
||||||
|
trait_token: def_site(),
|
||||||
|
ident: Ident::new("Visit", call_site()),
|
||||||
|
generics: Default::default(),
|
||||||
|
colon_token: None,
|
||||||
|
supertraits: Default::default(),
|
||||||
|
brace_token: def_site(),
|
||||||
|
items: methods.into_iter().map(TraitItem::Method).collect(),
|
||||||
|
});
|
||||||
|
|
||||||
|
tokens.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_arm_from_struct(path: &Path, variant: &Fields) -> Arm {
|
||||||
|
let mut stmts = vec![];
|
||||||
|
let mut fields: Punctuated<FieldValue, Token![,]> = Default::default();
|
||||||
|
|
||||||
|
for (i, field) in variant.iter().enumerate() {
|
||||||
|
let ty = &field.ty;
|
||||||
|
|
||||||
|
let visit_name = method_name(&ty);
|
||||||
|
let binding_ident = field
|
||||||
|
.ident
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| Ident::new(&format!("_{}", i), call_site()));
|
||||||
|
|
||||||
|
if !skip(ty) {
|
||||||
|
let mut expr: Expr = q!(
|
||||||
|
Vars {
|
||||||
|
binding_ident: &binding_ident
|
||||||
|
},
|
||||||
|
{ &*binding_ident }
|
||||||
|
)
|
||||||
|
.parse();
|
||||||
|
if is_option(&ty) {
|
||||||
|
expr = if is_opt_vec(ty) {
|
||||||
|
q!(
|
||||||
|
Vars {
|
||||||
|
binding_ident: &binding_ident
|
||||||
|
},
|
||||||
|
{ binding_ident.as_ref().map(|v| &**v) }
|
||||||
|
)
|
||||||
|
.parse()
|
||||||
|
} else {
|
||||||
|
q!(
|
||||||
|
Vars {
|
||||||
|
binding_ident: &binding_ident
|
||||||
|
},
|
||||||
|
{ binding_ident.as_ref() }
|
||||||
|
)
|
||||||
|
.parse()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let stmt = q!(Vars { expr, visit_name }, {
|
||||||
|
_visitor.visit_name(expr, n as _);
|
||||||
|
})
|
||||||
|
.parse();
|
||||||
|
stmts.push(stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if field.ident.is_some() {
|
||||||
|
fields.push(
|
||||||
|
q!(
|
||||||
|
Vars {
|
||||||
|
field: &binding_ident
|
||||||
|
},
|
||||||
|
{ field }
|
||||||
|
)
|
||||||
|
.parse(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
fields.push(FieldValue {
|
||||||
|
attrs: vec![],
|
||||||
|
member: Member::Unnamed(Index {
|
||||||
|
index: i as _,
|
||||||
|
span: path.span(),
|
||||||
|
}),
|
||||||
|
colon_token: Some(def_site()),
|
||||||
|
expr: q!(Vars { binding_ident }, { binding_ident }).parse(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let block = Block {
|
||||||
|
brace_token: def_site(),
|
||||||
|
stmts,
|
||||||
|
};
|
||||||
|
|
||||||
|
Arm {
|
||||||
|
attrs: vec![],
|
||||||
|
pat: q!(Vars { Path: path, fields }, { Path { fields } }).parse(),
|
||||||
|
guard: None,
|
||||||
|
fat_arrow_token: def_site(),
|
||||||
|
body: Box::new(Expr::Block(ExprBlock {
|
||||||
|
attrs: vec![],
|
||||||
|
label: None,
|
||||||
|
block,
|
||||||
|
})),
|
||||||
|
comma: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn method_sig(ty: &Type) -> Signature {
|
||||||
|
Signature {
|
||||||
|
constness: None,
|
||||||
|
asyncness: None,
|
||||||
|
unsafety: None,
|
||||||
|
abi: None,
|
||||||
|
fn_token: def_site(),
|
||||||
|
ident: method_name(ty),
|
||||||
|
generics: Default::default(),
|
||||||
|
paren_token: def_site(),
|
||||||
|
inputs: {
|
||||||
|
let mut p = Punctuated::default();
|
||||||
|
p.push_value(q!(Vars {}, { &mut self }).parse());
|
||||||
|
p.push_punct(def_site());
|
||||||
|
p.push_value(q!(Vars { Type: ty }, { n: &Type }).parse());
|
||||||
|
p.push_punct(def_site());
|
||||||
|
p.push_value(q!(Vars {}, { _parent: &dyn Node }).parse());
|
||||||
|
|
||||||
|
p
|
||||||
|
},
|
||||||
|
variadic: None,
|
||||||
|
output: ReturnType::Default,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn method_sig_from_ident(v: &Ident) -> Signature {
|
||||||
|
method_sig(&Type::Path(TypePath {
|
||||||
|
qself: None,
|
||||||
|
path: v.clone().into(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_method(e: Item, types: &mut Vec<Type>) -> TraitItemMethod {
|
||||||
|
match e {
|
||||||
|
Item::Struct(s) => {
|
||||||
|
let type_name = &s.ident;
|
||||||
|
types.push(Type::Path(TypePath {
|
||||||
|
qself: None,
|
||||||
|
path: type_name.clone().into(),
|
||||||
|
}));
|
||||||
|
for f in &s.fields {
|
||||||
|
types.push(f.ty.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let block = {
|
||||||
|
let arm = make_arm_from_struct(&s.ident.clone().into(), &s.fields);
|
||||||
|
|
||||||
|
let mut match_expr: ExprMatch = q!((match n {})).parse();
|
||||||
|
match_expr.arms.push(arm);
|
||||||
|
|
||||||
|
Block {
|
||||||
|
brace_token: def_site(),
|
||||||
|
stmts: vec![q!(Vars { match_expr }, { match_expr }).parse()],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TraitItemMethod {
|
||||||
|
attrs: vec![],
|
||||||
|
sig: method_sig_from_ident(type_name),
|
||||||
|
default: Some(block),
|
||||||
|
semi_token: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Item::Enum(e) => {
|
||||||
|
//
|
||||||
|
let type_name = &e.ident;
|
||||||
|
types.push(
|
||||||
|
TypePath {
|
||||||
|
qself: None,
|
||||||
|
path: e.ident.clone().into(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
let block = {
|
||||||
|
let mut arms = vec![];
|
||||||
|
|
||||||
|
for variant in &e.variants {
|
||||||
|
for f in &variant.fields {
|
||||||
|
types.push(f.ty.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let arm = make_arm_from_struct(
|
||||||
|
&q!(
|
||||||
|
Vars {
|
||||||
|
Enum: &e.ident,
|
||||||
|
Variant: &variant.ident
|
||||||
|
},
|
||||||
|
{ Enum::Variant }
|
||||||
|
)
|
||||||
|
.parse(),
|
||||||
|
&variant.fields,
|
||||||
|
);
|
||||||
|
arms.push(arm);
|
||||||
|
}
|
||||||
|
|
||||||
|
Block {
|
||||||
|
brace_token: def_site(),
|
||||||
|
stmts: vec![Stmt::Expr(Expr::Match(ExprMatch {
|
||||||
|
attrs: vec![],
|
||||||
|
match_token: def_site(),
|
||||||
|
expr: q!((n)).parse(),
|
||||||
|
brace_token: def_site(),
|
||||||
|
arms,
|
||||||
|
}))],
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TraitItemMethod {
|
||||||
|
attrs: vec![],
|
||||||
|
sig: method_sig_from_ident(type_name),
|
||||||
|
default: Some(block),
|
||||||
|
semi_token: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => unimplemented!(
|
||||||
|
"proper error reporting for item other than struct / enum: {:?}",
|
||||||
|
e
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prefix_method_name(v: &str) -> String {
|
||||||
|
format!("visit_{}", v.to_snake_case())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn method_name(v: &Type) -> Ident {
|
||||||
|
create_method_sig(v).ident
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_method_sig(ty: &Type) -> Signature {
|
||||||
|
fn mk_exact(ident: Ident, ty: &Type) -> Signature {
|
||||||
|
Signature {
|
||||||
|
constness: None,
|
||||||
|
asyncness: None,
|
||||||
|
unsafety: None,
|
||||||
|
abi: None,
|
||||||
|
fn_token: def_site(),
|
||||||
|
ident,
|
||||||
|
generics: Default::default(),
|
||||||
|
paren_token: def_site(),
|
||||||
|
inputs: {
|
||||||
|
let mut p = Punctuated::default();
|
||||||
|
p.push_value(q!(Vars {}, { &mut self }).parse());
|
||||||
|
p.push_punct(def_site());
|
||||||
|
p.push_value(q!(Vars { Type: ty }, { n: Type }).parse());
|
||||||
|
p.push_punct(def_site());
|
||||||
|
p.push_value(q!(Vars {}, { _parent: &dyn Node }).parse());
|
||||||
|
|
||||||
|
p
|
||||||
|
},
|
||||||
|
variadic: None,
|
||||||
|
output: ReturnType::Default,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mk(ident: Ident, ty: &Type) -> Signature {
|
||||||
|
mk_exact(
|
||||||
|
ident,
|
||||||
|
&Type::Reference(TypeReference {
|
||||||
|
and_token: def_site(),
|
||||||
|
lifetime: None,
|
||||||
|
mutability: None,
|
||||||
|
elem: Box::new(ty.clone()),
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
match ty {
|
||||||
|
Type::Array(_) => unimplemented!("type: array type"),
|
||||||
|
Type::BareFn(_) => unimplemented!("type: fn type"),
|
||||||
|
Type::Group(_) => unimplemented!("type: group type"),
|
||||||
|
Type::ImplTrait(_) => unimplemented!("type: impl trait"),
|
||||||
|
Type::Infer(_) => unreachable!("infer type"),
|
||||||
|
Type::Macro(_) => unimplemented!("type: macro"),
|
||||||
|
Type::Never(_) => unreachable!("never type"),
|
||||||
|
Type::Paren(ty) => return create_method_sig(&ty.elem),
|
||||||
|
Type::Path(p) => {
|
||||||
|
let last = p.path.segments.last().unwrap();
|
||||||
|
let ident = last.ident.new_ident_with(prefix_method_name);
|
||||||
|
|
||||||
|
if !last.arguments.is_empty() {
|
||||||
|
if last.ident == "Box" {
|
||||||
|
match &last.arguments {
|
||||||
|
PathArguments::AngleBracketed(tps) => {
|
||||||
|
let arg = tps.args.first().unwrap();
|
||||||
|
|
||||||
|
match arg {
|
||||||
|
GenericArgument::Type(arg) => {
|
||||||
|
let ident = method_name(&arg);
|
||||||
|
return mk(ident, &q!(Vars { arg }, { arg }).parse());
|
||||||
|
}
|
||||||
|
_ => unimplemented!("generic parameter other than type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unimplemented!("Box() -> T or Box without a type parameter"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if last.ident == "Option" {
|
||||||
|
match &last.arguments {
|
||||||
|
PathArguments::AngleBracketed(tps) => {
|
||||||
|
let arg = tps.args.first().unwrap();
|
||||||
|
|
||||||
|
match arg {
|
||||||
|
GenericArgument::Type(arg) => {
|
||||||
|
let ident = method_name(arg)
|
||||||
|
.new_ident_with(|v| v.replace("visit_", "visit_opt_"));
|
||||||
|
|
||||||
|
if let Some(item) = extract_vec(arg) {
|
||||||
|
return mk_exact(
|
||||||
|
ident,
|
||||||
|
&q!(Vars { item}, { Option<&[item]> }).parse(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mk_exact(
|
||||||
|
ident,
|
||||||
|
&q!(Vars { arg }, { Option<&arg> }).parse(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => unimplemented!("generic parameter other than type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unimplemented!("Box() -> T or Box without a type parameter"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if last.ident == "Vec" {
|
||||||
|
match &last.arguments {
|
||||||
|
PathArguments::AngleBracketed(tps) => {
|
||||||
|
let arg = tps.args.first().unwrap();
|
||||||
|
|
||||||
|
match arg {
|
||||||
|
GenericArgument::Type(arg) => {
|
||||||
|
let orig_name = method_name(arg);
|
||||||
|
let mut ident = orig_name.new_ident_with(|v| {
|
||||||
|
let v = v.to_plural();
|
||||||
|
if is_option(arg) {
|
||||||
|
return v.replace("visit_opt_", "visit_opt_vec_");
|
||||||
|
}
|
||||||
|
return v.to_plural();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rename if name conflicts
|
||||||
|
if orig_name == ident.to_string() {
|
||||||
|
ident = ident.new_ident_with(|v| format!("{}_vec", v));
|
||||||
|
}
|
||||||
|
|
||||||
|
return mk(ident, &q!(Vars { arg }, { [arg] }).parse());
|
||||||
|
}
|
||||||
|
_ => unimplemented!("generic parameter other than type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unimplemented!("Vec() -> Ret or Vec without a type parameter"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mk(ident, ty);
|
||||||
|
}
|
||||||
|
Type::Ptr(_) => unimplemented!("type: pointer"),
|
||||||
|
Type::Reference(ty) => {
|
||||||
|
return create_method_sig(&ty.elem);
|
||||||
|
}
|
||||||
|
Type::Slice(_) => unimplemented!("type: slice"),
|
||||||
|
Type::TraitObject(_) => unimplemented!("type: trait object"),
|
||||||
|
Type::Tuple(_) => unimplemented!("type: trait tuple"),
|
||||||
|
Type::Verbatim(_) => unimplemented!("type: verbatim"),
|
||||||
|
_ => unimplemented!("Unknown type: {:?}", ty),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_method_body(ty: &Type) -> Block {
|
||||||
|
match ty {
|
||||||
|
Type::Array(_) => unimplemented!("type: array type"),
|
||||||
|
Type::BareFn(_) => unimplemented!("type: fn type"),
|
||||||
|
Type::Group(_) => unimplemented!("type: group type"),
|
||||||
|
Type::ImplTrait(_) => unimplemented!("type: impl trait"),
|
||||||
|
Type::Infer(_) => unreachable!("infer type"),
|
||||||
|
Type::Macro(_) => unimplemented!("type: macro"),
|
||||||
|
Type::Never(_) => unreachable!("never type"),
|
||||||
|
Type::Paren(ty) => return create_method_body(&ty.elem),
|
||||||
|
Type::Path(p) => {
|
||||||
|
let last = p.path.segments.last().unwrap();
|
||||||
|
|
||||||
|
if !last.arguments.is_empty() {
|
||||||
|
if last.ident == "Box" {
|
||||||
|
match &last.arguments {
|
||||||
|
PathArguments::AngleBracketed(tps) => {
|
||||||
|
let arg = tps.args.first().unwrap();
|
||||||
|
|
||||||
|
match arg {
|
||||||
|
GenericArgument::Type(arg) => {
|
||||||
|
return create_method_body(arg);
|
||||||
|
}
|
||||||
|
_ => unimplemented!("generic parameter other than type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unimplemented!("Box() -> T or Box without a type parameter"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if last.ident == "Option" {
|
||||||
|
match &last.arguments {
|
||||||
|
PathArguments::AngleBracketed(tps) => {
|
||||||
|
let arg = tps.args.first().unwrap();
|
||||||
|
|
||||||
|
match arg {
|
||||||
|
GenericArgument::Type(arg) => {
|
||||||
|
let ident = method_name(arg);
|
||||||
|
|
||||||
|
return q!(
|
||||||
|
Vars { ident },
|
||||||
|
({
|
||||||
|
match n {
|
||||||
|
Some(n) => _visitor.ident(n, _parent),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.parse();
|
||||||
|
}
|
||||||
|
_ => unimplemented!("generic parameter other than type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unimplemented!("Box() -> T or Box without a type parameter"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if last.ident == "Vec" {
|
||||||
|
match &last.arguments {
|
||||||
|
PathArguments::AngleBracketed(tps) => {
|
||||||
|
let arg = tps.args.first().unwrap();
|
||||||
|
|
||||||
|
match arg {
|
||||||
|
GenericArgument::Type(arg) => {
|
||||||
|
let ident = method_name(arg);
|
||||||
|
|
||||||
|
return if is_option(arg) {
|
||||||
|
q!(
|
||||||
|
Vars { ident },
|
||||||
|
({
|
||||||
|
n.iter().for_each(|v| {
|
||||||
|
_visitor.ident(v.as_ref(), _parent)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.parse()
|
||||||
|
} else {
|
||||||
|
q!(
|
||||||
|
Vars { ident },
|
||||||
|
({ n.iter().for_each(|v| _visitor.ident(v, _parent)) })
|
||||||
|
)
|
||||||
|
.parse()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ => unimplemented!("generic parameter other than type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unimplemented!("Vec() -> Ret or Vec without a type parameter"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
q!(({})).parse()
|
||||||
|
}
|
||||||
|
Type::Ptr(_) => unimplemented!("type: pointer"),
|
||||||
|
Type::Reference(ty) => {
|
||||||
|
return create_method_body(&ty.elem);
|
||||||
|
}
|
||||||
|
Type::Slice(_) => unimplemented!("type: slice"),
|
||||||
|
Type::TraitObject(_) => unimplemented!("type: trait object"),
|
||||||
|
Type::Tuple(_) => unimplemented!("type: trait tuple"),
|
||||||
|
Type::Verbatim(_) => unimplemented!("type: verbatim"),
|
||||||
|
_ => unimplemented!("Unknown type: {:?}", ty),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_required(types: &mut Vec<Type>, ty: &Type) {
|
||||||
|
match ty {
|
||||||
|
Type::Path(p) => {
|
||||||
|
let last = p.path.segments.last().unwrap();
|
||||||
|
|
||||||
|
if !last.arguments.is_empty() {
|
||||||
|
if last.ident == "Option" || last.ident == "Vec" {
|
||||||
|
match &last.arguments {
|
||||||
|
PathArguments::AngleBracketed(tps) => {
|
||||||
|
let arg = tps.args.first().unwrap();
|
||||||
|
match arg {
|
||||||
|
GenericArgument::Type(arg) => {
|
||||||
|
types.push(arg.clone());
|
||||||
|
}
|
||||||
|
_ => unimplemented!("generic parameter other than type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => unimplemented!("Box() -> T or Box without a type parameter"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_option(ty: &Type) -> bool {
|
||||||
|
match ty {
|
||||||
|
Type::Path(p) => {
|
||||||
|
let last = p.path.segments.last().unwrap();
|
||||||
|
|
||||||
|
if !last.arguments.is_empty() {
|
||||||
|
if last.ident == "Option" {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extract_vec(ty: &Type) -> Option<&Type> {
|
||||||
|
match ty {
|
||||||
|
Type::Path(p) => {
|
||||||
|
let last = p.path.segments.last().unwrap();
|
||||||
|
|
||||||
|
if last.ident == "Vec" {
|
||||||
|
match &last.arguments {
|
||||||
|
PathArguments::AngleBracketed(tps) => {
|
||||||
|
let arg = tps.args.first().unwrap();
|
||||||
|
|
||||||
|
match arg {
|
||||||
|
GenericArgument::Type(arg) => return Some(arg),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_opt_vec(ty: &Type) -> bool {
|
||||||
|
match ty {
|
||||||
|
Type::Path(p) => {
|
||||||
|
let last = p.path.segments.last().unwrap();
|
||||||
|
|
||||||
|
if !last.arguments.is_empty() {
|
||||||
|
if last.ident == "Option" {
|
||||||
|
match &last.arguments {
|
||||||
|
PathArguments::AngleBracketed(tps) => {
|
||||||
|
let arg = tps.args.first().unwrap();
|
||||||
|
|
||||||
|
match arg {
|
||||||
|
GenericArgument::Type(arg) => return extract_vec(arg).is_some(),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn skip(ty: &Type) -> bool {
|
||||||
|
match ty {
|
||||||
|
Type::Path(p) => {
|
||||||
|
if !p.path.segments.last().unwrap().arguments.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let i = &p.path.segments.last().as_ref().unwrap().ident;
|
||||||
|
|
||||||
|
if i == "bool"
|
||||||
|
|| i == "u128"
|
||||||
|
|| i == "u128"
|
||||||
|
|| i == "u64"
|
||||||
|
|| i == "u32"
|
||||||
|
|| i == "u16"
|
||||||
|
|| i == "u8"
|
||||||
|
|| i == "isize"
|
||||||
|
|| i == "i128"
|
||||||
|
|| i == "i128"
|
||||||
|
|| i == "i64"
|
||||||
|
|| i == "i32"
|
||||||
|
|| i == "i16"
|
||||||
|
|| i == "i8"
|
||||||
|
|| i == "isize"
|
||||||
|
|| i == "f64"
|
||||||
|
|| i == "f32"
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
13
ecmascript/visit/scripts/update.sh
Normal file
13
ecmascript/visit/scripts/update.sh
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
for filename in ../ast/src/*.rs; do
|
||||||
|
# echo "$filename"
|
||||||
|
while read -r line; do
|
||||||
|
[[ "$line" =~ ^[[:space:]]*# ]] && continue
|
||||||
|
[[ "$line" =~ ^[[:space:]]*\/ ]] && continue
|
||||||
|
[[ "$line" =~ ^[[:space:]]*\/ ]] && continue
|
||||||
|
echo "$line"
|
||||||
|
done < "$filename"
|
||||||
|
|
||||||
|
done
|
1203
ecmascript/visit/src/lib.rs
Normal file
1203
ecmascript/visit/src/lib.rs
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user