From 11f97a2174fec64d568cfa460d1e37af15b7372c Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Thu, 18 Sep 2025 08:38:30 -0500 Subject: [PATCH] Auto gen pretty_print impls. --- ast-generator/src/lib.rs | 25 ++- ast-generator/src/pretty_print.rs | 256 ++++++++++++++++++++++++++++++ ast-generator/src/spec.rs | 19 +++ ast-generator/src/type_gen.rs | 29 +++- examples/forty_two.dm | 3 + src/ast/mod.rs | 17 ++ src/bin/dmc/main.rs | 99 ++++++------ src/bin/dmc/{p3.rs.bak => p3.rs} | 3 +- 8 files changed, 394 insertions(+), 57 deletions(-) create mode 100644 ast-generator/src/pretty_print.rs create mode 100644 examples/forty_two.dm rename src/bin/dmc/{p3.rs.bak => p3.rs} (82%) diff --git a/ast-generator/src/lib.rs b/ast-generator/src/lib.rs index 60a496b..47825a3 100644 --- a/ast-generator/src/lib.rs +++ b/ast-generator/src/lib.rs @@ -5,6 +5,7 @@ mod leaf_struct_build_fn; mod polymorphic_build_build_fn; mod polymorphic_build_fn; mod polymorphic_enum_build_fn; +mod pretty_print; mod production_build_fn; mod spec; mod struct_build_fn; @@ -17,6 +18,7 @@ use crate::leaf_struct_build_fn::make_leaf_struct_build_fn; use crate::polymorphic_build_build_fn::make_polymorphic_build_build_fn; use crate::polymorphic_build_fn::make_polymorphic_build_fn; use crate::polymorphic_enum_build_fn::make_polymorphic_enum_build_fn; +use crate::pretty_print::make_pretty_print_impl; use crate::production_build_fn::make_production_build_fn; use crate::struct_build_fn::make_struct_build_fn; use crate::type_gen::make_type; @@ -133,7 +135,7 @@ fn generate_build_file(build_specs: &[BuildSpec]) -> AstGeneratedFile { use pest::iterators::Pair; //noinspection RsUnusedImport use crate::ast::node::*; - + #(#build_fns)* }; AstGeneratedFile { @@ -157,9 +159,30 @@ fn generate_node_file(build_specs: &[BuildSpec]) -> AstGeneratedFile { } } +fn generate_pretty_print_file(build_specs: &[BuildSpec]) -> AstGeneratedFile { + let impls = build_specs + .iter() + .map(|build_spec| { + let stream = make_pretty_print_impl(build_spec); + debug_built_spec(build_spec, &stream); + stream + }) + .collect::>(); + + let combined = quote! { + use crate::ast::node::*; + #(#impls)* + }; + AstGeneratedFile { + name: String::from("pretty_print.rs"), + contents: token_stream_to_string(combined), + } +} + pub fn generate_files(build_specs: &[BuildSpec]) -> Vec { vec![ generate_build_file(build_specs), generate_node_file(build_specs), + generate_pretty_print_file(build_specs), ] } diff --git a/ast-generator/src/pretty_print.rs b/ast-generator/src/pretty_print.rs new file mode 100644 index 0000000..77f744b --- /dev/null +++ b/ast-generator/src/pretty_print.rs @@ -0,0 +1,256 @@ +use crate::spec::{ + AlternativeChild, BuildSpec, EnumBuildSpec, EnumRuleChildKind, LeafEnumBuildSpec, + LeafStructBuildSpec, LeafStructMemberKind, MemberChildToBuild, PolymorphicBuildBuildSpec, + PolymorphicEnumBuildSpec, PolymorphicTypeBuildSpec, ProductionBuildSpec, StructBuildSpec, + StructChildSpec, VecChildToBuild, +}; +use convert_case::{Case, Casing}; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; + +fn make_production_p2_impl(_spec: &ProductionBuildSpec) -> TokenStream { + quote! {} +} + +fn make_polymorphic_enum_p2_impl(_spec: &PolymorphicEnumBuildSpec) -> TokenStream { + quote! {} +} + +fn make_polymorphic_build_p2_impl(spec: &PolymorphicBuildBuildSpec) -> TokenStream { + let (_, build) = spec.primary_alternative(); + let type_ident = format_ident!("{}", spec.name()); + let name_str = spec.name(); + + let child_statements = build + .children() + .map(|child| match child { + AlternativeChild::Skip => None, + AlternativeChild::Build(build) => { + let child_ident = format_ident!("{}", build.name()); + Some(quote! { + self.#child_ident().pretty_print(writer)? + }) + } + }) + .filter(Option::is_some) + .map(Option::unwrap) + .collect::>(); + + quote! { + impl PrettyPrint for #type_ident { + fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + writer.writeln_indented(#name_str); + writer.increase_indent(); + #(#child_statements;)* + writer.decrease_indent(); + Ok(()) + } + } + } +} + +fn make_polymorphic_type_p2_impl(spec: &PolymorphicTypeBuildSpec) -> TokenStream { + let type_ident = format_ident!("{}", spec.name()); + let child_matchers = spec + .enum_members() + .map(|member| { + let enum_member_ident = format_ident!("{}", member.name()); + let inner_name = format_ident!("{}", member.inner_kind().to_case(Case::Snake)); + quote! { + #type_ident::#enum_member_ident(#inner_name) => #inner_name.pretty_print(writer) + } + }) + .collect::>(); + + quote! { + impl PrettyPrint for #type_ident { + fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + match self { + #(#child_matchers,)* + } + } + } + } +} + +fn make_leaf_enum_p2_impl(spec: &LeafEnumBuildSpec) -> TokenStream { + let type_ident = format_ident!("{}", spec.build()); + let child_matchers = spec + .rules() + .map(|rule| { + let enum_variant_ident = format_ident!("{}", rule.rule()); + let name_str = rule.rule(); + quote! { + #type_ident::#enum_variant_ident => writer.writeln_indented(#name_str) + } + }) + .collect::>(); + + quote! { + impl PrettyPrint for #type_ident { + fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + match self { + #(#child_matchers,)* + } + } + } + } +} + +fn make_enum_p2_impl(spec: &EnumBuildSpec) -> TokenStream { + let type_ident = format_ident!("{}", spec.build()); + let type_str = spec.build(); + + let child_matchers = spec + .rules() + .map(|rule| { + let enum_variant_ident = format_ident!("{}", rule.rule()); + if let Some(child) = rule.child() { + match child.kind() { + EnumRuleChildKind::Node(node_child) => { + let child_name_ident = + format_ident!("{}", node_child.build().to_case(Case::Snake)); + Some(quote! { + #type_ident::#enum_variant_ident(#child_name_ident) => { + #child_name_ident.pretty_print(writer)?; + } + }) + } + _ => None, + } + } else { + let variant_str = rule.rule(); + Some(quote! { + #type_ident::#enum_variant_ident => writer.writeln_indented(#variant_str)? + }) + } + }) + .filter(Option::is_some) + .map(Option::unwrap) + .collect::>(); + + quote! { + impl PrettyPrint for #type_ident { + fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + writer.writeln_indented(#type_str)?; + writer.increase_indent(); + match self { + #(#child_matchers,)* + _ => {} + } + writer.decrease_indent(); + Ok(()) + } + } + } +} + +fn make_leaf_struct_p2_impl(leaf_struct_build_spec: &LeafStructBuildSpec) -> TokenStream { + let type_ident = format_ident!("{}", leaf_struct_build_spec.build()); + let member_formatters = leaf_struct_build_spec + .members() + .map(|member| match member.kind() { + LeafStructMemberKind::String => Some("{}"), + }) + .filter(Option::is_some) + .map(Option::unwrap) + .collect::>() + .join(", "); + + let format_string = format!("{}({})", leaf_struct_build_spec.build(), member_formatters); + + let members = leaf_struct_build_spec + .members() + .map(|member| { + let member_ident = format_ident!("{}", member.name()); + quote! { + self.#member_ident() + } + }) + .collect::>(); + + quote! { + impl PrettyPrint for #type_ident { + fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + writer.writeln_indented(&format!(#format_string, #(#members),*)) + } + } + } +} + +fn make_struct_p2_impl(struct_build_spec: &StructBuildSpec) -> TokenStream { + let child_print_statements = struct_build_spec + .children() + .map(|child| match child { + StructChildSpec::SkipChild(_) => None, + StructChildSpec::VecChild(vec_child) => match vec_child.build() { + VecChildToBuild::Node(_) => { + let child_ident = format_ident!("{}", vec_child.name()); + Some(quote! { + for child in self.#child_ident() { + child.pretty_print(writer)?; + } + }) + } + VecChildToBuild::String => None, + }, + StructChildSpec::MemberChild(member_child) => match member_child.build() { + MemberChildToBuild::Node(node_member_child) => { + let child_ident = format_ident!("{}", member_child.name()); + if node_member_child.optional() { + Some(quote! { + if let Some(child) = self.#child_ident() { + child.pretty_print(writer)?; + } + }) + } else { + Some(quote! { + self.#child_ident().pretty_print(writer)?; + }) + } + } + MemberChildToBuild::Boolean(boolean_member_child) => { + let format_string = format!("{}({})", boolean_member_child.name(), "{}"); + let child_ident = format_ident!("{}", boolean_member_child.name()); + Some(quote! { + writer.writeln_indented(&format!(#format_string, self.#child_ident()))?; + }) + } + }, + }) + .filter(Option::is_some) + .map(Option::unwrap) + .collect::>(); + + let type_ident = format_ident!("{}", struct_build_spec.build()); + let type_string = struct_build_spec.build(); + + quote! { + impl PrettyPrint for #type_ident { + fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + writer.writeln_indented(#type_string)?; + writer.increase_indent(); + #(#child_print_statements)* + writer.decrease_indent(); + Ok(()) + } + } + } +} + +pub fn make_pretty_print_impl(build_spec: &BuildSpec) -> TokenStream { + match build_spec { + BuildSpec::Struct(struct_spec) => make_struct_p2_impl(struct_spec), + BuildSpec::LeafStruct(leaf_struct) => make_leaf_struct_p2_impl(leaf_struct), + BuildSpec::Enum(enum_spec) => make_enum_p2_impl(enum_spec), + BuildSpec::LeafEnum(leaf_enum) => make_leaf_enum_p2_impl(leaf_enum), + BuildSpec::Polymorphic(polymorphic) => make_polymorphic_type_p2_impl(polymorphic), + BuildSpec::PolymorphicBuild(polymorphic_build) => { + make_polymorphic_build_p2_impl(polymorphic_build) + } + BuildSpec::PolymorphicEnum(polymorphic_enum) => { + make_polymorphic_enum_p2_impl(polymorphic_enum) + } + BuildSpec::Production(production) => make_production_p2_impl(production), + } +} diff --git a/ast-generator/src/spec.rs b/ast-generator/src/spec.rs index c5f2bb6..cd8e237 100644 --- a/ast-generator/src/spec.rs +++ b/ast-generator/src/spec.rs @@ -512,6 +512,25 @@ impl PolymorphicBuildBuildSpec { pub fn alternatives(&self) -> impl Iterator { self.alternatives.iter().map(Box::as_ref) } + + pub fn primary_alternative(&self) -> (&AlternativeTest, &AlternativeBuild) { + let primary_build_alternative = self + .alternatives + .iter() + .find(|alternative| match alternative.action { + AlternativeAction::Build(_) => true, + _ => false, + }) + .map(Box::as_ref) + .unwrap(); + let alternative_build = + if let AlternativeAction::Build(build) = &primary_build_alternative.action { + build + } else { + unreachable!(); + }; + (primary_build_alternative.test(), alternative_build) + } } pub struct PolymorphicBuildAlternative { diff --git a/ast-generator/src/type_gen.rs b/ast-generator/src/type_gen.rs index 8feedfc..ec454c9 100644 --- a/ast-generator/src/type_gen.rs +++ b/ast-generator/src/type_gen.rs @@ -44,7 +44,7 @@ fn make_polymorphic_build_type(build_spec: &PolymorphicBuildBuildSpec) -> TokenS let initializers = alternative_build .children() - .map(|build_child| match build_child { + .map(|child| match child { AlternativeChild::Skip => None, AlternativeChild::Build(build_child) => Some(format_ident!("{}", build_child.name())), }) @@ -52,6 +52,27 @@ fn make_polymorphic_build_type(build_spec: &PolymorphicBuildBuildSpec) -> TokenS .map(Option::unwrap) .collect::>(); + let accessors = alternative_build + .children() + .map(|child| match child { + AlternativeChild::Skip => None, + AlternativeChild::Build(build_child) => { + let child_ident = format_ident!("{}", build_child.name()); + let child_ident_mut = format_ident!("{}_mut", build_child.name()); + let child_type_ident = format_ident!("{}", build_child.kind()); + Some(quote! { + pub fn #child_ident(&self) -> &#child_type_ident { + &self.#child_ident + } + + pub fn #child_ident_mut(&mut self) -> &mut #child_type_ident { + &mut self.#child_ident + } + }) + } + }) + .collect::>(); + let type_ident = format_ident!("{}", build_spec.name()); quote! { @@ -65,11 +86,13 @@ fn make_polymorphic_build_type(build_spec: &PolymorphicBuildBuildSpec) -> TokenS #(#initializers),* } } + + #(#accessors)* } } } -fn make_polymorphic_type(build_spec: &PolymorphicTypeBuildSpec) -> TokenStream { +fn make_polymorphic_type_type(build_spec: &PolymorphicTypeBuildSpec) -> TokenStream { let members = build_spec .enum_members() .map(|enum_member| { @@ -431,7 +454,7 @@ pub fn make_type(build_spec: &BuildSpec) -> Option { } BuildSpec::Production(_) => None, BuildSpec::Polymorphic(polymorphic_build_spec) => { - Some(make_polymorphic_type(polymorphic_build_spec)) + Some(make_polymorphic_type_type(polymorphic_build_spec)) } BuildSpec::PolymorphicBuild(polymorphic_build_build_spec) => { Some(make_polymorphic_build_type(polymorphic_build_build_spec)) diff --git a/examples/forty_two.dm b/examples/forty_two.dm new file mode 100644 index 0000000..8fdb45b --- /dev/null +++ b/examples/forty_two.dm @@ -0,0 +1,3 @@ +fn main() + println 42 +end \ No newline at end of file diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 6508bea..2d77399 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -45,7 +45,14 @@ pub mod node { } pub mod build { + use pest::iterators::Pairs; + include!(concat!(env!("OUT_DIR"), "/src/ast/build.rs")); + + pub fn build_ast(parsed_pairs: &mut Pairs) -> Box { + let compilation_unit_pair = parsed_pairs.next().unwrap(); + Box::new(build_compilation_unit(compilation_unit_pair)) + } #[cfg(test)] mod build_tests { @@ -124,3 +131,13 @@ pub mod build { } } } + +pub mod pretty_print { + use crate::util::indent_writer::IndentWriter; + + pub trait PrettyPrint { + fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()>; + } + + include!(concat!(env!("OUT_DIR"), "/src/ast/pretty_print.rs")); +} diff --git a/src/bin/dmc/main.rs b/src/bin/dmc/main.rs index 380ae2e..4dda545 100644 --- a/src/bin/dmc/main.rs +++ b/src/bin/dmc/main.rs @@ -1,58 +1,55 @@ // mod name_analysis; -// mod p3; +mod p3; // mod unparse; -// -// use std::path::PathBuf; -// + +use std::path::PathBuf; + // use crate::name_analysis::name_analysis; -// use crate::p3::pretty_print_parse; +use crate::p3::pretty_print_parse; // use crate::unparse::unparse; -// use clap::{Parser, Subcommand}; -// -// #[derive(Debug, Parser)] -// #[command(name = "dmc")] -// #[command(about = "Deimos Compiler", long_about = None)] -// struct Cli { -// #[command(subcommand)] -// command: Commands, -// } -// -// #[derive(Debug, Subcommand)] -// enum Commands { -// #[command(arg_required_else_help = true)] -// Unparse { -// paths: Vec, -// }, -// P3 { -// paths: Vec, -// }, -// NameAnalysis { -// paths: Vec, -// }, -// } -// -// fn main() { -// let args = Cli::parse(); -// match args.command { -// Commands::Unparse { paths } => { -// for path in paths { -// unparse(&path); -// } -// } -// Commands::P3 { paths } => { -// for path in paths { -// pretty_print_parse(&path) -// } -// } -// Commands::NameAnalysis { paths } => { -// let result = name_analysis(&paths); -// if let Err(e) = result { -// eprintln!("{}", e) -// } -// } -// } -// } +use clap::{Parser, Subcommand}; + +#[derive(Debug, Parser)] +#[command(name = "dmc")] +#[command(about = "Deimos Compiler", long_about = None)] +struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Debug, Subcommand)] +enum Commands { + #[command(arg_required_else_help = true)] + Unparse { + paths: Vec, + }, + P3 { + paths: Vec, + }, + NameAnalysis { + paths: Vec, + }, +} fn main() { - panic!("TODO") + let args = Cli::parse(); + match args.command { + // Commands::Unparse { paths } => { + // for path in paths { + // unparse(&path); + // } + // } + Commands::P3 { paths } => { + for path in paths { + pretty_print_parse(&path) + } + } + // Commands::NameAnalysis { paths } => { + // let result = name_analysis(&paths); + // if let Err(e) = result { + // eprintln!("{}", e) + // } + // } + _ => todo!() + } } diff --git a/src/bin/dmc/p3.rs.bak b/src/bin/dmc/p3.rs similarity index 82% rename from src/bin/dmc/p3.rs.bak rename to src/bin/dmc/p3.rs index 1cd3777..e7e99ac 100644 --- a/src/bin/dmc/p3.rs.bak +++ b/src/bin/dmc/p3.rs @@ -10,8 +10,7 @@ pub fn pretty_print_parse(path: &PathBuf) { let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src); match parse_result { Ok(mut pairs) => { - let compilation_unit_pair = pairs.next().unwrap(); - let compilation_unit = build_ast(&path.display().to_string(), 0, compilation_unit_pair); + let compilation_unit = build_ast(&mut pairs); let mut indent_writer = IndentWriter::new(0, " ", Box::new(std::io::stdout())); compilation_unit .pretty_print(&mut indent_writer)