mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 20:32:10 +00:00
refactor(ast_tools): centralize logic for outputting files (#6900)
Move all logic related to formatting and outputting files into one place.
This commit is contained in:
parent
26de0de326
commit
5f44d84744
13 changed files with 209 additions and 213 deletions
|
|
@ -1,18 +1,16 @@
|
|||
use std::{cell::RefCell, path::PathBuf};
|
||||
|
||||
use itertools::Itertools;
|
||||
use proc_macro2::TokenStream;
|
||||
use rustc_hash::{FxBuildHasher, FxHashMap};
|
||||
|
||||
use crate::{
|
||||
derives::{Derive, DeriveOutput},
|
||||
fmt::pretty_print,
|
||||
generators::{Generator, GeneratorOutput},
|
||||
derives::Derive,
|
||||
generators::Generator,
|
||||
log, logln,
|
||||
output::RawOutput,
|
||||
passes::Pass,
|
||||
rust_ast::{self, AstRef},
|
||||
schema::{lower_ast_types, Schema, TypeDef},
|
||||
util::write_all_to,
|
||||
Result, TypeId,
|
||||
};
|
||||
|
||||
|
|
@ -20,50 +18,13 @@ use crate::{
|
|||
pub struct AstCodegen {
|
||||
files: Vec<PathBuf>,
|
||||
passes: Vec<Box<dyn Runner<Output = (), Context = EarlyCtx>>>,
|
||||
generators: Vec<Box<dyn Runner<Output = GeneratorOutput, Context = LateCtx>>>,
|
||||
derives: Vec<Box<dyn Runner<Output = DeriveOutput, Context = LateCtx>>>,
|
||||
generators: Vec<Box<dyn Runner<Output = RawOutput, Context = LateCtx>>>,
|
||||
derives: Vec<Box<dyn Runner<Output = Vec<RawOutput>, Context = LateCtx>>>,
|
||||
}
|
||||
|
||||
pub struct AstCodegenResult {
|
||||
pub schema: Schema,
|
||||
pub outputs: Vec<SideEffect>,
|
||||
}
|
||||
|
||||
pub struct SideEffect {
|
||||
pub path: PathBuf,
|
||||
pub content: Vec<u8>,
|
||||
}
|
||||
|
||||
impl SideEffect {
|
||||
/// Apply the side-effect
|
||||
pub fn apply(self) -> std::io::Result<()> {
|
||||
let Self { path, content } = self;
|
||||
let path = path.into_os_string();
|
||||
let path = path.to_str().unwrap();
|
||||
write_all_to(&content, path)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn path(&self) -> String {
|
||||
let path = self.path.to_string_lossy();
|
||||
path.replace('\\', "/")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(PathBuf, TokenStream)> for SideEffect {
|
||||
fn from((path, stream): (PathBuf, TokenStream)) -> Self {
|
||||
let content = pretty_print(&stream);
|
||||
Self { path, content: content.into() }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GeneratorOutput> for SideEffect {
|
||||
fn from(output: GeneratorOutput) -> Self {
|
||||
match output {
|
||||
GeneratorOutput::Rust { path, tokens } => Self::from((path, tokens)),
|
||||
GeneratorOutput::Javascript { path, code } => Self { path, content: code.into() },
|
||||
}
|
||||
}
|
||||
pub outputs: Vec<RawOutput>,
|
||||
}
|
||||
|
||||
pub trait Runner {
|
||||
|
|
@ -162,7 +123,7 @@ impl AstCodegen {
|
|||
#[must_use]
|
||||
pub fn generate<G>(mut self, generator: G) -> Self
|
||||
where
|
||||
G: Generator + Runner<Output = GeneratorOutput, Context = LateCtx> + 'static,
|
||||
G: Generator + Runner<Output = RawOutput, Context = LateCtx> + 'static,
|
||||
{
|
||||
self.generators.push(Box::new(generator));
|
||||
self
|
||||
|
|
@ -171,7 +132,7 @@ impl AstCodegen {
|
|||
#[must_use]
|
||||
pub fn derive<D>(mut self, derive: D) -> Self
|
||||
where
|
||||
D: Derive + Runner<Output = DeriveOutput, Context = LateCtx> + 'static,
|
||||
D: Derive + Runner<Output = Vec<RawOutput>, Context = LateCtx> + 'static,
|
||||
{
|
||||
self.derives.push(Box::new(derive));
|
||||
self
|
||||
|
|
@ -213,24 +174,19 @@ impl AstCodegen {
|
|||
}
|
||||
result
|
||||
})
|
||||
.map_ok(|output| output.0.into_iter().map(SideEffect::from))
|
||||
.flatten_ok();
|
||||
|
||||
let outputs = self
|
||||
.generators
|
||||
.into_iter()
|
||||
.map(|mut runner| {
|
||||
let name = runner.name();
|
||||
log!("Generate {name}... ");
|
||||
let result = runner.run(&ctx);
|
||||
if result.is_ok() {
|
||||
logln!("Done!");
|
||||
} else {
|
||||
logln!("Fail!");
|
||||
}
|
||||
result
|
||||
})
|
||||
.map_ok(SideEffect::from);
|
||||
let outputs = self.generators.into_iter().map(|mut runner| {
|
||||
let name = runner.name();
|
||||
log!("Generate {name}... ");
|
||||
let result = runner.run(&ctx);
|
||||
if result.is_ok() {
|
||||
logln!("Done!");
|
||||
} else {
|
||||
logln!("Fail!");
|
||||
}
|
||||
result
|
||||
});
|
||||
|
||||
let outputs = derives.chain(outputs).collect::<Result<Vec<_>>>()?;
|
||||
|
||||
|
|
@ -242,27 +198,3 @@ impl AstCodegen {
|
|||
pub trait CodegenBase {
|
||||
fn file_path() -> &'static str;
|
||||
}
|
||||
|
||||
/// Creates a generated file warning + required information for a generated file.
|
||||
pub fn generate_rust_header(file_path: &str) -> TokenStream {
|
||||
let file_path = file_path.replace('\\', "/");
|
||||
|
||||
// TODO: Add generation date, AST source hash, etc here.
|
||||
let edit_comment = format!("@ To edit this generated file you have to edit `{file_path}`");
|
||||
quote::quote! {
|
||||
//!@ Auto-generated code, DO NOT EDIT DIRECTLY!
|
||||
#![doc = #edit_comment]
|
||||
//!@@line_break
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a generated file warning + required information for a generated file.
|
||||
pub fn generate_javascript_header(file_path: &str) -> String {
|
||||
let file_path = file_path.replace('\\', "/");
|
||||
|
||||
// TODO: Add generation date, AST source hash, etc here.
|
||||
format!(
|
||||
"// Auto-generated code, DO NOT EDIT DIRECTLY!\n\
|
||||
// To edit this generated file you have to edit `{file_path}`\n\n"
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use convert_case::{Case, Casing};
|
||||
use itertools::Itertools;
|
||||
use proc_macro2::TokenStream;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
|
||||
use crate::{
|
||||
codegen::{generate_rust_header, CodegenBase, LateCtx},
|
||||
codegen::{CodegenBase, LateCtx},
|
||||
output::{output_path, Output, RawOutput},
|
||||
schema::TypeDef,
|
||||
Result,
|
||||
};
|
||||
|
|
@ -23,9 +22,6 @@ pub use content_hash::DeriveContentHash;
|
|||
pub use estree::DeriveESTree;
|
||||
pub use get_span::{DeriveGetSpan, DeriveGetSpanMut};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DeriveOutput(pub Vec<(PathBuf, TokenStream)>);
|
||||
|
||||
pub trait Derive: CodegenBase {
|
||||
// Methods defined by implementer
|
||||
|
||||
|
|
@ -44,7 +40,6 @@ pub trait Derive: CodegenBase {
|
|||
// Standard methods
|
||||
|
||||
fn template(module_paths: Vec<&str>, impls: TokenStream) -> TokenStream {
|
||||
let header = generate_rust_header(Self::file_path());
|
||||
let prelude = Self::prelude();
|
||||
|
||||
// from `x::y::z` to `crate::y::z::*`
|
||||
|
|
@ -63,8 +58,6 @@ pub trait Derive: CodegenBase {
|
|||
});
|
||||
|
||||
quote::quote! {
|
||||
#header
|
||||
|
||||
#prelude
|
||||
|
||||
#(#use_modules)*
|
||||
|
|
@ -74,7 +67,7 @@ pub trait Derive: CodegenBase {
|
|||
}
|
||||
}
|
||||
|
||||
fn output(&mut self, ctx: &LateCtx) -> Result<DeriveOutput> {
|
||||
fn output(&mut self, ctx: &LateCtx) -> Result<Vec<RawOutput>> {
|
||||
let trait_name = Self::trait_name();
|
||||
let filename = format!("derive_{}.rs", Self::snake_name());
|
||||
let output = ctx
|
||||
|
|
@ -102,12 +95,12 @@ pub trait Derive: CodegenBase {
|
|||
let mut modules = Vec::from_iter(modules);
|
||||
modules.sort_unstable();
|
||||
|
||||
acc.push((
|
||||
crate::output(
|
||||
let output = Output::Rust {
|
||||
path: output_path(
|
||||
format!("crates/{}", path.split("::").next().unwrap()).as_str(),
|
||||
&filename,
|
||||
),
|
||||
Self::template(
|
||||
tokens: Self::template(
|
||||
modules,
|
||||
streams.into_iter().fold(TokenStream::new(), |mut acc, it| {
|
||||
acc.extend(quote::quote! {
|
||||
|
|
@ -117,10 +110,12 @@ pub trait Derive: CodegenBase {
|
|||
acc
|
||||
}),
|
||||
),
|
||||
));
|
||||
};
|
||||
|
||||
acc.push(output.output(Self::file_path()));
|
||||
acc
|
||||
});
|
||||
Ok(DeriveOutput(output))
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +124,7 @@ macro_rules! define_derive {
|
|||
const _: () = {
|
||||
use $crate::{
|
||||
codegen::{CodegenBase, LateCtx, Runner},
|
||||
derives::DeriveOutput,
|
||||
output::RawOutput,
|
||||
Result,
|
||||
};
|
||||
|
||||
|
|
@ -141,13 +136,13 @@ macro_rules! define_derive {
|
|||
|
||||
impl $($lifetime)? Runner for $ident $($lifetime)? {
|
||||
type Context = LateCtx;
|
||||
type Output = DeriveOutput;
|
||||
type Output = Vec<RawOutput>;
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
stringify!($ident)
|
||||
}
|
||||
|
||||
fn run(&mut self, ctx: &LateCtx) -> Result<DeriveOutput> {
|
||||
fn run(&mut self, ctx: &LateCtx) -> Result<Vec<RawOutput>> {
|
||||
self.output(ctx)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ use syn::Type;
|
|||
use super::define_generator;
|
||||
use crate::{
|
||||
codegen::LateCtx,
|
||||
output,
|
||||
output::{output_path, Output},
|
||||
schema::{FieldDef, ToType, TypeDef},
|
||||
util::ToIdent,
|
||||
Generator, GeneratorOutput,
|
||||
Generator,
|
||||
};
|
||||
|
||||
pub struct AssertLayouts;
|
||||
|
|
@ -16,7 +16,7 @@ pub struct AssertLayouts;
|
|||
define_generator!(AssertLayouts);
|
||||
|
||||
impl Generator for AssertLayouts {
|
||||
fn generate(&mut self, ctx: &LateCtx) -> GeneratorOutput {
|
||||
fn generate(&mut self, ctx: &LateCtx) -> Output {
|
||||
let (assertions_64, assertions_32) = ctx
|
||||
.schema()
|
||||
.into_iter()
|
||||
|
|
@ -26,8 +26,8 @@ impl Generator for AssertLayouts {
|
|||
})
|
||||
.collect::<(Vec<TokenStream>, Vec<TokenStream>)>();
|
||||
|
||||
GeneratorOutput::Rust {
|
||||
path: output(crate::AST_CRATE, "assert_layouts.rs"),
|
||||
Output::Rust {
|
||||
path: output_path(crate::AST_CRATE, "assert_layouts.rs"),
|
||||
tokens: quote! {
|
||||
use std::mem::{align_of, offset_of, size_of};
|
||||
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ use syn::{parse_quote, Ident, Type};
|
|||
use super::define_generator;
|
||||
use crate::{
|
||||
codegen::LateCtx,
|
||||
output,
|
||||
output::{output_path, Output},
|
||||
schema::{
|
||||
EnumDef, FieldDef, GetIdent, InheritDef, StructDef, ToType, TypeDef, TypeName, VariantDef,
|
||||
},
|
||||
util::{TypeAnalysis, TypeWrapper},
|
||||
Generator, GeneratorOutput,
|
||||
Generator,
|
||||
};
|
||||
|
||||
pub struct AstBuilderGenerator;
|
||||
|
|
@ -24,7 +24,7 @@ pub struct AstBuilderGenerator;
|
|||
define_generator!(AstBuilderGenerator);
|
||||
|
||||
impl Generator for AstBuilderGenerator {
|
||||
fn generate(&mut self, ctx: &LateCtx) -> GeneratorOutput {
|
||||
fn generate(&mut self, ctx: &LateCtx) -> Output {
|
||||
let fns = ctx
|
||||
.schema()
|
||||
.into_iter()
|
||||
|
|
@ -32,8 +32,8 @@ impl Generator for AstBuilderGenerator {
|
|||
.map(|it| generate_builder_fn(it, ctx))
|
||||
.collect_vec();
|
||||
|
||||
GeneratorOutput::Rust {
|
||||
path: output(crate::AST_CRATE, "ast_builder.rs"),
|
||||
Output::Rust {
|
||||
path: output_path(crate::AST_CRATE, "ast_builder.rs"),
|
||||
tokens: quote! {
|
||||
//! AST node factories
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ use syn::{parse_quote, Arm, ImplItemFn, Variant};
|
|||
use super::define_generator;
|
||||
use crate::{
|
||||
codegen::LateCtx,
|
||||
output,
|
||||
output::{output_path, Output},
|
||||
schema::{GetIdent, ToType},
|
||||
Generator, GeneratorOutput,
|
||||
Generator,
|
||||
};
|
||||
|
||||
pub struct AstKindGenerator;
|
||||
|
|
@ -80,7 +80,7 @@ pub const BLACK_LIST: [&str; 61] = [
|
|||
];
|
||||
|
||||
impl Generator for AstKindGenerator {
|
||||
fn generate(&mut self, ctx: &LateCtx) -> GeneratorOutput {
|
||||
fn generate(&mut self, ctx: &LateCtx) -> Output {
|
||||
let have_kinds = ctx
|
||||
.schema()
|
||||
.into_iter()
|
||||
|
|
@ -125,8 +125,8 @@ impl Generator for AstKindGenerator {
|
|||
})
|
||||
.collect_vec();
|
||||
|
||||
GeneratorOutput::Rust {
|
||||
path: output(crate::AST_CRATE, "ast_kind.rs"),
|
||||
Output::Rust {
|
||||
path: output_path(crate::AST_CRATE, "ast_kind.rs"),
|
||||
tokens: quote! {
|
||||
#![allow(missing_docs)] ///@ FIXME (in ast_tools/src/generators/ast_kind.rs)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
|
||||
use crate::{
|
||||
codegen::{generate_javascript_header, generate_rust_header, CodegenBase, LateCtx},
|
||||
codegen::{CodegenBase, LateCtx},
|
||||
output::{Output, RawOutput},
|
||||
Result,
|
||||
};
|
||||
|
||||
|
|
@ -20,37 +16,16 @@ pub use ast_kind::AstKindGenerator;
|
|||
pub use typescript::TypescriptGenerator;
|
||||
pub use visit::{VisitGenerator, VisitMutGenerator};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GeneratorOutput {
|
||||
Rust { path: PathBuf, tokens: TokenStream },
|
||||
Javascript { path: PathBuf, code: String },
|
||||
}
|
||||
|
||||
pub trait Generator: CodegenBase {
|
||||
// Methods defined by implementer
|
||||
|
||||
fn generate(&mut self, ctx: &LateCtx) -> GeneratorOutput;
|
||||
fn generate(&mut self, ctx: &LateCtx) -> Output;
|
||||
|
||||
// Standard methods
|
||||
|
||||
fn output(&mut self, ctx: &LateCtx) -> Result<GeneratorOutput> {
|
||||
let mut output = self.generate(ctx);
|
||||
|
||||
match &mut output {
|
||||
GeneratorOutput::Rust { tokens, .. } => {
|
||||
let header = generate_rust_header(Self::file_path());
|
||||
*tokens = quote! {
|
||||
#header
|
||||
#tokens
|
||||
};
|
||||
}
|
||||
GeneratorOutput::Javascript { code: content, .. } => {
|
||||
let header = generate_javascript_header(Self::file_path());
|
||||
*content = format!("{header}{content}");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
fn output(&mut self, ctx: &LateCtx) -> Result<RawOutput> {
|
||||
let output = self.generate(ctx);
|
||||
Ok(output.output(Self::file_path()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +34,7 @@ macro_rules! define_generator {
|
|||
const _: () = {
|
||||
use $crate::{
|
||||
codegen::{CodegenBase, LateCtx, Runner},
|
||||
generators::GeneratorOutput,
|
||||
output::RawOutput,
|
||||
Result,
|
||||
};
|
||||
|
||||
|
|
@ -71,13 +46,13 @@ macro_rules! define_generator {
|
|||
|
||||
impl $($lifetime)? Runner for $ident $($lifetime)? {
|
||||
type Context = LateCtx;
|
||||
type Output = GeneratorOutput;
|
||||
type Output = RawOutput;
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
stringify!($ident)
|
||||
}
|
||||
|
||||
fn run(&mut self, ctx: &LateCtx) -> Result<GeneratorOutput> {
|
||||
fn run(&mut self, ctx: &LateCtx) -> Result<RawOutput> {
|
||||
self.output(ctx)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,15 @@
|
|||
use convert_case::{Case, Casing};
|
||||
use itertools::Itertools;
|
||||
use std::{
|
||||
io::Write,
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
|
||||
use super::define_generator;
|
||||
use crate::{
|
||||
codegen::LateCtx,
|
||||
output,
|
||||
output::{output_path, Output},
|
||||
schema::{
|
||||
serialize::{enum_variant_name, get_type_tag},
|
||||
EnumDef, GetIdent, StructDef, TypeDef, TypeName,
|
||||
},
|
||||
Generator, GeneratorOutput,
|
||||
Generator,
|
||||
};
|
||||
|
||||
const CUSTOM_TYPESCRIPT: &str = include_str!("../../../../crates/oxc_ast/src/ast/types.d.ts");
|
||||
|
|
@ -23,7 +19,7 @@ pub struct TypescriptGenerator;
|
|||
define_generator!(TypescriptGenerator);
|
||||
|
||||
impl Generator for TypescriptGenerator {
|
||||
fn generate(&mut self, ctx: &LateCtx) -> GeneratorOutput {
|
||||
fn generate(&mut self, ctx: &LateCtx) -> Output {
|
||||
let mut code = format!("{CUSTOM_TYPESCRIPT}\n");
|
||||
|
||||
for def in ctx.schema() {
|
||||
|
|
@ -40,10 +36,7 @@ impl Generator for TypescriptGenerator {
|
|||
code.push_str("\n\n");
|
||||
}
|
||||
|
||||
GeneratorOutput::Javascript {
|
||||
path: output(crate::TYPESCRIPT_PACKAGE, "types.d.ts"),
|
||||
code: format_typescript(&code),
|
||||
}
|
||||
Output::Javascript { path: output_path(crate::TYPESCRIPT_PACKAGE, "types.d.ts"), code }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -127,19 +120,3 @@ fn type_to_string(ty: &TypeName) -> String {
|
|||
TypeName::Opt(type_name) => format!("{} | null", type_to_string(type_name)),
|
||||
}
|
||||
}
|
||||
|
||||
fn format_typescript(source_text: &str) -> String {
|
||||
let mut dprint = Command::new("dprint")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.args(["fmt", "--stdin", "types.d.ts"])
|
||||
.spawn()
|
||||
.expect("Failed to run dprint (is it installed?)");
|
||||
|
||||
let stdin = dprint.stdin.as_mut().unwrap();
|
||||
stdin.write_all(source_text.as_bytes()).unwrap();
|
||||
stdin.flush().unwrap();
|
||||
|
||||
let output = dprint.wait_with_output().unwrap();
|
||||
String::from_utf8(output.stdout).unwrap()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ use crate::{
|
|||
codegen::LateCtx,
|
||||
generators::ast_kind::BLACK_LIST as KIND_BLACK_LIST,
|
||||
markers::VisitArg,
|
||||
output,
|
||||
output::{output_path, Output},
|
||||
schema::{EnumDef, GetIdent, StructDef, ToType, TypeDef},
|
||||
util::{StrExt, TokenStreamExt, TypeWrapper},
|
||||
Generator, GeneratorOutput,
|
||||
Generator,
|
||||
};
|
||||
|
||||
pub struct VisitGenerator;
|
||||
|
|
@ -23,9 +23,9 @@ pub struct VisitGenerator;
|
|||
define_generator!(VisitGenerator);
|
||||
|
||||
impl Generator for VisitGenerator {
|
||||
fn generate(&mut self, ctx: &LateCtx) -> GeneratorOutput {
|
||||
GeneratorOutput::Rust {
|
||||
path: output(crate::AST_CRATE, "visit.rs"),
|
||||
fn generate(&mut self, ctx: &LateCtx) -> Output {
|
||||
Output::Rust {
|
||||
path: output_path(crate::AST_CRATE, "visit.rs"),
|
||||
tokens: generate_visit::<false>(ctx),
|
||||
}
|
||||
}
|
||||
|
|
@ -36,9 +36,9 @@ pub struct VisitMutGenerator;
|
|||
define_generator!(VisitMutGenerator);
|
||||
|
||||
impl Generator for VisitMutGenerator {
|
||||
fn generate(&mut self, ctx: &LateCtx) -> GeneratorOutput {
|
||||
GeneratorOutput::Rust {
|
||||
path: output(crate::AST_CRATE, "visit_mut.rs"),
|
||||
fn generate(&mut self, ctx: &LateCtx) -> Output {
|
||||
Output::Rust {
|
||||
path: output_path(crate::AST_CRATE, "visit_mut.rs"),
|
||||
tokens: generate_visit::<true>(ctx),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ use syn::parse_file;
|
|||
|
||||
mod codegen;
|
||||
mod derives;
|
||||
mod fmt;
|
||||
mod generators;
|
||||
mod layout;
|
||||
mod markers;
|
||||
mod output;
|
||||
mod passes;
|
||||
mod rust_ast;
|
||||
mod schema;
|
||||
|
|
@ -22,11 +22,12 @@ use derives::{
|
|||
DeriveGetSpanMut,
|
||||
};
|
||||
use generators::{
|
||||
AssertLayouts, AstBuilderGenerator, AstKindGenerator, Generator, GeneratorOutput,
|
||||
TypescriptGenerator, VisitGenerator, VisitMutGenerator,
|
||||
AssertLayouts, AstBuilderGenerator, AstKindGenerator, Generator, TypescriptGenerator,
|
||||
VisitGenerator, VisitMutGenerator,
|
||||
};
|
||||
use output::write_all_to;
|
||||
use passes::{CalcLayout, Linker};
|
||||
use util::{write_all_to, NormalizeError};
|
||||
use util::NormalizeError;
|
||||
|
||||
static SOURCE_PATHS: &[&str] = &[
|
||||
"crates/oxc_ast/src/ast/literal.rs",
|
||||
|
|
@ -90,7 +91,7 @@ fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
|
|||
.map(|it| {
|
||||
let path = it.path();
|
||||
log!("Writing {path}...");
|
||||
it.apply().unwrap();
|
||||
it.write_to_file().unwrap();
|
||||
logln!(" Done!");
|
||||
path
|
||||
})
|
||||
|
|
@ -107,10 +108,6 @@ fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn output(krate: &str, path: &str) -> PathBuf {
|
||||
std::path::PathBuf::from_iter(vec![krate, "src", "generated", path])
|
||||
}
|
||||
|
||||
fn write_ci_filter(
|
||||
inputs: &[&str],
|
||||
side_effects: Vec<String>,
|
||||
|
|
|
|||
39
tasks/ast_tools/src/output/javascript.rs
Normal file
39
tasks/ast_tools/src/output/javascript.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
use std::{
|
||||
io::Write,
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
|
||||
/// Format Javascript/Typescript code, and add header.
|
||||
pub fn print_javascript(code: &str, generator_path: &str) -> String {
|
||||
let header = generate_header(generator_path);
|
||||
let code = format!("{header}{code}");
|
||||
format(&code)
|
||||
}
|
||||
|
||||
/// Creates a generated file warning + required information for a generated file.
|
||||
fn generate_header(generator_path: &str) -> String {
|
||||
let generator_path = generator_path.replace('\\', "/");
|
||||
|
||||
// TODO: Add generation date, AST source hash, etc here.
|
||||
format!(
|
||||
"// Auto-generated code, DO NOT EDIT DIRECTLY!\n\
|
||||
// To edit this generated file you have to edit `{generator_path}`\n\n"
|
||||
)
|
||||
}
|
||||
|
||||
/// Format JS/TS code with dprint.
|
||||
fn format(source_text: &str) -> String {
|
||||
let mut dprint = Command::new("dprint")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
.args(["fmt", "--stdin", "dummy.ts"])
|
||||
.spawn()
|
||||
.expect("Failed to run dprint (is it installed?)");
|
||||
|
||||
let stdin = dprint.stdin.as_mut().unwrap();
|
||||
stdin.write_all(source_text.as_bytes()).unwrap();
|
||||
stdin.flush().unwrap();
|
||||
|
||||
let output = dprint.wait_with_output().unwrap();
|
||||
String::from_utf8(output.stdout).unwrap()
|
||||
}
|
||||
72
tasks/ast_tools/src/output/mod.rs
Normal file
72
tasks/ast_tools/src/output/mod.rs
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
use std::{fs, io::Write, path::PathBuf};
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
mod javascript;
|
||||
mod rust;
|
||||
use javascript::print_javascript;
|
||||
use rust::print_rust;
|
||||
|
||||
/// An output from codegen.
|
||||
///
|
||||
/// Can be either Rust or Javascript.
|
||||
pub enum Output {
|
||||
Rust { path: PathBuf, tokens: TokenStream },
|
||||
Javascript { path: PathBuf, code: String },
|
||||
}
|
||||
|
||||
impl Output {
|
||||
pub fn output(self, generator_path: &str) -> RawOutput {
|
||||
let (path, code) = match self {
|
||||
Self::Rust { path, tokens } => {
|
||||
let code = print_rust(&tokens, generator_path);
|
||||
(path, code)
|
||||
}
|
||||
Self::Javascript { path, code } => {
|
||||
let code = print_javascript(&code, generator_path);
|
||||
(path, code)
|
||||
}
|
||||
};
|
||||
RawOutput { path, content: code.into_bytes() }
|
||||
}
|
||||
}
|
||||
|
||||
/// A raw output from codegen.
|
||||
#[derive(Debug)]
|
||||
pub struct RawOutput {
|
||||
pub path: PathBuf,
|
||||
pub content: Vec<u8>,
|
||||
}
|
||||
|
||||
impl RawOutput {
|
||||
/// Get path of output as a string.
|
||||
pub fn path(&self) -> String {
|
||||
let path = self.path.to_string_lossy();
|
||||
path.replace('\\', "/")
|
||||
}
|
||||
|
||||
/// Write output to file
|
||||
pub fn write_to_file(self) -> std::io::Result<()> {
|
||||
let Self { path, content } = self;
|
||||
let path = path.into_os_string();
|
||||
let path = path.to_str().unwrap();
|
||||
write_all_to(&content, path)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get path for an output.
|
||||
pub fn output_path(krate: &str, path: &str) -> PathBuf {
|
||||
std::path::PathBuf::from_iter(vec![krate, "src", "generated", path])
|
||||
}
|
||||
|
||||
/// Write data to file.
|
||||
pub fn write_all_to<S: AsRef<std::path::Path>>(data: &[u8], path: S) -> std::io::Result<()> {
|
||||
let path = path.as_ref();
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
let mut file = fs::File::create(path)?;
|
||||
file.write_all(data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -5,17 +5,37 @@ use std::{
|
|||
|
||||
use lazy_static::lazy_static;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use regex::{Captures, Regex, Replacer};
|
||||
use syn::parse_file;
|
||||
|
||||
/// Pretty print
|
||||
pub fn pretty_print(input: &TokenStream) -> String {
|
||||
let result = prettyplease::unparse(&parse_file(input.to_string().as_str()).unwrap());
|
||||
/// Format Rust code, and add header.
|
||||
pub fn print_rust(tokens: &TokenStream, generator_path: &str) -> String {
|
||||
let header = generate_header(generator_path);
|
||||
let tokens = quote! {
|
||||
#header
|
||||
#tokens
|
||||
};
|
||||
|
||||
let result = prettyplease::unparse(&parse_file(tokens.to_string().as_str()).unwrap());
|
||||
let result = COMMENT_REGEX.replace_all(&result, CommentReplacer).to_string();
|
||||
rust_fmt(&result)
|
||||
}
|
||||
|
||||
pub fn rust_fmt(source_text: &str) -> String {
|
||||
/// Creates a generated file warning + required information for a generated file.
|
||||
fn generate_header(generator_path: &str) -> TokenStream {
|
||||
let generator_path = generator_path.replace('\\', "/");
|
||||
|
||||
// TODO: Add generation date, AST source hash, etc here.
|
||||
let edit_comment = format!("@ To edit this generated file you have to edit `{generator_path}`");
|
||||
quote::quote! {
|
||||
//!@ Auto-generated code, DO NOT EDIT DIRECTLY!
|
||||
#![doc = #edit_comment]
|
||||
//!@@line_break
|
||||
}
|
||||
}
|
||||
|
||||
fn rust_fmt(source_text: &str) -> String {
|
||||
let mut rustfmt = Command::new("rustfmt")
|
||||
.stdin(Stdio::piped())
|
||||
.stdout(Stdio::piped())
|
||||
|
|
@ -309,17 +309,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn write_all_to<S: AsRef<std::path::Path>>(data: &[u8], path: S) -> std::io::Result<()> {
|
||||
use std::{fs, io::Write};
|
||||
let path = path.as_ref();
|
||||
if let Some(parent) = path.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
let mut file = fs::File::create(path)?;
|
||||
file.write_all(data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unexpanded_macro_err(mac: &ItemMacro) -> String {
|
||||
format!("Unexpanded macro: {:?}:{:?}", mac.ident, mac.span())
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue