From 7ffa516c033db5ef5f803b43daecc635a202ee98 Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Sun, 23 Nov 2025 14:22:33 -0600 Subject: [PATCH] Add fields and special children for Polymorphic enum loop specs. --- .../polymorphic_enum_loop_ast_node.rs | 15 +- .../polymorphic_enum_loop_build_fn.rs | 30 ++- ast-generator/src/build_fn/struct_build_fn.rs | 55 +++-- .../deserialize/polymorphic_enum_loop_spec.rs | 35 ++- ast-generator/src/deserialize/struct_spec.rs | 1 + ast-generator/src/pretty_print.rs | 24 +- ast-generator/src/spec/mod.rs | 73 ++++++- .../src/spec/polymorphic_enum_loop_spec.rs | 37 +++- ast-generator/src/spec/struct_spec.rs | 71 +----- .../type_gen/polymorphic_enum_loop_type.rs | 205 +++++++++++++----- ast-generator/src/type_gen/struct_type.rs | 4 +- examples/incompat.dm | 3 + src/ast/mod.rs | 46 ++-- src/bin/dmc/ir.rs | 118 +++++++++- src/parser/ast.yaml | 9 + src/type_analysis/mod.rs | 21 +- 16 files changed, 524 insertions(+), 223 deletions(-) create mode 100644 examples/incompat.dm diff --git a/ast-generator/src/ast_node/polymorphic_enum_loop_ast_node.rs b/ast-generator/src/ast_node/polymorphic_enum_loop_ast_node.rs index 8894012..c3e3300 100644 --- a/ast-generator/src/ast_node/polymorphic_enum_loop_ast_node.rs +++ b/ast-generator/src/ast_node/polymorphic_enum_loop_ast_node.rs @@ -21,16 +21,21 @@ pub fn make_polymorphic_enum_loop_ast_node_impl( .map(|child| { let child_ident = match child { PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => { - format_ident!("{}", use_current.name()) + Some(format_ident!("{}", use_current.name())) } PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => { - format_ident!("{}", on_each.name()) + Some(format_ident!("{}", on_each.name())) } + PolymorphicEnumLoopRuleBuildChild::Special(_) => None, }; - quote! { - children.push(self.#child_ident() as &dyn AstNode); - } + child_ident.map(|child_ident| { + quote! { + children.push(self.#child_ident() as &dyn AstNode); + } + }) }) + .filter(Option::is_some) + .map(Option::unwrap) .collect::>(); quote! { diff --git a/ast-generator/src/build_fn/polymorphic_enum_loop_build_fn.rs b/ast-generator/src/build_fn/polymorphic_enum_loop_build_fn.rs index 0e57697..2825bd4 100644 --- a/ast-generator/src/build_fn/polymorphic_enum_loop_build_fn.rs +++ b/ast-generator/src/build_fn/polymorphic_enum_loop_build_fn.rs @@ -4,7 +4,8 @@ use crate::spec::polymorphic_enum_loop_spec::{ PolymorphicEnumLoopRuleBuildChild, PolymorphicEnumLoopRuleChildOnEach, PolymorphicEnumLoopRulePassThrough, }; -use proc_macro2::TokenStream; +use crate::spec::SpecialChildKind; +use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; fn make_pass_through(pass_through: &PolymorphicEnumLoopRulePassThrough) -> TokenStream { @@ -54,6 +55,16 @@ fn make_build( quote! { Box::new(result.unwrap()) } } PolymorphicEnumLoopRuleBuildChild::OnEach(_) => quote! { on_each_child }, + PolymorphicEnumLoopRuleBuildChild::Special(special_child) => { + match special_child.kind() { + SpecialChildKind::FileId => { + quote! { file_id } + } + SpecialChildKind::Range => { + quote! { Range { start: as_span.start(), end: as_span.end() } } + } + } + } }) .collect::>(); @@ -76,11 +87,27 @@ fn make_match_arm( } } +fn make_preamble(spec: &PolymorphicEnumLoopBuildSpec, pair_ident: &Ident) -> TokenStream { + if spec.rules().any(|rule| match rule { + PolymorphicEnumLoopRule::Build(build) => build.children().any(|child| match child { + PolymorphicEnumLoopRuleBuildChild::Special(_) => true, + _ => false, + }), + _ => false, + }) { + quote! { let as_span = #pair_ident.as_span(); } + } else { + quote! {} + } +} + pub fn make_polymorphic_enum_loop_build_fn(spec: &PolymorphicEnumLoopBuildSpec) -> TokenStream { let build_fn_ident = format_ident!("{}", make_build_fn_name(spec.name())); let pair_ident = format_ident!("{}", make_build_pair(spec.name())); let return_type_ident = format_ident!("{}", spec.kind()); + let preamble = make_preamble(spec, &pair_ident); + let iter_expr = if spec.reverse() { quote! { #pair_ident.into_inner().rev() } } else { @@ -94,6 +121,7 @@ pub fn make_polymorphic_enum_loop_build_fn(spec: &PolymorphicEnumLoopBuildSpec) quote! { fn #build_fn_ident(file_id: usize, #pair_ident: Pair) -> #return_type_ident { + #preamble let mut result: Option<#return_type_ident> = None; for inner_pair in #iter_expr { match inner_pair.as_rule() { diff --git a/ast-generator/src/build_fn/struct_build_fn.rs b/ast-generator/src/build_fn/struct_build_fn.rs index 5c0b3f6..3c18c7d 100644 --- a/ast-generator/src/build_fn/struct_build_fn.rs +++ b/ast-generator/src/build_fn/struct_build_fn.rs @@ -1,5 +1,8 @@ use crate::deserialize::util::{make_build_fn_name, make_build_pair}; -use crate::spec::struct_spec::{MemberChildBuild, NodeMemberBuild, SpecialChild, SpecialChildKind, StructChild, StructSpec, VecChild, VecChildBuild}; +use crate::spec::struct_spec::{ + MemberChildBuild, NodeMemberBuild, StructChild, StructSpec, VecChild, VecChildBuild, +}; +use crate::spec::{SpecialChild, SpecialChildKind}; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; @@ -118,14 +121,14 @@ fn make_boolean_member_child_match_action(name: &str) -> TokenStream { } } -fn make_rule_matcher(child_spec: &StructChild) -> Option { +fn make_rule_matcher(child_spec: &StructChild) -> Option { match child_spec { StructChild::SkipChild(skip_child) => { let rule_ident = format_ident!("{}", skip_child.rule()); Some(quote! { Rule::#rule_ident => {} }) - }, + } StructChild::VecChild(vec_child) => { let rule_ident = format_ident!("{}", vec_child.rule()); let action = make_vec_child_match_action(vec_child); @@ -133,28 +136,24 @@ fn make_rule_matcher(child_spec: &StructChild) -> Option { Rule::#rule_ident => { #action } }) } - StructChild::MemberChild(member_child) => { - match member_child.build() { - MemberChildBuild::Node(node_member_build) => { - let rule_ident = format_ident!("{}", member_child.rule()); - let action = make_node_member_child_match_action( - member_child.name(), - node_member_build - ); - Some(quote! { - Rule::#rule_ident => { #action } - }) - } - MemberChildBuild::Boolean(_) => { - let rule_ident = format_ident!("{}", member_child.rule()); - let action = make_boolean_member_child_match_action(member_child.name()); - Some(quote! { - Rule::#rule_ident => { #action } - }) - } + StructChild::MemberChild(member_child) => match member_child.build() { + MemberChildBuild::Node(node_member_build) => { + let rule_ident = format_ident!("{}", member_child.rule()); + let action = + make_node_member_child_match_action(member_child.name(), node_member_build); + Some(quote! { + Rule::#rule_ident => { #action } + }) + } + MemberChildBuild::Boolean(_) => { + let rule_ident = format_ident!("{}", member_child.rule()); + let action = make_boolean_member_child_match_action(member_child.name()); + Some(quote! { + Rule::#rule_ident => { #action } + }) } }, - StructChild::Special(_) => None + StructChild::Special(_) => None, } } @@ -204,7 +203,7 @@ fn make_child_arg(child_spec: &StructChild, pair_ident: &Ident) -> Option { Some(make_boolean_member_child_arg(member_child.name())) - }, + } }, StructChild::Special(special_child) => Some(make_special_child_arg(special_child)), } @@ -230,9 +229,9 @@ pub fn make_struct_build_fn(build_spec: &StructSpec) -> TokenStream { let build_fn_ident = format_ident!("{}", make_build_fn_name(build_spec.build())); let pair_ident = format_ident!("{}", make_build_pair(build_spec.build())); let return_type_ident = format_ident!("{}", build_spec.build()); - + let preamble = make_preamble(build_spec, &pair_ident); - + let special_children = make_special_children(build_spec); let child_holders = build_spec @@ -261,9 +260,9 @@ pub fn make_struct_build_fn(build_spec: &StructSpec) -> TokenStream { quote! { fn #build_fn_ident(file_id: usize, #pair_ident: Pair) -> #return_type_ident { #preamble - + #(#special_children;)* - + #(#child_holders;)* #iter_stream diff --git a/ast-generator/src/deserialize/polymorphic_enum_loop_spec.rs b/ast-generator/src/deserialize/polymorphic_enum_loop_spec.rs index 5fbd2ea..8f62524 100644 --- a/ast-generator/src/deserialize/polymorphic_enum_loop_spec.rs +++ b/ast-generator/src/deserialize/polymorphic_enum_loop_spec.rs @@ -4,6 +4,7 @@ use crate::spec::polymorphic_enum_loop_spec::{ PolymorphicEnumLoopRuleBuild, PolymorphicEnumLoopRuleBuildChild, PolymorphicEnumLoopRuleChildOnEach, PolymorphicEnumLoopRulePassThrough, }; +use crate::spec::{SpecialChild, SpecialChildKind, StructField}; use yaml_rust2::Yaml; fn deserialize_build_child(child_name: &str, props: &Yaml) -> PolymorphicEnumLoopRuleBuildChild { @@ -17,6 +18,13 @@ fn deserialize_build_child(child_name: &str, props: &Yaml) -> PolymorphicEnumLoo PolymorphicEnumLoopRuleBuildChild::OnEach(PolymorphicEnumLoopRuleChildOnEach::new( child_name, rule, )) + } else if props["special"].is_hash() { + let kind = match props["special"]["kind"].as_str().unwrap() { + "range" => SpecialChildKind::Range, + "file_id" => SpecialChildKind::FileId, + _ => panic!("Unknown special child kind"), + }; + PolymorphicEnumLoopRuleBuildChild::Special(SpecialChild::new(child_name, kind)) } else { panic!("Expected 'use_current' or 'on_each' hash for polymorphic enum loop build child"); } @@ -35,15 +43,30 @@ fn deserialize_build(name: &str, props: &Yaml) -> PolymorphicEnumLoopRuleBuild { .map(Box::new) .collect(); - PolymorphicEnumLoopRuleBuild::new(name, variant, children) + let fields = props["fields"] + .as_vec() + .map(|fields| { + fields.iter() + .map(|field_yaml| { + let (field_name, field_props) = unwrap_single_member_hash(field_yaml); + let kind = field_props["kind"].as_str().unwrap(); + StructField::new( + &field_name, + kind, + None, + false + ) + }) + .collect::>() + }) + .unwrap_or_else(|| vec![]); + + PolymorphicEnumLoopRuleBuild::new(name, variant, children, fields) } fn deserialize_pass_through(name: &str, props: &Yaml) -> PolymorphicEnumLoopRulePassThrough { let kind = props["kind"].as_str().unwrap(); - let with = make_build_fn_name( - props["with"].as_str() - .unwrap_or(kind) - ); + let with = make_build_fn_name(props["with"].as_str().unwrap_or(kind)); PolymorphicEnumLoopRulePassThrough::new(name, kind, &with) } @@ -63,7 +86,7 @@ fn deserialize_rule(rule_name: &str, props: &Yaml) -> PolymorphicEnumLoopRule { pub fn deserialize_polymorphic_enum_loop(name: &str, props: &Yaml) -> PolymorphicEnumLoopBuildSpec { let kind = props["kind"].as_str().unwrap(); let reverse = get_as_bool(&props["reverse"]); - + let rules = props["rules"] .as_vec() .unwrap() diff --git a/ast-generator/src/deserialize/struct_spec.rs b/ast-generator/src/deserialize/struct_spec.rs index cb0685a..32a88ba 100644 --- a/ast-generator/src/deserialize/struct_spec.rs +++ b/ast-generator/src/deserialize/struct_spec.rs @@ -3,6 +3,7 @@ use convert_case::{Case, Casing}; use crate::deserialize::util::{get_as_bool, make_build_fn_name, unwrap_single_member_hash}; use crate::spec::struct_spec::*; +use crate::spec::{SpecialChild, SpecialChildKind, StructField, StructFieldWrap}; use yaml_rust2::Yaml; fn deserialize_field(field_yaml: &Yaml) -> StructField { diff --git a/ast-generator/src/pretty_print.rs b/ast-generator/src/pretty_print.rs index 06e5f06..8317e8b 100644 --- a/ast-generator/src/pretty_print.rs +++ b/ast-generator/src/pretty_print.rs @@ -1,5 +1,8 @@ use crate::spec::leaf_enum_spec::LeafEnumBuildSpec; use crate::spec::leaf_struct_spec::{LeafStructBuildSpec, LeafStructMemberKind}; +use crate::spec::polymorphic_enum_inner_build::{ + PolymorphicEnumInnerBuild, PolymorphicEnumInnerBuildMemberKind, +}; use crate::spec::polymorphic_enum_loop_spec::{ PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuildChild, }; @@ -10,7 +13,6 @@ use crate::spec::BuildSpec; use convert_case::{Case, Casing}; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use crate::spec::polymorphic_enum_inner_build::{PolymorphicEnumInnerBuild, PolymorphicEnumInnerBuildMember, PolymorphicEnumInnerBuildMemberKind}; fn make_result() -> TokenStream { quote! { std::fmt::Result } @@ -19,10 +21,11 @@ fn make_result() -> TokenStream { fn make_polymorphic_enum_inner_build_p2_impl(spec: &PolymorphicEnumInnerBuild) -> TokenStream { let type_ident = format_ident!("{}", spec.name()); let result = make_result(); - + let type_string = spec.name(); - - let child_matchers = spec.members() + + let child_matchers = spec + .members() .map(|member| { let variant_ident = format_ident!("{}", member.name()); match member.kind() { @@ -43,7 +46,7 @@ fn make_polymorphic_enum_inner_build_p2_impl(spec: &PolymorphicEnumInnerBuild) - } }) .collect::>(); - + quote! { impl PrettyPrint for #type_ident { fn pretty_print(&self, writer: &mut IndentWriter) -> #result { @@ -78,6 +81,10 @@ fn make_polymorphic_enum_loop_p2_impl(spec: &PolymorphicEnumLoopBuildSpec) -> To let child_print_statements = build .children() + .filter(|child| match child { + PolymorphicEnumLoopRuleBuildChild::Special(_) => false, + _ => true, + }) .map(|child| { let child_ident = match child { PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => { @@ -86,6 +93,7 @@ fn make_polymorphic_enum_loop_p2_impl(spec: &PolymorphicEnumLoopBuildSpec) -> To PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => { format_ident!("{}", on_each.name()) } + _ => unreachable!(), }; quote! { self.#child_ident().pretty_print(writer)?; @@ -330,9 +338,9 @@ pub fn make_pretty_print_impl(build_spec: &BuildSpec) -> Option { BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop) => { Some(make_polymorphic_enum_loop_p2_impl(polymorphic_enum_loop)) } - BuildSpec::PolymorphicEnumInnerBuild(polymorphic_enum_inner_build) => { - Some(make_polymorphic_enum_inner_build_p2_impl(polymorphic_enum_inner_build)) - } + BuildSpec::PolymorphicEnumInnerBuild(polymorphic_enum_inner_build) => Some( + make_polymorphic_enum_inner_build_p2_impl(polymorphic_enum_inner_build), + ), BuildSpec::PolymorphicLeafEnum(_) => None, BuildSpec::PolymorphicTreeEnum(_) => None, } diff --git a/ast-generator/src/spec/mod.rs b/ast-generator/src/spec/mod.rs index 83ea54b..3d8ace2 100644 --- a/ast-generator/src/spec/mod.rs +++ b/ast-generator/src/spec/mod.rs @@ -1,15 +1,15 @@ -use polymorphic_leaf_enum::PolymorphicLeafEnum; use leaf_enum_spec::LeafEnumBuildSpec; use leaf_struct_spec::LeafStructBuildSpec; use node_production_spec::NodeProductionBuildSpec; use polymorphic_enum_inner_build::PolymorphicEnumInnerBuild; use polymorphic_enum_loop_spec::PolymorphicEnumLoopBuildSpec; +use polymorphic_leaf_enum::PolymorphicLeafEnum; use polymorphic_pass_through_spec::PolymorphicPassThroughBuildSpec; +use polymorphic_tree_enum_spec::PolymorphicTreeEnumSpec; use polymorphic_type_spec::PolymorphicTypeBuildSpec; use production_spec::ProductionBuildSpec; use struct_spec::StructSpec; use tree_enum_spec::TreeEnumBuildSpec; -use polymorphic_tree_enum_spec::PolymorphicTreeEnumSpec; pub(crate) mod leaf_enum_spec; pub(crate) mod leaf_struct_spec; @@ -38,3 +38,72 @@ pub enum BuildSpec { PolymorphicLeafEnum(PolymorphicLeafEnum), PolymorphicTreeEnum(PolymorphicTreeEnumSpec), } + +#[derive(Debug)] +pub struct SpecialChild { + name: String, + kind: SpecialChildKind, +} + +impl SpecialChild { + pub fn new(name: &str, kind: SpecialChildKind) -> Self { + Self { + name: name.to_string(), + kind, + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn kind(&self) -> &SpecialChildKind { + &self.kind + } +} + +#[derive(Debug)] +pub enum SpecialChildKind { + FileId, + Range, +} + +#[derive(Debug)] +pub struct StructField { + name: String, + kind: String, + wrap: Option, + vec: bool, +} + +impl StructField { + pub fn new(name: &str, kind: &str, wrap: Option, vec: bool) -> Self { + Self { + name: name.to_string(), + kind: kind.to_string(), + wrap, + vec, + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn kind(&self) -> &str { + &self.kind + } + + pub fn wrap(&self) -> Option<&StructFieldWrap> { + self.wrap.as_ref() + } + + pub fn vec(&self) -> bool { + self.vec + } +} + +#[derive(Debug)] +pub enum StructFieldWrap { + RcRefCell, +} diff --git a/ast-generator/src/spec/polymorphic_enum_loop_spec.rs b/ast-generator/src/spec/polymorphic_enum_loop_spec.rs index 7f519ac..7b3256d 100644 --- a/ast-generator/src/spec/polymorphic_enum_loop_spec.rs +++ b/ast-generator/src/spec/polymorphic_enum_loop_spec.rs @@ -1,3 +1,5 @@ +use crate::spec::{SpecialChild, StructField}; + pub struct PolymorphicEnumLoopBuildSpec { name: String, kind: String, @@ -6,7 +8,12 @@ pub struct PolymorphicEnumLoopBuildSpec { } impl PolymorphicEnumLoopBuildSpec { - pub fn new(name: &str, kind: &str, reverse: bool, rules: Vec>) -> Self { + pub fn new( + name: &str, + kind: &str, + reverse: bool, + rules: Vec>, + ) -> Self { Self { name: name.to_string(), kind: kind.to_string(), @@ -22,7 +29,7 @@ impl PolymorphicEnumLoopBuildSpec { pub fn kind(&self) -> &str { &self.kind } - + pub fn reverse(&self) -> bool { self.reverse } @@ -51,15 +58,15 @@ impl PolymorphicEnumLoopRulePassThrough { with: with.to_string(), } } - + pub fn name(&self) -> &str { &self.name } - + pub fn kind(&self) -> &str { &self.kind } - + pub fn with(&self) -> &str { &self.with } @@ -69,17 +76,24 @@ pub struct PolymorphicEnumLoopRuleBuild { name: String, variant: String, children: Vec>, + fields: Vec, } impl PolymorphicEnumLoopRuleBuild { - pub fn new(name: &str, variant: &str, children: Vec>) -> Self { + pub fn new( + name: &str, + variant: &str, + children: Vec>, + fields: Vec, + ) -> Self { Self { name: name.to_string(), variant: variant.to_string(), children, + fields, } } - + pub fn name(&self) -> &str { &self.name } @@ -91,11 +105,16 @@ impl PolymorphicEnumLoopRuleBuild { pub fn children(&self) -> impl Iterator { self.children.iter().map(Box::as_ref) } + + pub fn fields(&self) -> &[StructField] { + &self.fields + } } pub enum PolymorphicEnumLoopRuleBuildChild { UseCurrent(PolymorphicEnumLoopChildUseCurrent), OnEach(PolymorphicEnumLoopRuleChildOnEach), + Special(SpecialChild), } pub struct PolymorphicEnumLoopChildUseCurrent { @@ -110,7 +129,7 @@ impl PolymorphicEnumLoopChildUseCurrent { kind: kind.to_string(), } } - + pub fn name(&self) -> &str { &self.name } @@ -136,7 +155,7 @@ impl PolymorphicEnumLoopRuleChildOnEach { pub fn name(&self) -> &str { &self.name } - + pub fn rule(&self) -> &str { &self.rule } diff --git a/ast-generator/src/spec/struct_spec.rs b/ast-generator/src/spec/struct_spec.rs index 7ec8949..8cb2f17 100644 --- a/ast-generator/src/spec/struct_spec.rs +++ b/ast-generator/src/spec/struct_spec.rs @@ -1,3 +1,5 @@ +use crate::spec::{SpecialChild, StructField}; + pub struct StructSpec { build: String, children: Vec>, @@ -246,72 +248,3 @@ impl BooleanMemberBuild { pub enum BooleanMemberBuildOn { RulePresent, } - -#[derive(Debug)] -pub struct SpecialChild { - name: String, - kind: SpecialChildKind, -} - -impl SpecialChild { - pub fn new(name: &str, kind: SpecialChildKind) -> Self { - Self { - name: name.to_string(), - kind, - } - } - - pub fn name(&self) -> &str { - &self.name - } - - pub fn kind(&self) -> &SpecialChildKind { - &self.kind - } -} - -#[derive(Debug)] -pub enum SpecialChildKind { - FileId, - Range, -} - -#[derive(Debug)] -pub struct StructField { - name: String, - kind: String, - wrap: Option, - vec: bool, -} - -impl StructField { - pub fn new(name: &str, kind: &str, wrap: Option, vec: bool) -> Self { - Self { - name: name.to_string(), - kind: kind.to_string(), - wrap, - vec, - } - } - - pub fn name(&self) -> &str { - &self.name - } - - pub fn kind(&self) -> &str { - &self.kind - } - - pub fn wrap(&self) -> Option<&StructFieldWrap> { - self.wrap.as_ref() - } - - pub fn vec(&self) -> bool { - self.vec - } -} - -#[derive(Debug)] -pub enum StructFieldWrap { - RcRefCell, -} diff --git a/ast-generator/src/type_gen/polymorphic_enum_loop_type.rs b/ast-generator/src/type_gen/polymorphic_enum_loop_type.rs index d8466a3..1eb2a65 100644 --- a/ast-generator/src/type_gen/polymorphic_enum_loop_type.rs +++ b/ast-generator/src/type_gen/polymorphic_enum_loop_type.rs @@ -1,96 +1,193 @@ -use crate::spec::polymorphic_enum_loop_spec::{PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuildChild}; +use crate::spec::polymorphic_enum_loop_spec::{ + PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuildChild, +}; +use crate::spec::SpecialChildKind; use proc_macro2::TokenStream; use quote::{format_ident, quote}; pub fn make_polymorphic_enum_loop_type(spec: &PolymorphicEnumLoopBuildSpec) -> TokenStream { let type_ident = format_ident!("{}", spec.name()); - - let build = spec.rules() - .find(|rule| { - match rule { - PolymorphicEnumLoopRule::Build(_) => true, - _ => false - } + + let build = spec + .rules() + .find(|rule| match rule { + PolymorphicEnumLoopRule::Build(_) => true, + _ => false, }) - .map(|rule| { - match rule { - PolymorphicEnumLoopRule::Build(build) => build, - _ => unreachable!() - } + .map(|rule| match rule { + PolymorphicEnumLoopRule::Build(build) => build, + _ => unreachable!(), }) .unwrap(); - - let annotated_members = build.children() + + let annotated_members = build + .children() + .map(|child| match child { + PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => { + let child_ident = format_ident!("{}", use_current.name()); + let child_type_ident = format_ident!("{}", use_current.kind()); + quote! { + #child_ident: Box<#child_type_ident> + } + } + PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => { + let child_ident = format_ident!("{}", on_each.name()); + let child_type_ident = format_ident!("{}", on_each.rule()); + quote! { + #child_ident: Box<#child_type_ident> + } + } + PolymorphicEnumLoopRuleBuildChild::Special(special_child) => { + let child_ident = format_ident!("{}", special_child.name()); + let child_type_ident = match special_child.kind() { + SpecialChildKind::FileId => { + quote! { usize } + } + SpecialChildKind::Range => { + quote! { Range } + } + }; + quote! { + #child_ident: #child_type_ident + } + } + }) + .collect::>(); + + let annotated_fields = build + .fields() + .iter() + .map(|field| { + let field_ident = format_ident!("{}", field.name()); + let field_type = format_ident!("{}", field.kind()); + quote! { + #field_ident: Option<#field_type> + } + }) + .collect::>(); + + let member_names = build + .children() + .map(|child| match child { + PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => { + format_ident!("{}", use_current.name()) + } + PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => { + format_ident!("{}", on_each.name()) + } + PolymorphicEnumLoopRuleBuildChild::Special(special_child) => { + format_ident!("{}", special_child.name()) + } + }) + .collect::>(); + + let field_initializers = build + .fields() + .iter() + .map(|field| { + let field_ident = format_ident!("{}", field.name()); + quote! { #field_ident: None } + }) + .collect::>(); + + let accessors = build + .children() .map(|child| { - match child { + let (child_ident, child_type_ident) = match child { PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => { let child_ident = format_ident!("{}", use_current.name()); let child_type_ident = format_ident!("{}", use_current.kind()); - quote! { - #child_ident: Box<#child_type_ident> - } + (child_ident, quote! { #child_type_ident }) } PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => { let child_ident = format_ident!("{}", on_each.name()); let child_type_ident = format_ident!("{}", on_each.rule()); - quote! { - #child_ident: Box<#child_type_ident> - } + (child_ident, quote! { #child_type_ident }) } - } - }) - .collect::>(); - - let member_names = build.children() - .map(|child| { - match child { - PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => { - format_ident!("{}", use_current.name()) - } - PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => { - format_ident!("{}", on_each.name()) - } - } - }) - .collect::>(); - - let accessors = build.children() - .map(|child| { - let (child_ident, child_type_ident) = match child { - PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => { - (format_ident!("{}", use_current.name()), format_ident!("{}", use_current.kind())) - } - PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => { - (format_ident!("{}", on_each.name()), format_ident!("{}", on_each.rule())) + PolymorphicEnumLoopRuleBuildChild::Special(special_child) => { + let child_ident = format_ident!("{}", special_child.name()); + let child_type_ident = match special_child.kind() { + SpecialChildKind::FileId => { + quote! { usize } + } + SpecialChildKind::Range => { + quote! { Range } + } + }; + (child_ident, child_type_ident) } }; let child_mut_ident = format_ident!("{}_mut", child_ident); - + + let as_ref = match child { + PolymorphicEnumLoopRuleBuildChild::Special(_) => { + quote! { &self.#child_ident } + } + _ => quote! { self.#child_ident.as_ref() }, + }; + + let as_mut = match child { + PolymorphicEnumLoopRuleBuildChild::Special(_) => { + quote! { &mut self.#child_ident } + } + _ => quote! { self.#child_ident.as_mut() }, + }; + quote! { pub fn #child_ident(&self) -> &#child_type_ident { - self.#child_ident.as_ref() + #as_ref } - + pub fn #child_mut_ident(&mut self) -> &mut #child_type_ident { - self.#child_ident.as_mut() + #as_mut } } }) .collect::>(); - + + let field_accessors = build + .fields() + .iter() + .map(|field| { + let field_ident = format_ident!("{}", field.name()); + let field_ident_mut = format_ident!("{}_mut", field.name()); + let field_type = format_ident!("{}", field.kind()); + + let set_ident = format_ident!("set_{}", field.name()); + + quote! { + pub fn #field_ident(&self) -> Option<&#field_type> { + self.#field_ident.as_ref() + } + + pub fn #field_ident_mut(&mut self) -> Option<&mut #field_type> { + self.#field_ident.as_mut() + } + + pub fn #set_ident(&mut self, #field_ident: #field_type) { + self.#field_ident = Some(#field_ident); + } + } + }) + .collect::>(); + quote! { pub struct #type_ident { - #(#annotated_members),* + #(#annotated_members,)* + #(#annotated_fields,)* } impl #type_ident { pub fn new(#(#annotated_members),*) -> Self { Self { - #(#member_names),* + #(#member_names,)* + #(#field_initializers,)* } } #(#accessors)* + + #(#field_accessors)* } } } diff --git a/ast-generator/src/type_gen/struct_type.rs b/ast-generator/src/type_gen/struct_type.rs index e015ba2..c082b45 100644 --- a/ast-generator/src/type_gen/struct_type.rs +++ b/ast-generator/src/type_gen/struct_type.rs @@ -1,7 +1,7 @@ use crate::spec::struct_spec::{ - MemberChild, MemberChildBuild, SpecialChild, SpecialChildKind, StructChild, StructField, - StructFieldWrap, StructSpec, VecChild, VecChildBuild, + MemberChild, MemberChildBuild, StructChild, StructSpec, VecChild, VecChildBuild, }; +use crate::spec::{SpecialChild, SpecialChildKind, StructField, StructFieldWrap}; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; diff --git a/examples/incompat.dm b/examples/incompat.dm new file mode 100644 index 0000000..0c9f88d --- /dev/null +++ b/examples/incompat.dm @@ -0,0 +1,3 @@ +fn main() + let x = 1 + 2L +end \ No newline at end of file diff --git a/src/ast/mod.rs b/src/ast/mod.rs index d61c0b3..518744c 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -30,14 +30,16 @@ pub mod node { } } } - + impl Expression { pub fn analyzed_kind(&self) -> Kind { match self { Expression::Ternary(ternary_expression) => { todo!() } - Expression::Or(or_expression) => {todo!()} + Expression::Or(or_expression) => { + todo!() + } Expression::And(and_expression) => { todo!() } @@ -47,9 +49,10 @@ pub mod node { Expression::Shift(shift_expression) => { todo!() } - Expression::Additive(additive_expression) => { - todo!() - } + Expression::Additive(additive_expression) => additive_expression + .analyzed_kind() + .expect("AdditiveExpression's analyzed_kind not set.") + .clone(), Expression::Multiplicative(multiplicative_expression) => { todo!() } @@ -59,12 +62,11 @@ pub mod node { Expression::Suffix(suffix_expression) => { todo!() } - Expression::Literal(literal) => { - literal.analyzed_kind() - } - Expression::Identifier(identifier) => { - identifier.analyzed_kind().expect("IdentifierExpression's analyzed_kind not set.").clone() - } + Expression::Literal(literal) => literal.analyzed_kind(), + Expression::Identifier(identifier) => identifier + .analyzed_kind() + .expect("IdentifierExpression's analyzed_kind not set.") + .clone(), Expression::Fqn(fqn) => { todo!() } @@ -77,31 +79,21 @@ pub mod node { } } } - + impl Literal { pub fn analyzed_kind(&self) -> Kind { match self { - Literal::IntLiteral(_) => { - Kind::Primitive(PrimitiveKind::Int.into()) - } - Literal::LongLiteral(_) => { - Kind::Primitive(PrimitiveKind::Long.into()) - } - Literal::DoubleLiteral(_) => { - Kind::Primitive(PrimitiveKind::Double.into()) - } - Literal::SingleQuoteString(_) => { - Kind::Primitive(PrimitiveKind::String.into()) - } + Literal::IntLiteral(_) => Kind::Primitive(PrimitiveKind::Int.into()), + Literal::LongLiteral(_) => Kind::Primitive(PrimitiveKind::Long.into()), + Literal::DoubleLiteral(_) => Kind::Primitive(PrimitiveKind::Double.into()), + Literal::SingleQuoteString(_) => Kind::Primitive(PrimitiveKind::String.into()), Literal::DString(_) => { todo!() } Literal::BacktickString(_) => { todo!() } - Literal::BooleanLiteral(_) => { - Kind::Primitive(PrimitiveKind::Boolean.into()) - } + Literal::BooleanLiteral(_) => Kind::Primitive(PrimitiveKind::Boolean.into()), } } } diff --git a/src/bin/dmc/ir.rs b/src/bin/dmc/ir.rs index 0ca18c7..424cc43 100644 --- a/src/bin/dmc/ir.rs +++ b/src/bin/dmc/ir.rs @@ -1,20 +1,128 @@ -use crate::name_analysis::name_analysis; +use codespan_reporting::files::SimpleFiles; +use codespan_reporting::term; +use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use deimos::asm::assemble_ir::assemble_ir_function; +use deimos::ast::build::build_ast; use deimos::ast::node::CompilationUnit; use deimos::ir::lower_ast::lower_compilation_unit; use deimos::ir::Ir; +use deimos::name_analysis::analyze_names; +use deimos::name_analysis::symbol_table::SymbolTable; +use deimos::parser::{DeimosParser, Rule}; +use deimos::std_core::add_std_core_symbols; use deimos::type_analysis::analyze_types; +use pest::Parser; +use std::collections::HashMap; +use std::fmt::{Debug, Display, Formatter}; use std::path::PathBuf; +struct ParseErrors { + errors: Vec>, +} + +impl Debug for ParseErrors { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + Display::fmt(self, f) + } +} + +impl Display for ParseErrors { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + writeln!(f, "There were errors during parsing.")?; + for parse_error in &self.errors { + writeln!(f, "{}", parse_error)?; + } + Ok(()) + } +} + +impl std::error::Error for ParseErrors {} + +#[derive(Debug)] +struct CompilationErrors { + count: usize, +} + +impl Display for CompilationErrors { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if self.count == 1 { + writeln!(f, "There was 1 error during compilation. See above.") + } else { + writeln!( + f, + "There were {} errors during compilation. See above.", + self.count + ) + } + } +} + +impl std::error::Error for CompilationErrors {} + pub fn compile_to_ir( paths: &[PathBuf], ) -> Result, Box> { - let mut compilation_units = name_analysis(&paths)?; + let mut paths_and_sources: HashMap = HashMap::new(); + for path in paths { + let src = std::fs::read_to_string(path).unwrap(); + paths_and_sources.insert(path.display().to_string(), src); + } + + let mut compilation_units = vec![]; + let mut files: SimpleFiles<&str, &str> = SimpleFiles::new(); + let mut parse_errors = vec![]; + + for (path, source) in &paths_and_sources { + let parse_result = DeimosParser::parse(Rule::CompilationUnit, source); + match parse_result { + Ok(mut pairs) => { + let file_id = files.add(path, source); + let compilation_unit = build_ast(file_id, &mut pairs); + compilation_units.push(compilation_unit); + } + Err(error) => { + parse_errors.push(error); + } + } + } + + if !parse_errors.is_empty() { + return Err(Box::new(ParseErrors { + errors: parse_errors, + })); + } + + let mut symbol_table = SymbolTable::new(); + add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols."); + + let name_diagnostics = analyze_names(&mut compilation_units, &files, &mut symbol_table); + + if name_diagnostics.is_empty() { + println!("Name analysis complete."); + } else { + let writer = StandardStream::stderr(ColorChoice::Always); + let config = term::Config::default(); + for diagnostic in &name_diagnostics { + term::emit(&mut writer.lock(), &config, &files, diagnostic)?; + } + return Err(Box::new(CompilationErrors { + count: name_diagnostics.len(), + })); + } let type_diagnostics = analyze_types(&mut compilation_units); - - if !type_diagnostics.is_empty() { - eprintln!("There were type diagnostics") + + if type_diagnostics.is_empty() { + println!("Type analysis complete."); + } else { + let writer = StandardStream::stderr(ColorChoice::Always); + let config = term::Config::default(); + for diagnostic in &type_diagnostics { + term::emit(&mut writer.lock(), &config, &files, &diagnostic)?; + } + return Err(Box::new(CompilationErrors { + count: type_diagnostics.len(), + })); } for compilation_unit in &compilation_units { diff --git a/src/parser/ast.yaml b/src/parser/ast.yaml index 59a1746..96ecc69 100644 --- a/src/parser/ast.yaml +++ b/src/parser/ast.yaml @@ -1057,6 +1057,15 @@ AdditiveExpression: - rhs: on_each: rule: AdditiveRhs + - file_id: + special: + kind: file_id + - range: + special: + kind: range + fields: + - analyzed_kind: + kind: Kind AdditiveRhs: struct: children: diff --git a/src/type_analysis/mod.rs b/src/type_analysis/mod.rs index f109dcc..e1c3a8a 100644 --- a/src/type_analysis/mod.rs +++ b/src/type_analysis/mod.rs @@ -10,6 +10,7 @@ use crate::name_analysis::symbol::ExpressibleSymbol; use crate::type_analysis::kinds::class_kind::ClassKind; use crate::type_analysis::kinds::function_kind::FunctionKind; use crate::type_analysis::kinds::Kind; +use codespan_reporting::diagnostic::Label; use codespan_reporting::files::Files; pub fn analyze_types(compilation_units: &mut [CompilationUnit]) -> Vec { @@ -193,14 +194,20 @@ fn ta_additive_expression( let right_kind = additive_expression.rhs().expression().analyzed_kind(); if left_kind != right_kind { - diagnostics.push(DmDiagnostic::error().with_message(&format!( - "Incompatible types for additive expression: {} vs. {}", - left_kind, right_kind - ))); - todo!("Error file_id and range; set Error type on additive expression") - } else { - todo!("set analyzed type for additive expression") + diagnostics.push( + DmDiagnostic::error() + .with_message(&format!( + "Incompatible types for additive expression: {} vs. {}", + left_kind, right_kind + )) + .with_label(Label::primary( + *additive_expression.file_id(), + *additive_expression.range(), + ) + .with_message("Incompatible types here.")), + ); } + additive_expression.set_analyzed_kind(left_kind); } fn ta_identifier_expression(identifier_expression: &mut IdentifierExpression) {