Compare commits
	
		
			No commits in common. "ast-node-ref-mut" and "main" have entirely different histories.
		
	
	
		
			ast-node-r
			...
			main
		
	
		
							
								
								
									
										12
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										12
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -169,17 +169,6 @@ dependencies = [ | ||||
|  "typenum", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "cst-test-generator" | ||||
| version = "0.1.0" | ||||
| dependencies = [ | ||||
|  "convert_case", | ||||
|  "prettyplease", | ||||
|  "proc-macro2", | ||||
|  "quote", | ||||
|  "syn", | ||||
| ] | ||||
| 
 | ||||
| [[package]] | ||||
| name = "deimos" | ||||
| version = "0.1.0" | ||||
| @ -187,7 +176,6 @@ dependencies = [ | ||||
|  "ast-generator", | ||||
|  "clap", | ||||
|  "codespan-reporting", | ||||
|  "cst-test-generator", | ||||
|  "indoc", | ||||
|  "log", | ||||
|  "pest", | ||||
|  | ||||
| @ -21,8 +21,7 @@ indoc = "2.0.6" | ||||
| 
 | ||||
| [build-dependencies] | ||||
| ast-generator = { path = "ast-generator" } | ||||
| cst-test-generator = { path = "cst-test-generator" } | ||||
| 
 | ||||
| [workspace] | ||||
| resolver = "3" | ||||
| members = ["ast-generator", "cst-test-generator"] | ||||
| members = ["ast-generator"] | ||||
|  | ||||
| @ -1,71 +0,0 @@ | ||||
| use crate::spec::tree_enum_spec::{ | ||||
|     EnumRuleChildKind, TreeEnumBuildSpec, TreeEnumRule, | ||||
| }; | ||||
| use convert_case::{Case, Casing}; | ||||
| use proc_macro2::{Ident, TokenStream}; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| fn make_match_arm(rule: &TreeEnumRule, type_ident: &Ident, is_mut: bool) -> TokenStream { | ||||
|     let rule_ident = format_ident!("{}", rule.rule()); | ||||
|     match rule.child() { | ||||
|         Some(child) => match child.kind() { | ||||
|             EnumRuleChildKind::Node(node_child) => { | ||||
|                 let child_ident = format_ident!("{}", node_child.node_kind().to_case(Case::Snake)); | ||||
|                 let as_node_ref_ident = if is_mut { | ||||
|                     quote! { as_node_ref_mut } | ||||
|                 } else { | ||||
|                     quote! { as_node_ref } | ||||
|                 }; | ||||
|                 quote! { | ||||
|                     #type_ident::#rule_ident(#child_ident) => vec![ | ||||
|                         #child_ident.#as_node_ref_ident() | ||||
|                     ] | ||||
|                 } | ||||
|             } | ||||
|             _ => quote! { | ||||
|                 #type_ident::#rule_ident(_) => vec![] | ||||
|             }, | ||||
|         }, | ||||
|         None => { | ||||
|             quote! { | ||||
|                 #type_ident::#rule_ident => vec![] | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn make_enum_ast_node_impl(enum_spec: &TreeEnumBuildSpec) -> TokenStream { | ||||
|     let type_ident = format_ident!("{}", enum_spec.build()); | ||||
|     let match_arms = enum_spec | ||||
|         .rules() | ||||
|         .map(|rule| make_match_arm(rule, &type_ident, false)) | ||||
|         .collect::<Vec<_>>(); | ||||
|     let match_arms_mut = enum_spec | ||||
|         .rules() | ||||
|         .map(|rule| make_match_arm(rule, &type_ident, true)) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     quote! { | ||||
|         impl AstNode for #type_ident { | ||||
|             fn children(&self) -> Vec<AstNodeRef> { | ||||
|                 match self { | ||||
|                     #(#match_arms,)* | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             fn children_mut(&mut self) -> Vec<AstNodeRefMut> { | ||||
|                 match self { | ||||
|                     #(#match_arms_mut,)* | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             fn as_node_ref(&self) -> AstNodeRef { | ||||
|                 AstNodeRef::#type_ident(&self) | ||||
|             } | ||||
| 
 | ||||
|             fn as_node_ref_mut(&mut self) -> AstNodeRefMut { | ||||
|                 AstNodeRefMut::#type_ident(self) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,26 +0,0 @@ | ||||
| use crate::spec::leaf_enum_spec::LeafEnumBuildSpec; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| pub fn make_leaf_enum_ast_node_impl(spec: &LeafEnumBuildSpec) -> TokenStream { | ||||
|     let type_ident = format_ident!("{}", spec.build()); | ||||
|     quote! { | ||||
|         impl AstNode for #type_ident { | ||||
|             fn children(&self) -> Vec<AstNodeRef> { | ||||
|                 vec![] | ||||
|             } | ||||
| 
 | ||||
|             fn children_mut(&mut self) -> Vec<AstNodeRefMut> { | ||||
|                 vec![] | ||||
|             } | ||||
| 
 | ||||
|             fn as_node_ref(&self) -> AstNodeRef { | ||||
|                 AstNodeRef::#type_ident(&self) | ||||
|             } | ||||
| 
 | ||||
|             fn as_node_ref_mut(&mut self) -> AstNodeRefMut { | ||||
|                 AstNodeRefMut::#type_ident(self) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,26 +0,0 @@ | ||||
| use crate::spec::leaf_struct_spec::LeafStructBuildSpec; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| pub fn make_leaf_struct_ast_node_impl(spec: &LeafStructBuildSpec) -> TokenStream { | ||||
|     let type_ident = format_ident!("{}", spec.build()); | ||||
|     quote! { | ||||
|         impl AstNode for #type_ident { | ||||
|             fn children(&self) -> Vec<AstNodeRef> { | ||||
|                 vec![] | ||||
|             } | ||||
| 
 | ||||
|             fn children_mut(&mut self) -> Vec<AstNodeRefMut> { | ||||
|                 vec![] | ||||
|             } | ||||
| 
 | ||||
|             fn as_node_ref(&self) -> AstNodeRef { | ||||
|                 AstNodeRef::#type_ident(&self) | ||||
|             } | ||||
| 
 | ||||
|             fn as_node_ref_mut(&mut self) -> AstNodeRefMut { | ||||
|                 AstNodeRefMut::#type_ident(self) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,68 +0,0 @@ | ||||
| mod enum_ast_node; | ||||
| mod leaf_enum_ast_node; | ||||
| mod leaf_struct_ast_node; | ||||
| mod polymorphic_enum_loop_ast_node; | ||||
| mod polymorphic_type_ast_node; | ||||
| mod struct_ast_node; | ||||
| 
 | ||||
| use crate::ast_node::enum_ast_node::make_enum_ast_node_impl; | ||||
| use crate::ast_node::leaf_enum_ast_node::make_leaf_enum_ast_node_impl; | ||||
| use crate::ast_node::leaf_struct_ast_node::make_leaf_struct_ast_node_impl; | ||||
| use crate::ast_node::polymorphic_enum_loop_ast_node::make_polymorphic_enum_loop_ast_node_impl; | ||||
| use crate::ast_node::polymorphic_type_ast_node::make_polymorphic_type_ast_node_impl; | ||||
| use crate::ast_node::struct_ast_node::make_struct_ast_node_impl; | ||||
| use crate::spec::BuildSpec; | ||||
| use proc_macro2::{Ident, TokenStream}; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| pub fn make_ast_node_impl(build_spec: &BuildSpec) -> Option<TokenStream> { | ||||
|     match build_spec { | ||||
|         BuildSpec::Enum(enum_spec) => Some(make_enum_ast_node_impl(enum_spec)), | ||||
|         BuildSpec::LeafEnum(leaf_enum) => Some(make_leaf_enum_ast_node_impl(leaf_enum)), | ||||
|         BuildSpec::Struct(struct_spec) => Some(make_struct_ast_node_impl(struct_spec)), | ||||
|         BuildSpec::LeafStruct(leaf_struct) => Some(make_leaf_struct_ast_node_impl(leaf_struct)), | ||||
|         BuildSpec::Production(_) => None, | ||||
|         BuildSpec::NodeProduction(_) => None, | ||||
|         BuildSpec::PolymorphicType(polymorphic_type) => { | ||||
|             Some(make_polymorphic_type_ast_node_impl(polymorphic_type)) | ||||
|         } | ||||
|         BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop) => Some( | ||||
|             make_polymorphic_enum_loop_ast_node_impl(polymorphic_enum_loop), | ||||
|         ), | ||||
|         BuildSpec::PolymorphicPassThrough(_) => None, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_ast_enum_ident(build_spec: &BuildSpec) -> Option<Ident> { | ||||
|     match build_spec { | ||||
|         BuildSpec::Struct(struct_spec) => Some(format_ident!("{}", struct_spec.build())), | ||||
|         BuildSpec::LeafStruct(leaf_struct) => Some(format_ident!("{}", leaf_struct.build())), | ||||
|         BuildSpec::Enum(enum_spec) => Some(format_ident!("{}", enum_spec.build())), | ||||
|         BuildSpec::LeafEnum(leaf_enum) => Some(format_ident!("{}", leaf_enum.build())), | ||||
|         BuildSpec::PolymorphicType(polymorphic_type) => { | ||||
|             Some(format_ident!("{}", polymorphic_type.name())) | ||||
|         } | ||||
|         BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop) => { | ||||
|             Some(format_ident!("{}", polymorphic_enum_loop.name())) | ||||
|         } | ||||
|         BuildSpec::PolymorphicPassThrough(_) => None, | ||||
|         BuildSpec::Production(_) => None, | ||||
|         BuildSpec::NodeProduction(_) => None, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn make_ast_enum_member(build_spec: &BuildSpec) -> Option<TokenStream> { | ||||
|     make_ast_enum_ident(build_spec).map(|type_ident| { | ||||
|         quote! { | ||||
|             #type_ident(&'a #type_ident) | ||||
|         } | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| pub fn make_ast_enum_member_mut(build_spec: &BuildSpec) -> Option<TokenStream> { | ||||
|     make_ast_enum_ident(build_spec).map(|type_ident| { | ||||
|         quote! { | ||||
|             #type_ident(&'a mut #type_ident) | ||||
|         } | ||||
|     }) | ||||
| } | ||||
| @ -1,79 +0,0 @@ | ||||
| use crate::spec::polymorphic_enum_loop_spec::{ | ||||
|     PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuildChild, | ||||
| }; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| fn make_child_adder(child: &PolymorphicEnumLoopRuleBuildChild, is_mut: bool) -> TokenStream { | ||||
|     let child_ident = { | ||||
|         let base = match child { | ||||
|             PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => { | ||||
|                 format_ident!("{}", use_current.name()) | ||||
|             } | ||||
|             PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => { | ||||
|                 format_ident!("{}", on_each.name()) | ||||
|             } | ||||
|         }; | ||||
|         if is_mut { | ||||
|             format_ident!("{}_mut", base) | ||||
|         } else { | ||||
|             base | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     let as_node_ref_ident = if is_mut { | ||||
|         quote! { as_node_ref_mut } | ||||
|     } else { | ||||
|         quote! { as_node_ref } | ||||
|     }; | ||||
|     quote! { | ||||
|         children.push(self.#child_ident().#as_node_ref_ident()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn make_polymorphic_enum_loop_ast_node_impl( | ||||
|     spec: &PolymorphicEnumLoopBuildSpec, | ||||
| ) -> TokenStream { | ||||
|     let type_ident = format_ident!("{}", spec.name()); | ||||
|     let build_rule = spec | ||||
|         .rules() | ||||
|         .filter_map(|rule| match rule { | ||||
|             PolymorphicEnumLoopRule::PassThrough(_) => None, | ||||
|             PolymorphicEnumLoopRule::Build(build_rule) => Some(build_rule), | ||||
|         }) | ||||
|         .next() | ||||
|         .unwrap(); | ||||
|     let child_adders = build_rule | ||||
|         .children() | ||||
|         .map(|child| make_child_adder(child, false)) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let child_adders_mut = build_rule | ||||
|         .children() | ||||
|         .map(|child| make_child_adder(child, true)) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     quote! { | ||||
|         impl AstNode for #type_ident { | ||||
|             fn children(&self) -> Vec<AstNodeRef> { | ||||
|                 let mut children: Vec<AstNodeRef> = vec![]; | ||||
|                 #(#child_adders;)* | ||||
|                 children | ||||
|             } | ||||
| 
 | ||||
|             fn children_mut(&mut self) -> Vec<AstNodeRefMut> { | ||||
|                 let mut children: Vec<AstNodeRefMut> = vec![]; | ||||
|                 #(#child_adders_mut;)* | ||||
|                 children | ||||
|             } | ||||
| 
 | ||||
|             fn as_node_ref(&self) -> AstNodeRef { | ||||
|                 AstNodeRef::#type_ident(&self) | ||||
|             } | ||||
| 
 | ||||
|             fn as_node_ref_mut(&mut self) -> AstNodeRefMut { | ||||
|                 AstNodeRefMut::#type_ident(self) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,59 +0,0 @@ | ||||
| use crate::spec::polymorphic_type_spec::{PolymorphicTypeBuildSpec, PolymorphicTypeVariant}; | ||||
| use convert_case::{Case, Casing}; | ||||
| use proc_macro2::{Ident, TokenStream}; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| fn make_match_arm( | ||||
|     variant: &PolymorphicTypeVariant, | ||||
|     type_ident: &Ident, | ||||
|     is_mut: bool, | ||||
| ) -> TokenStream { | ||||
|     let variant_ident = format_ident!("{}", variant.name()); | ||||
|     let child_ident = format_ident!("{}", variant.inner_kind().to_case(Case::Snake)); | ||||
|     let as_node_ref_ident = if is_mut { | ||||
|         quote! { as_node_ref_mut } | ||||
|     } else { | ||||
|         quote! { as_node_ref } | ||||
|     }; | ||||
| 
 | ||||
|     quote! { | ||||
|         #type_ident::#variant_ident(#child_ident) => vec![#child_ident.#as_node_ref_ident()] | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn make_polymorphic_type_ast_node_impl(spec: &PolymorphicTypeBuildSpec) -> TokenStream { | ||||
|     let type_ident = format_ident!("{}", spec.name()); | ||||
|     let match_arms = spec | ||||
|         .variants() | ||||
|         .map(|variant| make_match_arm(variant, &type_ident, false)) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let match_arms_mut = spec | ||||
|         .variants() | ||||
|         .map(|variant| make_match_arm(variant, &type_ident, true)) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     quote! { | ||||
|         impl AstNode for #type_ident { | ||||
|             fn children(&self) -> Vec<AstNodeRef> { | ||||
|                 match self { | ||||
|                     #(#match_arms,)* | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             fn children_mut(&mut self) -> Vec<AstNodeRefMut> { | ||||
|                 match self { | ||||
|                     #(#match_arms_mut,)* | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             fn as_node_ref(&self) -> AstNodeRef { | ||||
|                 AstNodeRef::#type_ident(&self) | ||||
|             } | ||||
| 
 | ||||
|             fn as_node_ref_mut(&mut self) -> AstNodeRefMut { | ||||
|                 AstNodeRefMut::#type_ident(self) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,95 +0,0 @@ | ||||
| use crate::spec::struct_spec::{MemberChildBuild, StructChild, StructSpec, VecChildBuild}; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| fn make_child_adder(child: &StructChild, is_mut: bool) -> Option<TokenStream> { | ||||
|     let as_node_ref_ident = if is_mut { | ||||
|         format_ident!("as_node_ref_mut") | ||||
|     } else { | ||||
|         format_ident!("as_node_ref") | ||||
|     }; | ||||
| 
 | ||||
|     match child { | ||||
|         StructChild::SkipChild(_) => None, | ||||
|         StructChild::VecChild(vec_child) => match vec_child.build() { | ||||
|             VecChildBuild::String(_) => None, | ||||
|             VecChildBuild::Node(_) => { | ||||
|                 let child_ident = if is_mut { | ||||
|                     format_ident!("{}_mut", vec_child.name()) | ||||
|                 } else { | ||||
|                     format_ident!("{}", vec_child.name()) | ||||
|                 }; | ||||
| 
 | ||||
|                 let children_stream = quote! { | ||||
|                     for child in self.#child_ident().map(AstNode::#as_node_ref_ident) { | ||||
|                         children.push(child); | ||||
|                     } | ||||
|                 }; | ||||
|                 Some(children_stream) | ||||
|             } | ||||
|         }, | ||||
|         StructChild::MemberChild(member_child) => match member_child.build() { | ||||
|             MemberChildBuild::Node(_) => { | ||||
|                 let child_ident = if is_mut { | ||||
|                     format_ident!("{}_mut", member_child.name()) | ||||
|                 } else { | ||||
|                     format_ident!("{}", member_child.name()) | ||||
|                 }; | ||||
|                 if member_child.optional() { | ||||
|                     Some(quote! { | ||||
|                         if let Some(#child_ident) = self.#child_ident() { | ||||
|                             children.push(#child_ident.#as_node_ref_ident()); | ||||
|                         } | ||||
|                     }) | ||||
|                 } else { | ||||
|                     Some(quote! { | ||||
|                         children.push(self.#child_ident().#as_node_ref_ident()); | ||||
|                     }) | ||||
|                 } | ||||
|             } | ||||
|             MemberChildBuild::Boolean(_) => None, | ||||
|         }, | ||||
|         StructChild::Special(_) => None, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn make_struct_ast_node_impl(spec: &StructSpec) -> TokenStream { | ||||
|     let type_ident = format_ident!("{}", spec.build()); | ||||
|     let child_adders = spec | ||||
|         .children() | ||||
|         .map(|child| make_child_adder(child, false)) | ||||
|         .filter(Option::is_some) | ||||
|         .map(Option::unwrap) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let child_adders_mut = spec | ||||
|         .children() | ||||
|         .map(|child| make_child_adder(child, true)) | ||||
|         .filter(Option::is_some) | ||||
|         .map(Option::unwrap) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     quote! { | ||||
|         impl AstNode for #type_ident { | ||||
|             fn children(&self) -> Vec<AstNodeRef> { | ||||
|                 let mut children: Vec<AstNodeRef> = vec![]; | ||||
|                 #(#child_adders;)* | ||||
|                 children | ||||
|             } | ||||
| 
 | ||||
|             fn children_mut(&mut self) -> Vec<AstNodeRefMut> { | ||||
|                 let mut children: Vec<AstNodeRefMut> = vec![]; | ||||
|                 #(#child_adders_mut;)* | ||||
|                 children | ||||
|             } | ||||
| 
 | ||||
|             fn as_node_ref(&self) -> AstNodeRef { | ||||
|                 AstNodeRef::#type_ident(&self) | ||||
|             } | ||||
| 
 | ||||
|             fn as_node_ref_mut(&mut self) -> AstNodeRefMut { | ||||
|                 AstNodeRefMut::#type_ident(self) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,30 +0,0 @@ | ||||
| use crate::deserialize::util::{make_build_fn_name, make_build_pair}; | ||||
| use crate::spec::leaf_enum_spec::LeafEnumBuildSpec; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| pub fn make_leaf_enum_build_fn(leaf_enum_build_spec: &LeafEnumBuildSpec) -> TokenStream { | ||||
|     let build_fn_ident = format_ident!("{}", make_build_fn_name(leaf_enum_build_spec.build())); | ||||
|     let pair_ident = format_ident!("{}", make_build_pair(leaf_enum_build_spec.build())); | ||||
|     let return_type_ident = format_ident!("{}", leaf_enum_build_spec.build()); | ||||
| 
 | ||||
|     let rule_branches = leaf_enum_build_spec | ||||
|         .rules() | ||||
|         .map(|leaf_enum_rule| { | ||||
|             let rule_ident = format_ident!("{}", leaf_enum_rule); | ||||
|             quote! { | ||||
|                 Rule::#rule_ident => #return_type_ident::#rule_ident | ||||
|             } | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     quote! { | ||||
|         fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident { | ||||
|             let inner_pair = #pair_ident.into_inner().next().unwrap(); | ||||
|             match inner_pair.as_rule() { | ||||
|                 #(#rule_branches,)* | ||||
|                 _ => unreachable!() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,54 +0,0 @@ | ||||
| use crate::deserialize::util::{make_build_fn_name, make_build_pair}; | ||||
| use crate::spec::leaf_struct_spec::{LeafStructBuildSpec, LeafStructMemberKind}; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| pub fn make_leaf_struct_build_fn(build_spec: &LeafStructBuildSpec) -> 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 child_builders = build_spec | ||||
|         .members() | ||||
|         .map(|member| { | ||||
|             let child_ident = format_ident!("{}", member.name()); | ||||
|             match member.kind() { | ||||
|                 LeafStructMemberKind::String => { | ||||
|                     quote! { | ||||
|                         let #child_ident = #pair_ident.as_str() | ||||
|                     } | ||||
|                 } | ||||
|                 LeafStructMemberKind::FileId => { | ||||
|                     quote! { | ||||
|                         let #child_ident = file_id | ||||
|                     } | ||||
|                 } | ||||
|                 LeafStructMemberKind::Range => { | ||||
|                     quote! { | ||||
|                         let #child_ident = { | ||||
|                             let as_span = #pair_ident.as_span(); | ||||
|                             Range { | ||||
|                                 start: as_span.start(), | ||||
|                                 end: as_span.end() | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let child_args = build_spec | ||||
|         .members() | ||||
|         .map(|member| format_ident!("{}", member.name())) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     quote! { | ||||
|         fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident { | ||||
|             #(#child_builders;)* | ||||
|             #return_type_ident::new( | ||||
|                 #(#child_args,)* | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,43 +0,0 @@ | ||||
| use crate::build_fn::leaf_enum_build_fn::make_leaf_enum_build_fn; | ||||
| use crate::build_fn::leaf_struct_build_fn::make_leaf_struct_build_fn; | ||||
| use crate::build_fn::node_production_build_fn::make_node_production_build_fn; | ||||
| use crate::build_fn::polymorphic_enum_loop_build_fn::make_polymorphic_enum_loop_build_fn; | ||||
| use crate::build_fn::polymorphic_pass_through_build_fn::make_polymorphic_pass_through_build_fn; | ||||
| use crate::build_fn::polymorphic_type_build_fn::make_polymorphic_type_build_fn; | ||||
| use crate::build_fn::production_build_fn::make_production_build_fn; | ||||
| use crate::build_fn::struct_build_fn::make_struct_build_fn; | ||||
| use crate::build_fn::tree_enum_build_fn::make_enum_build_fn; | ||||
| use crate::spec::BuildSpec; | ||||
| use proc_macro2::TokenStream; | ||||
| 
 | ||||
| mod leaf_enum_build_fn; | ||||
| mod leaf_struct_build_fn; | ||||
| mod node_production_build_fn; | ||||
| mod polymorphic_enum_loop_build_fn; | ||||
| mod polymorphic_pass_through_build_fn; | ||||
| mod polymorphic_type_build_fn; | ||||
| mod production_build_fn; | ||||
| mod struct_build_fn; | ||||
| mod tree_enum_build_fn; | ||||
| 
 | ||||
| pub fn make_build_fn(build_spec: &BuildSpec) -> TokenStream { | ||||
|     match build_spec { | ||||
|         BuildSpec::Struct(struct_spec) => make_struct_build_fn(struct_spec), | ||||
|         BuildSpec::LeafStruct(leaf_struct_spec) => make_leaf_struct_build_fn(leaf_struct_spec), | ||||
|         BuildSpec::Enum(enum_spec) => make_enum_build_fn(enum_spec), | ||||
|         BuildSpec::LeafEnum(leaf_enum_spec) => make_leaf_enum_build_fn(leaf_enum_spec), | ||||
|         BuildSpec::Production(production_spec) => make_production_build_fn(production_spec), | ||||
|         BuildSpec::NodeProduction(node_production_spec) => { | ||||
|             make_node_production_build_fn(node_production_spec) | ||||
|         } | ||||
|         BuildSpec::PolymorphicType(polymorphic_type_spec) => { | ||||
|             make_polymorphic_type_build_fn(polymorphic_type_spec) | ||||
|         } | ||||
|         BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop_spec) => { | ||||
|             make_polymorphic_enum_loop_build_fn(polymorphic_enum_loop_spec) | ||||
|         } | ||||
|         BuildSpec::PolymorphicPassThrough(polymorphic_pass_through_spec) => { | ||||
|             make_polymorphic_pass_through_build_fn(polymorphic_pass_through_spec) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,18 +0,0 @@ | ||||
| use crate::deserialize::util::{make_build_fn_name, make_build_pair}; | ||||
| use crate::spec::node_production_spec::NodeProductionBuildSpec; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| pub fn make_node_production_build_fn(spec: &NodeProductionBuildSpec) -> 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 inner_build_fn_ident = format_ident!("{}", spec.with()); | ||||
|     
 | ||||
|     quote! { | ||||
|         fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident { | ||||
|             let inner_pair = #pair_ident.into_inner().next().unwrap(); | ||||
|             #inner_build_fn_ident(file_id, inner_pair) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,107 +0,0 @@ | ||||
| use crate::deserialize::util::{make_build_fn_name, make_build_pair}; | ||||
| use crate::spec::polymorphic_enum_loop_spec::{ | ||||
|     PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuild, | ||||
|     PolymorphicEnumLoopRuleBuildChild, PolymorphicEnumLoopRuleChildOnEach, | ||||
|     PolymorphicEnumLoopRulePassThrough, | ||||
| }; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| fn make_pass_through(pass_through: &PolymorphicEnumLoopRulePassThrough) -> TokenStream { | ||||
|     let rule_ident = format_ident!("{}", pass_through.name()); | ||||
|     let inner_build_fn_ident = format_ident!("{}", pass_through.with()); | ||||
|     quote! { | ||||
|         Rule::#rule_ident => { | ||||
|             result = Some(#inner_build_fn_ident(file_id, inner_pair)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_on_each_child_build(child: &PolymorphicEnumLoopRuleChildOnEach) -> TokenStream { | ||||
|     let child_build_fn_ident = format_ident!("{}", make_build_fn_name(child.rule())); | ||||
|     quote! { | ||||
|         Box::new(#child_build_fn_ident(file_id, inner_pair)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_build( | ||||
|     spec: &PolymorphicEnumLoopBuildSpec, | ||||
|     build: &PolymorphicEnumLoopRuleBuild, | ||||
| ) -> TokenStream { | ||||
|     let rule_ident = format_ident!("{}", build.name()); | ||||
|     let variant_type_ident = format_ident!("{}", spec.name()); | ||||
|     let return_type_ident = format_ident!("{}", spec.kind()); | ||||
|     let variant_ident = format_ident!("{}", build.variant()); | ||||
| 
 | ||||
|     let on_each_child = build | ||||
|         .children() | ||||
|         .find(|child| match child { | ||||
|             PolymorphicEnumLoopRuleBuildChild::OnEach(_) => true, | ||||
|             _ => false, | ||||
|         }) | ||||
|         .map(|child| match child { | ||||
|             PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => on_each, | ||||
|             _ => unreachable!(), | ||||
|         }) | ||||
|         .unwrap(); | ||||
| 
 | ||||
|     let build_on_each_child = make_on_each_child_build(on_each_child); | ||||
| 
 | ||||
|     let child_args = build | ||||
|         .children() | ||||
|         .map(|child| match child { | ||||
|             PolymorphicEnumLoopRuleBuildChild::UseCurrent(_) => { | ||||
|                 quote! { Box::new(result.unwrap()) } | ||||
|             } | ||||
|             PolymorphicEnumLoopRuleBuildChild::OnEach(_) => quote! { on_each_child }, | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     quote! { | ||||
|         Rule::#rule_ident => { | ||||
|             let on_each_child = #build_on_each_child; | ||||
|             let built = #variant_type_ident::new(#(#child_args),*); | ||||
|             result = Some(#return_type_ident::#variant_ident(built)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_match_arm( | ||||
|     spec: &PolymorphicEnumLoopBuildSpec, | ||||
|     rule: &PolymorphicEnumLoopRule, | ||||
| ) -> TokenStream { | ||||
|     match rule { | ||||
|         PolymorphicEnumLoopRule::PassThrough(pass_through) => make_pass_through(pass_through), | ||||
|         PolymorphicEnumLoopRule::Build(build) => make_build(spec, build), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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 iter_expr = if spec.reverse() { | ||||
|         quote! { #pair_ident.into_inner().rev() } | ||||
|     } else { | ||||
|         quote! { #pair_ident.into_inner() } | ||||
|     }; | ||||
| 
 | ||||
|     let match_arms = spec | ||||
|         .rules() | ||||
|         .map(|rule| make_match_arm(spec, rule)) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     quote! { | ||||
|         fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident { | ||||
|             let mut result: Option<#return_type_ident> = None; | ||||
|             for inner_pair in #iter_expr { | ||||
|                 match inner_pair.as_rule() { | ||||
|                     #(#match_arms,)* | ||||
|                     _ => unreachable!("Unexpected parse rule: {:?} (inner pair: {:#?}", inner_pair.as_rule(), inner_pair), | ||||
|                 } | ||||
|             } | ||||
|             result.unwrap() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,51 +0,0 @@ | ||||
| use crate::deserialize::util::{make_build_fn_name, make_build_pair}; | ||||
| use crate::spec::polymorphic_pass_through_spec::{ | ||||
|     PolymorphicPassThroughBuildSpec, PolymorphicPassThroughVariant, | ||||
| }; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| pub fn make_polymorphic_pass_through_build_fn( | ||||
|     spec: &PolymorphicPassThroughBuildSpec, | ||||
| ) -> 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.build_kind()); | ||||
| 
 | ||||
|     let match_arms = spec | ||||
|         .variants() | ||||
|         .map(|variant| match variant { | ||||
|             PolymorphicPassThroughVariant::Inner { name, kind } => { | ||||
|                 let rule_ident = format_ident!("{}", kind); | ||||
|                 let variant_ident = format_ident!("{}", name); | ||||
|                 let inner_build_fn_ident = format_ident!("{}", make_build_fn_name(kind)); | ||||
| 
 | ||||
|                 quote! { | ||||
|                     Rule::#rule_ident => { | ||||
|                         #return_type_ident::#variant_ident( | ||||
|                             #inner_build_fn_ident(file_id, inner_pair) | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             PolymorphicPassThroughVariant::PassThrough { name, kind: _kind } => { | ||||
|                 let rule_ident = format_ident!("{}", name); | ||||
|                 let inner_build_fn_ident = format_ident!("{}", make_build_fn_name(name)); | ||||
| 
 | ||||
|                 quote! { | ||||
|                     Rule::#rule_ident => #inner_build_fn_ident(file_id, inner_pair) | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     quote! { | ||||
|         fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident { | ||||
|             let inner_pair = #pair_ident.into_inner().next().unwrap(); | ||||
|             match inner_pair.as_rule() { | ||||
|                 #(#match_arms,)* | ||||
|                 _ => unreachable!(), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,18 +0,0 @@ | ||||
| use crate::deserialize::util::{make_build_fn_name, make_build_pair}; | ||||
| use crate::spec::polymorphic_type_spec::PolymorphicTypeBuildSpec; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| pub fn make_polymorphic_type_build_fn(build_spec: &PolymorphicTypeBuildSpec) -> TokenStream { | ||||
|     let build_fn_ident = format_ident!("{}", make_build_fn_name(build_spec.name())); | ||||
|     let pair_ident = format_ident!("{}", make_build_pair(&build_spec.name())); | ||||
|     let return_type_ident = format_ident!("{}", build_spec.name()); | ||||
|     let inner_build_fn_ident = format_ident!("{}", make_build_fn_name(build_spec.kind())); | ||||
| 
 | ||||
|     quote! { | ||||
|         fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident { | ||||
|             let inner_pair = #pair_ident.into_inner().next().unwrap(); | ||||
|             #inner_build_fn_ident(file_id, inner_pair) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,87 +0,0 @@ | ||||
| use crate::deserialize::util::{make_build_fn_name, make_build_pair}; | ||||
| use crate::spec::production_spec::{ProductionBuildSpec, ProductionKind, ProductionStringFrom}; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| pub fn make_production_build_fn(production_build_spec: &ProductionBuildSpec) -> TokenStream { | ||||
|     let build_fn_ident = format_ident!("{}", make_build_fn_name(production_build_spec.name())); | ||||
|     let return_type_ident = match production_build_spec.kind() { | ||||
|         ProductionKind::Int => format_ident!("i32"), | ||||
|         ProductionKind::Long => format_ident!("i64"), | ||||
|         ProductionKind::Double => format_ident!("f64"), | ||||
|         ProductionKind::String(_) => format_ident!("String"), | ||||
|         ProductionKind::Boolean(_) => format_ident!("bool"), | ||||
|     }; | ||||
|     let pair_ident = format_ident!("{}", make_build_pair(production_build_spec.name())); | ||||
| 
 | ||||
|     let pair_mapper = match production_build_spec.kind() { | ||||
|         ProductionKind::Int => quote! { | ||||
|             let number_base_pair = #pair_ident.into_inner() | ||||
|                 .next() | ||||
|                 .unwrap(); | ||||
|             let inner_number_base_pair = number_base_pair.into_inner() | ||||
|                 .next() | ||||
|                 .unwrap(); | ||||
|             match inner_number_base_pair.as_rule() { | ||||
|                 Rule::BinaryBase => { | ||||
|                     todo!() | ||||
|                 } | ||||
|                 Rule::HexadecimalBase => { | ||||
|                     todo!() | ||||
|                 } | ||||
|                 Rule::DecimalBase => { | ||||
|                     inner_number_base_pair.as_str().parse::<i32>().unwrap() | ||||
|                 } | ||||
|                 _ => panic!() | ||||
|             } | ||||
|         }, | ||||
|         ProductionKind::Long => quote! { | ||||
|             let number_base_pair = #pair_ident.into_inner() | ||||
|                 .next() | ||||
|                 .unwrap(); | ||||
|             let inner_number_base_pair = number_base_pair.into_inner() | ||||
|                 .next() | ||||
|                 .unwrap(); | ||||
|             match inner_number_base_pair.as_rule() { | ||||
|                 Rule::BinaryBase => { | ||||
|                     todo!() | ||||
|                 } | ||||
|                 Rule::HexadecimalBase => { | ||||
|                     todo!() | ||||
|                 } | ||||
|                 Rule::DecimalBase => { | ||||
|                     inner_number_base_pair.as_str().parse::<i64>().unwrap() | ||||
|                 } | ||||
|                 _ => panic!() | ||||
|             } | ||||
|         }, | ||||
|         ProductionKind::Double => quote! { | ||||
|             #pair_ident.as_str().parse::<f64>().unwrap() | ||||
|         }, | ||||
|         ProductionKind::String(from) => match from { | ||||
|             ProductionStringFrom::StringInner => { | ||||
|                 quote! { | ||||
|                     #pair_ident.into_inner() | ||||
|                         .next() | ||||
|                         .unwrap() | ||||
|                         .as_str() | ||||
|                         .to_string() | ||||
|                 } | ||||
|             } | ||||
|             ProductionStringFrom::WholePair => { | ||||
|                 quote! { | ||||
|                     #pair_ident.as_str().to_string() | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         ProductionKind::Boolean(_) => quote! { | ||||
|             #pair_ident.as_str().parse::<bool>().unwrap() | ||||
|         }, | ||||
|     }; | ||||
| 
 | ||||
|     quote! { | ||||
|         fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident { | ||||
|             #pair_mapper | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,273 +0,0 @@ | ||||
| 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 proc_macro2::{Ident, TokenStream}; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| fn make_preamble(spec: &StructSpec, pair_ident: &Ident) -> TokenStream { | ||||
|     if spec.children().any(StructChild::is_special) { | ||||
|         quote! { | ||||
|             let as_span = #pair_ident.as_span(); | ||||
|         } | ||||
|     } else { | ||||
|         quote! {} | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_special_children(spec: &StructSpec) -> Vec<TokenStream> { | ||||
|     spec.children() | ||||
|         .map(StructChild::unwrap_special) | ||||
|         .filter(Option::is_some) | ||||
|         .map(Option::unwrap) | ||||
|         .map(|special_child| { | ||||
|             let child_ident = format_ident!("{}", special_child.name()); | ||||
|             match special_child.kind() { | ||||
|                 SpecialChildKind::FileId => { | ||||
|                     quote! { | ||||
|                         let #child_ident = file_id | ||||
|                     } | ||||
|                 } | ||||
|                 SpecialChildKind::Range => { | ||||
|                     quote! { | ||||
|                         let #child_ident = Range { | ||||
|                             start: as_span.start(), | ||||
|                             end: as_span.end() | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         .collect::<Vec<_>>() | ||||
| } | ||||
| 
 | ||||
| fn make_vec_child_holder(vec_child: &VecChild) -> TokenStream { | ||||
|     let child_ident = format_ident!("{}", vec_child.name()); | ||||
|     match vec_child.build() { | ||||
|         VecChildBuild::String(_) => { | ||||
|             quote! { | ||||
|                 let mut #child_ident: Vec<String> = vec![] | ||||
|             } | ||||
|         } | ||||
|         VecChildBuild::Node(node_build) => { | ||||
|             let child_type_ident = format_ident!("{}", node_build.kind()); | ||||
|             quote! { | ||||
|                 let mut #child_ident: Vec<Box<#child_type_ident>> = vec![] | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_node_child_holder(name: &str, node_child: &NodeMemberBuild) -> TokenStream { | ||||
|     let child_ident = format_ident!("{}", name); | ||||
|     let child_type_ident = format_ident!("{}", node_child.kind()); | ||||
|     quote! { | ||||
|         let mut #child_ident: Option<Box<#child_type_ident>> = None | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_boolean_child_holder(name: &str) -> TokenStream { | ||||
|     let child_ident = format_ident!("{}", name); | ||||
|     quote! { | ||||
|         let mut #child_ident: bool = false | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_child_holder(child_spec: &StructChild) -> Option<TokenStream> { | ||||
|     match child_spec { | ||||
|         StructChild::SkipChild(_) => None, | ||||
|         StructChild::VecChild(vec_child) => Some(make_vec_child_holder(vec_child)), | ||||
|         StructChild::MemberChild(member_child) => match member_child.build() { | ||||
|             MemberChildBuild::Node(node_child) => { | ||||
|                 Some(make_node_child_holder(member_child.name(), node_child)) | ||||
|             } | ||||
|             MemberChildBuild::Boolean(_) => Some(make_boolean_child_holder(member_child.name())), | ||||
|         }, | ||||
|         StructChild::Special(_) => None, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_vec_child_match_action(vec_child: &VecChild) -> TokenStream { | ||||
|     let child_name_ident = format_ident!("{}", vec_child.name()); | ||||
|     match vec_child.build() { | ||||
|         VecChildBuild::String(string_build) => { | ||||
|             let build_fn_ident = format_ident!("{}", string_build.with()); | ||||
|             quote! { | ||||
|                 #child_name_ident.push(#build_fn_ident(file_id, inner_pair)) | ||||
|             } | ||||
|         } | ||||
|         VecChildBuild::Node(node_build) => { | ||||
|             let build_fn_ident = format_ident!("{}", node_build.with()); | ||||
|             quote! { | ||||
|                 #child_name_ident.push(Box::new(#build_fn_ident(file_id, inner_pair))) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_node_member_child_match_action(name: &str, node_child: &NodeMemberBuild) -> TokenStream { | ||||
|     let child_name_ident = format_ident!("{}", name); | ||||
|     let build_fn_ident = format_ident!("{}", node_child.with()); | ||||
|     quote! { | ||||
|         #child_name_ident = Some(Box::new(#build_fn_ident(file_id, inner_pair))) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_boolean_member_child_match_action(name: &str) -> TokenStream { | ||||
|     let child_name_ident = format_ident!("{}", name); | ||||
|     quote! { | ||||
|         #child_name_ident = true | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_rule_matcher(child_spec: &StructChild) -> Option<TokenStream> { | ||||
|     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); | ||||
|             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, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_vec_child_arg(vec_child: &VecChild) -> TokenStream { | ||||
|     let child_ident = format_ident!("{}", vec_child.name()); | ||||
|     quote! { #child_ident } | ||||
| } | ||||
| 
 | ||||
| fn make_node_member_child_arg( | ||||
|     name: &str, | ||||
|     optional: bool, | ||||
|     node_child: &NodeMemberBuild, | ||||
| ) -> TokenStream { | ||||
|     let child_ident = format_ident!("{}", name); | ||||
|     if optional { | ||||
|         quote! { #child_ident } | ||||
|     } else if let Some(or_else) = node_child.or_else() { | ||||
|         let child_type_ident = format_ident!("{}", node_child.kind()); | ||||
|         let or_else_ident = format_ident!("{}", or_else); | ||||
|         quote! { | ||||
|             #child_ident.unwrap_or_else(|| Box::new(#child_type_ident::#or_else_ident())) | ||||
|         } | ||||
|     } else { | ||||
|         quote! { #child_ident.unwrap() } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_boolean_member_child_arg(name: &str) -> TokenStream { | ||||
|     let child_ident = format_ident!("{}", name); | ||||
|     quote! { #child_ident } | ||||
| } | ||||
| 
 | ||||
| fn make_special_child_arg(special_child: &SpecialChild) -> TokenStream { | ||||
|     let child_ident = format_ident!("{}", special_child.name()); | ||||
|     quote! { #child_ident } | ||||
| } | ||||
| 
 | ||||
| fn make_child_arg(child_spec: &StructChild) -> Option<TokenStream> { | ||||
|     match child_spec { | ||||
|         StructChild::SkipChild(_) => None, | ||||
|         StructChild::VecChild(vec_child) => Some(make_vec_child_arg(vec_child)), | ||||
|         StructChild::MemberChild(member_child) => match member_child.build() { | ||||
|             MemberChildBuild::Node(node_member_build) => Some(make_node_member_child_arg( | ||||
|                 member_child.name(), | ||||
|                 member_child.optional(), | ||||
|                 node_member_build, | ||||
|             )), | ||||
|             MemberChildBuild::Boolean(_) => { | ||||
|                 Some(make_boolean_member_child_arg(member_child.name())) | ||||
|             } | ||||
|         }, | ||||
|         StructChild::Special(special_child) => Some(make_special_child_arg(special_child)), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_return_value_stream(build_spec: &StructSpec, pair_ident: &Ident) -> TokenStream { | ||||
|     let type_ident = format_ident!("{}", build_spec.build()); | ||||
|     let child_args = build_spec | ||||
|         .children() | ||||
|         .map(|child| make_child_arg(child)) | ||||
|         .filter(|child_arg| child_arg.is_some()) | ||||
|         .map(|child_arg| child_arg.unwrap()) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     quote! { | ||||
|         #type_ident::new( | ||||
|             #(#child_args,)* | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
|         .children() | ||||
|         .map(|child_spec| make_child_holder(child_spec)) | ||||
|         .filter(|child_holder| child_holder.is_some()) | ||||
|         .map(|child_holder| child_holder.unwrap()) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let rule_matchers = build_spec | ||||
|         .children() | ||||
|         .map(|child_spec| make_rule_matcher(child_spec)) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let iter_stream = quote! { | ||||
|         for inner_pair in #pair_ident.into_inner() { | ||||
|             match inner_pair.as_rule() { | ||||
|                 #(#rule_matchers)* | ||||
|                 _ => panic!("Unexpected rule: {:?}", inner_pair.as_rule()) | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     let new_stream = make_return_value_stream(build_spec, &pair_ident); | ||||
| 
 | ||||
|     quote! { | ||||
|         fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident { | ||||
|             #preamble | ||||
| 
 | ||||
|             #(#special_children;)* | ||||
| 
 | ||||
|             #(#child_holders;)* | ||||
| 
 | ||||
|             #iter_stream | ||||
| 
 | ||||
|             #new_stream | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,63 +0,0 @@ | ||||
| use crate::deserialize::util::{make_build_fn_name, make_build_pair}; | ||||
| use crate::spec::tree_enum_spec::{EnumRuleChildKind, TreeEnumBuildSpec}; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| pub fn make_enum_build_fn(enum_build_spec: &TreeEnumBuildSpec) -> TokenStream { | ||||
|     let build_fn_ident = format_ident!("{}", make_build_fn_name(enum_build_spec.build())); | ||||
|     let pair_ident = format_ident!("{}", make_build_pair(enum_build_spec.build())); | ||||
|     let return_type_ident = format_ident!("{}", enum_build_spec.build()); | ||||
| 
 | ||||
|     let rule_branches = enum_build_spec | ||||
|         .rules() | ||||
|         .map(|enum_rule| { | ||||
|             let rule_ident = format_ident!("{}", enum_rule.rule()); | ||||
|             if let Some(child) = enum_rule.child() { | ||||
|                 let inner_builder = match child.kind() { | ||||
|                     EnumRuleChildKind::Node(node_child) => { | ||||
|                         let inner_build_fn_ident = | ||||
|                             format_ident!("{}", node_child.with()); | ||||
|                         quote! { #inner_build_fn_ident(file_id, inner_pair) } | ||||
|                     } | ||||
|                     EnumRuleChildKind::Int(name_and_with) => { | ||||
|                         let inner_build_fn_ident = format_ident!("{}", name_and_with.with()); | ||||
|                         quote! { #inner_build_fn_ident(file_id, inner_pair) } | ||||
|                     } | ||||
|                     EnumRuleChildKind::Long(name_and_with) => { | ||||
|                         let inner_build_fn_ident = format_ident!("{}", name_and_with.with()); | ||||
|                         quote! { #inner_build_fn_ident(file_id, inner_pair) } | ||||
|                     } | ||||
|                     EnumRuleChildKind::Double(name_and_with) => { | ||||
|                         let inner_build_fn_ident = format_ident!("{}", name_and_with.with()); | ||||
|                         quote! { #inner_build_fn_ident(file_id, inner_pair) } | ||||
|                     } | ||||
|                     EnumRuleChildKind::String(name_and_with) => { | ||||
|                         let inner_build_fn_ident = format_ident!("{}", name_and_with.with()); | ||||
|                         quote! { #inner_build_fn_ident(file_id, inner_pair) } | ||||
|                     } | ||||
|                     EnumRuleChildKind::Boolean(name_and_with) => { | ||||
|                         let inner_build_fn_ident = format_ident!("{}", name_and_with.with()); | ||||
|                         quote! { #inner_build_fn_ident(file_id, inner_pair) } | ||||
|                     } | ||||
|                 }; | ||||
|                 quote! { | ||||
|                     Rule::#rule_ident => #return_type_ident::#rule_ident(#inner_builder) | ||||
|                 } | ||||
|             } else { | ||||
|                 quote! { | ||||
|                     Rule::#rule_ident => #return_type_ident::#rule_ident | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     quote! { | ||||
|         fn #build_fn_ident(file_id: usize, #pair_ident: Pair<Rule>) -> #return_type_ident { | ||||
|             let inner_pair = #pair_ident.into_inner().next().unwrap(); | ||||
|             match inner_pair.as_rule() { | ||||
|                 #(#rule_branches,)* | ||||
|                 _ => unreachable!() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										127
									
								
								ast-generator/src/build_fn_gen.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								ast-generator/src/build_fn_gen.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,127 @@ | ||||
| use crate::spec::{ | ||||
|     BuildBooleanOn, ChildSpec, SingleChildToBuild, StructBuildSpec, VecChildToBuild, | ||||
| }; | ||||
| use convert_case::{Case, Casing}; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| pub fn make_build_fn_name(s: &str) -> String { | ||||
|     format!("build_{}", s.to_case(Case::Snake)) | ||||
| } | ||||
| 
 | ||||
| fn make_child_holder(child_spec: &ChildSpec) -> Option<TokenStream> { | ||||
|     match child_spec { | ||||
|         ChildSpec::SkipChild(_) => None, | ||||
|         ChildSpec::VecChild(vec_child) => { | ||||
|             let (child_ident, child_type_ident) = match vec_child.build() { | ||||
|                 VecChildToBuild::Type(vec_type_child) => ( | ||||
|                     format_ident!("{}", vec_type_child.var_name()), | ||||
|                     format_ident!("{}", vec_type_child.build()), | ||||
|                 ), | ||||
|             }; | ||||
|             Some(quote! { | ||||
|                 let mut #child_ident: Vec<#child_type_ident> = vec![] | ||||
|             }) | ||||
|         } | ||||
|         ChildSpec::SingleChild(single_child) => match single_child.build() { | ||||
|             SingleChildToBuild::Type(single_type_child) => { | ||||
|                 let child_ident = format_ident!("{}", single_type_child.var_name()); | ||||
|                 let child_type_ident = format_ident!("{}", single_type_child.build()); | ||||
|                 Some(quote! { | ||||
|                     let mut #child_ident: Option<#child_type_ident> = None; | ||||
|                 }) | ||||
|             } | ||||
|             SingleChildToBuild::Boolean(boolean_child) => { | ||||
|                 let child_ident = format_ident!("{}", boolean_child.var_name()); | ||||
|                 Some(quote! { | ||||
|                     let #child_ident: bool = false | ||||
|                 }) | ||||
|             } | ||||
|         }, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_match_action(child_spec: &ChildSpec) -> TokenStream { | ||||
|     match child_spec { | ||||
|         ChildSpec::SkipChild(_) => quote! {}, | ||||
|         ChildSpec::VecChild(vec_child) => { | ||||
|             let (child_name_ident, build_fn_ident) = match vec_child.build() { | ||||
|                 VecChildToBuild::Type(vec_type_child) => ( | ||||
|                     format_ident!("{}", vec_type_child.var_name()), | ||||
|                     format_ident!("{}", vec_type_child.with()), | ||||
|                 ), | ||||
|             }; | ||||
|             quote! { | ||||
|                 #child_name_ident.push(#build_fn_ident(inner_pair)) | ||||
|             } | ||||
|         } | ||||
|         ChildSpec::SingleChild(single_child) => match single_child.build() { | ||||
|             SingleChildToBuild::Type(single_type_child) => { | ||||
|                 let child_name_ident = format_ident!("{}", single_type_child.var_name()); | ||||
|                 let build_fn_ident = format_ident!("{}", single_type_child.with()); | ||||
|                 quote! { | ||||
|                     #child_name_ident = Some(#build_fn_ident(inner_pair)) | ||||
|                 } | ||||
|             } | ||||
|             SingleChildToBuild::Boolean(single_boolean_child) => { | ||||
|                 let child_name_ident = format_ident!("{}", single_boolean_child.var_name()); | ||||
|                 match single_boolean_child.on() { | ||||
|                     BuildBooleanOn::RulePresent => quote! { | ||||
|                         #child_name_ident = true | ||||
|                     }, | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_rule_matcher(child_spec: &ChildSpec) -> TokenStream { | ||||
|     let rule_ident = match child_spec { | ||||
|         ChildSpec::SkipChild(skip_child) => format_ident!("{}", skip_child.rule()), | ||||
|         ChildSpec::VecChild(vec_child) => format_ident!("{}", vec_child.rule()), | ||||
|         ChildSpec::SingleChild(single_child) => format_ident!("{}", single_child.rule()), | ||||
|     }; | ||||
|     let action = make_match_action(child_spec); | ||||
| 
 | ||||
|     quote! { | ||||
|         Rule::#rule_ident => { | ||||
|             #action; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn make_struct_build_fn(build_spec: &StructBuildSpec) -> TokenStream { | ||||
|     let build_fn_ident = format_ident!("{}", build_spec.with()); | ||||
|     let pair_ident = format_ident!("{}_pair", build_spec.build().to_case(Case::Snake)); | ||||
|     let return_type_ident = format_ident!("{}", build_spec.build()); | ||||
| 
 | ||||
|     let child_holders = build_spec | ||||
|         .children() | ||||
|         .iter() | ||||
|         .map(|child_spec| make_child_holder(child_spec)) | ||||
|         .filter(|child_holder| child_holder.is_some()) | ||||
|         .map(|child_holder| child_holder.unwrap()) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let rule_matchers = build_spec | ||||
|         .children() | ||||
|         .iter() | ||||
|         .map(|child_spec| make_rule_matcher(child_spec)) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let iter_stream = quote! { | ||||
|         for inner_pair in #pair_ident.into_inner() { | ||||
|             match inner_pair.as_rule() { | ||||
|                 #(#rule_matchers)* | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     quote! { | ||||
|         fn #build_fn_ident(#pair_ident: Pair<Rule>) -> #return_type_ident { | ||||
|             #(#child_holders;)* | ||||
| 
 | ||||
|             #iter_stream | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										168
									
								
								ast-generator/src/deserialize.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								ast-generator/src/deserialize.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,168 @@ | ||||
| use crate::build_fn_gen::make_build_fn_name; | ||||
| use crate::spec::{ | ||||
|     BuildBooleanOn, BuildSpec, ChildSpec, EnumBuildSpec, EnumRule, SingleBooleanChildToBuild, | ||||
|     SingleChild, SingleChildToBuild, SingleTypeChildToBuild, SkipChild, StructBuildSpec, VecChild, | ||||
|     VecChildToBuild, VecTypeChildToBuild, | ||||
| }; | ||||
| use convert_case::{Case, Casing}; | ||||
| use yaml_rust2::{Yaml, YamlLoader}; | ||||
| 
 | ||||
| fn get_skip(skip: &Yaml) -> bool { | ||||
|     skip.as_bool().unwrap_or_else(|| false) | ||||
| } | ||||
| 
 | ||||
| fn get_vec(vec: &Yaml) -> bool { | ||||
|     vec.as_bool().unwrap_or_else(|| false) | ||||
| } | ||||
| 
 | ||||
| fn get_vec_child_to_build(build: &Yaml, name: &str, rule: &str) -> VecChildToBuild { | ||||
|     if build.is_hash() { | ||||
|         let build_type = build["build"].as_str().unwrap(); | ||||
|         let var_name = build["var"] | ||||
|             .as_str() | ||||
|             .map(|s| s.to_string()) | ||||
|             .unwrap_or_else(|| build_type.to_case(Case::Snake)); | ||||
|         let with = build["with"] | ||||
|             .as_str() | ||||
|             .map(|s| s.to_string()) | ||||
|             .unwrap_or_else(|| make_build_fn_name(build_type)); | ||||
| 
 | ||||
|         VecChildToBuild::Type(VecTypeChildToBuild::new(build_type, &var_name, &with)) | ||||
|     } else { | ||||
|         let build_as_str = build.as_str().unwrap_or(rule); | ||||
|         VecChildToBuild::Type(VecTypeChildToBuild::new( | ||||
|             build_as_str, | ||||
|             name, | ||||
|             make_build_fn_name(build_as_str).as_str(), | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn get_vec_child(name: &str, rule: &str, build: &Yaml) -> ChildSpec { | ||||
|     ChildSpec::VecChild(VecChild::new( | ||||
|         name, | ||||
|         rule, | ||||
|         get_vec_child_to_build(build, name, rule), | ||||
|     )) | ||||
| } | ||||
| 
 | ||||
| fn get_single_child_to_build(name: &str, rule: &str, build: &Yaml) -> SingleChildToBuild { | ||||
|     if build.is_hash() { | ||||
|         let r#type = build["type"].as_str().unwrap(); | ||||
|         let var_name = build["var"] | ||||
|             .as_str() | ||||
|             .map(|s| s.to_string()) | ||||
|             .unwrap_or(name.to_string()); | ||||
|         let on = build["on"].as_str().unwrap(); | ||||
|         if r#type.eq("boolean") && on.eq("rule_present") { | ||||
|             SingleChildToBuild::Boolean(SingleBooleanChildToBuild::new( | ||||
|                 &var_name, | ||||
|                 BuildBooleanOn::RulePresent, | ||||
|             )) | ||||
|         } else { | ||||
|             todo!("currently on boolean types with on: rule_present are supported") | ||||
|         } | ||||
|     } else { | ||||
|         match build.as_str() { | ||||
|             Some(s) => SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(s)), | ||||
|             None => SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule(rule)), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn get_single_child(name: &str, rule: &str, build: &Yaml) -> ChildSpec { | ||||
|     ChildSpec::SingleChild(SingleChild::new( | ||||
|         name, | ||||
|         rule, | ||||
|         get_single_child_to_build(name, rule, build), | ||||
|     )) | ||||
| } | ||||
| 
 | ||||
| fn get_child_specs(children: &Yaml) -> Vec<ChildSpec> { | ||||
|     children | ||||
|         .as_vec() | ||||
|         .unwrap() | ||||
|         .iter() | ||||
|         .map(|child_spec| { | ||||
|             if child_spec.is_hash() { | ||||
|                 let as_hash = child_spec.as_hash().unwrap(); | ||||
|                 let (name, props) = as_hash | ||||
|                     .iter() | ||||
|                     .next() | ||||
|                     .map(|(name, props)| (name.as_str().unwrap(), props)) | ||||
|                     .unwrap(); | ||||
| 
 | ||||
|                 let rule = props["rule"].as_str().unwrap(); | ||||
| 
 | ||||
|                 if get_skip(&props["skip"]) { | ||||
|                     return ChildSpec::SkipChild(SkipChild::new(name, rule)); | ||||
|                 } | ||||
| 
 | ||||
|                 let build = &props["build"]; | ||||
| 
 | ||||
|                 if get_vec(&props["vec"]) { | ||||
|                     get_vec_child(name, rule, build) | ||||
|                 } else { | ||||
|                     get_single_child(name, rule, build) | ||||
|                 } | ||||
|             } else { | ||||
|                 ChildSpec::SingleChild(SingleChild::from_name_snake(child_spec.as_str().unwrap())) | ||||
|             } | ||||
|         }) | ||||
|         .collect() | ||||
| } | ||||
| 
 | ||||
| fn get_enum_rule_specs(rule_specs: &Yaml) -> Vec<EnumRule> { | ||||
|     rule_specs | ||||
|         .as_vec() | ||||
|         .unwrap() | ||||
|         .iter() | ||||
|         .map(|rule_spec| { | ||||
|             if rule_spec.is_hash() { | ||||
|                 let rule = rule_spec["rule"].as_str().unwrap(); | ||||
|                 let build = rule_spec["build"].as_str().unwrap(); | ||||
|                 let with = if !rule_spec["with"].is_badvalue() { | ||||
|                     rule_spec.as_str().unwrap().to_string() | ||||
|                 } else { | ||||
|                     make_build_fn_name(build) | ||||
|                 }; | ||||
| 
 | ||||
|                 EnumRule::new(rule, build, &with) | ||||
|             } else { | ||||
|                 EnumRule::from_rule(rule_spec.as_str().unwrap()) | ||||
|             } | ||||
|         }) | ||||
|         .collect() | ||||
| } | ||||
| 
 | ||||
| fn deserialize_build_spec(build_spec_name: &Yaml, build_spec: &Yaml) -> BuildSpec { | ||||
|     let build_spec_name_pascal = build_spec_name.as_str().unwrap(); | ||||
|     let children = &build_spec["children"]; | ||||
| 
 | ||||
|     if children.is_array() { | ||||
|         let child_specs = get_child_specs(children); | ||||
|         BuildSpec::Struct(StructBuildSpec::from_name( | ||||
|             build_spec_name_pascal, | ||||
|             child_specs, | ||||
|         )) | ||||
|     } else { | ||||
|         let rule_specs = &build_spec["rules"]; | ||||
|         if rule_specs.is_array() { | ||||
|             let enum_rules = get_enum_rule_specs(rule_specs); | ||||
|             BuildSpec::Enum(EnumBuildSpec::from_name(build_spec_name_pascal, enum_rules)) | ||||
|         } else { | ||||
|             panic!("either children or rules must be present on the build spec"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn deserialize_yaml_spec(yaml: &str) -> Vec<BuildSpec> { | ||||
|     let docs = YamlLoader::load_from_str(yaml).unwrap(); | ||||
|     let doc = &docs[0]; | ||||
| 
 | ||||
|     doc.as_hash() | ||||
|         .unwrap() | ||||
|         .iter() | ||||
|         .map(|(build_spec_name, build_spec)| deserialize_build_spec(build_spec_name, build_spec)) | ||||
|         .collect() | ||||
| } | ||||
| @ -1,13 +0,0 @@ | ||||
| use yaml_rust2::Yaml; | ||||
| use crate::spec::leaf_enum_spec::LeafEnumBuildSpec; | ||||
| 
 | ||||
| pub fn deserialize_leaf_enum(name: &str, props: &Yaml) -> LeafEnumBuildSpec { | ||||
|     let rules = props["rules"].as_vec() | ||||
|         .unwrap() | ||||
|         .iter() | ||||
|         .map(|rule_yaml| { | ||||
|             rule_yaml.as_str().unwrap().to_string() | ||||
|         }) | ||||
|         .collect(); | ||||
|     LeafEnumBuildSpec::new(name, rules) | ||||
| } | ||||
| @ -1,37 +0,0 @@ | ||||
| use crate::deserialize::util::unwrap_single_member_hash; | ||||
| use yaml_rust2::Yaml; | ||||
| use crate::spec::leaf_struct_spec::{LeafStructBuildSpec, LeafStructMember, LeafStructMemberKind}; | ||||
| 
 | ||||
| fn deserialize_member(member_name: &str, member_props: &Yaml) -> LeafStructMember { | ||||
|     let kind = match member_props["kind"].as_str().unwrap() { | ||||
|         "string" => LeafStructMemberKind::String, | ||||
|         "file_id" => LeafStructMemberKind::FileId, | ||||
|         "range" => LeafStructMemberKind::Range, | ||||
|         _ => panic!() | ||||
|     }; | ||||
|     LeafStructMember::new(member_name, kind) | ||||
| } | ||||
| 
 | ||||
| pub fn deserialize_leaf_struct(name: &str, props: &Yaml) -> LeafStructBuildSpec { | ||||
|     let members = props["members"] | ||||
|         .as_vec() | ||||
|         .unwrap() | ||||
|         .iter() | ||||
|         .map(|member_hash| { | ||||
|             let (member_name, props) = unwrap_single_member_hash(member_hash); | ||||
|             deserialize_member(&member_name, props) | ||||
|         }) | ||||
|         .map(Box::new) | ||||
|         .collect(); | ||||
|     let derive = props["derive"] | ||||
|         .as_vec() | ||||
|         .map(|derive_yaml| { | ||||
|             derive_yaml.iter() | ||||
|                 .map(|trait_yaml| { 
 | ||||
|                     trait_yaml.as_str().unwrap().to_string() 
 | ||||
|                 }).collect::<Vec<_>>() | ||||
|         }) | ||||
|         .unwrap_or_default(); | ||||
|     
 | ||||
|     LeafStructBuildSpec::new(name, members, derive) | ||||
| } | ||||
| @ -1,87 +0,0 @@ | ||||
| mod leaf_enum_spec; | ||||
| mod leaf_struct_spec; | ||||
| mod node_production_spec; | ||||
| mod polymorphic_enum_loop_spec; | ||||
| mod polymorphic_pass_through_spec; | ||||
| mod polymorphic_type_spec; | ||||
| mod production_spec; | ||||
| mod struct_spec; | ||||
| mod tree_enum_spec; | ||||
| pub(crate) mod util; | ||||
| 
 | ||||
| use crate::deserialize::leaf_enum_spec::deserialize_leaf_enum; | ||||
| use crate::deserialize::leaf_struct_spec::deserialize_leaf_struct; | ||||
| use crate::deserialize::node_production_spec::deserialize_node_production; | ||||
| use crate::deserialize::polymorphic_enum_loop_spec::deserialize_polymorphic_enum_loop; | ||||
| use crate::deserialize::polymorphic_type_spec::deserialize_polymorphic_type; | ||||
| use crate::deserialize::production_spec::deserialize_production; | ||||
| use crate::deserialize::struct_spec::deserialize_struct_spec; | ||||
| use crate::deserialize::tree_enum_spec::deserialize_tree_enum; | ||||
| use crate::spec::BuildSpec; | ||||
| use yaml_rust2::{Yaml, YamlLoader}; | ||||
| use crate::deserialize::polymorphic_pass_through_spec::deserialize_polymorphic_pass_through; | ||||
| 
 | ||||
| fn deserialize_build_spec(build_spec_name: &str, build_spec: &Yaml) -> BuildSpec { | ||||
|     if build_spec["struct"].is_hash() { | ||||
|         BuildSpec::Struct(deserialize_struct_spec( | ||||
|             build_spec_name, | ||||
|             &build_spec["struct"], | ||||
|         )) | ||||
|     } else if build_spec["leaf_struct"].is_hash() { | ||||
|         BuildSpec::LeafStruct(deserialize_leaf_struct( | ||||
|             build_spec_name, | ||||
|             &build_spec["leaf_struct"], | ||||
|         )) | ||||
|     } else if build_spec["tree_enum"].is_hash() { | ||||
|         BuildSpec::Enum(deserialize_tree_enum( | ||||
|             build_spec_name, | ||||
|             &build_spec["tree_enum"], | ||||
|         )) | ||||
|     } else if build_spec["leaf_enum"].is_hash() { | ||||
|         BuildSpec::LeafEnum(deserialize_leaf_enum( | ||||
|             build_spec_name, | ||||
|             &build_spec["leaf_enum"], | ||||
|         )) | ||||
|     } else if build_spec["production"].is_hash() { | ||||
|         BuildSpec::Production(deserialize_production( | ||||
|             build_spec_name, | ||||
|             &build_spec["production"], | ||||
|         )) | ||||
|     } else if build_spec["node_production"].is_hash() { | ||||
|         BuildSpec::NodeProduction(deserialize_node_production( | ||||
|             build_spec_name, | ||||
|             &build_spec["node_production"], | ||||
|         )) | ||||
|     } else if build_spec["polymorphic_type"].is_hash() { | ||||
|         BuildSpec::PolymorphicType(deserialize_polymorphic_type( | ||||
|             build_spec_name, | ||||
|             &build_spec["polymorphic_type"], | ||||
|         )) | ||||
|     } else if build_spec["polymorphic_enum_loop_build"].is_hash() { | ||||
|         BuildSpec::PolymorphicEnumLoop(deserialize_polymorphic_enum_loop( | ||||
|             build_spec_name, | ||||
|             &build_spec["polymorphic_enum_loop_build"], | ||||
|         )) | ||||
|     } else if build_spec["polymorphic_pass_through"].is_hash() { | ||||
|         BuildSpec::PolymorphicPassThrough(deserialize_polymorphic_pass_through( | ||||
|             build_spec_name, | ||||
|             &build_spec["polymorphic_pass_through"], | ||||
|         )) | ||||
|     } else { | ||||
|         panic!("Missing or incorrect build type for {}", build_spec_name); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn deserialize_yaml_spec(yaml: &str) -> Vec<BuildSpec> { | ||||
|     let docs = YamlLoader::load_from_str(yaml).unwrap(); | ||||
|     let doc = &docs[0]; | ||||
| 
 | ||||
|     doc.as_hash() | ||||
|         .unwrap() | ||||
|         .iter() | ||||
|         .map(|(build_spec_name, build_spec)| { | ||||
|             let name_as_str = build_spec_name.as_str().unwrap(); | ||||
|             deserialize_build_spec(name_as_str, build_spec) | ||||
|         }) | ||||
|         .collect() | ||||
| } | ||||
| @ -1,9 +0,0 @@ | ||||
| use crate::deserialize::util::make_build_fn_name; | ||||
| use crate::spec::node_production_spec::NodeProductionBuildSpec; | ||||
| use yaml_rust2::Yaml; | ||||
| 
 | ||||
| pub fn deserialize_node_production(name: &str, props: &Yaml) -> NodeProductionBuildSpec { | ||||
|     let kind = props["kind"].as_str().unwrap(); | ||||
|     let with = make_build_fn_name(props["with"].as_str().unwrap()); | ||||
|     NodeProductionBuildSpec::new(name, kind, &with) | ||||
| } | ||||
| @ -1,79 +0,0 @@ | ||||
| use crate::deserialize::util::{get_as_bool, make_build_fn_name, unwrap_single_member_hash}; | ||||
| use crate::spec::polymorphic_enum_loop_spec::{ | ||||
|     PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopChildUseCurrent, PolymorphicEnumLoopRule, | ||||
|     PolymorphicEnumLoopRuleBuild, PolymorphicEnumLoopRuleBuildChild, | ||||
|     PolymorphicEnumLoopRuleChildOnEach, PolymorphicEnumLoopRulePassThrough, | ||||
| }; | ||||
| use yaml_rust2::Yaml; | ||||
| 
 | ||||
| fn deserialize_build_child(child_name: &str, props: &Yaml) -> PolymorphicEnumLoopRuleBuildChild { | ||||
|     if props["use_current"].is_hash() { | ||||
|         let kind = props["use_current"]["kind"].as_str().unwrap(); | ||||
|         PolymorphicEnumLoopRuleBuildChild::UseCurrent(PolymorphicEnumLoopChildUseCurrent::new( | ||||
|             child_name, kind, | ||||
|         )) | ||||
|     } else if props["on_each"].is_hash() { | ||||
|         let rule = props["on_each"]["rule"].as_str().unwrap(); | ||||
|         PolymorphicEnumLoopRuleBuildChild::OnEach(PolymorphicEnumLoopRuleChildOnEach::new( | ||||
|             child_name, rule, | ||||
|         )) | ||||
|     } else { | ||||
|         panic!("Expected 'use_current' or 'on_each' hash for polymorphic enum loop build child"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn deserialize_build(name: &str, props: &Yaml) -> PolymorphicEnumLoopRuleBuild { | ||||
|     let variant = props["variant"].as_str().unwrap(); | ||||
|     let children = props["children"] | ||||
|         .as_vec() | ||||
|         .unwrap() | ||||
|         .iter() | ||||
|         .map(|child_yaml| { | ||||
|             let (child_name, child_props) = unwrap_single_member_hash(child_yaml); | ||||
|             deserialize_build_child(&child_name, child_props) | ||||
|         }) | ||||
|         .map(Box::new) | ||||
|         .collect(); | ||||
| 
 | ||||
|     PolymorphicEnumLoopRuleBuild::new(name, variant, children) | ||||
| } | ||||
| 
 | ||||
| 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) | ||||
|     ); | ||||
|     PolymorphicEnumLoopRulePassThrough::new(name, kind, &with) | ||||
| } | ||||
| 
 | ||||
| fn deserialize_rule(rule_name: &str, props: &Yaml) -> PolymorphicEnumLoopRule { | ||||
|     if props["pass_through"].is_hash() { | ||||
|         PolymorphicEnumLoopRule::PassThrough(deserialize_pass_through( | ||||
|             rule_name, | ||||
|             &props["pass_through"], | ||||
|         )) | ||||
|     } else if props["build"].is_hash() { | ||||
|         PolymorphicEnumLoopRule::Build(deserialize_build(rule_name, &props["build"])) | ||||
|     } else { | ||||
|         panic!("Polymorphic enum loop rule must have 'pass_through' or 'build' hash."); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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() | ||||
|         .iter() | ||||
|         .map(|rule_yaml| { | ||||
|             let (rule_name, rule_props) = unwrap_single_member_hash(rule_yaml); | ||||
|             deserialize_rule(&rule_name, rule_props) | ||||
|         }) | ||||
|         .map(Box::new) | ||||
|         .collect(); | ||||
| 
 | ||||
|     PolymorphicEnumLoopBuildSpec::new(name, kind, reverse, rules) | ||||
| } | ||||
| @ -1,30 +0,0 @@ | ||||
| use crate::deserialize::util::unwrap_single_member_hash; | ||||
| use crate::spec::polymorphic_pass_through_spec::{PolymorphicPassThroughBuildSpec, PolymorphicPassThroughVariant}; | ||||
| use yaml_rust2::Yaml; | ||||
| 
 | ||||
| pub fn deserialize_polymorphic_pass_through(name: &str, props: &Yaml) -> PolymorphicPassThroughBuildSpec { | ||||
|     let build_kind = props["build"]["kind"].as_str().unwrap(); | ||||
|     let variants = props["variants"].as_vec().unwrap() | ||||
|         .iter() | ||||
|         .map(|variant_yaml| { | ||||
|             let (variant_name, variant_props) = unwrap_single_member_hash(variant_yaml); | ||||
|             if variant_props["inner"].is_hash() { | ||||
|                 let inner_kind = variant_props["inner"]["kind"].as_str().unwrap(); | ||||
|                 PolymorphicPassThroughVariant::Inner { | ||||
|                     name: variant_name, | ||||
|                     kind: inner_kind.to_string(), | ||||
|                 } | ||||
|             } else if variant_props["pass_through"].is_hash() { | ||||
|                 let pass_through_kind = variant_props["pass_through"]["kind"].is_hash(); | ||||
|                 PolymorphicPassThroughVariant::PassThrough { | ||||
|                     name: variant_name, | ||||
|                     kind: pass_through_kind.to_string(), | ||||
|                 } | ||||
|             } else { | ||||
|                 panic!() | ||||
|             } | ||||
|         }) | ||||
|         .map(Box::new) | ||||
|         .collect(); | ||||
|     PolymorphicPassThroughBuildSpec::new(name, build_kind, variants) | ||||
| } | ||||
| @ -1,23 +0,0 @@ | ||||
| use crate::deserialize::util::unwrap_single_member_hash; | ||||
| use crate::spec::polymorphic_type_spec::{PolymorphicTypeBuildSpec, PolymorphicTypeVariant}; | ||||
| use yaml_rust2::Yaml; | ||||
| 
 | ||||
| fn deserialize_variant(variant_name: &str, props: &Yaml) -> PolymorphicTypeVariant { | ||||
|     let inner_kind = props["inner"]["kind"].as_str().unwrap(); | ||||
|     PolymorphicTypeVariant::new(variant_name, inner_kind)    
 | ||||
| } | ||||
| 
 | ||||
| pub fn deserialize_polymorphic_type(name: &str, props: &Yaml) -> PolymorphicTypeBuildSpec { | ||||
|     let variants = props["variants"] | ||||
|         .as_vec() | ||||
|         .unwrap() | ||||
|         .iter() | ||||
|         .map(|variant_yaml| { | ||||
|             let (variant_name, variant_props) = unwrap_single_member_hash(variant_yaml); | ||||
|             deserialize_variant(&variant_name, variant_props) | ||||
|         }) | ||||
|         .map(Box::new) | ||||
|         .collect(); | ||||
|     let kind = props["build"]["kind"].as_str().unwrap(); | ||||
|     PolymorphicTypeBuildSpec::new(name, variants, kind) | ||||
| } | ||||
| @ -1,21 +0,0 @@ | ||||
| use crate::spec::production_spec::{ProductionBooleanFrom, ProductionBuildSpec, ProductionKind, ProductionStringFrom}; | ||||
| use yaml_rust2::Yaml; | ||||
| 
 | ||||
| pub fn deserialize_production(name: &str, props: &Yaml) -> ProductionBuildSpec { | ||||
|     let kind = match props["kind"].as_str().unwrap() { | ||||
|         "int" => ProductionKind::Int, | ||||
|         "long" => ProductionKind::Long, | ||||
|         "double" => ProductionKind::Double, | ||||
|         "string" => { | ||||
|             let from = match props["from"].as_str().unwrap() { | ||||
|                 "string_inner" => ProductionStringFrom::StringInner, | ||||
|                 "whole_pair" => ProductionStringFrom::WholePair, | ||||
|                 _ => panic!("Unknown string production from: {}", props["from"].as_str().unwrap()) | ||||
|             }; | ||||
|             ProductionKind::String(from) | ||||
|         }, | ||||
|         "boolean" => ProductionKind::Boolean(ProductionBooleanFrom::ParseWholePair), | ||||
|         _ => panic!("Unknown production kind: {}", props["kind"].as_str().unwrap()), | ||||
|     }; | ||||
|     ProductionBuildSpec::new(name, kind) | ||||
| } | ||||
| @ -1,182 +0,0 @@ | ||||
| use crate::deserialize_error; | ||||
| 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 yaml_rust2::Yaml; | ||||
| 
 | ||||
| fn deserialize_fields(fields: &Yaml) -> Vec<Box<StructField>> { | ||||
|     fields | ||||
|         .as_vec() | ||||
|         .unwrap() | ||||
|         .iter() | ||||
|         .map(|field| unwrap_single_member_hash(field)) | ||||
|         .map(|(name, props)| { | ||||
|             let kind = match props["kind"].as_str().unwrap() { | ||||
|                 "usize" => StructFieldKind::USize, | ||||
|                 _ => panic!() | ||||
|             }; | ||||
|             StructField::new(&name, kind, get_as_bool(&props["optional"])) | ||||
|         }) | ||||
|         .map(Box::new) | ||||
|         .collect() | ||||
| } | ||||
| 
 | ||||
| fn deserialize_skip_child(props: &Yaml) -> StructChild { | ||||
|     let rule = props["rule"].as_str().unwrap(); | ||||
|     StructChild::SkipChild(SkipChild::new(rule)) | ||||
| } | ||||
| 
 | ||||
| fn deserialize_vec_child(child_name: &str, props: &Yaml) -> StructChild { | ||||
|     let rule = props["rule"].as_str().unwrap(); | ||||
|     let kind = props["kind"].as_str().unwrap_or(rule); | ||||
|     if kind == "string" { | ||||
|         let build = VecChildBuild::String(VecChildStringBuild::new(&make_build_fn_name(rule))); | ||||
|         StructChild::VecChild(VecChild::new(child_name, rule, Box::new(build))) | ||||
|     } else { | ||||
|         let build = VecChildBuild::Node(VecChildNodeBuild::new(rule, &make_build_fn_name(rule))); | ||||
|         StructChild::VecChild(VecChild::new(child_name, rule, Box::new(build))) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn deserialize_member_build(child_name: &str, rule: &str, props: &Yaml) -> MemberChildBuild { | ||||
|     if props["node"].is_hash() { | ||||
|         let node_props = &props["node"]; | ||||
|         let kind = node_props["kind"].as_str().unwrap_or(rule); | ||||
|         let with = node_props["with"] | ||||
|             .as_str() | ||||
|             .map(|with| make_build_fn_name(with)) | ||||
|             .unwrap_or_else(|| make_build_fn_name(kind)); | ||||
| 
 | ||||
|         let or_else = if get_as_bool(&node_props["or_else_default"]) { | ||||
|             Some(String::from("default")) | ||||
|         } else if let Some(or_else) = node_props["or_else"].as_str() { | ||||
|             Some(or_else.to_string()) | ||||
|         } else { | ||||
|             None | ||||
|         }; | ||||
| 
 | ||||
|         MemberChildBuild::Node(NodeMemberBuild::new(kind, &with, or_else)) | ||||
|     } else if props["boolean"].is_hash() { | ||||
|         let boolean_props = &props["boolean"]; | ||||
|         if let Some(on) = boolean_props["on"].as_str() { | ||||
|             if on == "rule_present" { | ||||
|                 MemberChildBuild::Boolean(BooleanMemberBuild::new( | ||||
|                     BooleanMemberBuildOn::RulePresent, | ||||
|                 )) | ||||
|             } else { | ||||
|                 panic!( | ||||
|                     "Expected 'on' in 'boolean' in 'build' in {} to be 'rule_present'", | ||||
|                     child_name | ||||
|                 ); | ||||
|             } | ||||
|         } else { | ||||
|             panic!("Expected 'on' in 'boolean' in 'build' in {}", child_name); | ||||
|         } | ||||
|     } else { | ||||
|         panic!( | ||||
|             "Expected one of 'node', 'boolean', or 'special' in 'build' in {}", | ||||
|             child_name | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn deserialize_member_child(child_name: &str, props: &Yaml) -> StructChild { | ||||
|     let rule = props["rule"] | ||||
|         .as_str() | ||||
|         .map(ToString::to_string) | ||||
|         .unwrap_or_else(|| child_name.to_case(Case::Pascal)); | ||||
|     let optional = get_as_bool(&props["optional"]); | ||||
|     if props["build"].is_hash() { | ||||
|         let build = deserialize_member_build(child_name, &rule, &props["build"]); | ||||
|         StructChild::MemberChild(MemberChild::new( | ||||
|             child_name, | ||||
|             &rule, | ||||
|             optional, | ||||
|             Box::new(build), | ||||
|         )) | ||||
|     } else { | ||||
|         StructChild::MemberChild(MemberChild::new( | ||||
|             child_name, | ||||
|             &rule, | ||||
|             optional, | ||||
|             Box::new(MemberChildBuild::Node(NodeMemberBuild::new( | ||||
|                 &rule, | ||||
|                 &make_build_fn_name(&rule), | ||||
|                 None, | ||||
|             ))), | ||||
|         )) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn deserialize_special_child(name: &str, props: &Yaml) -> StructChild { | ||||
|     match props["kind"].as_str().unwrap() { | ||||
|         "file_id" => StructChild::Special(SpecialChild::new(name, SpecialChildKind::FileId)), | ||||
|         "range" => StructChild::Special(SpecialChild::new(name, SpecialChildKind::Range)), | ||||
|         _ => panic!( | ||||
|             "Invalid special child kind {} in {}", | ||||
|             props["kind"].as_str().unwrap(), | ||||
|             name | ||||
|         ), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn deserialize_hash_child(name: &str, props: &Yaml) -> StructChild { | ||||
|     if props["skip"].is_hash() { | ||||
|         deserialize_skip_child(&props["skip"]) | ||||
|     } else if props["vec"].is_hash() { | ||||
|         deserialize_vec_child(name, &props["vec"]) | ||||
|     } else if props["member"].is_hash() { | ||||
|         deserialize_member_child(name, &props["member"]) | ||||
|     } else if props["special"].is_hash() { | ||||
|         deserialize_special_child(name, &props["special"]) | ||||
|     } else { | ||||
|         panic!("Expected 'skip' or 'vec' in 'member' in {}", name); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn deserialize_string_child(name: &str) -> StructChild { | ||||
|     let name_pascal = name.to_case(Case::Pascal); | ||||
|     StructChild::MemberChild(MemberChild::new( | ||||
|         name, | ||||
|         &name_pascal, | ||||
|         false, | ||||
|         Box::new(MemberChildBuild::Node(NodeMemberBuild::new( | ||||
|             &name_pascal, | ||||
|             &make_build_fn_name(name), | ||||
|             None, | ||||
|         ))), | ||||
|     )) | ||||
| } | ||||
| 
 | ||||
| fn deserialize_children(children_yaml: &Yaml) -> Vec<Box<StructChild>> { | ||||
|     children_yaml | ||||
|         .as_vec() | ||||
|         .unwrap() | ||||
|         .iter() | ||||
|         .map(|child_yaml| { | ||||
|             if let Some(name) = child_yaml.as_str() { | ||||
|                 deserialize_string_child(name) | ||||
|             } else { | ||||
|                 let (child_name, child_props) = unwrap_single_member_hash(child_yaml); | ||||
|                 deserialize_hash_child(&child_name, child_props) | ||||
|             } | ||||
|         }) | ||||
|         .map(Box::new) | ||||
|         .collect() | ||||
| } | ||||
| 
 | ||||
| pub fn deserialize_struct_spec(name: &str, struct_yaml: &Yaml) -> StructSpec { | ||||
|     let children = if struct_yaml["children"].is_array() { | ||||
|         deserialize_children(&struct_yaml["children"]) | ||||
|     } else { | ||||
|         deserialize_error!("array", "children", name); | ||||
|     }; | ||||
|     let fields = if struct_yaml["fields"].is_array() { | ||||
|         deserialize_fields(&struct_yaml["fields"]) | ||||
|     } else { | ||||
|         vec![] | ||||
|     }; | ||||
| 
 | ||||
|     StructSpec::new(name, children, fields) | ||||
| } | ||||
| @ -1,56 +0,0 @@ | ||||
| use convert_case::{Case, Casing}; | ||||
| use crate::deserialize::util::{get_as_bool_or, make_build_fn_name, unwrap_single_member_hash}; | ||||
| use crate::spec::tree_enum_spec::{EnumRuleChild, EnumRuleChildKind, EnumRuleChildNodeKind, NameAndWith, TreeEnumBuildSpec, TreeEnumRule}; | ||||
| use yaml_rust2::Yaml; | ||||
| 
 | ||||
| fn deserialize_hash_enum_rule(rule: &str, props: &Yaml) -> TreeEnumRule { | ||||
|     if get_as_bool_or(&props["child"], true) { | ||||
|         let name_and_with = NameAndWith::new( | ||||
|             &rule.to_case(Case::Snake), | ||||
|             &make_build_fn_name(rule) | ||||
|         ); | ||||
|         let kind = match props["kind"].as_str().unwrap() { | ||||
|             "int" => EnumRuleChildKind::Int(name_and_with), | ||||
|             "long" => EnumRuleChildKind::Long(name_and_with), | ||||
|             "double" => EnumRuleChildKind::Double(name_and_with), | ||||
|             "string" => EnumRuleChildKind::String(name_and_with), | ||||
|             "boolean" => EnumRuleChildKind::Boolean(name_and_with), | ||||
|             _ => panic!("Invalid kind: {}", props["kind"].as_str().unwrap()), | ||||
|         }; | ||||
|         TreeEnumRule::new(rule, Some(Box::new(EnumRuleChild::new(Box::new(kind))))) | ||||
|     } else { | ||||
|         // no child
 | ||||
|         TreeEnumRule::new(rule, None) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn deserialize_string_enum_rule(rule: &str) -> TreeEnumRule { | ||||
|     TreeEnumRule::new( | ||||
|         rule, | ||||
|         Some(Box::new(EnumRuleChild::new(Box::new( | ||||
|             EnumRuleChildKind::Node(EnumRuleChildNodeKind::new( | ||||
|                 rule, | ||||
|                 &make_build_fn_name(rule) | ||||
|             )), | ||||
|         )))), | ||||
|     ) | ||||
| } | ||||
| 
 | ||||
| pub fn deserialize_tree_enum(name: &str, props: &Yaml) -> TreeEnumBuildSpec { | ||||
|     let rules = props["rules"] | ||||
|         .as_vec() | ||||
|         .unwrap() | ||||
|         .iter() | ||||
|         .map(|rule_yaml| { | ||||
|             if let Some(rule) = rule_yaml.as_str() { | ||||
|                 deserialize_string_enum_rule(rule) | ||||
|             } else { | ||||
|                 let (rule, props) = unwrap_single_member_hash(rule_yaml); | ||||
|                 deserialize_hash_enum_rule(&rule, props) | ||||
|             } | ||||
|         }) | ||||
|         .map(Box::new) | ||||
|         .collect(); | ||||
| 
 | ||||
|     TreeEnumBuildSpec::new(name, rules) | ||||
| } | ||||
| @ -1,37 +0,0 @@ | ||||
| use convert_case::{Case, Casing}; | ||||
| use yaml_rust2::Yaml; | ||||
| 
 | ||||
| #[macro_export] | ||||
| macro_rules! deserialize_error { | ||||
|     ($kind:expr, $key:expr, $name:expr) => { | ||||
|         panic!("Expected {} '{}' in {}.", $kind, $key, $name); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| pub fn make_build_fn_name(s: &str) -> String { | ||||
|     format!("build_{}", s.to_case(Case::Snake)) | ||||
| } | ||||
| 
 | ||||
| pub fn make_build_pair(s: &str) -> String { | ||||
|     format!("{}_pair", s.to_case(Case::Snake)) | ||||
| } | ||||
| 
 | ||||
| pub fn unwrap_single_member_hash(hash: &Yaml) -> (String, &Yaml) { | ||||
|     let as_hash = hash.as_hash().unwrap(); | ||||
|     if as_hash.is_empty() { | ||||
|         panic!("empty hash"); | ||||
|     } else if as_hash.len() > 1 { | ||||
|         panic!("hash contains more than one key"); | ||||
|     } | ||||
|     let (member_key, member_value) = as_hash.iter().collect::<Vec<(&Yaml, &Yaml)>>()[0]; | ||||
|     let key_as_string = member_key.as_str().unwrap().to_string(); | ||||
|     (key_as_string, member_value) | ||||
| } | ||||
| 
 | ||||
| pub fn get_as_bool(yaml: &Yaml) -> bool { | ||||
|     yaml.as_bool().unwrap_or(false) | ||||
| } | ||||
| 
 | ||||
| pub fn get_as_bool_or(yaml: &Yaml, or: bool) -> bool { | ||||
|     yaml.as_bool().unwrap_or(or) | ||||
| } | ||||
| @ -1,17 +1,10 @@ | ||||
| mod ast_node; | ||||
| mod build_fn; | ||||
| mod build_fn_gen; | ||||
| mod deserialize; | ||||
| mod pretty_print; | ||||
| mod spec; | ||||
| mod type_gen; | ||||
| mod walk; | ||||
| 
 | ||||
| use crate::ast_node::{make_ast_enum_member, make_ast_enum_member_mut, make_ast_node_impl}; | ||||
| use crate::build_fn::make_build_fn; | ||||
| use crate::deserialize::deserialize_yaml_spec; | ||||
| use crate::pretty_print::make_pretty_print_impl; | ||||
| use crate::build_fn_gen::make_struct_build_fn; | ||||
| use crate::type_gen::make_type; | ||||
| use crate::walk::make_walk_fn; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::quote; | ||||
| use spec::BuildSpec; | ||||
| @ -21,46 +14,10 @@ fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) { | ||||
|     println!("*** BuildSpec ***"); | ||||
|     match build_spec { | ||||
|         BuildSpec::Enum(enum_build_spec) => { | ||||
|             println!("Enum Spec - build: {}", enum_build_spec.build()); | ||||
|         } | ||||
|         BuildSpec::LeafEnum(leaf_enum_build_spec) => { | ||||
|             println!("Leaf Enum Spec - build: {}", leaf_enum_build_spec.build()); | ||||
|             println!("Spec name: {}", enum_build_spec.name()); | ||||
|         } | ||||
|         BuildSpec::Struct(struct_build_spec) => { | ||||
|             println!("Struct Spec - build: {}", struct_build_spec.build()); | ||||
|         } | ||||
|         BuildSpec::LeafStruct(leaf_struct_build_spec) => { | ||||
|             println!( | ||||
|                 "Leaf Struct Spec - build: {}", | ||||
|                 leaf_struct_build_spec.build() | ||||
|             ); | ||||
|         } | ||||
|         BuildSpec::Production(production_build_spec) => { | ||||
|             println!("Production Spec - name: {}", production_build_spec.name()); | ||||
|         } | ||||
|         BuildSpec::NodeProduction(node_production_build_spec) => { | ||||
|             println!( | ||||
|                 "Node Production Spec - name: {}", | ||||
|                 node_production_build_spec.name() | ||||
|             ); | ||||
|         } | ||||
|         BuildSpec::PolymorphicType(polymorphic_build_spec) => { | ||||
|             println!( | ||||
|                 "Polymorphic Type Spec - name: {}", | ||||
|                 polymorphic_build_spec.name() | ||||
|             ); | ||||
|         } | ||||
|         BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop_build_spec) => { | ||||
|             println!( | ||||
|                 "Polymorphic Enum Loop Spec - name: {}", | ||||
|                 polymorphic_enum_loop_build_spec.name() | ||||
|             ); | ||||
|         } | ||||
|         BuildSpec::PolymorphicPassThrough(pass_through_build_spec) => { | ||||
|             println!( | ||||
|                 "Polymorphic Pass Through Spec - name: {}", | ||||
|                 pass_through_build_spec.name() | ||||
|             ); | ||||
|             println!("Spec name: {}", struct_build_spec.name()); | ||||
|         } | ||||
|     } | ||||
|     println!("{:#?}", token_stream); | ||||
| @ -68,157 +25,30 @@ fn debug_built_spec(build_spec: &BuildSpec, token_stream: &TokenStream) { | ||||
|     println!("{}", prettyplease::unparse(&parsed)); | ||||
| } | ||||
| 
 | ||||
| pub struct AstGeneratedFile { | ||||
|     pub name: String, | ||||
|     pub contents: String, | ||||
| } | ||||
| pub fn test_dump() -> String { | ||||
|     let build_specs = deserialize::deserialize_yaml_spec(include_str!("../../src/parser/ast.yaml")); | ||||
|     let mut streams: Vec<TokenStream> = vec![]; | ||||
| 
 | ||||
| fn token_stream_to_string(token_stream: TokenStream) -> String { | ||||
|     let file: File = syn::parse2(token_stream).unwrap(); | ||||
|     for build_spec in &build_specs { | ||||
|         let type_stream = make_type(build_spec); | ||||
|         debug_built_spec(build_spec, &type_stream); | ||||
|         streams.push(type_stream); | ||||
|     } | ||||
| 
 | ||||
|     for build_spec in &build_specs { | ||||
|         match build_spec { | ||||
|             BuildSpec::Enum(_) => {} | ||||
|             BuildSpec::Struct(struct_spec) => { | ||||
|                 let struct_build_fn_stream = make_struct_build_fn(struct_spec); | ||||
|                 debug_built_spec(build_spec, &struct_build_fn_stream); | ||||
|                 streams.push(struct_build_fn_stream); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     let combined = quote! { | ||||
|         #(#streams)* | ||||
|     }; | ||||
|     let file: File = syn::parse2(combined).unwrap(); | ||||
|     prettyplease::unparse(&file) | ||||
| } | ||||
| 
 | ||||
| fn generate_build_file(build_specs: &[BuildSpec]) -> AstGeneratedFile { | ||||
|     let build_fns = build_specs | ||||
|         .iter() | ||||
|         .map(|build_spec| { | ||||
|             let build_fn = make_build_fn(build_spec); | ||||
|             debug_built_spec(build_spec, &build_fn); | ||||
|             build_fn | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let combined = quote! { | ||||
|         use crate::parser::Rule; | ||||
|         use pest::iterators::Pair; | ||||
|         use crate::ast::node::*; | ||||
|         use std::range::Range; | ||||
| 
 | ||||
|         #(#build_fns)* | ||||
|     }; | ||||
| 
 | ||||
|     AstGeneratedFile { | ||||
|         name: String::from("build.rs"), | ||||
|         contents: token_stream_to_string(combined), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn generate_node_file(build_specs: &[BuildSpec]) -> AstGeneratedFile { | ||||
|     let types = build_specs | ||||
|         .iter() | ||||
|         .map(|build_spec| make_type(build_spec)) | ||||
|         .filter(Option::is_some) | ||||
|         .collect::<Vec<_>>(); | ||||
|     let combined = quote! { | ||||
|         use std::range::Range; | ||||
| 
 | ||||
|         #(#types)* | ||||
|     }; | ||||
|     AstGeneratedFile { | ||||
|         name: String::from("node.rs"), | ||||
|         contents: token_stream_to_string(combined), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn generate_pretty_print_file(build_specs: &[BuildSpec]) -> AstGeneratedFile { | ||||
|     let impls = build_specs | ||||
|         .iter() | ||||
|         .map(|build_spec| { | ||||
|             let maybe_stream = make_pretty_print_impl(build_spec); | ||||
|             if let Some(stream) = &maybe_stream { | ||||
|                 debug_built_spec(build_spec, &stream); | ||||
|             } | ||||
|             maybe_stream | ||||
|         }) | ||||
|         .filter(Option::is_some) | ||||
|         .map(Option::unwrap) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let combined = quote! { | ||||
|         use crate::ast::node::*; | ||||
|         #(#impls)* | ||||
|     }; | ||||
|     AstGeneratedFile { | ||||
|         name: String::from("pretty_print.rs"), | ||||
|         contents: token_stream_to_string(combined), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn generate_ast_node_file(build_specs: &[BuildSpec]) -> AstGeneratedFile { | ||||
|     let impls = build_specs | ||||
|         .iter() | ||||
|         .map(|build_spec| { | ||||
|             let maybe_stream = make_ast_node_impl(build_spec); | ||||
|             if let Some(stream) = &maybe_stream { | ||||
|                 debug_built_spec(build_spec, &stream); | ||||
|             } | ||||
|             maybe_stream | ||||
|         }) | ||||
|         .filter(Option::is_some) | ||||
|         .map(Option::unwrap) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let ast_enum_members = build_specs | ||||
|         .iter() | ||||
|         .map(|build_spec| make_ast_enum_member(build_spec)) | ||||
|         .filter(Option::is_some) | ||||
|         .map(Option::unwrap) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let ast_enum_members_mut = build_specs | ||||
|         .iter() | ||||
|         .map(|build_spec| make_ast_enum_member_mut(build_spec)) | ||||
|         .filter(Option::is_some) | ||||
|         .map(Option::unwrap) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let combined = quote! { | ||||
|         use crate::ast::node::*; | ||||
| 
 | ||||
|         pub enum AstNodeRef<'a> { | ||||
|             #(#ast_enum_members,)* | ||||
|         } | ||||
| 
 | ||||
|         pub enum AstNodeRefMut<'a> { | ||||
|             #(#ast_enum_members_mut,)* | ||||
|         } | ||||
| 
 | ||||
|         pub trait AstNode { | ||||
|             fn children(&self) -> Vec<AstNodeRef>; | ||||
| 
 | ||||
|             fn children_mut(&mut self) -> Vec<AstNodeRefMut>; | ||||
| 
 | ||||
|             fn as_node_ref(&self) -> AstNodeRef; | ||||
| 
 | ||||
|             fn as_node_ref_mut(&mut self) -> AstNodeRefMut; | ||||
|         } | ||||
| 
 | ||||
|         #(#impls)* | ||||
|     }; | ||||
|     AstGeneratedFile { | ||||
|         name: String::from("ast_node.rs"), | ||||
|         contents: token_stream_to_string(combined), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn generate_walk_file(build_specs: &[BuildSpec]) -> AstGeneratedFile { | ||||
|     let stream = make_walk_fn(build_specs); | ||||
|     AstGeneratedFile { | ||||
|         name: String::from("walk.rs"), | ||||
|         contents: token_stream_to_string(stream), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn get_build_specs(yaml: &str) -> Vec<BuildSpec> { | ||||
|     deserialize_yaml_spec(yaml) | ||||
| } | ||||
| 
 | ||||
| pub fn generate_files(build_specs: &[BuildSpec]) -> Vec<AstGeneratedFile> { | ||||
|     vec![ | ||||
|         generate_build_file(build_specs), | ||||
|         generate_node_file(build_specs), | ||||
|         generate_pretty_print_file(build_specs), | ||||
|         generate_ast_node_file(build_specs), | ||||
|         generate_walk_file(build_specs), | ||||
|     ] | ||||
| } | ||||
|  | ||||
							
								
								
									
										6
									
								
								ast-generator/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								ast-generator/src/main.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| use ast_generator::test_dump; | ||||
| 
 | ||||
| fn main() { | ||||
|     let s = test_dump(); | ||||
|     println!("{}", s); | ||||
| } | ||||
| @ -1,273 +0,0 @@ | ||||
| use crate::spec::leaf_enum_spec::LeafEnumBuildSpec; | ||||
| use crate::spec::leaf_struct_spec::{LeafStructBuildSpec, LeafStructMemberKind}; | ||||
| use crate::spec::polymorphic_enum_loop_spec::{ | ||||
|     PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuildChild, | ||||
| }; | ||||
| use crate::spec::polymorphic_type_spec::PolymorphicTypeBuildSpec; | ||||
| use crate::spec::struct_spec::{MemberChildBuild, StructChild, StructSpec, VecChildBuild}; | ||||
| use crate::spec::tree_enum_spec::{EnumRuleChildKind, TreeEnumBuildSpec}; | ||||
| use crate::spec::BuildSpec; | ||||
| use convert_case::{Case, Casing}; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| fn make_polymorphic_enum_loop_p2_impl(spec: &PolymorphicEnumLoopBuildSpec) -> TokenStream { | ||||
|     let type_ident = format_ident!("{}", spec.name()); | ||||
|     let type_string = spec.name(); | ||||
| 
 | ||||
|     let build = spec | ||||
|         .rules() | ||||
|         .find(|rule| match rule { | ||||
|             PolymorphicEnumLoopRule::Build(_) => true, | ||||
|             _ => false, | ||||
|         }) | ||||
|         .map(|rule| match rule { | ||||
|             PolymorphicEnumLoopRule::Build(build) => build, | ||||
|             _ => unreachable!(), | ||||
|         }) | ||||
|         .unwrap(); | ||||
| 
 | ||||
|     let child_print_statements = build | ||||
|         .children() | ||||
|         .map(|child| { | ||||
|             let child_ident = match child { | ||||
|                 PolymorphicEnumLoopRuleBuildChild::UseCurrent(use_current) => { | ||||
|                     format_ident!("{}", use_current.name()) | ||||
|                 } | ||||
|                 PolymorphicEnumLoopRuleBuildChild::OnEach(on_each) => { | ||||
|                     format_ident!("{}", on_each.name()) | ||||
|                 } | ||||
|             }; | ||||
|             quote! { | ||||
|                 self.#child_ident().pretty_print(writer)?; | ||||
|             } | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     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(()) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_polymorphic_type_p2_impl(spec: &PolymorphicTypeBuildSpec) -> TokenStream { | ||||
|     let type_ident = format_ident!("{}", spec.name()); | ||||
|     let child_matchers = spec | ||||
|         .variants() | ||||
|         .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::<Vec<_>>(); | ||||
| 
 | ||||
|     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); | ||||
|             let name_str = rule; | ||||
|             quote! { | ||||
|                 #type_ident::#enum_variant_ident => writer.writeln_indented(#name_str) | ||||
|             } | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     quote! { | ||||
|         impl PrettyPrint for #type_ident { | ||||
|             fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { | ||||
|                 match self { | ||||
|                     #(#child_matchers,)* | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_tree_enum_p2_impl(spec: &TreeEnumBuildSpec) -> 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.node_kind().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::<Vec<_>>(); | ||||
| 
 | ||||
|     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("{}"), | ||||
|             LeafStructMemberKind::FileId => None, | ||||
|             LeafStructMemberKind::Range => None, | ||||
|         }) | ||||
|         .filter(Option::is_some) | ||||
|         .map(Option::unwrap) | ||||
|         .collect::<Vec<_>>() | ||||
|         .join(", "); | ||||
| 
 | ||||
|     let format_string = format!("{}({})", leaf_struct_build_spec.build(), member_formatters); | ||||
| 
 | ||||
|     let members = leaf_struct_build_spec | ||||
|         .members() | ||||
|         .map(|member| match member.kind() { | ||||
|             LeafStructMemberKind::String => { | ||||
|                 let member_ident = format_ident!("{}", member.name()); | ||||
|                 Some(quote! { | ||||
|                     self.#member_ident() | ||||
|                 }) | ||||
|             } | ||||
|             _ => None, | ||||
|         }) | ||||
|         .filter(Option::is_some) | ||||
|         .map(Option::unwrap) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     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: &StructSpec) -> TokenStream { | ||||
|     let child_print_statements = struct_build_spec | ||||
|         .children() | ||||
|         .map(|child| match child { | ||||
|             StructChild::SkipChild(_) => None, | ||||
|             StructChild::VecChild(vec_child) => match vec_child.build() { | ||||
|                 VecChildBuild::Node(_) => { | ||||
|                     let child_ident = format_ident!("{}", vec_child.name()); | ||||
|                     Some(quote! { | ||||
|                         for child in self.#child_ident() { | ||||
|                             child.pretty_print(writer)?; | ||||
|                         } | ||||
|                     }) | ||||
|                 } | ||||
|                 VecChildBuild::String(_) => None, | ||||
|             }, | ||||
|             StructChild::MemberChild(member_child) => match member_child.build() { | ||||
|                 MemberChildBuild::Node(_) => { | ||||
|                     let child_ident = format_ident!("{}", member_child.name()); | ||||
|                     if 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)?; | ||||
|                         }) | ||||
|                     } | ||||
|                 } | ||||
|                 MemberChildBuild::Boolean(_) => { | ||||
|                     let format_string = format!("{}({})", member_child.name(), "{}"); | ||||
|                     let child_ident = format_ident!("{}", member_child.name()); | ||||
|                     Some(quote! { | ||||
|                         writer.writeln_indented(&format!(#format_string, self.#child_ident()))?; | ||||
|                     }) | ||||
|                 } | ||||
|             }, | ||||
|             StructChild::Special(_) => None | ||||
|         }) | ||||
|         .filter(Option::is_some) | ||||
|         .map(Option::unwrap) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     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) -> Option<TokenStream> { | ||||
|     match build_spec { | ||||
|         BuildSpec::Struct(struct_spec) => Some(make_struct_p2_impl(struct_spec)), | ||||
|         BuildSpec::LeafStruct(leaf_struct) => Some(make_leaf_struct_p2_impl(leaf_struct)), | ||||
|         BuildSpec::Enum(enum_spec) => Some(make_tree_enum_p2_impl(enum_spec)), | ||||
|         BuildSpec::LeafEnum(leaf_enum) => Some(make_leaf_enum_p2_impl(leaf_enum)), | ||||
|         BuildSpec::Production(_) => None, | ||||
|         BuildSpec::NodeProduction(_) => None, | ||||
|         BuildSpec::PolymorphicType(polymorphic_type) => { | ||||
|             Some(make_polymorphic_type_p2_impl(polymorphic_type)) | ||||
|         } | ||||
|         BuildSpec::PolymorphicPassThrough(_) => None, | ||||
|         BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop) => { | ||||
|             Some(make_polymorphic_enum_loop_p2_impl(polymorphic_enum_loop)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										328
									
								
								ast-generator/src/spec.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										328
									
								
								ast-generator/src/spec.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,328 @@ | ||||
| use crate::build_fn_gen::make_build_fn_name; | ||||
| use convert_case::{Case, Casing}; | ||||
| 
 | ||||
| pub enum BuildSpec { | ||||
|     Enum(EnumBuildSpec), | ||||
|     Struct(StructBuildSpec), | ||||
| } | ||||
| 
 | ||||
| pub struct EnumBuildSpec { | ||||
|     name: String, | ||||
|     build: String, | ||||
|     rules: Vec<EnumRule>, | ||||
| } | ||||
| 
 | ||||
| impl EnumBuildSpec { | ||||
|     pub fn from_name(name: &str, rules: Vec<EnumRule>) -> Self { | ||||
|         EnumBuildSpec { | ||||
|             name: name.to_string(), | ||||
|             build: name.to_string(), | ||||
|             rules, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// The top-level key for the build spec in the yaml file.
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
| 
 | ||||
|     /// The enum type to be built, in Pascal case.
 | ||||
|     pub fn build(&self) -> &str { | ||||
|         &self.build | ||||
|     } | ||||
| 
 | ||||
|     /// The individual rule specs.
 | ||||
|     pub fn rules(&self) -> &[EnumRule] { | ||||
|         &self.rules | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct EnumRule { | ||||
|     rule: String, | ||||
|     build: String, | ||||
|     with: String, | ||||
| } | ||||
| 
 | ||||
| impl EnumRule { | ||||
|     pub fn from_rule(rule: &str) -> Self { | ||||
|         Self { | ||||
|             rule: rule.to_string(), | ||||
|             build: rule.to_string(), | ||||
|             with: make_build_fn_name(rule), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn new(rule: &str, build: &str, with: &str) -> Self { | ||||
|         Self { | ||||
|             rule: rule.to_string(), | ||||
|             build: build.to_string(), | ||||
|             with: with.to_string(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// The enum rule to match, in Pascal case.
 | ||||
|     pub fn rule(&self) -> &str { | ||||
|         &self.rule | ||||
|     } | ||||
| 
 | ||||
|     /// The type to build, in Pascal case.
 | ||||
|     pub fn build(&self) -> &str { | ||||
|         &self.build | ||||
|     } | ||||
| 
 | ||||
|     /// The build-fn name, in snake case.
 | ||||
|     pub fn with(&self) -> &str { | ||||
|         &self.with | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct StructBuildSpec { | ||||
|     name: String, | ||||
|     build: String, | ||||
|     var_name: String, | ||||
|     with: String, | ||||
|     children: Vec<ChildSpec>, | ||||
| } | ||||
| 
 | ||||
| impl StructBuildSpec { | ||||
|     pub fn from_name(name: &str, child_specs: Vec<ChildSpec>) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             build: name.to_string(), | ||||
|             var_name: name.to_case(Case::Snake), | ||||
|             with: make_build_fn_name(name), | ||||
|             children: child_specs, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// The top-level name of this build spec.
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
| 
 | ||||
|     /// The type to be built, in Pascal case.
 | ||||
|     pub fn build(&self) -> &str { | ||||
|         &self.build | ||||
|     } | ||||
| 
 | ||||
|     /// The name of the variable to be built, in snake case.
 | ||||
|     pub fn var_name(&self) -> &str { | ||||
|         &self.var_name | ||||
|     } | ||||
| 
 | ||||
|     /// The build-fn name, in snake case.
 | ||||
|     pub fn with(&self) -> &str { | ||||
|         &self.with | ||||
|     } | ||||
| 
 | ||||
|     /// The children for this build spec.
 | ||||
|     pub fn children(&self) -> &[ChildSpec] { | ||||
|         &self.children | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub enum ChildSpec { | ||||
|     SkipChild(SkipChild), | ||||
|     VecChild(VecChild), | ||||
|     SingleChild(SingleChild), | ||||
| } | ||||
| 
 | ||||
| pub struct SkipChild { | ||||
|     name: String, | ||||
|     rule: String, | ||||
| } | ||||
| 
 | ||||
| impl SkipChild { | ||||
|     pub fn new(name: &str, rule: &str) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             rule: rule.to_string(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// The name of this child spec.
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
| 
 | ||||
|     /// The grammar rule to match.
 | ||||
|     pub fn rule(&self) -> &str { | ||||
|         &self.rule | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct VecChild { | ||||
|     name: String, | ||||
|     rule: String, | ||||
|     build: VecChildToBuild, | ||||
| } | ||||
| 
 | ||||
| impl VecChild { | ||||
|     pub fn new(name: &str, rule: &str, build: VecChildToBuild) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             rule: rule.to_string(), | ||||
|             build, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// The name of this child.
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
| 
 | ||||
|     /// The rule to match to build this child.
 | ||||
|     pub fn rule(&self) -> &str { | ||||
|         &self.rule | ||||
|     } | ||||
| 
 | ||||
|     /// The build info for this child.
 | ||||
|     pub fn build(&self) -> &VecChildToBuild { | ||||
|         &self.build | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum VecChildToBuild { | ||||
|     Type(VecTypeChildToBuild), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct VecTypeChildToBuild { | ||||
|     build: String, | ||||
|     var_name: String, | ||||
|     with: String, | ||||
| } | ||||
| 
 | ||||
| impl VecTypeChildToBuild { | ||||
|     pub fn new(build: &str, var_name: &str, with: &str) -> Self { | ||||
|         Self { | ||||
|             build: build.to_string(), | ||||
|             var_name: var_name.to_string(), | ||||
|             with: with.to_string(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// The type to build, in Pascal case.
 | ||||
|     pub fn build(&self) -> &str { | ||||
|         &self.build | ||||
|     } | ||||
| 
 | ||||
|     /// The name of the variable to build, in snake case.
 | ||||
|     pub fn var_name(&self) -> &str { | ||||
|         &self.var_name | ||||
|     } | ||||
| 
 | ||||
|     /// The build-fn name.
 | ||||
|     pub fn with(&self) -> &str { | ||||
|         &self.with | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct SingleChild { | ||||
|     name: String, | ||||
|     rule: String, | ||||
|     build: SingleChildToBuild, | ||||
| } | ||||
| 
 | ||||
| impl SingleChild { | ||||
|     pub fn from_name_snake(name: &str) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             rule: name.to_case(Case::Pascal), | ||||
|             build: SingleChildToBuild::Type(SingleTypeChildToBuild::from_build_or_rule( | ||||
|                 &name.to_case(Case::Pascal), | ||||
|             )), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn new(name: &str, rule: &str, build: SingleChildToBuild) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             rule: rule.to_string(), | ||||
|             build, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// The name of this child in the yaml file, in snake case.
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
| 
 | ||||
|     /// The grammar rule to match to build this child.
 | ||||
|     pub fn rule(&self) -> &str { | ||||
|         &self.rule | ||||
|     } | ||||
| 
 | ||||
|     /// The specification for what to actually build.
 | ||||
|     pub fn build(&self) -> &SingleChildToBuild { | ||||
|         &self.build | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum SingleChildToBuild { | ||||
|     Type(SingleTypeChildToBuild), | ||||
|     Boolean(SingleBooleanChildToBuild), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct SingleTypeChildToBuild { | ||||
|     build: String, | ||||
|     var_name: String, | ||||
|     with: String, | ||||
| } | ||||
| 
 | ||||
| impl SingleTypeChildToBuild { | ||||
|     pub fn from_build_or_rule(build_or_rule: &str) -> Self { | ||||
|         Self { | ||||
|             build: build_or_rule.to_string(), | ||||
|             var_name: build_or_rule.to_case(Case::Snake), | ||||
|             with: make_build_fn_name(build_or_rule), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// The type to build, in Pascal case.
 | ||||
|     pub fn build(&self) -> &str { | ||||
|         &self.build | ||||
|     } | ||||
| 
 | ||||
|     /// The variable name to build, in snake case.
 | ||||
|     pub fn var_name(&self) -> &str { | ||||
|         &self.var_name | ||||
|     } | ||||
| 
 | ||||
|     /// The build-fn name.
 | ||||
|     pub fn with(&self) -> &str { | ||||
|         &self.with | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct SingleBooleanChildToBuild { | ||||
|     var_name: String, | ||||
|     on: BuildBooleanOn, | ||||
| } | ||||
| 
 | ||||
| impl SingleBooleanChildToBuild { | ||||
|     pub fn new(var_name: &str, on: BuildBooleanOn) -> Self { | ||||
|         Self { | ||||
|             var_name: var_name.to_string(), | ||||
|             on, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn var_name(&self) -> &str { | ||||
|         &self.var_name | ||||
|     } | ||||
| 
 | ||||
|     pub fn on(&self) -> &BuildBooleanOn { | ||||
|         &self.on | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum BuildBooleanOn { | ||||
|     RulePresent, | ||||
| } | ||||
| @ -1,21 +0,0 @@ | ||||
| pub struct LeafEnumBuildSpec { | ||||
|     build: String, | ||||
|     rules: Vec<String>, | ||||
| } | ||||
| 
 | ||||
| impl LeafEnumBuildSpec { | ||||
|     pub fn new(build: &str, rules: Vec<String>) -> Self { | ||||
|         Self { | ||||
|             build: build.to_string(), | ||||
|             rules, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn build(&self) -> &str { | ||||
|         &self.build | ||||
|     } | ||||
| 
 | ||||
|     pub fn rules(&self) -> impl Iterator<Item = &str> { | ||||
|         self.rules.iter().map(AsRef::as_ref) | ||||
|     } | ||||
| } | ||||
| @ -1,55 +0,0 @@ | ||||
| pub struct LeafStructBuildSpec { | ||||
|     build: String, | ||||
|     members: Vec<Box<LeafStructMember>>, | ||||
|     derive: Vec<String>, | ||||
| } | ||||
| 
 | ||||
| impl LeafStructBuildSpec { | ||||
|     pub fn new(build: &str, members: Vec<Box<LeafStructMember>>, derive: Vec<String>) -> Self { | ||||
|         Self { | ||||
|             build: build.to_string(), | ||||
|             members, | ||||
|             derive, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn build(&self) -> &str { | ||||
|         &self.build | ||||
|     } | ||||
| 
 | ||||
|     pub fn members(&self) -> impl Iterator<Item = &LeafStructMember> { | ||||
|         self.members.iter().map(Box::as_ref) | ||||
|     } | ||||
|     
 | ||||
|     pub fn derive(&self) -> impl Iterator<Item = &str> { | ||||
|         self.derive.iter().map(String::as_str) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct LeafStructMember { | ||||
|     name: String, | ||||
|     kind: LeafStructMemberKind, | ||||
| } | ||||
| 
 | ||||
| impl LeafStructMember { | ||||
|     pub fn new(name: &str, kind: LeafStructMemberKind) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             kind, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
| 
 | ||||
|     pub fn kind(&self) -> &LeafStructMemberKind { | ||||
|         &self.kind | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub enum LeafStructMemberKind { | ||||
|     String, | ||||
|     FileId, | ||||
|     Range | ||||
| } | ||||
| @ -1,31 +0,0 @@ | ||||
| use crate::spec::node_production_spec::NodeProductionBuildSpec; | ||||
| use crate::spec::polymorphic_enum_loop_spec::PolymorphicEnumLoopBuildSpec; | ||||
| use leaf_enum_spec::LeafEnumBuildSpec; | ||||
| use leaf_struct_spec::LeafStructBuildSpec; | ||||
| use polymorphic_type_spec::PolymorphicTypeBuildSpec; | ||||
| use production_spec::ProductionBuildSpec; | ||||
| use struct_spec::StructSpec; | ||||
| use tree_enum_spec::TreeEnumBuildSpec; | ||||
| use crate::spec::polymorphic_pass_through_spec::PolymorphicPassThroughBuildSpec; | ||||
| 
 | ||||
| pub(crate) mod leaf_enum_spec; | ||||
| pub(crate) mod leaf_struct_spec; | ||||
| pub(crate) mod node_production_spec; | ||||
| pub(crate) mod polymorphic_enum_loop_spec; | ||||
| pub(crate) mod polymorphic_pass_through_spec; | ||||
| pub(crate) mod polymorphic_type_spec; | ||||
| pub(crate) mod production_spec; | ||||
| pub(crate) mod struct_spec; | ||||
| pub(crate) mod tree_enum_spec; | ||||
| 
 | ||||
| pub enum BuildSpec { | ||||
|     Enum(TreeEnumBuildSpec), | ||||
|     LeafEnum(LeafEnumBuildSpec), | ||||
|     Struct(StructSpec), | ||||
|     LeafStruct(LeafStructBuildSpec), | ||||
|     Production(ProductionBuildSpec), | ||||
|     NodeProduction(NodeProductionBuildSpec), | ||||
|     PolymorphicType(PolymorphicTypeBuildSpec), | ||||
|     PolymorphicEnumLoop(PolymorphicEnumLoopBuildSpec), | ||||
|     PolymorphicPassThrough(PolymorphicPassThroughBuildSpec) | ||||
| } | ||||
| @ -1,27 +0,0 @@ | ||||
| pub struct NodeProductionBuildSpec { | ||||
|     name: String, | ||||
|     kind: String, | ||||
|     with: String | ||||
| } | ||||
| 
 | ||||
| impl NodeProductionBuildSpec { | ||||
|     pub fn new(name: &str, kind: &str, with: &str) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             kind: kind.to_string(), | ||||
|             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 | ||||
|     } | ||||
| } | ||||
| @ -1,143 +0,0 @@ | ||||
| pub struct PolymorphicEnumLoopBuildSpec { | ||||
|     name: String, | ||||
|     kind: String, | ||||
|     reverse: bool, | ||||
|     rules: Vec<Box<PolymorphicEnumLoopRule>>, | ||||
| } | ||||
| 
 | ||||
| impl PolymorphicEnumLoopBuildSpec { | ||||
|     pub fn new(name: &str, kind: &str, reverse: bool, rules: Vec<Box<PolymorphicEnumLoopRule>>) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             kind: kind.to_string(), | ||||
|             reverse, | ||||
|             rules, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
| 
 | ||||
|     pub fn kind(&self) -> &str { | ||||
|         &self.kind | ||||
|     } | ||||
|     
 | ||||
|     pub fn reverse(&self) -> bool { | ||||
|         self.reverse | ||||
|     } | ||||
| 
 | ||||
|     pub fn rules(&self) -> impl Iterator<Item = &PolymorphicEnumLoopRule> { | ||||
|         self.rules.iter().map(Box::as_ref) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub enum PolymorphicEnumLoopRule { | ||||
|     PassThrough(PolymorphicEnumLoopRulePassThrough), | ||||
|     Build(PolymorphicEnumLoopRuleBuild), | ||||
| } | ||||
| 
 | ||||
| pub struct PolymorphicEnumLoopRulePassThrough { | ||||
|     name: String, | ||||
|     kind: String, | ||||
|     with: String, | ||||
| } | ||||
| 
 | ||||
| impl PolymorphicEnumLoopRulePassThrough { | ||||
|     pub fn new(name: &str, kind: &str, with: &str) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             kind: kind.to_string(), | ||||
|             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 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct PolymorphicEnumLoopRuleBuild { | ||||
|     name: String, | ||||
|     variant: String, | ||||
|     children: Vec<Box<PolymorphicEnumLoopRuleBuildChild>>, | ||||
| } | ||||
| 
 | ||||
| impl PolymorphicEnumLoopRuleBuild { | ||||
|     pub fn new(name: &str, variant: &str, children: Vec<Box<PolymorphicEnumLoopRuleBuildChild>>) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             variant: variant.to_string(), | ||||
|             children, | ||||
|         } | ||||
|     } | ||||
|     
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
| 
 | ||||
|     pub fn variant(&self) -> &str { | ||||
|         &self.variant | ||||
|     } | ||||
| 
 | ||||
|     pub fn children(&self) -> impl Iterator<Item = &PolymorphicEnumLoopRuleBuildChild> { | ||||
|         self.children.iter().map(Box::as_ref) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub enum PolymorphicEnumLoopRuleBuildChild { | ||||
|     UseCurrent(PolymorphicEnumLoopChildUseCurrent), | ||||
|     OnEach(PolymorphicEnumLoopRuleChildOnEach), | ||||
| } | ||||
| 
 | ||||
| pub struct PolymorphicEnumLoopChildUseCurrent { | ||||
|     name: String, | ||||
|     kind: String, | ||||
| } | ||||
| 
 | ||||
| impl PolymorphicEnumLoopChildUseCurrent { | ||||
|     pub fn new(name: &str, kind: &str) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             kind: kind.to_string(), | ||||
|         } | ||||
|     } | ||||
|     
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
| 
 | ||||
|     pub fn kind(&self) -> &str { | ||||
|         &self.kind | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct PolymorphicEnumLoopRuleChildOnEach { | ||||
|     name: String, | ||||
|     rule: String, | ||||
| } | ||||
| 
 | ||||
| impl PolymorphicEnumLoopRuleChildOnEach { | ||||
|     pub fn new(name: &str, rule: &str) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             rule: rule.to_string(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
|     
 | ||||
|     pub fn rule(&self) -> &str { | ||||
|         &self.rule | ||||
|     } | ||||
| } | ||||
| @ -1,36 +0,0 @@ | ||||
| pub struct PolymorphicPassThroughBuildSpec { | ||||
|     name: String, | ||||
|     build_kind: String, | ||||
|     variants: Vec<Box<PolymorphicPassThroughVariant>>, | ||||
| } | ||||
| 
 | ||||
| impl PolymorphicPassThroughBuildSpec { | ||||
|     pub fn new( | ||||
|         name: &str, | ||||
|         build_kind: &str, | ||||
|         variants: Vec<Box<PolymorphicPassThroughVariant>>, | ||||
|     ) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             build_kind: build_kind.to_string(), | ||||
|             variants, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
| 
 | ||||
|     pub fn build_kind(&self) -> &str { | ||||
|         &self.build_kind | ||||
|     } | ||||
| 
 | ||||
|     pub fn variants(&self) -> impl Iterator<Item = &PolymorphicPassThroughVariant> { | ||||
|         self.variants.iter().map(Box::as_ref) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub enum PolymorphicPassThroughVariant { | ||||
|     Inner { name: String, kind: String }, | ||||
|     PassThrough { name: String, kind: String }, | ||||
| } | ||||
| @ -1,49 +0,0 @@ | ||||
| pub struct PolymorphicTypeBuildSpec { | ||||
|     name: String, | ||||
|     variants: Vec<Box<PolymorphicTypeVariant>>, | ||||
|     kind: String, | ||||
| } | ||||
| 
 | ||||
| impl PolymorphicTypeBuildSpec { | ||||
|     pub fn new(name: &str, variants: Vec<Box<PolymorphicTypeVariant>>, kind: &str) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             variants, | ||||
|             kind: kind.to_string(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
| 
 | ||||
|     pub fn variants(&self) -> impl Iterator<Item = &PolymorphicTypeVariant> { | ||||
|         self.variants.iter().map(Box::as_ref) | ||||
|     } | ||||
| 
 | ||||
|     pub fn kind(&self) -> &str { | ||||
|         self.kind.as_str() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct PolymorphicTypeVariant { | ||||
|     name: String, | ||||
|     inner_kind: String, | ||||
| } | ||||
| 
 | ||||
| impl PolymorphicTypeVariant { | ||||
|     pub fn new(name: &str, inner_kind: &str) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             inner_kind: inner_kind.to_string(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
| 
 | ||||
|     pub fn inner_kind(&self) -> &str { | ||||
|         &self.inner_kind | ||||
|     } | ||||
| } | ||||
| @ -1,38 +0,0 @@ | ||||
| pub struct ProductionBuildSpec { | ||||
|     name: String, | ||||
|     kind: ProductionKind, | ||||
| } | ||||
| 
 | ||||
| impl ProductionBuildSpec { | ||||
|     pub fn new(name: &str, kind: ProductionKind) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             kind, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
| 
 | ||||
|     pub fn kind(&self) -> &ProductionKind { | ||||
|         &self.kind | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub enum ProductionKind { | ||||
|     Int, | ||||
|     Long, | ||||
|     Double, | ||||
|     String(ProductionStringFrom), | ||||
|     Boolean(ProductionBooleanFrom), | ||||
| } | ||||
| 
 | ||||
| pub enum ProductionStringFrom { | ||||
|     StringInner, | ||||
|     WholePair, | ||||
| } | ||||
| 
 | ||||
| pub enum ProductionBooleanFrom { | ||||
|     ParseWholePair, | ||||
| } | ||||
| @ -1,302 +0,0 @@ | ||||
| pub struct StructSpec { | ||||
|     build: String, | ||||
|     children: Vec<Box<StructChild>>, | ||||
|     fields: Vec<Box<StructField>>, | ||||
| } | ||||
| 
 | ||||
| impl StructSpec { | ||||
|     pub fn new( | ||||
|         build: &str, | ||||
|         children: Vec<Box<StructChild>>, | ||||
|         fields: Vec<Box<StructField>>, | ||||
|     ) -> Self { | ||||
|         Self { | ||||
|             build: build.to_string(), | ||||
|             children, | ||||
|             fields, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// The type to be built, in Pascal case.
 | ||||
|     pub fn build(&self) -> &str { | ||||
|         &self.build | ||||
|     } | ||||
| 
 | ||||
|     /// The children for this build spec.
 | ||||
|     pub fn children(&self) -> impl Iterator<Item = &StructChild> { | ||||
|         self.children.iter().map(Box::as_ref) | ||||
|     } | ||||
| 
 | ||||
|     pub fn fields(&self) -> impl Iterator<Item = &StructField> { | ||||
|         self.fields.iter().map(Box::as_ref) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub enum StructChild { | ||||
|     SkipChild(SkipChild), | ||||
|     VecChild(VecChild), | ||||
|     MemberChild(MemberChild), | ||||
|     Special(SpecialChild), | ||||
| } | ||||
| 
 | ||||
| impl StructChild { | ||||
|     pub fn is_special(&self) -> bool { | ||||
|         match self { | ||||
|             StructChild::Special(_) => true, | ||||
|             _ => false, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn unwrap_special(&self) -> Option<&SpecialChild> { | ||||
|         match self { | ||||
|             StructChild::Special(special_child) => Some(special_child), | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct SkipChild { | ||||
|     rule: String, | ||||
| } | ||||
| 
 | ||||
| impl SkipChild { | ||||
|     pub fn new(rule: &str) -> Self { | ||||
|         Self { | ||||
|             rule: rule.to_string(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// The grammar rule to match.
 | ||||
|     pub fn rule(&self) -> &str { | ||||
|         &self.rule | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct VecChild { | ||||
|     name: String, | ||||
|     rule: String, | ||||
|     build: Box<VecChildBuild>, | ||||
| } | ||||
| 
 | ||||
| impl VecChild { | ||||
|     pub fn new(name: &str, rule: &str, build: Box<VecChildBuild>) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             rule: rule.to_string(), | ||||
|             build, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// The name of this child.
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
| 
 | ||||
|     /// The rule to match to build this child.
 | ||||
|     pub fn rule(&self) -> &str { | ||||
|         &self.rule | ||||
|     } | ||||
| 
 | ||||
|     pub fn build(&self) -> &VecChildBuild { | ||||
|         &self.build | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub enum VecChildBuild { | ||||
|     String(VecChildStringBuild), | ||||
|     Node(VecChildNodeBuild), | ||||
| } | ||||
| 
 | ||||
| pub struct VecChildStringBuild { | ||||
|     with: String, | ||||
| } | ||||
| 
 | ||||
| impl VecChildStringBuild { | ||||
|     pub fn new(with: &str) -> Self { | ||||
|         Self { | ||||
|             with: with.to_string(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn with(&self) -> &str { | ||||
|         &self.with | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct VecChildNodeBuild { | ||||
|     kind: String, | ||||
|     with: String, | ||||
| } | ||||
| 
 | ||||
| impl VecChildNodeBuild { | ||||
|     pub fn new(kind: &str, with: &str) -> Self { | ||||
|         Self { | ||||
|             kind: kind.to_string(), | ||||
|             with: with.to_string(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn kind(&self) -> &str { | ||||
|         &self.kind | ||||
|     } | ||||
| 
 | ||||
|     pub fn with(&self) -> &str { | ||||
|         &self.with | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct MemberChild { | ||||
|     name: String, | ||||
|     rule: String, | ||||
|     optional: bool, | ||||
|     build: Box<MemberChildBuild>, | ||||
| } | ||||
| 
 | ||||
| impl MemberChild { | ||||
|     pub fn new(name: &str, rule: &str, optional: bool, build: Box<MemberChildBuild>) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             rule: rule.to_string(), | ||||
|             optional, | ||||
|             build, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// The name of this child in the yaml file, in snake case.
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
| 
 | ||||
|     /// The grammar rule to match to build this child.
 | ||||
|     pub fn rule(&self) -> &str { | ||||
|         &self.rule | ||||
|     } | ||||
| 
 | ||||
|     pub fn optional(&self) -> bool { | ||||
|         self.optional | ||||
|     } | ||||
| 
 | ||||
|     /// The specification for what to actually build.
 | ||||
|     pub fn build(&self) -> &MemberChildBuild { | ||||
|         &self.build | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum MemberChildBuild { | ||||
|     Node(NodeMemberBuild), | ||||
|     Boolean(BooleanMemberBuild), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct NodeMemberBuild { | ||||
|     kind: String, | ||||
|     with: String, | ||||
|     or_else: Option<String>, | ||||
| } | ||||
| 
 | ||||
| impl NodeMemberBuild { | ||||
|     pub fn new(kind: &str, with: &str, or_else: Option<String>) -> Self { | ||||
|         Self { | ||||
|             kind: kind.to_string(), | ||||
|             with: with.to_string(), | ||||
|             or_else, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// The type to build, in Pascal case.
 | ||||
|     pub fn kind(&self) -> &str { | ||||
|         &self.kind | ||||
|     } | ||||
| 
 | ||||
|     pub fn with(&self) -> &str { | ||||
|         &self.with | ||||
|     } | ||||
| 
 | ||||
|     /// The default fn to call when unwrapping the child (before passing as arg to new).
 | ||||
|     pub fn or_else(&self) -> Option<&str> { | ||||
|         self.or_else.as_deref() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct BooleanMemberBuild { | ||||
|     on: BooleanMemberBuildOn, | ||||
| } | ||||
| 
 | ||||
| impl BooleanMemberBuild { | ||||
|     pub fn new(on: BooleanMemberBuildOn) -> Self { | ||||
|         Self { on } | ||||
|     } | ||||
| 
 | ||||
|     pub fn on(&self) -> &BooleanMemberBuildOn { | ||||
|         &self.on | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| 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, | ||||
| } | ||||
| 
 | ||||
| pub struct StructField { | ||||
|     name: String, | ||||
|     kind: StructFieldKind, | ||||
|     optional: bool, | ||||
| } | ||||
| 
 | ||||
| impl StructField { | ||||
|     pub fn new(name: &str, kind: StructFieldKind, optional: bool) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             kind, | ||||
|             optional, | ||||
|         } | ||||
|     } | ||||
|     
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
|     
 | ||||
|     pub fn kind(&self) -> &StructFieldKind { | ||||
|         &self.kind | ||||
|     } | ||||
|     
 | ||||
|     pub fn optional(&self) -> bool { | ||||
|         self.optional | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub enum StructFieldKind { | ||||
|     USize, | ||||
| } | ||||
| @ -1,117 +0,0 @@ | ||||
| pub struct TreeEnumBuildSpec { | ||||
|     build: String, | ||||
|     rules: Vec<Box<TreeEnumRule>>, | ||||
| } | ||||
| 
 | ||||
| impl TreeEnumBuildSpec { | ||||
|     pub fn new(build: &str, rules: Vec<Box<TreeEnumRule>>) -> Self { | ||||
|         TreeEnumBuildSpec { | ||||
|             build: build.to_string(), | ||||
|             rules, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// The enum type to be built, in Pascal case.
 | ||||
|     pub fn build(&self) -> &str { | ||||
|         &self.build | ||||
|     } | ||||
| 
 | ||||
|     /// The individual rule specs.
 | ||||
|     pub fn rules(&self) -> impl Iterator<Item = &TreeEnumRule> { | ||||
|         self.rules.iter().map(Box::as_ref) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct TreeEnumRule { | ||||
|     rule: String, | ||||
|     child: Option<Box<EnumRuleChild>>, | ||||
| } | ||||
| 
 | ||||
| impl TreeEnumRule { | ||||
|     pub fn new(rule: &str, child: Option<Box<EnumRuleChild>>) -> Self { | ||||
|         Self { | ||||
|             rule: rule.to_string(), | ||||
|             child, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// The enum rule to match, in Pascal case.
 | ||||
|     pub fn rule(&self) -> &str { | ||||
|         &self.rule | ||||
|     } | ||||
| 
 | ||||
|     pub fn child(&self) -> Option<&EnumRuleChild> { | ||||
|         if let Some(child) = &self.child { | ||||
|             Some(child.as_ref()) | ||||
|         } else { | ||||
|             None | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct EnumRuleChild { | ||||
|     kind: Box<EnumRuleChildKind>, | ||||
| } | ||||
| 
 | ||||
| impl EnumRuleChild { | ||||
|     pub fn new(kind: Box<EnumRuleChildKind>) -> Self { | ||||
|         Self { kind } | ||||
|     } | ||||
| 
 | ||||
|     pub fn kind(&self) -> &EnumRuleChildKind { | ||||
|         &self.kind | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub enum EnumRuleChildKind { | ||||
|     Node(EnumRuleChildNodeKind), | ||||
|     Int(NameAndWith), | ||||
|     Long(NameAndWith), | ||||
|     Double(NameAndWith), | ||||
|     String(NameAndWith), | ||||
|     Boolean(NameAndWith), | ||||
| } | ||||
| 
 | ||||
| pub struct EnumRuleChildNodeKind { | ||||
|     node_kind: String, | ||||
|     with: String, | ||||
| } | ||||
| 
 | ||||
| impl EnumRuleChildNodeKind { | ||||
|     pub fn new(node_kind: &str, with: &str) -> Self { | ||||
|         Self { | ||||
|             node_kind: node_kind.to_string(), | ||||
|             with: with.to_string(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn node_kind(&self) -> &str { | ||||
|         &self.node_kind | ||||
|     } | ||||
|     
 | ||||
|     pub fn with(&self) -> &str { | ||||
|         &self.with | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct NameAndWith { | ||||
|     name: String, | ||||
|     with: String, | ||||
| } | ||||
| 
 | ||||
| impl NameAndWith { | ||||
|     pub fn new(name: &str, with: &str) -> Self { | ||||
|         Self { | ||||
|             name: name.to_string(), | ||||
|             with: with.to_string(), | ||||
|         } | ||||
|     } | ||||
|     
 | ||||
|     pub fn name(&self) -> &str { | ||||
|         &self.name | ||||
|     } | ||||
|     
 | ||||
|     pub fn with(&self) -> &str { | ||||
|         &self.with | ||||
|     } | ||||
| } | ||||
							
								
								
									
										162
									
								
								ast-generator/src/type_gen.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								ast-generator/src/type_gen.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,162 @@ | ||||
| use crate::spec::{ | ||||
|     BuildSpec, ChildSpec, EnumBuildSpec, SingleBooleanChildToBuild, SingleChildToBuild, | ||||
|     SingleTypeChildToBuild, StructBuildSpec, VecChild, VecChildToBuild, | ||||
| }; | ||||
| use proc_macro2::{Ident, TokenStream}; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| fn make_enum_type(build_spec: &EnumBuildSpec) -> TokenStream { | ||||
|     let children: Vec<TokenStream> = build_spec | ||||
|         .rules() | ||||
|         .iter() | ||||
|         .map(|rule| { | ||||
|             let member_name_ident = format_ident!("{}", rule.rule()); | ||||
|             let child_name_ident = format_ident!("{}", rule.build()); | ||||
|             quote! { | ||||
|                 #member_name_ident(#child_name_ident) | ||||
|             } | ||||
|         }) | ||||
|         .collect(); | ||||
|     let type_name_ident = format_ident!("{}", build_spec.build()); | ||||
|     quote! { | ||||
|         pub enum #type_name_ident { | ||||
|             #(#children),* | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn handle_vec_child( | ||||
|     vec_child: &VecChild, | ||||
|     member_names: &mut Vec<Ident>, | ||||
|     annotated_members: &mut Vec<TokenStream>, | ||||
|     accessors: &mut Vec<TokenStream>, | ||||
| ) { | ||||
|     let (child_ident, child_ident_mut, child_type_ident) = match vec_child.build() { | ||||
|         VecChildToBuild::Type(vec_type_child) => ( | ||||
|             format_ident!("{}", vec_type_child.var_name()), | ||||
|             format_ident!("{}_mut", vec_type_child.var_name()), | ||||
|             format_ident!("{}", vec_type_child.build()), | ||||
|         ), | ||||
|     }; | ||||
| 
 | ||||
|     member_names.push(child_ident.clone()); | ||||
|     annotated_members.push(quote! { | ||||
|         #child_ident: Vec<Box<#child_type_ident>> | ||||
|     }); | ||||
|     accessors.push(quote! { | ||||
|         pub fn #child_ident(&self) -> impl Iterator<Item = &#child_type_ident> { | ||||
|             self.#child_ident.iter().map(Box::as_ref) | ||||
|         } | ||||
| 
 | ||||
|         pub fn #child_ident_mut(&mut self) -> impl Iterator<Item = &mut #child_type_ident> { | ||||
|             self.#child_ident.iter_mut().map(Box::as_mut) | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| fn handle_single_type_child( | ||||
|     single_type_child: &SingleTypeChildToBuild, | ||||
|     member_names: &mut Vec<Ident>, | ||||
|     annotated_members: &mut Vec<TokenStream>, | ||||
|     accessors: &mut Vec<TokenStream>, | ||||
| ) { | ||||
|     let child_ident = format_ident!("{}", single_type_child.var_name()); | ||||
|     let child_ident_mut = format_ident!("{}_mut", single_type_child.var_name()); | ||||
|     let child_type_ident = format_ident!("{}", single_type_child.build()); | ||||
|     member_names.push(child_ident.clone()); | ||||
|     annotated_members.push(quote! { | ||||
|         #child_ident: Box<#child_type_ident> | ||||
|     }); | ||||
|     accessors.push(quote! { | ||||
|         pub fn #child_ident(&self) -> &#child_type_ident { | ||||
|             self.#child_ident.as_ref() | ||||
|         } | ||||
| 
 | ||||
|         pub fn #child_ident_mut(&mut self) -> &mut #child_type_ident { | ||||
|             self.#child_ident.as_mut() | ||||
|         } | ||||
|     }); | ||||
| } | ||||
| 
 | ||||
| fn handle_single_boolean_child( | ||||
|     single_boolean_child: &SingleBooleanChildToBuild, | ||||
|     member_names: &mut Vec<Ident>, | ||||
|     annotated_members: &mut Vec<TokenStream>, | ||||
|     accessors: &mut Vec<TokenStream>, | ||||
| ) { | ||||
|     let child_ident = format_ident!("{}", single_boolean_child.var_name()); | ||||
|     member_names.push(child_ident.clone()); | ||||
|     annotated_members.push(quote! { | ||||
|         #child_ident: bool | ||||
|     }); | ||||
|     accessors.push(quote! { | ||||
|         pub fn #child_ident(&self) -> bool { | ||||
|             self.#child_ident | ||||
|         } | ||||
|     }) | ||||
| } | ||||
| 
 | ||||
| fn make_struct_type(build_spec: &StructBuildSpec) -> TokenStream { | ||||
|     let mut member_names: Vec<Ident> = vec![]; | ||||
|     let mut annotated_members: Vec<TokenStream> = vec![]; | ||||
|     let mut accessors: Vec<TokenStream> = vec![]; | ||||
| 
 | ||||
|     for child_spec in build_spec.children().iter() { | ||||
|         match child_spec { | ||||
|             ChildSpec::SkipChild(_) => {} | ||||
|             ChildSpec::VecChild(vec_child) => { | ||||
|                 handle_vec_child( | ||||
|                     vec_child, | ||||
|                     &mut member_names, | ||||
|                     &mut annotated_members, | ||||
|                     &mut accessors, | ||||
|                 ); | ||||
|             } | ||||
|             ChildSpec::SingleChild(single_child) => { | ||||
|                 match single_child.build() { | ||||
|                     SingleChildToBuild::Type(single_type_child) => { | ||||
|                         handle_single_type_child( | ||||
|                             single_type_child, | ||||
|                             &mut member_names, | ||||
|                             &mut annotated_members, | ||||
|                             &mut accessors, | ||||
|                         ); | ||||
|                     } | ||||
|                     SingleChildToBuild::Boolean(single_boolean_child) => { | ||||
|                         handle_single_boolean_child( | ||||
|                             single_boolean_child, | ||||
|                             &mut member_names, | ||||
|                             &mut annotated_members, | ||||
|                             &mut accessors, | ||||
|                         ); | ||||
|                     } | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     let type_ident = format_ident!("{}", build_spec.build()); | ||||
| 
 | ||||
|     quote! { | ||||
|         pub struct #type_ident { | ||||
|             #(#annotated_members),* | ||||
|         } | ||||
| 
 | ||||
|         impl #type_ident { | ||||
|             pub fn new(#(#annotated_members),*) -> Self { | ||||
|                 Self { | ||||
|                     #(#member_names),* | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             #(#accessors)* | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn make_type(build_spec: &BuildSpec) -> TokenStream { | ||||
|     match build_spec { | ||||
|         BuildSpec::Enum(enum_build_spec) => make_enum_type(enum_build_spec), | ||||
|         BuildSpec::Struct(struct_build_spec) => make_struct_type(struct_build_spec), | ||||
|     } | ||||
| } | ||||
| @ -1,37 +0,0 @@ | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| use crate::spec::tree_enum_spec::{EnumRuleChildKind, TreeEnumBuildSpec}; | ||||
| 
 | ||||
| pub fn make_enum_type(build_spec: &TreeEnumBuildSpec) -> TokenStream { | ||||
|     let children: Vec<TokenStream> = build_spec | ||||
|         .rules() | ||||
|         .map(|enum_rule| { | ||||
|             let member_name_ident = format_ident!("{}", enum_rule.rule()); | ||||
|             if let Some(enum_rule_child) = enum_rule.child() { | ||||
|                 let child_type_ident = match enum_rule_child.kind() { | ||||
|                     EnumRuleChildKind::Node(node_child) => { | ||||
|                         format_ident!("{}", node_child.node_kind()) | ||||
|                     } | ||||
|                     EnumRuleChildKind::Int(_) => format_ident!("i32"), | ||||
|                     EnumRuleChildKind::Long(_) => format_ident!("i64"), | ||||
|                     EnumRuleChildKind::Double(_) => format_ident!("f64"), | ||||
|                     EnumRuleChildKind::String(_) => format_ident!("String"), | ||||
|                     EnumRuleChildKind::Boolean(_) => format_ident!("bool"), | ||||
|                 }; | ||||
|                 quote! { | ||||
|                     #member_name_ident(#child_type_ident) | ||||
|                 } | ||||
|             } else { | ||||
|                 quote! { | ||||
|                     #member_name_ident | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         .collect(); | ||||
|     let type_name_ident = format_ident!("{}", build_spec.build()); | ||||
|     quote! { | ||||
|         pub enum #type_name_ident { | ||||
|             #(#children),* | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,22 +0,0 @@ | ||||
| use crate::spec::leaf_enum_spec::LeafEnumBuildSpec; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| pub fn make_leaf_enum_type(build_spec: &LeafEnumBuildSpec) -> TokenStream { | ||||
|     let type_name_ident = format_ident!("{}", build_spec.build()); | ||||
|     let children = build_spec | ||||
|         .rules() | ||||
|         .map(|leaf_enum_rule| { | ||||
|             let rule_name_ident = format_ident!("{}", leaf_enum_rule); | ||||
|             quote! { | ||||
|                 #rule_name_ident | ||||
|             } | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     quote! { | ||||
|         pub enum #type_name_ident { | ||||
|             #(#children),* | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,118 +0,0 @@ | ||||
| use crate::spec::leaf_struct_spec::{LeafStructBuildSpec, LeafStructMemberKind}; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| pub fn make_leaf_struct_type(build_spec: &LeafStructBuildSpec) -> TokenStream { | ||||
|     let type_ident = format_ident!("{}", build_spec.build()); | ||||
| 
 | ||||
|     let annotated_members = build_spec | ||||
|         .members() | ||||
|         .map(|member| { | ||||
|             let name_ident = format_ident!("{}", member.name()); | ||||
|             let type_ident = match member.kind() { | ||||
|                 LeafStructMemberKind::String => quote! { String }, | ||||
|                 LeafStructMemberKind::FileId => quote! { usize }, | ||||
|                 LeafStructMemberKind::Range => quote! { Range<usize> }, | ||||
|             }; | ||||
|             quote! { | ||||
|                 #name_ident: #type_ident | ||||
|             } | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let member_args = build_spec.members().map(|member| { | ||||
|         let name_ident = format_ident!("{}", member.name()); | ||||
|         let type_stream = match member.kind() { | ||||
|             LeafStructMemberKind::String => { | ||||
|                 quote! { &str } | ||||
|             } | ||||
|             LeafStructMemberKind::FileId => { | ||||
|                 quote! { usize } | ||||
|             } | ||||
|             LeafStructMemberKind::Range => { | ||||
|                 quote! { Range<usize> } | ||||
|             } | ||||
|         }; | ||||
|         quote! { | ||||
|             #name_ident: #type_stream | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     let initializers = build_spec | ||||
|         .members() | ||||
|         .map(|leaf_struct_member| { | ||||
|             let member_ident = format_ident!("{}", leaf_struct_member.name()); | ||||
|             match leaf_struct_member.kind() { | ||||
|                 LeafStructMemberKind::String => { | ||||
|                     quote! { | ||||
|                         #member_ident: #member_ident.to_string() | ||||
|                     } | ||||
|                 } | ||||
|                 _ => quote! { #member_ident }, | ||||
|             } | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let accessors = build_spec | ||||
|         .members() | ||||
|         .map(|member| { | ||||
|             let name_ident = format_ident!("{}", member.name()); | ||||
|             match member.kind() { | ||||
|                 LeafStructMemberKind::String => { | ||||
|                     quote! { | ||||
|                         pub fn #name_ident(&self) -> &str { | ||||
|                             &self.#name_ident | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 LeafStructMemberKind::FileId => { | ||||
|                     quote! { | ||||
|                         pub fn #name_ident(&self) -> usize { | ||||
|                             self.#name_ident | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 LeafStructMemberKind::Range => { | ||||
|                     quote! { | ||||
|                         pub fn #name_ident(&self) -> Range<usize> { | ||||
|                             self.#name_ident | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let struct_stream = if build_spec.derive().count() > 0 { | ||||
|         let derives = build_spec.derive().map(|derive| { | ||||
|             format_ident!("{}", derive) | ||||
|         }).collect::<Vec<_>>(); | ||||
|         
 | ||||
|         quote! { | ||||
|             #[derive(#(#derives),*)] | ||||
|             pub struct #type_ident { | ||||
|                 #(#annotated_members),* | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         quote! { | ||||
|             pub struct #type_ident { | ||||
|                 #(#annotated_members),* | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|     
 | ||||
|     quote! { | ||||
|         #struct_stream | ||||
| 
 | ||||
|         impl #type_ident { | ||||
|             pub fn new(#(#member_args),*) -> Self { | ||||
|                 Self { | ||||
|                     #(#initializers),* | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             #(#accessors)* | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,37 +0,0 @@ | ||||
| mod enum_type; | ||||
| mod leaf_enum_type; | ||||
| mod leaf_struct_type; | ||||
| mod polymorphic_enum_loop_type; | ||||
| mod polymorphic_type_type; | ||||
| mod struct_type; | ||||
| 
 | ||||
| use crate::spec::BuildSpec; | ||||
| use crate::type_gen::enum_type::make_enum_type; | ||||
| use crate::type_gen::leaf_enum_type::make_leaf_enum_type; | ||||
| use crate::type_gen::leaf_struct_type::make_leaf_struct_type; | ||||
| use crate::type_gen::polymorphic_type_type::make_polymorphic_type_type; | ||||
| use crate::type_gen::struct_type::make_struct_type; | ||||
| use proc_macro2::TokenStream; | ||||
| use crate::type_gen::polymorphic_enum_loop_type::make_polymorphic_enum_loop_type; | ||||
| 
 | ||||
| pub fn make_type(build_spec: &BuildSpec) -> Option<TokenStream> { | ||||
|     match build_spec { | ||||
|         BuildSpec::Struct(struct_build_spec) => Some(make_struct_type(struct_build_spec)), | ||||
|         BuildSpec::LeafStruct(leaf_struct_build_spec) => { | ||||
|             Some(make_leaf_struct_type(leaf_struct_build_spec)) | ||||
|         } | ||||
|         BuildSpec::Enum(enum_build_spec) => Some(make_enum_type(enum_build_spec)), | ||||
|         BuildSpec::LeafEnum(leaf_enum_build_spec) => { | ||||
|             Some(make_leaf_enum_type(leaf_enum_build_spec)) | ||||
|         } | ||||
|         BuildSpec::Production(_) => None, | ||||
|         BuildSpec::NodeProduction(_) => None, | ||||
|         BuildSpec::PolymorphicType(polymorphic_build_spec) => { | ||||
|             Some(make_polymorphic_type_type(polymorphic_build_spec)) | ||||
|         } | ||||
|         BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop_spec) => { | ||||
|             Some(make_polymorphic_enum_loop_type(polymorphic_enum_loop_spec)) | ||||
|         }, | ||||
|         BuildSpec::PolymorphicPassThrough(_) => None, | ||||
|     } | ||||
| } | ||||
| @ -1,96 +0,0 @@ | ||||
| use crate::spec::polymorphic_enum_loop_spec::{PolymorphicEnumLoopBuildSpec, PolymorphicEnumLoopRule, PolymorphicEnumLoopRuleBuildChild}; | ||||
| 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 | ||||
|             } | ||||
|         }) | ||||
|         .map(|rule| { | ||||
|             match rule { | ||||
|                 PolymorphicEnumLoopRule::Build(build) => build, | ||||
|                 _ => unreachable!() | ||||
|             } | ||||
|         }) | ||||
|         .unwrap(); | ||||
|     
 | ||||
|     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> | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
|     
 | ||||
|     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::<Vec<_>>(); | ||||
|     
 | ||||
|     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())) | ||||
|                 } | ||||
|             }; | ||||
|             let child_mut_ident = format_ident!("{}_mut", child_ident); | ||||
|             
 | ||||
|             quote! { | ||||
|                 pub fn #child_ident(&self) -> &#child_type_ident { | ||||
|                     self.#child_ident.as_ref() | ||||
|                 } | ||||
|                 
 | ||||
|                 pub fn #child_mut_ident(&mut self) -> &mut #child_type_ident { | ||||
|                     self.#child_ident.as_mut() | ||||
|                 } | ||||
|             } | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
|     
 | ||||
|     quote! { | ||||
|         pub struct #type_ident { | ||||
|             #(#annotated_members),* | ||||
|         } | ||||
| 
 | ||||
|         impl #type_ident { | ||||
|             pub fn new(#(#annotated_members),*) -> Self { | ||||
|                 Self { | ||||
|                     #(#member_names),* | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             #(#accessors)* | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,22 +0,0 @@ | ||||
| use crate::spec::polymorphic_type_spec::PolymorphicTypeBuildSpec; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| pub fn make_polymorphic_type_type(build_spec: &PolymorphicTypeBuildSpec) -> TokenStream { | ||||
|     let members = build_spec | ||||
|         .variants() | ||||
|         .map(|enum_member| { | ||||
|             let member_ident = format_ident!("{}", enum_member.name()); | ||||
|             let inner_type_ident = format_ident!("{}", enum_member.inner_kind()); | ||||
|             quote! { | ||||
|                 #member_ident(#inner_type_ident) | ||||
|             } | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
|     let type_name_ident = format_ident!("{}", build_spec.name()); | ||||
|     quote! { | ||||
|         pub enum #type_name_ident { | ||||
|             #(#members),* | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,289 +0,0 @@ | ||||
| use crate::spec::struct_spec::{ | ||||
|     MemberChild, MemberChildBuild, SpecialChild, SpecialChildKind, StructChild, StructField, | ||||
|     StructFieldKind, StructSpec, VecChild, VecChildBuild, | ||||
| }; | ||||
| use proc_macro2::{Ident, TokenStream}; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| fn make_vec_child_accessors(vec_child: &VecChild) -> TokenStream { | ||||
|     let child_ident = format_ident!("{}", vec_child.name()); | ||||
|     match vec_child.build() { | ||||
|         VecChildBuild::String(_) => { | ||||
|             quote! { | ||||
|                 pub fn #child_ident(&self) -> impl Iterator<Item = &str> { | ||||
|                     self.#child_ident.iter().map(String::as_str) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         VecChildBuild::Node(vec_child_node_build) => { | ||||
|             let child_type_ident = format_ident!("{}", vec_child_node_build.kind()); | ||||
|             let child_ident_mut = format_ident!("{}_mut", vec_child.name()); | ||||
| 
 | ||||
|             quote! { | ||||
|                 pub fn #child_ident(&self) -> impl Iterator<Item = &#child_type_ident> { | ||||
|                     self.#child_ident.iter().map(Box::as_ref) | ||||
|                 } | ||||
| 
 | ||||
|                 pub fn #child_ident_mut(&mut self) -> impl Iterator<Item = &mut #child_type_ident> { | ||||
|                     self.#child_ident.iter_mut().map(Box::as_mut) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_member_child_accessors(member_child: &MemberChild) -> TokenStream { | ||||
|     let child_ident = format_ident!("{}", member_child.name()); | ||||
|     match member_child.build() { | ||||
|         MemberChildBuild::Node(node_member_build) => { | ||||
|             let return_type_ident = format_ident!("{}", node_member_build.kind()); | ||||
|             let child_ident_mut = format_ident!("{}_mut", member_child.name()); | ||||
| 
 | ||||
|             if member_child.optional() { | ||||
|                 quote! { | ||||
|                     pub fn #child_ident(&self) -> Option<&#return_type_ident> { | ||||
|                         if let Some(#child_ident) = &self.#child_ident { | ||||
|                             Some(#child_ident.as_ref()) | ||||
|                         } else { | ||||
|                             None | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
|                     pub fn #child_ident_mut(&mut self) -> Option<&mut #return_type_ident> { | ||||
|                         if let Some(#child_ident) = &mut self.#child_ident { | ||||
|                             Some(#child_ident.as_mut()) | ||||
|                         } else { | ||||
|                             None | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 quote! { | ||||
|                     pub fn #child_ident(&self) -> &#return_type_ident { | ||||
|                         self.#child_ident.as_ref() | ||||
|                     } | ||||
| 
 | ||||
|                     pub fn #child_ident_mut(&mut self) -> &mut #return_type_ident { | ||||
|                         self.#child_ident.as_mut() | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         MemberChildBuild::Boolean(_) => { | ||||
|             quote! { | ||||
|                 pub fn #child_ident(&self) -> bool { | ||||
|                     self.#child_ident | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_special_child_accessors(special_child: &SpecialChild) -> TokenStream { | ||||
|     let child_ident = format_ident!("{}", special_child.name()); | ||||
|     match special_child.kind() { | ||||
|         SpecialChildKind::FileId => { | ||||
|             quote! { | ||||
|                 pub fn #child_ident(&self) -> usize { | ||||
|                     self.#child_ident | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         SpecialChildKind::Range => { | ||||
|             quote! { | ||||
|                 pub fn #child_ident(&self) -> Range<usize> { | ||||
|                     self.#child_ident | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_child_accessors(child: &StructChild) -> Option<TokenStream> { | ||||
|     match child { | ||||
|         StructChild::SkipChild(_) => None, | ||||
|         StructChild::VecChild(vec_child) => Some(make_vec_child_accessors(vec_child)), | ||||
|         StructChild::MemberChild(member_child) => Some(make_member_child_accessors(member_child)), | ||||
|         StructChild::Special(special_child) => Some(make_special_child_accessors(special_child)), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_member_ident(child: &StructChild) -> Option<Ident> { | ||||
|     match child { | ||||
|         StructChild::SkipChild(_) => None, | ||||
|         StructChild::VecChild(vec_child) => Some(format_ident!("{}", vec_child.name())), | ||||
|         StructChild::MemberChild(member_child) => Some(format_ident!("{}", member_child.name())), | ||||
|         StructChild::Special(special_child) => Some(format_ident!("{}", special_child.name())), | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_vec_child_annotated_member(vec_child: &VecChild) -> TokenStream { | ||||
|     let child_ident = format_ident!("{}", vec_child.name()); | ||||
|     let type_stream = match vec_child.build() { | ||||
|         VecChildBuild::String(_) => quote! { String }, | ||||
|         VecChildBuild::Node(vec_child_node_build) => { | ||||
|             let type_ident = format_ident!("{}", vec_child_node_build.kind()); | ||||
|             quote! { Box<#type_ident> } | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     quote! { | ||||
|         #child_ident: Vec<#type_stream> | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_member_child_type_ident(member_child: &MemberChild) -> TokenStream { | ||||
|     match member_child.build() { | ||||
|         MemberChildBuild::Node(node_member_build) => { | ||||
|             let type_ident = format_ident!("{}", node_member_build.kind()); | ||||
|             quote! { Box<#type_ident> } | ||||
|         } | ||||
|         MemberChildBuild::Boolean(_) => { | ||||
|             quote! { bool } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_member_child_annotated_member(member_child: &MemberChild) -> TokenStream { | ||||
|     let child_name_ident = format_ident!("{}", member_child.name()); | ||||
|     let type_ident = make_member_child_type_ident(member_child); | ||||
|     let type_stream = if member_child.optional() { | ||||
|         quote! { Option<#type_ident> } | ||||
|     } else { | ||||
|         quote! { #type_ident } | ||||
|     }; | ||||
| 
 | ||||
|     quote! { | ||||
|         #child_name_ident: #type_stream | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_special_child_annotated_member(special_child: &SpecialChild) -> TokenStream { | ||||
|     let child_ident = format_ident!("{}", special_child.name()); | ||||
|     let child_type_ident = match special_child.kind() { | ||||
|         SpecialChildKind::FileId => quote! { usize }, | ||||
|         SpecialChildKind::Range => quote! { Range<usize> }, | ||||
|     }; | ||||
|     quote! { | ||||
|         #child_ident: #child_type_ident | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_annotated_member(child: &StructChild) -> Option<TokenStream> { | ||||
|     match child { | ||||
|         StructChild::SkipChild(_) => None, | ||||
|         StructChild::VecChild(vec_child) => Some(make_vec_child_annotated_member(vec_child)), | ||||
|         StructChild::MemberChild(member_child) => { | ||||
|             Some(make_member_child_annotated_member(member_child)) | ||||
|         } | ||||
|         StructChild::Special(special_child) => { | ||||
|             Some(make_special_child_annotated_member(special_child)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_annotated_field(field: &StructField) -> TokenStream { | ||||
|     let field_ident = format_ident!("{}", field.name()); | ||||
|     let field_type_ident = match field.kind() { | ||||
|         StructFieldKind::USize => quote! { usize }, | ||||
|     }; | ||||
|     if field.optional() { | ||||
|         quote! { | ||||
|             #field_ident: Option<#field_type_ident> | ||||
|         } | ||||
|     } else { | ||||
|         quote! { | ||||
|             #field_ident: #field_type_ident | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_field_init(field: &StructField) -> TokenStream { | ||||
|     let field_ident = format_ident!("{}", field.name()); | ||||
|     if field.optional() { | ||||
|         quote! { | ||||
|             #field_ident: None | ||||
|         } | ||||
|     } else { | ||||
|         todo!() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn make_field_accessors(field: &StructField) -> TokenStream { | ||||
|     let field_ident = format_ident!("{}", field.name()); | ||||
|     let field_type_ident = match field.kind() { | ||||
|         StructFieldKind::USize => quote! { usize }, | ||||
|     }; | ||||
|     if field.optional() { | ||||
|         let setter_ident = format_ident!("set_{}", field.name()); | ||||
|         quote! { | ||||
|             pub fn #field_ident(&self) -> Option<#field_type_ident> { | ||||
|                 self.#field_ident | ||||
|             } | ||||
| 
 | ||||
|             pub fn #setter_ident(&mut self, #field_ident: #field_type_ident) { | ||||
|                 self.#field_ident = Some(#field_ident); | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         todo!() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn make_struct_type(build_spec: &StructSpec) -> TokenStream { | ||||
|     let type_ident = format_ident!("{}", build_spec.build()); | ||||
|     let annotated_children = build_spec | ||||
|         .children() | ||||
|         .map(|child| make_annotated_member(child)) | ||||
|         .filter(Option::is_some) | ||||
|         .map(Option::unwrap) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let annotated_fields = build_spec | ||||
|         .fields() | ||||
|         .map(|field| make_annotated_field(field)) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let member_names = build_spec | ||||
|         .children() | ||||
|         .map(|child| make_member_ident(child)) | ||||
|         .filter(Option::is_some) | ||||
|         .map(Option::unwrap) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let field_inits = build_spec | ||||
|         .fields() | ||||
|         .map(|field| make_field_init(field)) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let child_accessors = build_spec | ||||
|         .children() | ||||
|         .map(|child| make_child_accessors(child)) | ||||
|         .filter(Option::is_some) | ||||
|         .map(Option::unwrap) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     let field_accessors = build_spec | ||||
|         .fields() | ||||
|         .map(|field| make_field_accessors(field)) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     quote! { | ||||
|         pub struct #type_ident { | ||||
|             #(#annotated_children,)* | ||||
|             #(#annotated_fields,)* | ||||
|         } | ||||
| 
 | ||||
|         impl #type_ident { | ||||
|             pub fn new(#(#annotated_children),*) -> Self { | ||||
|                 Self { | ||||
|                     #(#member_names,)* | ||||
|                     #(#field_inits,)* | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             #(#child_accessors)* | ||||
|             #(#field_accessors)* | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,61 +0,0 @@ | ||||
| use crate::spec::BuildSpec; | ||||
| use convert_case::{Case, Casing}; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| 
 | ||||
| pub fn make_walk_fn(specs: &[BuildSpec]) -> TokenStream { | ||||
|     let child_match_arms = specs | ||||
|         .iter() | ||||
|         .map(|spec| match spec { | ||||
|             BuildSpec::Enum(enum_spec) => Some(( | ||||
|                 format_ident!("{}", enum_spec.build()), | ||||
|                 format_ident!("{}", enum_spec.build().to_case(Case::Snake)), | ||||
|             )), | ||||
|             BuildSpec::LeafEnum(leaf_enum) => Some(( | ||||
|                 format_ident!("{}", leaf_enum.build()), | ||||
|                 format_ident!("{}", leaf_enum.build().to_case(Case::Snake)), | ||||
|             )), | ||||
|             BuildSpec::Struct(struct_spec) => Some(( | ||||
|                 format_ident!("{}", struct_spec.build()), | ||||
|                 format_ident!("{}", struct_spec.build().to_case(Case::Snake)), | ||||
|             )), | ||||
|             BuildSpec::LeafStruct(leaf_struct) => Some(( | ||||
|                 format_ident!("{}", leaf_struct.build()), | ||||
|                 format_ident!("{}", leaf_struct.build().to_case(Case::Snake)), | ||||
|             )), | ||||
|             BuildSpec::Production(_) => None, | ||||
|             BuildSpec::NodeProduction(_) => None, | ||||
|             BuildSpec::PolymorphicType(polymorphic_type) => Some(( | ||||
|                 format_ident!("{}", polymorphic_type.name()), | ||||
|                 format_ident!("{}", polymorphic_type.name().to_case(Case::Snake)), | ||||
|             )), | ||||
|             BuildSpec::PolymorphicEnumLoop(polymorphic_enum_loop) => Some(( | ||||
|                 format_ident!("{}", polymorphic_enum_loop.name()), | ||||
|                 format_ident!("{}", polymorphic_enum_loop.name().to_case(Case::Snake)), | ||||
|             )), | ||||
|             BuildSpec::PolymorphicPassThrough(_) => None, | ||||
|         }) | ||||
|         .filter(Option::is_some) | ||||
|         .map(Option::unwrap) | ||||
|         .map(|(type_ident, inner_ident)| { | ||||
|             quote! { | ||||
|                 #type_ident(#inner_ident) => walk_depth_first(#inner_ident, f) | ||||
|             } | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
| 
 | ||||
|     quote! { | ||||
|         use crate::ast::node::*; | ||||
|         use crate::ast::ast_node::*; | ||||
| 
 | ||||
|         pub fn walk_depth_first(node: &impl AstNode, f: &mut impl FnMut(AstNodeRef)) { | ||||
|             use AstNodeRef::*; | ||||
|             for child in node.children() { | ||||
|                 match child { | ||||
|                     #(#child_match_arms,)* | ||||
|                 } | ||||
|             } | ||||
|             f(node.as_node_ref()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										43
									
								
								build.rs
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								build.rs
									
									
									
									
									
								
							| @ -1,40 +1,9 @@ | ||||
| use ast_generator::{get_build_specs, generate_files}; | ||||
| use cst_test_generator::generate_test_files; | ||||
| use std::env; | ||||
| use std::fs; | ||||
| use std::io; | ||||
| use std::path::Path; | ||||
| 
 | ||||
| fn generate_parser_tests(out_dir: &Path) -> io::Result<()> { | ||||
|     let parser_tests_dir = out_dir.join("src").join("parser").join("tests"); | ||||
|     fs::create_dir_all(&parser_tests_dir)?; | ||||
|     let test_suites_file = generate_test_files(Path::new("src/parser/tests"))?; | ||||
|     let file_path = parser_tests_dir.join(&test_suites_file.file_name); | ||||
|     fs::write(file_path, &test_suites_file.contents)?; | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| fn generate_ast_files(out_dir: &Path) -> io::Result<()> { | ||||
|     let gen_ast_dir = out_dir.join("src").join("ast"); | ||||
|     fs::create_dir_all(&gen_ast_dir)?; | ||||
| 
 | ||||
|     let ast_yaml = include_str!("src/parser/ast.yaml"); | ||||
|     let build_specs = get_build_specs(ast_yaml); | ||||
|     let generated_files = generate_files(&build_specs); | ||||
|     for generated_file in &generated_files { | ||||
|         let path = gen_ast_dir.join(&generated_file.name); | ||||
|         fs::write(path, &generated_file.contents)?; | ||||
|     } | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
| fn main() -> io::Result<()> { | ||||
|     println!("cargo:rerun-if-changed=src/parser/tests"); | ||||
|     println!("cargo:rerun-if-changed=src/parser/ast.yaml"); | ||||
| fn main() -> std::io::Result<()> { | ||||
|     println!("cargo:rerun-if-changed=src/parser/deimos.pest"); | ||||
|     let out_dir = env::var_os("OUT_DIR").unwrap(); | ||||
|     let out_dir_path = Path::new(&out_dir); | ||||
|     generate_parser_tests(out_dir_path)?; | ||||
|     generate_ast_files(out_dir_path)?; | ||||
|     // let out_dir = env::var("OUT_DIR").unwrap();
 | ||||
|     // let out_dir_path = Path::new(&out_dir);
 | ||||
|     // let testing_txt_path = out_dir_path.join("testing.rs");
 | ||||
|     // let output = test_dump();
 | ||||
|     // write(&testing_txt_path, output)?;
 | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| @ -1,11 +0,0 @@ | ||||
| [package] | ||||
| name = "cst-test-generator" | ||||
| version = "0.1.0" | ||||
| edition = "2024" | ||||
| 
 | ||||
| [dependencies] | ||||
| convert_case = "0.8.0" | ||||
| prettyplease = "0.2.37" | ||||
| proc-macro2 = "1.0.101" | ||||
| quote = "1.0.40" | ||||
| syn = "2.0.106" | ||||
| @ -1,72 +0,0 @@ | ||||
| use convert_case::{Case, Casing}; | ||||
| use proc_macro2::TokenStream; | ||||
| use quote::{format_ident, quote}; | ||||
| use std::path::Path; | ||||
| use std::{fs, io}; | ||||
| use syn::File; | ||||
| 
 | ||||
| pub struct ParserTestSuitesFile { | ||||
|     pub file_name: String, | ||||
|     pub contents: String, | ||||
| } | ||||
| 
 | ||||
| pub fn generate_test_files(tests_dir: &Path) -> io::Result<ParserTestSuitesFile> { | ||||
|     let mut test_suites: Vec<TokenStream> = vec![]; | ||||
| 
 | ||||
|     // generate test file for each sub dir
 | ||||
|     for sub_dir in fs::read_dir(tests_dir)? { | ||||
|         let sub_dir = sub_dir?; | ||||
|         let sub_dir_path = sub_dir.path(); | ||||
|         if sub_dir_path.is_dir() { | ||||
|             let sub_dir_file_name = sub_dir.file_name(); | ||||
|             let sub_dir_string = sub_dir_file_name.to_string_lossy(); | ||||
| 
 | ||||
|             let sub_dir_pascal = sub_dir_string.to_case(Case::Pascal); | ||||
|             let rule_ident = format_ident!("{}", sub_dir_pascal); | ||||
| 
 | ||||
|             let mut tests: Vec<TokenStream> = vec![]; | ||||
|             for test_file in fs::read_dir(sub_dir_path)? { | ||||
|                 let test_file = test_file?; | ||||
|                 let test_file_name = test_file.file_name(); | ||||
|                 let test_ident = format_ident!("{}", test_file_name.to_string_lossy()); | ||||
| 
 | ||||
|                 let src_input = fs::read_to_string(test_file.path())?; | ||||
| 
 | ||||
|                 let test = quote! { | ||||
|                     #[test] | ||||
|                     fn #test_ident() { | ||||
|                         parses_to(Rule::#rule_ident, #src_input) | ||||
|                     } | ||||
|                 }; | ||||
| 
 | ||||
|                 tests.push(test); | ||||
|             } | ||||
| 
 | ||||
|             let tests_mod_ident = format_ident!("{}_tests", sub_dir.file_name().to_string_lossy()); | ||||
|             let test_suite = quote! { | ||||
|                 mod #tests_mod_ident { | ||||
|                     use super::*; | ||||
| 
 | ||||
|                     #(#tests)* | ||||
|                 } | ||||
|             }; | ||||
|             test_suites.push(test_suite); | ||||
|         } else { | ||||
|             println!("Warning: not a directory: {:?}", sub_dir_path); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // generate main test_suites file
 | ||||
|     let test_suites = quote! { | ||||
|         use super::*; | ||||
| 
 | ||||
|         #(#test_suites)* | ||||
|     }; | ||||
|     let test_suites_file: File = syn::parse2(test_suites).unwrap(); | ||||
|     let test_suites_file_contents = prettyplease::unparse(&test_suites_file); | ||||
| 
 | ||||
|     Ok(ParserTestSuitesFile { | ||||
|         file_name: String::from("test_suites.rs"), | ||||
|         contents: test_suites_file_contents, | ||||
|     }) | ||||
| } | ||||
| @ -1,3 +0,0 @@ | ||||
| fn main() | ||||
|     println 42 | ||||
| end | ||||
| @ -1,4 +0,0 @@ | ||||
| fn main() | ||||
|     let n = 1 + 2 * 3 + 4 | ||||
|     println n | ||||
| end | ||||
| @ -1,5 +0,0 @@ | ||||
| fn main() -> Void | ||||
|     $0 = 2 * 3 | ||||
|     $1 = 1 + $0 | ||||
|     $2 = $1 + 4 | ||||
|     call std::core::println($2) | ||||
| @ -1,19 +0,0 @@ | ||||
| class World(pub name: String, pub color: String) end | ||||
| 
 | ||||
| fn getWorlds() -> List<World> = [ | ||||
|     World('Mercury', 'Red'), | ||||
|     World('Earth', 'Blue'), | ||||
|     World('Jupiter', 'Orange') | ||||
| ] | ||||
| 
 | ||||
| fn findWorldByColor(worlds: List<World>, color: String) -> String | ||||
|     worlds.find { it.color == color } | ||||
|         .map { it.name } | ||||
|         .expect "No world has the given color ${color}" | ||||
| end | ||||
| 
 | ||||
| fn main() | ||||
|     let worlds = getWorlds() | ||||
|     let blueWorld = findWorldByColor(worlds, 'Blue') | ||||
|     println "Hello, ${blueWorld}!" | ||||
| end | ||||
| @ -1,89 +0,0 @@ | ||||
| struct World | ||||
|     pub String* name | ||||
|     pub String* color | ||||
| end | ||||
| 
 | ||||
| void World::ctor(World *self, String* name, String* color) | ||||
|     self.name = move name | ||||
|     self.color = move color | ||||
| end | ||||
| 
 | ||||
| const String %s0 = 'Mercury' | ||||
| const String %s1 = 'Red' | ||||
| const String %s2 = 'Earth' | ||||
| const String %s3 = 'Blue' | ||||
| const String %s4 = 'Jupiter' | ||||
| const String %s5 = 'Orange' | ||||
| 
 | ||||
| List<World> *getWorlds() | ||||
|     List<World> *$0 = alloc std::list::ArrayList | ||||
|     call std::list::ArrayList::ctor($0) | ||||
|     World *$1 = alloc World | ||||
|     call World::ctor($1, %s0, %s1) | ||||
|     call std::list::ArrayList::add($0, $1) | ||||
|     World *$2 = alloc World | ||||
|     call World::ctor($2, %s2, %s3) | ||||
|     call std::list::ArrayList::add($0, $2) | ||||
|     World *$3 = alloc World | ||||
|     call World::ctor($3, %s4, %s5) | ||||
|     call std::list::ArrayList::add($0, $3) | ||||
|     ret $0 | ||||
| end | ||||
| 
 | ||||
| struct __findWorldByColor__cl0_captures | ||||
|     pub String* color | ||||
| end | ||||
| 
 | ||||
| Boolean __findWorldByColor_cl0(__findWorldByColor__cl0_captures *__captures, World *it) | ||||
|     String *$0 = it.color | ||||
|     String *$1 = __captures.color | ||||
|     Boolean $2 = *$0 == *$1 | ||||
|     ret $2 | ||||
| end | ||||
| 
 | ||||
| String* __findWorldByColor_cl1(World* it) | ||||
|     $0 = it.name | ||||
|     ret $0 | ||||
| end | ||||
| 
 | ||||
| 
 | ||||
| String *findWorldByColor(List<World> *worlds, String *color) | ||||
|     __findWorldByColor__cl0_captures *$0 = alloc __findWorldByColor__cl0_captures | ||||
|     $0.color = color | ||||
|     Closure(__findWorldByColor__cl0_captures*)(World*)<Boolean> $1 | ||||
|         = closure(__findWorldByColor_cl0, $0) | ||||
|     List<World> $2 = call std::list::ArrayList_impl_Find::find(worlds, $1) | ||||
|     Closure()(World*)<String*> $3 = closure(__findWorldByColor_cl1) | ||||
|     Option<String*> $4 = call std::list::ArrayList_impl_Monad($2, $3) | ||||
| 
 | ||||
|     Display*[1] $5 = alloc Display[1] | ||||
|     $5[0] = color | ||||
|     DString *$6 = alloc DString | ||||
|     call DString::ctor($6, { }, $5) | ||||
|     String *$7 = call DString::toString($6) | ||||
| 
 | ||||
|     String* $8 = call option::Option::expect($4, $7) | ||||
| 
 | ||||
|     drop $0 | ||||
|     drop $1 | ||||
|     drop $2 | ||||
|     drop $3 | ||||
|     drop $5 | ||||
|     drop $6 | ||||
| 
 | ||||
|     ret $8 | ||||
| end | ||||
| 
 | ||||
| const String %s7 = 'Blue' | ||||
| const String[2] %sa0 = { 'Hello, ', '!' } | ||||
| 
 | ||||
| void main() | ||||
|     List<World> *$0 = call getWorlds() | ||||
|     World *$1 = call findWorldByColor($0, %s7) | ||||
|     Display[1] $2 = alloc Display[1] | ||||
|     $2[0] = move $1 | ||||
|     std::string::DString *$3 = alloc std::string::DString | ||||
|     call std::string::DString::ctor($3, %sa0, $2) | ||||
|     String *$4 = call std::DString::toString($3) | ||||
|     call std::core::println($4) | ||||
| end | ||||
| @ -1,5 +0,0 @@ | ||||
| use math::add | ||||
| 
 | ||||
| fn main() | ||||
|     println add(1, 2) // 3 | ||||
| end | ||||
| @ -1,3 +0,0 @@ | ||||
| mod math | ||||
| 
 | ||||
| pub fn add(a: Int, b: Int) = a + b | ||||
| @ -1,81 +0,0 @@ | ||||
| pub class Rc<T> : Drop, Copy | ||||
| 
 | ||||
|     class Inner<T>(t: T) | ||||
|         mut count = 1 | ||||
|     end | ||||
| 
 | ||||
|     inner: &weak Inner = Null | ||||
| 
 | ||||
|     pub ctor(t: T) | ||||
|         inner = Inner(t) | ||||
|     end | ||||
| 
 | ||||
|     ctor(parent: &Self) | ||||
|         parent.inner++ | ||||
|         inner = &weak parent.inner | ||||
|     end | ||||
| 
 | ||||
|     pub fn copy() -> Self | ||||
|         Self(self) | ||||
|     end | ||||
| 
 | ||||
|     impl fn drop(self) | ||||
|         if inner.upgrade() is Some(inner) then | ||||
|             inner.count-- | ||||
|             if inner.count == 0 then | ||||
|                 drop inner | ||||
|             end | ||||
|         else | ||||
|             throw Exception('rc.inner could not be converted from a weak reference') | ||||
|         end | ||||
|     end | ||||
| 
 | ||||
| end | ||||
| 
 | ||||
| class Game(player: Player) end | ||||
| 
 | ||||
| class Player | ||||
|     pub mut game: &weak Game = Null | ||||
| end | ||||
| 
 | ||||
| fn circular() | ||||
|     let player = Player() | ||||
|     let game = Game(player) | ||||
| 
 | ||||
|     expectThrows(EmptyWeakReferenceException) { | ||||
|         player.game.upgrade() | ||||
|     } | ||||
| 
 | ||||
|     player.game = &weak game | ||||
| 
 | ||||
|     expectDoesNotThrow { | ||||
|         player.game.upgrade() | ||||
|     } | ||||
| end | ||||
| 
 | ||||
| class Board | ||||
|     pub fn flip() | ||||
|         println 'Flipped!' | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| class ChessGame(pub board: rc Board) end | ||||
| 
 | ||||
| class ChessPlayer(pub board: rc Board) end | ||||
| 
 | ||||
| fn getGameAndPlayer() -> (ChessGame, ChessPlayer) | ||||
|     let board = rc Board() // Rc<Board> count is 1 | ||||
|     let game = ChessGame(board) // Rc<Board> copied, count is 2 | ||||
|     let player = ChessPlayer(board) // Rc<Board> copied, count is 3 | ||||
|     (game, player) // initial Rc<Board> dropped before fn return, count now 2 | ||||
| end | ||||
| 
 | ||||
| fn playChess() | ||||
|     let (game, player) = getGameAndPlayer() | ||||
|     // Rc<Board> count is 2 | ||||
|     let board = game.board // Copy, so count is now 3 | ||||
|     board.flip() | ||||
|     drop board // count now 2 | ||||
|     drop game // count now 1 | ||||
|     drop player // count now 0, board memory finally dropped | ||||
| end | ||||
| @ -1,236 +0,0 @@ | ||||
| pub trait Functor[*T] | ||||
|     fn <U> map(ftu: fn (t: &*T) -> U) -> Self<U> | ||||
| end | ||||
| 
 | ||||
| pub trait Functor[*L -> *R] | ||||
|     fn mapLeft(flr: fn (l: &*L) -> *R) -> Self<*L, *R> | ||||
| end | ||||
| 
 | ||||
| pub enum Option<T> | ||||
|     Some(T), | ||||
|     None | ||||
| end | ||||
| 
 | ||||
| pub enum Either<L, R> | ||||
|     Left(L), | ||||
|     Right(R) | ||||
| end | ||||
| 
 | ||||
| pub impl Functor[*T] for Option<T> | ||||
|     fn <U> map(ftu: fn (t: &*T) -> U) -> Self<U> | ||||
|         if self is Some(t) then | ||||
|             Some(ftu(t)) | ||||
|         else | ||||
|             None | ||||
|         end | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| pub impl Functor[*R] for Either<_, *R> | ||||
|     fn <U> map(f: fn (t: &*R) -> U) -> Self<_, U> | ||||
|         if self is Right(t) then | ||||
|             Right(f(t)) | ||||
|         else | ||||
|             self | ||||
|         end | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| pub impl Functor[*L -> *R] for Either<L, R> | ||||
|     fn mapLeft(f: fn (l: &*L) -> *R) -> Self<*L, *R> | ||||
|         if self is Left(l) then | ||||
|             Right(f(l)) | ||||
|         else | ||||
|             self | ||||
|         end | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| pub trait Monad[*T] | ||||
|     static fn lift(t: *T) -> Self<*T> | ||||
|     fn <U> flatMap(f: fn (t: &*T) -> Self<U>) -> Self<U> | ||||
| end | ||||
| 
 | ||||
| pub trait Default | ||||
|     static fn default() -> Self | ||||
| end | ||||
| 
 | ||||
| pub trait Empty[*T] | ||||
|     static fn empty() -> Self<*T> | ||||
| end | ||||
| 
 | ||||
| pub trait Semigroup[*T] | ||||
|     type Other<*T> | ||||
| 
 | ||||
|     fn concat(other: &Other<*T>) -> Self<&*T> | ||||
|     cons fn consConcat(other: Other<*T>) -> Self<*T> | ||||
| end | ||||
| 
 | ||||
| pub int Iterable<T> | ||||
|     fn iter() -> Iterator<T> ref self | ||||
|     cons fn consIter() -> ConsIterator<T> | ||||
| end | ||||
| 
 | ||||
| pub int Iterator<T> | ||||
|     fn next() -> Option<&T ref self> | ||||
| end | ||||
| 
 | ||||
| pub int ConsIterator<T> | ||||
|     fn next() -> Option<T> | ||||
| end | ||||
| 
 | ||||
| pub impl Semigroup[*T] for Iterable<*T> | ||||
|     type Other = Iterable<*T> | ||||
| 
 | ||||
|     fn concat(other: &Other<*T>) -> Self<*T> | ||||
|         let result: Self<*T> = ArrayList(count()) | ||||
|         for t in self do | ||||
|             result.add(t) | ||||
|         end | ||||
|         for t in other do | ||||
|             result.add(t) | ||||
|         end | ||||
|         result | ||||
|     end | ||||
| 
 | ||||
|     cons fn consConcat(other: Other<*T>) -> Self<*T> | ||||
|         let result: Self<*T> = ArrayList(count()) | ||||
|         for t in self do | ||||
|             result.add(t) | ||||
|         end | ||||
|         for t in other do | ||||
|             result.add(t) | ||||
|         end | ||||
|         result | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| pub int List<T> : Iterable<T> | ||||
|     fn add(t: T) -> Void | ||||
| end | ||||
| 
 | ||||
| pub impl Functor[*T] for List<*T> | ||||
|     fn <U> map(f: (t: &*T) -> U) -> Self<U> | ||||
|         let mut result: Self<U> = ArrayList(count()) | ||||
|         for t in self do | ||||
|             result.add(f(t)) | ||||
|         end | ||||
|         result | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| pub impl Empty[*T] for List<*T> | ||||
|     static fn empty() -> List<*T> = ArrayList() | ||||
| end | ||||
| 
 | ||||
| pub inst Monad[*T] for List<*T> | ||||
|     static fn lift(t: *T) -> List<*T> | ||||
|         let result: List<*T> = ArrayList() | ||||
|         result.add(t) | ||||
|         result | ||||
|     end | ||||
| 
 | ||||
|     fn <U> flatMap(f: fn (t: &*T) -> List<U>) -> List<U> | ||||
|         let result: List<U> = ArrayList(count()) | ||||
|         for t in self do | ||||
|             for u in f(t).consIter() do | ||||
|                 result.add(u) | ||||
|             end | ||||
|         end | ||||
|         result | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| pub class ArrayList<T> : List<T> | ||||
| 
 | ||||
|     let capacity: USize | ||||
|     let mut ts: Array<Option<T>> | ||||
|     let count: USize = 0 | ||||
| 
 | ||||
|     pub ctor(startCapacity: USize = 10) | ||||
|         capacity = startCapacity | ||||
|         ts = array(startCapacity, None) | ||||
|     end | ||||
| 
 | ||||
|     class ArrayListIterator<T>(owner: &ArrayList<T>) /* ref owner (implied by constructor) */ : Iterator<T> | ||||
|         let i: USize = 0 | ||||
| 
 | ||||
|         impl fn next() -> Option<&T ref self> | ||||
|             if owner.ts[i] is Some(t) then | ||||
|                 i++ | ||||
|                 Some(t) | ||||
|             else | ||||
|                 None | ||||
|             end | ||||
|         end | ||||
|     end | ||||
| 
 | ||||
|     class ConsArrayListIterator<T>(ts: Array<Option<T>>) : ConsIterator<T> | ||||
|         let i: USize = 0 | ||||
| 
 | ||||
|         impl fn next() -> Option<T> | ||||
|             if ts[i] is Some then | ||||
|                 let wrappedT = mem::take(ts[i], None) | ||||
|                 i++ | ||||
|                 wrappedT | ||||
|             else | ||||
|                 None | ||||
|             end | ||||
|         end | ||||
|     end | ||||
| 
 | ||||
|     impl fn iter() -> Iterator<&T> ref self | ||||
|         ArrayListIterator(&self) | ||||
|     end | ||||
| 
 | ||||
|     impl cons fn consIter() -> Iterator<T> | ||||
|         ConsArrayListIterator(mem::take(ts, arrays::empty())) | ||||
|     end | ||||
| 
 | ||||
|     impl fn add(t: T) -> Void | ||||
|         ts[count] = Some(t) | ||||
|         count++ | ||||
|         if count == capacity then | ||||
|             resize() | ||||
|         end | ||||
|     end | ||||
| 
 | ||||
|     fn resize() -> Void | ||||
|         let oldTs = mem::take(&ts, array(capacity * 2, None)) | ||||
|         for (i, wrappedT) in oldTs.consIter() do | ||||
|             ts[i] = wrappedT | ||||
|         end | ||||
|     end | ||||
| 
 | ||||
| end | ||||
| 
 | ||||
| pub mod std::core | ||||
| 
 | ||||
|     pub mod mem | ||||
|         pub native fn <T> take(target: &T, replacement: T) -> T | ||||
|     end | ||||
| 
 | ||||
|     pub native fn <T> array(capacity: USize, init: fn () -> T) -> Array<T> | ||||
| 
 | ||||
|     pub native fn <T> array(capacity: USize) -> Array<T> where T has Default | ||||
| 
 | ||||
|     pub fn <T> list(...items: Array<T>) -> List<T> | ||||
|         let result = ArrayList(items.capacity()) | ||||
|         for item in items.consIter() do | ||||
|             result.add(item) | ||||
|         end | ||||
|         result | ||||
|     end | ||||
| 
 | ||||
| end | ||||
| 
 | ||||
| // Other file | ||||
| use std::hkt::{Functor[List], Monad[List]} | ||||
| 
 | ||||
| fn main() | ||||
|     let someNums: List<Int> = [1, 2, 3, 4, 5] | ||||
|     let plusOnes = someNums.map { it + 1 } | ||||
|     println plusOnes // 2, 3, 4, 5, 6 | ||||
|     let twoOfEach = someNums.flatMap { [it, it] } | ||||
|     println twoOfEach // 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 | ||||
| end | ||||
| @ -1,198 +0,0 @@ | ||||
| pub trait Functor<T>[Self<T>] | ||||
|     fn map(mapper: fn (t: &T) -> U) -> Self<U> | ||||
| end | ||||
| 
 | ||||
| pub trait Sum<T> | ||||
|     fn sum() -> T | ||||
| end | ||||
| 
 | ||||
| pub trait Default | ||||
|     fn default() -> Self | ||||
| end | ||||
| 
 | ||||
| pub trait Iter<T> | ||||
|     ref fn iter() -> Iterator<&T> | ||||
| end | ||||
| 
 | ||||
| pub trait ConsIter<T> | ||||
|     cons fn consIter() -> Iterator<T> | ||||
| end | ||||
| 
 | ||||
| pub int Iterator<T> | ||||
|     fn next() -> Option<T> | ||||
| end | ||||
| 
 | ||||
| pub int List<T> impl Functor<T>, Iter<T>, ConsIter<T> | ||||
|     fn add(t: T) -> Void | ||||
| end | ||||
| 
 | ||||
| pub class ArrayList<T> : List<T> | ||||
|     let mut currentIndex = 0 | ||||
|     let mut inner: Array<T> | ||||
| 
 | ||||
|     ctor(capacity: Int) where T impl Default | ||||
|         inner = Array(capacity, Default[T]) | ||||
|     end | ||||
| 
 | ||||
|     ctor(capacity: Int, init: fn () -> T) | ||||
|         inner = Array(capacity, init) | ||||
|     end | ||||
| 
 | ||||
|     impl fn add(t: T) -> Void | ||||
|         inner[currentIndex] = t | ||||
|         let currentCapacity = inner.capacity() | ||||
|         currentIndex++ | ||||
|         if currentIndex == currentCapacity then | ||||
|             let oldInner = std::mem::take(inner, Array(currentCapacity * 2)) | ||||
|             arrays::copy(oldInner, inner) | ||||
|         end | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| impl Functor[ArrayList<*>] | ||||
|     fn map(mapper: fn (t: &T) -> U) -> Self<U> | ||||
|         let mut result: Array<U> = Array(inner.capacity) | ||||
|         for (i, t) in inner.iterWithIndex() do | ||||
|             result[i] = mapper(t) | ||||
|         end | ||||
|         result | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| impl Sum[Self = List<Int>, T = Int] | ||||
|     fn sum() -> Int | ||||
|         let mut result = 0 | ||||
|         for value in self do | ||||
|             result += value | ||||
|         end | ||||
|         result | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| impl<T impl Default> Default[ArrayList<T>] | ||||
|     const DEFAULT_SIZE = 10 | ||||
| 
 | ||||
|     static fn default() -> Self = ArrayList(DEFAULT_SIZE, Default[T]::default) | ||||
| end | ||||
| 
 | ||||
| impl<T> Iter[ArrayList<T>] | ||||
|     ref fn iter() -> Iterator<&T> | ||||
|         let mut currentIndex = 0 | ||||
|         return { | ||||
|             if currentIndex < inner.count() then | ||||
|                 let item = inner[currentIndex] | ||||
|                 currentIndex++ | ||||
|                 Some(item) | ||||
|             else | ||||
|                 None | ||||
|             end | ||||
|         } | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| impl<T impl Default> ConsIter[ArrayList<T>] | ||||
|     cons fn consIter() -> Iterator<T> | ||||
|         let mut currentIndex = 0 | ||||
|         let inner = std::mem::take(inner, std::arrays::empty()) | ||||
|         return |inner| { | ||||
|             if currentIndex < inner.count() then | ||||
|                 let item = inner.take(currentIndex, Default[T]::default) | ||||
|                 currentIndex++ | ||||
|                 Some(item) | ||||
|             else | ||||
|                 None | ||||
|             end | ||||
|         } | ||||
|     end | ||||
| end | ||||
| 
 | ||||
| pub enum Option<T> | ||||
|     Some(t: T), | ||||
|     None | ||||
| end | ||||
| 
 | ||||
| impl Default[Option<*>] | ||||
|     static fn default() -> Self = None | ||||
| end | ||||
| 
 | ||||
| pub trait HashCode | ||||
|     fn hash() -> USize | ||||
| end | ||||
| 
 | ||||
| pub int Map<K, V> | ||||
|     fn get(key: K) -> Option<&V> | ||||
|     fn put(key: K, value: V) -> Void | ||||
|     fn take(key: K) -> Option<V> | ||||
| end | ||||
| 
 | ||||
| pub class HashMap<K, V> : Map<K, V> | ||||
|     where K impl HashCode | ||||
| 
 | ||||
|     const LOAD_FACTOR = 0.8 | ||||
| 
 | ||||
|     class Entry<K, V>(pub key: K, pub value: V) end | ||||
| 
 | ||||
|     let mut buckets: List<List<Option<Entry<K, V>>>> = ArrayList(10) | ||||
| 
 | ||||
|     fn getBucketIndex(key: K) -> Int | ||||
|         HashCode[K].hash(key) % buckets.size() | ||||
|     end | ||||
| 
 | ||||
|     fn getCount() -> Int = buckets.map { it.count() }.sum::<Int>() | ||||
| 
 | ||||
|     fn getCapacity() -> Int = buckets.map { it.capacity() }.sum::<Int>() | ||||
| 
 | ||||
|     fn redistribute() | ||||
|         use std::mem::take | ||||
|         let oldBucketCapacity = buckets.first().unwrap().capacity() | ||||
|         let oldBuckets = take(buckets, ArrayList(oldBucketCapacity * 2)) | ||||
|         for bucket in oldBuckets.consIter() do | ||||
|             for entry in bucket.consIter() do | ||||
|                 let newIndex = getBucketIndex(entry.key) | ||||
|                 let newBucket = buckets[newIndex] | ||||
|                 newBucket.add(Entry(entry.key, entry.value)) | ||||
|             end | ||||
|         end | ||||
|     end | ||||
| 
 | ||||
|     impl fn put(key: K, value: V) -> Void | ||||
|         let bucket = buckets[getBucketIndex(key)] | ||||
|         bucket.add(Entry(key, value)) | ||||
|         if getCount() / getCapacity() > LOAD_FACTOR then | ||||
|             redistribute() | ||||
|         end | ||||
|     end | ||||
| 
 | ||||
|     impl fn get(key: K) -> Option<&V> | ||||
|         let bucket = buckets[getBucketIndex(key)] // bucket: &List<Entry<K, V>> | ||||
|         bucket.find { it.key == key } // it: &Entry<K, V> | ||||
|             .map { it.value } // it.value: &V | ||||
|     end | ||||
| 
 | ||||
|     impl fn take(key: K) -> Option<V> | ||||
|         let mut bucket = buckets.take(getBucketIndex(key)) // bucket: List<Entry<K, V>> | ||||
|         if bucket.findIndex { it.key == key } is Some(index) then | ||||
|             let entry = bucket.take(index) // entry: Entry<K, V> | ||||
|             Some(entry.value) // moved out of Entry | ||||
|         else | ||||
|             None | ||||
|         end | ||||
|     end | ||||
| 
 | ||||
| end | ||||
| 
 | ||||
| impl HashCode for String | ||||
|     fn hash() -> Int = todo() | ||||
| end | ||||
| 
 | ||||
| class Greeter(pub greeting: String) end | ||||
| 
 | ||||
| fn main() | ||||
|     let greeterMap: Map<String, Greeter> = HashMap() | ||||
|     greeterMap.put("friendly", Greeter("Friendly hello!")) | ||||
|     greeterMap.put("nasty", Greeter("Nasty hello!")) | ||||
|     let friendlyGreeter: &Greeter = greeterMap.get("friendly").unwrap() | ||||
|     println friendlyGreeter.greeting | ||||
|     let nastyGreeter: Greeter = greeterMap.take("nasty").unwrap() | ||||
|     println nastyGreeter.greeting | ||||
| end | ||||
| @ -1 +0,0 @@ | ||||
| Hello, World! | ||||
| @ -1 +0,0 @@ | ||||
| `Hello, ${world}!` | ||||
| @ -1 +0,0 @@ | ||||
| false | ||||
| @ -1 +0,0 @@ | ||||
| true | ||||
							
								
								
									
										203
									
								
								src/ast/mod.rs
									
									
									
									
									
								
							
							
						
						
									
										203
									
								
								src/ast/mod.rs
									
									
									
									
									
								
							| @ -1,197 +1,6 @@ | ||||
| pub mod node { | ||||
|     include!(concat!(env!("OUT_DIR"), "/src/ast/node.rs")); | ||||
| 
 | ||||
|     impl OperatorInner { | ||||
|         pub fn name(&self) -> &'static str { | ||||
|             match self { | ||||
|                 OperatorInner::Or => "op_or", | ||||
|                 OperatorInner::And => "op_and", | ||||
|                 OperatorInner::EqualTo => "op_eq", | ||||
|                 OperatorInner::NotEqualTo => "op_neq", | ||||
|                 OperatorInner::Greater => "op_gt", | ||||
|                 OperatorInner::Less => "op_lt", | ||||
|                 OperatorInner::GreaterEqual => "op_ge", | ||||
|                 OperatorInner::LessEqual => "op_le", | ||||
|                 OperatorInner::Add => "op_add", | ||||
|                 OperatorInner::Subtract => "op_sub", | ||||
|                 OperatorInner::Multiply => "op_mul", | ||||
|                 OperatorInner::Divide => "op_div", | ||||
|                 OperatorInner::Modulo => "op_mod", | ||||
|                 OperatorInner::LeftShift => "op_ls", | ||||
|                 OperatorInner::RightShift => "op_rs", | ||||
|                 OperatorInner::Spread => "op_spread", | ||||
|                 OperatorInner::Star => "op_star", | ||||
|                 OperatorInner::Not => "op_not", | ||||
|                 OperatorInner::Negative => "op_neg", | ||||
|                 OperatorInner::PlusPlus => "op_pp", | ||||
|                 OperatorInner::MinusMinus => "op_mm", | ||||
|                 OperatorInner::CallOp => "op_call", | ||||
|                 OperatorInner::Index => "op_index", | ||||
|             } | ||||
|         }    
 | ||||
|     } | ||||
|     
 | ||||
|     impl Default for Parameters { | ||||
|         fn default() -> Self { | ||||
|             Self::new(vec![]) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl Default for GenericParameters { | ||||
|         fn default() -> Self { | ||||
|             Self::new(Box::new(IdentifierList::new(vec![]))) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl Default for GenericArguments { | ||||
|         fn default() -> Self { | ||||
|             Self::new(Box::new(TypeUseList::new(vec![]))) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl Default for ImplementsList { | ||||
|         fn default() -> Self { | ||||
|             Self::new(vec![]) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl ReturnType { | ||||
|         pub fn void() -> Self { | ||||
|             Self::new(Box::new(TypeUse::PrimitiveType(PrimitiveType::Void))) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl Default for ClassConstructor { | ||||
|         fn default() -> Self { | ||||
|             Self::new(vec![]) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl Default for ClosureParameters { | ||||
|         fn default() -> Self { | ||||
|             Self::new(vec![]) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub mod build { | ||||
|     use pest::iterators::Pairs; | ||||
| 
 | ||||
|     include!(concat!(env!("OUT_DIR"), "/src/ast/build.rs")); | ||||
| 
 | ||||
|     pub fn build_ast(file_id: usize, parsed_pairs: &mut Pairs<Rule>) -> Box<CompilationUnit> { | ||||
|         let compilation_unit_pair = parsed_pairs.next().unwrap(); | ||||
|         Box::new(build_compilation_unit(file_id, compilation_unit_pair)) | ||||
|     } | ||||
| 
 | ||||
|     #[cfg(test)] | ||||
|     mod build_tests { | ||||
|         use super::*; | ||||
|         use crate::parser::DeimosParser; | ||||
|         use pest::Parser; | ||||
| 
 | ||||
|         fn parse(rule: Rule, input: &str) -> Pair<Rule> { | ||||
|             let parse_result = DeimosParser::parse(rule, input); | ||||
|             parse_result.expect("parsing failed").next().unwrap() | ||||
|         } | ||||
| 
 | ||||
|         #[test] | ||||
|         fn boolean_literal_true() { | ||||
|             let pair = parse( | ||||
|                 Rule::BooleanLiteral, | ||||
|                 include_str!("build_tests/boolean_literal/true"), | ||||
|             ); | ||||
|             assert_eq!(true, build_boolean_literal(0, pair)); | ||||
|         } | ||||
| 
 | ||||
|         #[test] | ||||
|         fn boolean_literal_false() { | ||||
|             let pair = parse( | ||||
|                 Rule::BooleanLiteral, | ||||
|                 include_str!("build_tests/boolean_literal/false"), | ||||
|             ); | ||||
|             assert_eq!(false, build_boolean_literal(0, pair)); | ||||
|         } | ||||
| 
 | ||||
|         #[test] | ||||
|         fn backtick_inner_greeting() { | ||||
|             let pair = parse( | ||||
|                 Rule::BacktickInner, | ||||
|                 include_str!("build_tests/backtick_inner/greeting"), | ||||
|             ); | ||||
|             assert_eq!("Hello, World!", build_backtick_inner(0, pair)); | ||||
|         } | ||||
| 
 | ||||
|         #[test] | ||||
|         fn backtick_string_mixed() { | ||||
|             let pair = parse( | ||||
|                 Rule::BacktickString, | ||||
|                 include_str!("build_tests/backtick_string/mixed"), | ||||
|             ); | ||||
|             let backtick_string = build_backtick_string(0, pair); | ||||
|             assert_eq!(backtick_string.inners().count(), 2); | ||||
|             assert_eq!(backtick_string.expressions().count(), 1); | ||||
|         } | ||||
| 
 | ||||
|         #[test] | ||||
|         fn d_string_expression_simple() { | ||||
|             let pair = parse(Rule::DStringExpression, "${thing}"); | ||||
|             let d_string_expression = build_d_string_expression(0, pair); | ||||
|         } | ||||
| 
 | ||||
|         #[test] | ||||
|         fn d_string_inner() { | ||||
|             let pair = parse(Rule::DStringInner, "Hello!"); | ||||
|             let d_string_inner = build_d_string_inner(0, pair); | ||||
|             assert_eq!("Hello!", d_string_inner); | ||||
|         } | ||||
| 
 | ||||
|         #[test] | ||||
|         fn d_string_mixed() { | ||||
|             let pair = parse(Rule::DString, "\"Hello, ${world}!\""); | ||||
|             let d_string = build_d_string(0, pair); | ||||
|             assert_eq!(d_string.inners().count(), 2); | ||||
|             assert_eq!(d_string.expressions().count(), 1); | ||||
|         } | ||||
| 
 | ||||
|         #[test] | ||||
|         fn expression_simple_call() { | ||||
|             let pair = parse(Rule::Expression, "hello(42)"); | ||||
|             let expression = build_expression(0, pair); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 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")); | ||||
| } | ||||
| 
 | ||||
| pub mod ast_node { | ||||
|     include!(concat!(env!("OUT_DIR"), "/src/ast/ast_node.rs")); | ||||
| 
 | ||||
|     #[cfg(test)] | ||||
|     mod tests { | ||||
|         use crate::ast::ast_node::AstNode; | ||||
|         use crate::ast::node::CompilationUnit; | ||||
| 
 | ||||
|         fn get_cu() -> CompilationUnit { | ||||
|             todo!() | ||||
|         } | ||||
| 
 | ||||
|         #[test] | ||||
|         fn simple() { | ||||
|             let cu = get_cu(); | ||||
|             for child in cu.children() {} | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub mod walk { | ||||
|     include!(concat!(env!("OUT_DIR"), "/src/ast/walk.rs")); | ||||
| } | ||||
| pub mod build; | ||||
| pub mod children; | ||||
| pub mod node; | ||||
| pub mod pretty_print; | ||||
| pub mod unparse; | ||||
| pub mod walk; | ||||
|  | ||||
| @ -101,45 +101,45 @@ where | ||||
|     f(node.as_node_ref()); | ||||
| } | ||||
| 
 | ||||
| // #[cfg(test)]
 | ||||
| // mod tests {
 | ||||
| //     use crate::ast::build::build_ast;
 | ||||
| //     use crate::ast::children::NodeRef;
 | ||||
| //     use crate::ast::walk::walk_depth_first;
 | ||||
| //     use crate::parser::{DeimosParser, Rule};
 | ||||
| //     use indoc::indoc;
 | ||||
| //     use pest::Parser;
 | ||||
| // 
 | ||||
| //     #[test]
 | ||||
| //     fn collect_identifiers() {
 | ||||
| //         let parse_result = DeimosParser::parse(
 | ||||
| //             Rule::CompilationUnit,
 | ||||
| //             indoc! {"
 | ||||
| //             ns greeter;
 | ||||
| //             
 | ||||
| //             class Greeter {}
 | ||||
| // 
 | ||||
| //             fn main() {
 | ||||
| //                 let greeter = Greeter();
 | ||||
| //             }
 | ||||
| //         "},
 | ||||
| //         );
 | ||||
| //         match parse_result {
 | ||||
| //             Ok(cu_pairs) => {
 | ||||
| //                 let cu = build_ast("greeter.dm", 0, cu_pairs.into_iter().next().unwrap());
 | ||||
| //                 let mut identifier_count = 0;
 | ||||
| //                 walk_depth_first(&cu, &mut |node_ref| match node_ref {
 | ||||
| //                     NodeRef::Identifier(identifier) => {
 | ||||
| //                         dbg!(identifier);
 | ||||
| //                         identifier_count += 1;
 | ||||
| //                     }
 | ||||
| //                     _ => {}
 | ||||
| //                 });
 | ||||
| //                 assert_eq!(identifier_count, 5);
 | ||||
| //             }
 | ||||
| //             Err(err) => {
 | ||||
| //                 panic!("{}", err);
 | ||||
| //             }
 | ||||
| //         }
 | ||||
| //     }
 | ||||
| // }
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use crate::ast::build::build_ast; | ||||
|     use crate::ast::children::NodeRef; | ||||
|     use crate::ast::walk::walk_depth_first; | ||||
|     use crate::parser::{DeimosParser, Rule}; | ||||
|     use indoc::indoc; | ||||
|     use pest::Parser; | ||||
| 
 | ||||
|     #[test] | ||||
|     fn collect_identifiers() { | ||||
|         let parse_result = DeimosParser::parse( | ||||
|             Rule::CompilationUnit, | ||||
|             indoc! {" | ||||
|             ns greeter; | ||||
|             
 | ||||
|             class Greeter {} | ||||
| 
 | ||||
|             fn main() { | ||||
|                 let greeter = Greeter(); | ||||
|             } | ||||
|         "},
 | ||||
|         ); | ||||
|         match parse_result { | ||||
|             Ok(cu_pairs) => { | ||||
|                 let cu = build_ast("greeter.dm", 0, cu_pairs.into_iter().next().unwrap()); | ||||
|                 let mut identifier_count = 0; | ||||
|                 walk_depth_first(&cu, &mut |node_ref| match node_ref { | ||||
|                     NodeRef::Identifier(identifier) => { | ||||
|                         dbg!(identifier); | ||||
|                         identifier_count += 1; | ||||
|                     } | ||||
|                     _ => {} | ||||
|                 }); | ||||
|                 assert_eq!(identifier_count, 5); | ||||
|             } | ||||
|             Err(err) => { | ||||
|                 panic!("{}", err); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,12 +1,12 @@ | ||||
| mod name_analysis; | ||||
| mod p3; | ||||
| // mod unparse;
 | ||||
| mod unparse; | ||||
| 
 | ||||
| use std::path::PathBuf; | ||||
| 
 | ||||
| use crate::name_analysis::name_analysis; | ||||
| use crate::p3::pretty_print_parse; | ||||
| // use crate::unparse::unparse;
 | ||||
| use crate::unparse::unparse; | ||||
| use clap::{Parser, Subcommand}; | ||||
| 
 | ||||
| #[derive(Debug, Parser)] | ||||
| @ -34,11 +34,11 @@ enum Commands { | ||||
| fn main() { | ||||
|     let args = Cli::parse(); | ||||
|     match args.command { | ||||
|         // Commands::Unparse { paths } => {
 | ||||
|         //     for path in paths {
 | ||||
|         //         unparse(&path);
 | ||||
|         //     }
 | ||||
|         // }
 | ||||
|         Commands::Unparse { paths } => { | ||||
|             for path in paths { | ||||
|                 unparse(&path); | ||||
|             } | ||||
|         } | ||||
|         Commands::P3 { paths } => { | ||||
|             for path in paths { | ||||
|                 pretty_print_parse(&path) | ||||
| @ -50,6 +50,5 @@ fn main() { | ||||
|                 eprintln!("{}", e) | ||||
|             } | ||||
|         } | ||||
|         _ => todo!(), | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -5,14 +5,14 @@ use deimos::ast::build::build_ast; | ||||
| 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 pest::Parser; | ||||
| use std::path::PathBuf; | ||||
| use deimos::std_core::add_std_core_symbols; | ||||
| 
 | ||||
| pub fn name_analysis(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Error>> { | ||||
|     let mut compilation_units = vec![]; | ||||
|     let mut files: SimpleFiles<String, String> = SimpleFiles::new(); | ||||
| 
 | ||||
|     let mut files = SimpleFiles::new(); | ||||
|     
 | ||||
|     for path in paths { | ||||
|         let src = std::fs::read_to_string(path).unwrap(); | ||||
|         let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src); | ||||
| @ -20,7 +20,8 @@ pub fn name_analysis(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Err | ||||
| 
 | ||||
|         match parse_result { | ||||
|             Ok(mut pairs) => { | ||||
|                 let compilation_unit = build_ast(file_id, &mut pairs); | ||||
|                 let compilation_unit_pair = pairs.next().unwrap(); | ||||
|                 let compilation_unit = build_ast(&path.display().to_string(), file_id, compilation_unit_pair); | ||||
|                 compilation_units.push(compilation_unit); | ||||
|                 Ok::<(), Box<dyn std::error::Error>>(()) | ||||
|             } | ||||
| @ -30,11 +31,11 @@ pub fn name_analysis(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Err | ||||
| 
 | ||||
|     let mut symbol_table = SymbolTable::new(); | ||||
|     add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols."); | ||||
| 
 | ||||
|     let diagnostics = analyze_names(compilation_units.as_mut_slice(), &files, &mut symbol_table); | ||||
|     
 | ||||
|     let diagnostics = analyze_names(&mut compilation_units, &mut symbol_table); | ||||
|     if diagnostics.is_empty() { | ||||
|         println!("Name analysis complete."); | ||||
|         println!("{}", symbol_table); | ||||
|         println!("Symbol table\n-------\n{}", symbol_table); | ||||
|     } else { | ||||
|         let writer = StandardStream::stderr(ColorChoice::Always); | ||||
|         let config = term::Config::default(); | ||||
| @ -42,6 +43,6 @@ pub fn name_analysis(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Err | ||||
|             term::emit(&mut writer.lock(), &config, &files, &diagnostic)?; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     
 | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
| @ -10,7 +10,8 @@ pub fn pretty_print_parse(path: &PathBuf) { | ||||
|     let parse_result = DeimosParser::parse(Rule::CompilationUnit, &src); | ||||
|     match parse_result { | ||||
|         Ok(mut pairs) => { | ||||
|             let compilation_unit = build_ast(0, &mut pairs); | ||||
|             let compilation_unit_pair = pairs.next().unwrap(); | ||||
|             let compilation_unit = build_ast(&path.display().to_string(), 0, compilation_unit_pair); | ||||
|             let mut indent_writer = IndentWriter::new(0, "  ", Box::new(std::io::stdout())); | ||||
|             compilation_unit | ||||
|                 .pretty_print(&mut indent_writer) | ||||
|  | ||||
							
								
								
									
										199
									
								
								src/ir/mod.rs
									
									
									
									
									
								
							
							
						
						
									
										199
									
								
								src/ir/mod.rs
									
									
									
									
									
								
							| @ -1,199 +0,0 @@ | ||||
| pub enum DvmIr { | ||||
|     Struct(Box<DvmIrStruct>), | ||||
|     Function(DvmIrFunction), | ||||
|     Const(DvmIrConst), | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrStruct { | ||||
|     name: String, | ||||
|     kind: Box<DvmIrKind>, | ||||
|     is_public: bool, | ||||
|     members: Vec<Box<DvmIrStructMember>> | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrStructMember { | ||||
|     name: String, | ||||
|     kind: Box<DvmIrKind>, | ||||
|     is_public: bool, | ||||
|     is_mut: bool, | ||||
| } | ||||
| 
 | ||||
| pub enum DvmIrKind { | ||||
|     Primitive(Box<DvmIrPrimitiveKind>), | ||||
|     Struct(Box<DvmIrStructKind>), | ||||
| } | ||||
| 
 | ||||
| pub enum DvmIrPrimitiveKind { | ||||
|     I8, | ||||
|     I16, | ||||
|     I32, | ||||
|     I64, | ||||
|     I128, | ||||
|     ISize, | ||||
|     U8, | ||||
|     U16, | ||||
|     U32, | ||||
|     U64, | ||||
|     U128, | ||||
|     USize, | ||||
|     Boolean, | ||||
|     Array(Box<DvmIrKind>), | ||||
|     String, | ||||
|     Closure(Box<DvmIrClosureKind>), | ||||
|     Ref(Box<DvmIrKind>), | ||||
|     Void, | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrStructKind { | ||||
|     name: String, | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrClosureKind { | ||||
|     captures_name: String, | ||||
|     parameters: Vec<Box<DvmIrKind>>, | ||||
|     return_type: Box<DvmIrKind>, | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrFunction { | ||||
|     name: String, | ||||
|     parameters: Vec<Box<DvmIrKind>>, | ||||
|     return_type: Box<DvmIrKind>, | ||||
|     statements: Vec<Box<DvmIrStatement>>, | ||||
| } | ||||
| 
 | ||||
| pub enum DvmIrStatement { | ||||
|     Alloc(DvmIrAlloc), | ||||
|     Call(DvmIrCallStmt), | ||||
|     Ret(DvmIrRet), | ||||
|     Assign(DvmIrAssign), | ||||
|     MakeClosure(DvmIrMakeClosure), | ||||
|     Drop(DvmIrDrop), | ||||
| } | ||||
| 
 | ||||
| pub enum DvmIrAllocable { | ||||
|     Struct(Box<DvmIrStructKind>), | ||||
|     Array(Box<DvmIrArrayKind>), | ||||
| } | ||||
| 
 | ||||
| pub enum DvmIrArrayKind { | ||||
|     StaticSize(Box<DvmIrStaticArrayKind>), | ||||
|     DynamicSize(Box<DvmIrDynamicArrayKind>) | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrStaticArrayKind { | ||||
|     inner_kind: Box<DvmIrKind>, | ||||
|     size: usize, | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrDynamicArrayKind { | ||||
|     inner_kind: Box<DvmIrKind>, | ||||
|     size_expr: Box<DvmIrExpr> | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrAlloc { | ||||
|     declared_kind: Box<DvmIrAllocable>, | ||||
|     name: String, | ||||
|     kind_to_alloc: Box<DvmIrAllocable>, | ||||
| } | ||||
| 
 | ||||
| pub enum DvmIrConst { | ||||
|     String(DvmIrStringConst), | ||||
|     StringArray(DvmIrStringArrayConst), | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrStringConst { | ||||
|     name: String, | ||||
|     value: String, | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrStringArrayConst { | ||||
|     name: String, | ||||
|     value: Vec<String>, | ||||
| } | ||||
| 
 | ||||
| pub enum DvmIrExpr { | ||||
|     Call(Box<DvmIrCallExpr>), | ||||
|     Variable(Box<DvmIrVariable>), | ||||
|     ConstRef(Box<DvmIrConstRef>), | ||||
|     Literal(Box<DvmIrLiteral>) | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrVariable { | ||||
|     name: String, | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrConstRef { | ||||
|     name: String,    
 | ||||
| } | ||||
| 
 | ||||
| pub enum DvmIrLiteral { | ||||
|     I8(i8), | ||||
|     I16(i16), | ||||
|     I32(i32), | ||||
|     I64(i64), | ||||
|     I128(i128), | ||||
|     ISize(isize), | ||||
|     U8(u8), | ||||
|     U16(u16), | ||||
|     U32(u32), | ||||
|     U64(u64), | ||||
|     U128(u128), | ||||
|     USize(usize), | ||||
|     Boolean(bool), | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrCallStmt { | ||||
|     declared_type: Box<DvmIrKind>, | ||||
|     name: String, | ||||
|     call_expr: Box<DvmIrCallExpr>, | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrCallExpr { | ||||
|     name: String, | ||||
|     arguments: Vec<Box<DvmIrExpr>>, | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrAssign { | ||||
|     lhs: Box<DvmIrAssignLhs>, | ||||
|     rhs: Box<DvmIrExpr> | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrAssignLhs { | ||||
|     base: Box<DvmIrVariable>, | ||||
|     suffixes: Vec<Box<DvmIrAssignLhsSuffix>> | ||||
| } | ||||
| 
 | ||||
| pub enum DvmIrAssignLhsSuffix { | ||||
|     Index(DvmIrAssignLhsIndex), | ||||
|     Property(DvmIrAssignLhsProperty) | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrAssignLhsIndex { | ||||
|     expr: Box<DvmIrExpr>, | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrAssignLhsProperty { | ||||
|     name: String, | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrRet { | ||||
|     value: Box<DvmIrReturnable> | ||||
| } | ||||
| 
 | ||||
| pub enum DvmIrReturnable { | ||||
|     Variable(DvmIrVariable), | ||||
|     Literal(DvmIrLiteral) | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrMakeClosure { | ||||
|     captures_kind: Option<Box<DvmIrStructKind>>, | ||||
|     parameters: Vec<Box<DvmIrKind>>, | ||||
|     return_type: Box<DvmIrKind>, | ||||
|     name: String, | ||||
|     fn_name: String, | ||||
|     captures_variable: Box<DvmIrVariable>, | ||||
| } | ||||
| 
 | ||||
| pub struct DvmIrDrop { | ||||
|     variable: Box<DvmIrVariable>, | ||||
| } | ||||
| @ -11,4 +11,3 @@ pub mod parser; | ||||
| pub mod std_core; | ||||
| pub mod util; | ||||
| pub mod vm; | ||||
| pub mod ir; | ||||
|  | ||||
| @ -4,12 +4,14 @@ pub struct DmModule { | ||||
| 
 | ||||
| pub enum NamespaceVisibility { | ||||
|     Public, | ||||
|     Partial { visible_to_fqns: Vec<String> }, | ||||
|     Private, | ||||
|     Partial { | ||||
|         visible_to_fqns: Vec<String> | ||||
|     }, | ||||
|     Private | ||||
| } | ||||
| 
 | ||||
| pub struct DmNamespace { | ||||
|     pub name: String, | ||||
|     pub dependencies: Vec<String>, | ||||
|     pub visibility: NamespaceVisibility, | ||||
|     pub visibility: NamespaceVisibility | ||||
| } | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| pub struct FqnContext { | ||||
| pub(super) struct FqnContext { | ||||
|     stack: Vec<String>, | ||||
| } | ||||
| 
 | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -19,254 +19,250 @@ The resolve phase has one main responsibility: resolve all references based on t | ||||
| `scope_id` property. | ||||
| */ | ||||
| 
 | ||||
| use crate::ast::node::{CompilationUnit, Identifier}; | ||||
| use crate::ast::node::compilation_unit::CompilationUnit; | ||||
| use crate::ast::node::named::Named; | ||||
| use crate::diagnostic::DmDiagnostic; | ||||
| use crate::name_analysis::gather::gather_compilation_unit; | ||||
| // use crate::name_analysis::resolve::resolve_compilation_unit;
 | ||||
| use crate::name_analysis::resolve::resolve_compilation_unit; | ||||
| use crate::name_analysis::symbol_table::SymbolTable; | ||||
| use codespan_reporting::files::Files; | ||||
| use std::collections::HashMap; | ||||
| 
 | ||||
| pub(self) mod fqn_context; | ||||
| mod fqn_context; | ||||
| mod gather; | ||||
| // mod resolve;
 | ||||
| mod resolve; | ||||
| pub mod symbol; | ||||
| pub mod symbol_table; | ||||
| 
 | ||||
| pub fn analyze_names<'a, F: Files<'a, FileId = usize, Name = String>>( | ||||
|     compilation_units: &mut [Box<CompilationUnit>], | ||||
|     files: &'a F, | ||||
| pub fn analyze_names( | ||||
|     compilation_units: &mut Vec<CompilationUnit>, | ||||
|     symbol_table: &mut SymbolTable, | ||||
| ) -> Vec<DmDiagnostic> { | ||||
|     let mut diagnostics = vec![]; | ||||
|     let mut identifier_scope_ids: HashMap<Identifier, usize> = HashMap::new(); | ||||
| 
 | ||||
|     // gather symbols
 | ||||
|     for compilation_unit in compilation_units.iter_mut() { | ||||
|         let file_name = files.name(compilation_unit.file_id()).unwrap(); | ||||
|         gather_compilation_unit(compilation_unit, &file_name, symbol_table, &mut diagnostics); | ||||
|         gather_compilation_unit(compilation_unit, symbol_table, &mut diagnostics); | ||||
|     } | ||||
| 
 | ||||
|     // resolve symbols
 | ||||
|     for compilation_unit in compilation_units.iter_mut() { | ||||
|         // resolve_compilation_unit(compilation_unit, symbol_table, &mut diagnostics);
 | ||||
|         resolve_compilation_unit(compilation_unit, symbol_table, &mut diagnostics); | ||||
|     } | ||||
| 
 | ||||
|     diagnostics.into() | ||||
| } | ||||
| 
 | ||||
| // #[cfg(test)]
 | ||||
| // mod tests {
 | ||||
| //     use super::*;
 | ||||
| //     use crate::ast::build::build_ast;
 | ||||
| //     use crate::ast::children::NodeRef;
 | ||||
| //     use crate::ast::walk::walk_depth_first;
 | ||||
| //     use crate::parser::{DeimosParser, Rule};
 | ||||
| //     use crate::std_core::add_std_core_symbols;
 | ||||
| //     use codespan_reporting::files::SimpleFiles;
 | ||||
| //     use codespan_reporting::term;
 | ||||
| //     use codespan_reporting::term::termcolor::{ColorChoice, StandardStream};
 | ||||
| //     use indoc::indoc;
 | ||||
| //     use pest::Parser;
 | ||||
| //     use std::collections::HashMap;
 | ||||
| //
 | ||||
| //     fn assert_number_of_diagnostics(
 | ||||
| //         sources: HashMap<&str, &str>,
 | ||||
| //         symbol_table: &mut SymbolTable,
 | ||||
| //         n_diagnostics: usize,
 | ||||
| //     ) -> Vec<CompilationUnit> {
 | ||||
| //         let mut files = SimpleFiles::new();
 | ||||
| //         let mut compilation_units = vec![];
 | ||||
| //
 | ||||
| //         for (file_name, source) in sources {
 | ||||
| //             let file_id = files.add(file_name, source);
 | ||||
| //             let parse_result = DeimosParser::parse(Rule::CompilationUnit, source);
 | ||||
| //             if let Err(err) = &parse_result {
 | ||||
| //                 panic!("{}", err);
 | ||||
| //             }
 | ||||
| //             let mut pairs = parse_result.unwrap();
 | ||||
| //             if pairs.as_str().trim() != source.trim() {
 | ||||
| //                 panic!("Parsing did not consume entire input.");
 | ||||
| //             }
 | ||||
| //
 | ||||
| //             compilation_units.push(build_ast(file_name, file_id, pairs.next().unwrap()));
 | ||||
| //         }
 | ||||
| //
 | ||||
| //         let diagnostics = analyze_names(&mut compilation_units, symbol_table);
 | ||||
| //
 | ||||
| //         if diagnostics.len() != n_diagnostics {
 | ||||
| //             let writer = StandardStream::stderr(ColorChoice::Always);
 | ||||
| //             let config = term::Config::default();
 | ||||
| //
 | ||||
| //             for diagnostic in &diagnostics {
 | ||||
| //                 term::emit(&mut writer.lock(), &config, &files, &diagnostic).unwrap();
 | ||||
| //             }
 | ||||
| //
 | ||||
| //             eprintln!("{}", symbol_table);
 | ||||
| //         }
 | ||||
| //
 | ||||
| //         assert_eq!(n_diagnostics, diagnostics.len());
 | ||||
| //
 | ||||
| //         compilation_units
 | ||||
| //     }
 | ||||
| //
 | ||||
| //     fn assert_no_diagnostics(
 | ||||
| //         sources: HashMap<&str, &str>,
 | ||||
| //         symbol_table: &mut SymbolTable,
 | ||||
| //     ) -> Vec<CompilationUnit> {
 | ||||
| //         assert_number_of_diagnostics(sources, symbol_table, 0)
 | ||||
| //     }
 | ||||
| //
 | ||||
| //     fn assert_saved_symbols(compilation_unit: &CompilationUnit) {
 | ||||
| //         walk_depth_first(compilation_unit, &mut |node_ref| match node_ref {
 | ||||
| //             NodeRef::Identifier(identifier) => {
 | ||||
| //                 if identifier.saved_symbol().is_none() {
 | ||||
| //                     panic!("{:?} does not have a saved symbol.", identifier)
 | ||||
| //                 }
 | ||||
| //             }
 | ||||
| //             NodeRef::FullyQualifiedName(fqn) => {
 | ||||
| //                 if fqn.saved_symbol().is_none() {
 | ||||
| //                     panic!("{:?} does not have a saved symbol.", fqn)
 | ||||
| //                 }
 | ||||
| //             }
 | ||||
| //             NodeRef::UseStatement(use_statement) => match use_statement {
 | ||||
| //                 _ => todo!(),
 | ||||
| //             },
 | ||||
| //             _ => {}
 | ||||
| //         })
 | ||||
| //     }
 | ||||
| //
 | ||||
| //     fn assert_resolved_symbols(compilation_unit: &CompilationUnit) {
 | ||||
| //         walk_depth_first(compilation_unit, &mut |node_ref| match node_ref {
 | ||||
| //             NodeRef::UseStatement(use_statement) => match use_statement {
 | ||||
| //                 _ => todo!(),
 | ||||
| //             },
 | ||||
| //             _ => {}
 | ||||
| //         })
 | ||||
| //     }
 | ||||
| //
 | ||||
| //     #[test]
 | ||||
| //     fn params_seen() {
 | ||||
| //         let sources: HashMap<&str, &str> = HashMap::from([(
 | ||||
| //             "main.dm",
 | ||||
| //             indoc! {"
 | ||||
| //             fn main(args: Array<String>) {
 | ||||
| //                 let x = args;
 | ||||
| //             }"},
 | ||||
| //         )]);
 | ||||
| //
 | ||||
| //         let cus = assert_no_diagnostics(sources, &mut SymbolTable::new());
 | ||||
| //         for ref cu in cus {
 | ||||
| //             assert_saved_symbols(cu);
 | ||||
| //         }
 | ||||
| //     }
 | ||||
| //
 | ||||
| //     #[test]
 | ||||
| //     fn two_files() {
 | ||||
| //         let sources: HashMap<&str, &str> = HashMap::from([
 | ||||
| //             (
 | ||||
| //                 "main.dm",
 | ||||
| //                 indoc! {"
 | ||||
| //                 use test::Greeter;
 | ||||
| //             "},
 | ||||
| //             ),
 | ||||
| //             (
 | ||||
| //                 "deps.dm",
 | ||||
| //                 indoc! {"
 | ||||
| //                 ns test;
 | ||||
| //
 | ||||
| //                 pub class Greeter {}
 | ||||
| //             "},
 | ||||
| //             ),
 | ||||
| //         ]);
 | ||||
| //
 | ||||
| //         let cus = assert_no_diagnostics(sources, &mut SymbolTable::new());
 | ||||
| //         for ref cu in cus {
 | ||||
| //             assert_saved_symbols(cu);
 | ||||
| //             assert_resolved_symbols(cu);
 | ||||
| //         }
 | ||||
| //     }
 | ||||
| //
 | ||||
| //     #[test]
 | ||||
| //     fn sees_std_core_println() {
 | ||||
| //         let sources: HashMap<&str, &str> = HashMap::from([(
 | ||||
| //             "main.dm",
 | ||||
| //             indoc! {"
 | ||||
| //                 fn main(args: Array<String>) {
 | ||||
| //                     println(args)
 | ||||
| //                 }
 | ||||
| //             "},
 | ||||
| //         )]);
 | ||||
| //
 | ||||
| //         let mut symbol_table = SymbolTable::new();
 | ||||
| //         add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols.");
 | ||||
| //         let cus = assert_no_diagnostics(sources, &mut symbol_table);
 | ||||
| //         for ref cu in cus {
 | ||||
| //             assert_saved_symbols(cu);
 | ||||
| //             assert_resolved_symbols(cu);
 | ||||
| //         }
 | ||||
| //     }
 | ||||
| //
 | ||||
| //     #[test]
 | ||||
| //     fn sees_duplicate_fn() {
 | ||||
| //         let sources: HashMap<&str, &str> = HashMap::from([(
 | ||||
| //             "main.dm",
 | ||||
| //             indoc! {"
 | ||||
| //                 fn main(args: Array<String>) {}
 | ||||
| //                 fn main(args: Array<String>) {}
 | ||||
| //             "},
 | ||||
| //         )]);
 | ||||
| //         assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1);
 | ||||
| //     }
 | ||||
| //
 | ||||
| //     #[test]
 | ||||
| //     fn use_class_from_other_file() {
 | ||||
| //         let sources: HashMap<&str, &str> = HashMap::from([
 | ||||
| //             (
 | ||||
| //                 "main.dm",
 | ||||
| //                 indoc! {"
 | ||||
| //                     use greeter::Greeter;
 | ||||
| //
 | ||||
| //                     fn test(greeter: Greeter) {}
 | ||||
| //                 "},
 | ||||
| //             ),
 | ||||
| //             (
 | ||||
| //                 "greeter.dm",
 | ||||
| //                 indoc! {"
 | ||||
| //                     ns greeter;
 | ||||
| //
 | ||||
| //                     class Greeter {}
 | ||||
| //                 "},
 | ||||
| //             ),
 | ||||
| //         ]);
 | ||||
| //         let mut symbol_table = SymbolTable::new();
 | ||||
| //         let cus = assert_no_diagnostics(sources, &mut symbol_table);
 | ||||
| //         for ref cu in cus {
 | ||||
| //             assert_saved_symbols(cu);
 | ||||
| //             assert_resolved_symbols(cu);
 | ||||
| //         }
 | ||||
| //     }
 | ||||
| //
 | ||||
| //     #[test]
 | ||||
| //     fn shadow_import() {
 | ||||
| //         let sources: HashMap<&str, &str> = HashMap::from([
 | ||||
| //             (
 | ||||
| //                 "main.dm",
 | ||||
| //                 indoc! {"
 | ||||
| //                     use greeter::Greeter;
 | ||||
| //
 | ||||
| //                     class Greeter {}
 | ||||
| //                 "},
 | ||||
| //             ),
 | ||||
| //             (
 | ||||
| //                 "greeter.dm",
 | ||||
| //                 indoc! {"
 | ||||
| //                     ns greeter;
 | ||||
| //
 | ||||
| //                     class Greeter {}
 | ||||
| //                 "},
 | ||||
| //             ),
 | ||||
| //         ]);
 | ||||
| //         assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1);
 | ||||
| //     }
 | ||||
| // }
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use super::*; | ||||
|     use crate::ast::build::build_ast; | ||||
|     use crate::ast::children::NodeRef; | ||||
|     use crate::ast::walk::walk_depth_first; | ||||
|     use crate::parser::{DeimosParser, Rule}; | ||||
|     use crate::std_core::add_std_core_symbols; | ||||
|     use codespan_reporting::files::SimpleFiles; | ||||
|     use codespan_reporting::term; | ||||
|     use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; | ||||
|     use indoc::indoc; | ||||
|     use pest::Parser; | ||||
|     use std::collections::HashMap; | ||||
| 
 | ||||
|     fn assert_number_of_diagnostics( | ||||
|         sources: HashMap<&str, &str>, | ||||
|         symbol_table: &mut SymbolTable, | ||||
|         n_diagnostics: usize, | ||||
|     ) -> Vec<CompilationUnit> { | ||||
|         let mut files = SimpleFiles::new(); | ||||
|         let mut compilation_units = vec![]; | ||||
| 
 | ||||
|         for (file_name, source) in sources { | ||||
|             let file_id = files.add(file_name, source); | ||||
|             let parse_result = DeimosParser::parse(Rule::CompilationUnit, source); | ||||
|             if let Err(err) = &parse_result { | ||||
|                 panic!("{}", err); | ||||
|             } | ||||
|             let mut pairs = parse_result.unwrap(); | ||||
|             if pairs.as_str().trim() != source.trim() { | ||||
|                 panic!("Parsing did not consume entire input."); | ||||
|             } | ||||
| 
 | ||||
|             compilation_units.push(build_ast(file_name, file_id, pairs.next().unwrap())); | ||||
|         } | ||||
| 
 | ||||
|         let diagnostics = analyze_names(&mut compilation_units, symbol_table); | ||||
| 
 | ||||
|         if diagnostics.len() != n_diagnostics { | ||||
|             let writer = StandardStream::stderr(ColorChoice::Always); | ||||
|             let config = term::Config::default(); | ||||
| 
 | ||||
|             for diagnostic in &diagnostics { | ||||
|                 term::emit(&mut writer.lock(), &config, &files, &diagnostic).unwrap(); | ||||
|             } | ||||
| 
 | ||||
|             eprintln!("{}", symbol_table); | ||||
|         } | ||||
| 
 | ||||
|         assert_eq!(n_diagnostics, diagnostics.len()); | ||||
| 
 | ||||
|         compilation_units | ||||
|     } | ||||
| 
 | ||||
|     fn assert_no_diagnostics( | ||||
|         sources: HashMap<&str, &str>, | ||||
|         symbol_table: &mut SymbolTable, | ||||
|     ) -> Vec<CompilationUnit> { | ||||
|         assert_number_of_diagnostics(sources, symbol_table, 0) | ||||
|     } | ||||
| 
 | ||||
|     fn assert_saved_symbols(compilation_unit: &CompilationUnit) { | ||||
|         walk_depth_first(compilation_unit, &mut |node_ref| match node_ref { | ||||
|             NodeRef::Identifier(identifier) => { | ||||
|                 if identifier.saved_symbol().is_none() { | ||||
|                     panic!("{:?} does not have a saved symbol.", identifier) | ||||
|                 } | ||||
|             } | ||||
|             NodeRef::FullyQualifiedName(fqn) => { | ||||
|                 if fqn.saved_symbol().is_none() { | ||||
|                     panic!("{:?} does not have a saved symbol.", fqn) | ||||
|                 } | ||||
|             } | ||||
|             NodeRef::UseStatement(use_statement) => match use_statement { | ||||
|                 _ => todo!(), | ||||
|             }, | ||||
|             _ => {} | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     fn assert_resolved_symbols(compilation_unit: &CompilationUnit) { | ||||
|         walk_depth_first(compilation_unit, &mut |node_ref| match node_ref { | ||||
|             NodeRef::UseStatement(use_statement) => match use_statement { | ||||
|                 _ => todo!(), | ||||
|             }, | ||||
|             _ => {} | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn params_seen() { | ||||
|         let sources: HashMap<&str, &str> = HashMap::from([( | ||||
|             "main.dm", | ||||
|             indoc! {" | ||||
|             fn main(args: Array<String>) { | ||||
|                 let x = args; | ||||
|             }"},
 | ||||
|         )]); | ||||
| 
 | ||||
|         let cus = assert_no_diagnostics(sources, &mut SymbolTable::new()); | ||||
|         for ref cu in cus { | ||||
|             assert_saved_symbols(cu); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn two_files() { | ||||
|         let sources: HashMap<&str, &str> = HashMap::from([ | ||||
|             ( | ||||
|                 "main.dm", | ||||
|                 indoc! {" | ||||
|                 use test::Greeter; | ||||
|             "},
 | ||||
|             ), | ||||
|             ( | ||||
|                 "deps.dm", | ||||
|                 indoc! {" | ||||
|                 ns test; | ||||
| 
 | ||||
|                 pub class Greeter {} | ||||
|             "},
 | ||||
|             ), | ||||
|         ]); | ||||
| 
 | ||||
|         let cus = assert_no_diagnostics(sources, &mut SymbolTable::new()); | ||||
|         for ref cu in cus { | ||||
|             assert_saved_symbols(cu); | ||||
|             assert_resolved_symbols(cu); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn sees_std_core_println() { | ||||
|         let sources: HashMap<&str, &str> = HashMap::from([( | ||||
|             "main.dm", | ||||
|             indoc! {" | ||||
|                 fn main(args: Array<String>) { | ||||
|                     println(args) | ||||
|                 } | ||||
|             "},
 | ||||
|         )]); | ||||
| 
 | ||||
|         let mut symbol_table = SymbolTable::new(); | ||||
|         add_std_core_symbols(&mut symbol_table).expect("Failed to add std::core symbols."); | ||||
|         let cus = assert_no_diagnostics(sources, &mut symbol_table); | ||||
|         for ref cu in cus { | ||||
|             assert_saved_symbols(cu); | ||||
|             assert_resolved_symbols(cu); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn sees_duplicate_fn() { | ||||
|         let sources: HashMap<&str, &str> = HashMap::from([( | ||||
|             "main.dm", | ||||
|             indoc! {" | ||||
|                 fn main(args: Array<String>) {} | ||||
|                 fn main(args: Array<String>) {} | ||||
|             "},
 | ||||
|         )]); | ||||
|         assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1); | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn use_class_from_other_file() { | ||||
|         let sources: HashMap<&str, &str> = HashMap::from([ | ||||
|             ( | ||||
|                 "main.dm", | ||||
|                 indoc! {" | ||||
|                     use greeter::Greeter; | ||||
| 
 | ||||
|                     fn test(greeter: Greeter) {} | ||||
|                 "},
 | ||||
|             ), | ||||
|             ( | ||||
|                 "greeter.dm", | ||||
|                 indoc! {" | ||||
|                     ns greeter; | ||||
| 
 | ||||
|                     class Greeter {} | ||||
|                 "},
 | ||||
|             ), | ||||
|         ]); | ||||
|         let mut symbol_table = SymbolTable::new(); | ||||
|         let cus = assert_no_diagnostics(sources, &mut symbol_table); | ||||
|         for ref cu in cus { | ||||
|             assert_saved_symbols(cu); | ||||
|             assert_resolved_symbols(cu); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     #[test] | ||||
|     fn shadow_import() { | ||||
|         let sources: HashMap<&str, &str> = HashMap::from([ | ||||
|             ( | ||||
|                 "main.dm", | ||||
|                 indoc! {" | ||||
|                     use greeter::Greeter; | ||||
| 
 | ||||
|                     class Greeter {} | ||||
|                 "},
 | ||||
|             ), | ||||
|             ( | ||||
|                 "greeter.dm", | ||||
|                 indoc! {" | ||||
|                     ns greeter; | ||||
| 
 | ||||
|                     class Greeter {} | ||||
|                 "},
 | ||||
|             ), | ||||
|         ]); | ||||
|         assert_number_of_diagnostics(sources, &mut SymbolTable::new(), 1); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -1,9 +1,30 @@ | ||||
| use crate::ast::node::*; | ||||
| use crate::ast::node::call_expression::*; | ||||
| use crate::ast::node::class::*; | ||||
| use crate::ast::node::closure::*; | ||||
| use crate::ast::node::compilation_unit::*; | ||||
| use crate::ast::node::d_string::*; | ||||
| use crate::ast::node::expression::*; | ||||
| use crate::ast::node::function::*; | ||||
| use crate::ast::node::generics::*; | ||||
| use crate::ast::node::implements_list::*; | ||||
| use crate::ast::node::interface::*; | ||||
| use crate::ast::node::level::*; | ||||
| use crate::ast::node::literal::*; | ||||
| use crate::ast::node::module::*; | ||||
| use crate::ast::node::names::*; | ||||
| use crate::ast::node::object_access::*; | ||||
| use crate::ast::node::statement::*; | ||||
| use crate::ast::node::tuple_arguments::*; | ||||
| use crate::ast::node::type_use::*; | ||||
| use crate::ast::node::use_statement::*; | ||||
| use crate::diagnostic::DmDiagnostic; | ||||
| use crate::name_analysis::symbol::Symbol; | ||||
| use crate::name_analysis::symbol_table::{SymbolLookupError, SymbolTable}; | ||||
| use codespan_reporting::diagnostic::{Diagnostic, Label}; | ||||
| use std::ops::DerefMut; | ||||
| use std::range::Range; | ||||
| use crate::ast::node::named::Named; | ||||
| /* Type Use */ | ||||
| 
 | ||||
| fn resolve_type_use( | ||||
|     type_use: &mut TypeUse, | ||||
							
								
								
									
										475
									
								
								src/name_analysis/symbol.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										475
									
								
								src/name_analysis/symbol.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,475 @@ | ||||
| use crate::ast::node::named::Named; | ||||
| use crate::ast::node::names::Identifier; | ||||
| use crate::ast::node::use_statement::UseStatement; | ||||
| use std::cell::RefCell; | ||||
| use std::fmt::{Debug, Display, Formatter}; | ||||
| use std::ops::Deref; | ||||
| use std::range::Range; | ||||
| use std::rc::Rc; | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct SourceDefinition { | ||||
|     file_id: usize, | ||||
|     range: Range<usize>, | ||||
| } | ||||
| 
 | ||||
| impl SourceDefinition { | ||||
|     pub fn from_identifier(identifier: &Identifier) -> Self { | ||||
|         SourceDefinition { | ||||
|             file_id: identifier.file_id(), | ||||
|             range: identifier.range(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn from_identifier_rc(identifier: Rc<RefCell<Identifier>>) -> Self { | ||||
|         let borrowed = identifier.borrow(); | ||||
|         SourceDefinition { | ||||
|             file_id: borrowed.file_id(), | ||||
|             range: borrowed.range(), | ||||
|         } | ||||
|     } | ||||
|     
 | ||||
|     pub fn file_id(&self) -> usize { | ||||
|         self.file_id | ||||
|     } | ||||
| 
 | ||||
|     pub fn range(&self) -> Range<usize> { | ||||
|         self.range.clone() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub trait SymbolInner { | ||||
|     fn declared_name(&self) -> &str; | ||||
|     fn definition(&self) -> Option<SourceDefinition>; | ||||
| } | ||||
| 
 | ||||
| /* Symbol */ | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub enum Symbol { | ||||
|     UseStatement(Rc<RefCell<UseStatementSymbol>>), | ||||
|     Module(Rc<ModuleSymbol>), | ||||
|     Type(Rc<TypeSymbol>), | ||||
|     Function(Rc<RefCell<FunctionSymbol>>), | ||||
|     Parameter(Rc<ParameterSymbol>), | ||||
|     Variable(Rc<VariableSymbol>), | ||||
|     ClassMember(Rc<ClassMemberSymbol>), | ||||
| } | ||||
| 
 | ||||
| impl Symbol { | ||||
|     pub fn definition(&self) -> Option<SourceDefinition> { | ||||
|         match self { | ||||
|             Symbol::UseStatement(s) => s.borrow().definition(), | ||||
|             Symbol::Module(s) => s.definition(), | ||||
|             Symbol::Type(s) => match s.deref() { | ||||
|                 TypeSymbol::Concrete(cts) => cts.definition(), | ||||
|                 TypeSymbol::Generic(gts) => gts.definition(), | ||||
|             }, | ||||
|             Symbol::Function(s) => s.borrow().definition(), | ||||
|             Symbol::Parameter(s) => s.definition(), | ||||
|             Symbol::Variable(s) => s.definition(), | ||||
|             Symbol::ClassMember(s) => s.definition(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn unwrap_use_statement_symbol(&self) -> Rc<RefCell<UseStatementSymbol>> { | ||||
|         match self { | ||||
|             Symbol::UseStatement(s) => s.clone(), | ||||
|             _ => panic!("unwrap_use_statement_symbol called on non-use statement symbol"), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Use-statement */ | ||||
| 
 | ||||
| pub struct UseStatementSymbol { | ||||
|     pub fqn: String, | ||||
|     pub declared_name: String, | ||||
|     definition: Option<SourceDefinition>, | ||||
|     referenced_symbol: Option<Box<Symbol>>, | ||||
| } | ||||
| 
 | ||||
| impl UseStatementSymbol { | ||||
|     pub fn new(fqn: &str, declared_name: &str, identifier: Option<&Identifier>) -> Self { | ||||
|         UseStatementSymbol { | ||||
|             fqn: fqn.to_string(), | ||||
|             declared_name: declared_name.to_string(), | ||||
|             definition: identifier.map(SourceDefinition::from_identifier), | ||||
|             referenced_symbol: None, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn set_referenced_symbol(&mut self, referenced_symbol: Symbol) { | ||||
|         self.referenced_symbol = Some(Box::new(referenced_symbol)); | ||||
|     } | ||||
| 
 | ||||
|     pub fn referenced_symbol(&self) -> Option<Box<Symbol>> { | ||||
|         self.referenced_symbol.clone() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SymbolInner for UseStatementSymbol { | ||||
|     fn declared_name(&self) -> &str { | ||||
|         &self.declared_name | ||||
|     } | ||||
| 
 | ||||
|     fn definition(&self) -> Option<SourceDefinition> { | ||||
|         self.definition.clone() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for UseStatementSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("UseStatementSymbol") | ||||
|             .field("fqn", &self.fqn) | ||||
|             .field("declared_name", &self.declared_name) | ||||
|             .field("referenced_symbol", &self.referenced_symbol) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Module */ | ||||
| 
 | ||||
| pub struct ModuleSymbol { | ||||
|     fqn: String, | ||||
|     declared_name: String, | ||||
|     is_public: bool, | ||||
|     definition: Option<SourceDefinition>, | ||||
| } | ||||
| 
 | ||||
| impl ModuleSymbol { | ||||
|     pub fn new( | ||||
|         fqn: &str, | ||||
|         declared_name: &str, | ||||
|         is_public: bool, | ||||
|         identifier: Option<&Identifier>, | ||||
|     ) -> ModuleSymbol { | ||||
|         ModuleSymbol { | ||||
|             fqn: fqn.to_string(), | ||||
|             declared_name: declared_name.to_string(), | ||||
|             is_public, | ||||
|             definition: identifier.map(SourceDefinition::from_identifier), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SymbolInner for ModuleSymbol { | ||||
|     fn declared_name(&self) -> &str { | ||||
|         self.declared_name.as_str() | ||||
|     } | ||||
| 
 | ||||
|     fn definition(&self) -> Option<SourceDefinition> { | ||||
|         self.definition.clone() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for ModuleSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("ModuleSymbol") | ||||
|             .field("fqn", &self.fqn) | ||||
|             .field("declared_name", &self.declared_name) | ||||
|             .field("is_public", &self.is_public) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* TypeSymbol */ | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum TypeSymbol { | ||||
|     Concrete(ConcreteTypeSymbol), | ||||
|     Generic(GenericTypeSymbol), | ||||
| } | ||||
| 
 | ||||
| impl TypeSymbol { | ||||
|     pub fn declared_name(&self) -> &str { | ||||
|         match self { | ||||
|             TypeSymbol::Concrete(t) => t.declared_name(), | ||||
|             TypeSymbol::Generic(t) => t.declared_name(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct ConcreteTypeSymbol { | ||||
|     fqn: String, | ||||
|     declared_name: String, | ||||
|     is_public: bool, | ||||
|     definition: Option<SourceDefinition>, | ||||
| } | ||||
| 
 | ||||
| impl ConcreteTypeSymbol { | ||||
|     pub fn new( | ||||
|         fqn: &str, | ||||
|         declared_name: &str, | ||||
|         is_public: bool, | ||||
|         identifier: Option<&Identifier>, | ||||
|     ) -> Self { | ||||
|         ConcreteTypeSymbol { | ||||
|             fqn: fqn.to_string(), | ||||
|             declared_name: declared_name.to_string(), | ||||
|             is_public, | ||||
|             definition: identifier.map(SourceDefinition::from_identifier), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn fqn(&self) -> &str { | ||||
|         &self.fqn | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_public(&self) -> bool { | ||||
|         self.is_public | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SymbolInner for ConcreteTypeSymbol { | ||||
|     fn declared_name(&self) -> &str { | ||||
|         &self.declared_name | ||||
|     } | ||||
| 
 | ||||
|     fn definition(&self) -> Option<SourceDefinition> { | ||||
|         self.definition.clone() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for ConcreteTypeSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("TypeSymbol") | ||||
|             .field("fqn", &self.fqn) | ||||
|             .field("declared_name", &self.declared_name) | ||||
|             .field("is_public", &self.is_public) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct GenericTypeSymbol { | ||||
|     declared_name: String, | ||||
|     source_definition: SourceDefinition, | ||||
| } | ||||
| 
 | ||||
| impl GenericTypeSymbol { | ||||
|     pub fn new(declared_name: &str, source_definition: SourceDefinition) -> Self { | ||||
|         GenericTypeSymbol { | ||||
|             declared_name: declared_name.to_string(), | ||||
|             source_definition, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SymbolInner for GenericTypeSymbol { | ||||
|     fn declared_name(&self) -> &str { | ||||
|         self.declared_name.as_str() | ||||
|     } | ||||
| 
 | ||||
|     fn definition(&self) -> Option<SourceDefinition> { | ||||
|         Some(self.source_definition.clone()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for GenericTypeSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("GenericTypeSymbol") | ||||
|             .field("declared_name", &self.declared_name) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Function */ | ||||
| 
 | ||||
| pub struct FunctionSymbol { | ||||
|     fqn: String, | ||||
|     declared_name: String, | ||||
|     is_public: bool, | ||||
|     is_platform: bool, | ||||
|     definition: Option<SourceDefinition>, | ||||
|     parameters: Vec<Rc<ParameterSymbol>>, | ||||
|     return_type: Option<Rc<ConcreteTypeSymbol>>, | ||||
| } | ||||
| 
 | ||||
| impl FunctionSymbol { | ||||
|     pub fn new( | ||||
|         fqn: &str, | ||||
|         declared_name: &str, | ||||
|         is_public: bool, | ||||
|         is_platform: bool, | ||||
|         identifier: Option<&Identifier>, | ||||
|     ) -> FunctionSymbol { | ||||
|         FunctionSymbol { | ||||
|             fqn: fqn.to_string(), | ||||
|             declared_name: declared_name.to_string(), | ||||
|             is_public, | ||||
|             is_platform, | ||||
|             definition: identifier.map(SourceDefinition::from_identifier), | ||||
|             parameters: Vec::new(), | ||||
|             return_type: None, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn with_parameters(self, parameters: Vec<ParameterSymbol>) -> Self { | ||||
|         Self { | ||||
|             fqn: self.fqn, | ||||
|             declared_name: self.declared_name, | ||||
|             is_public: self.is_public, | ||||
|             is_platform: self.is_platform, | ||||
|             definition: self.definition, | ||||
|             parameters: parameters | ||||
|                 .into_iter() | ||||
|                 .map(|parameter| Rc::new(parameter)) | ||||
|                 .collect(), | ||||
|             return_type: self.return_type, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn with_return_type(self, return_type: ConcreteTypeSymbol) -> Self { | ||||
|         Self { | ||||
|             fqn: self.fqn, | ||||
|             declared_name: self.declared_name, | ||||
|             is_public: self.is_public, | ||||
|             is_platform: self.is_platform, | ||||
|             definition: self.definition, | ||||
|             parameters: self.parameters, | ||||
|             return_type: Some(Rc::new(return_type)), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn fqn(&self) -> &str { | ||||
|         &self.fqn | ||||
|     } | ||||
| 
 | ||||
|     pub fn set_parameters(&mut self, parameters: Vec<Rc<ParameterSymbol>>) { | ||||
|         self.parameters = parameters; | ||||
|     } | ||||
| 
 | ||||
|     pub fn set_return_type(&mut self, return_type: Rc<ConcreteTypeSymbol>) { | ||||
|         self.return_type = Some(return_type); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SymbolInner for FunctionSymbol { | ||||
|     fn declared_name(&self) -> &str { | ||||
|         self.declared_name.as_str() | ||||
|     } | ||||
| 
 | ||||
|     fn definition(&self) -> Option<SourceDefinition> { | ||||
|         self.definition.clone() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for FunctionSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("FunctionSymbol") | ||||
|             .field("fqn", &self.fqn) | ||||
|             .field("declared_name", &self.declared_name) | ||||
|             .field("is_public", &self.is_public) | ||||
|             .field("is_platform", &self.is_platform) | ||||
|             .field("parameters", &self.parameters) | ||||
|             .field("return_type", &self.return_type) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Parameter */ | ||||
| 
 | ||||
| pub struct ParameterSymbol { | ||||
|     declared_name: String, | ||||
|     definition: Option<SourceDefinition>, | ||||
| } | ||||
| 
 | ||||
| impl ParameterSymbol { | ||||
|     pub fn new(declared_name: &str, identifier: Option<&Identifier>) -> Self { | ||||
|         ParameterSymbol { | ||||
|             declared_name: declared_name.to_string(), | ||||
|             definition: identifier.map(SourceDefinition::from_identifier), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SymbolInner for ParameterSymbol { | ||||
|     fn declared_name(&self) -> &str { | ||||
|         &self.declared_name | ||||
|     } | ||||
| 
 | ||||
|     fn definition(&self) -> Option<SourceDefinition> { | ||||
|         self.definition.clone() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for ParameterSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("ParameterSymbol") | ||||
|             .field("declared_name", &self.declared_name) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Variable */ | ||||
| 
 | ||||
| pub struct VariableSymbol { | ||||
|     declared_name: String, | ||||
|     is_mutable: bool, | ||||
|     definition: Option<SourceDefinition>, | ||||
| } | ||||
| 
 | ||||
| impl VariableSymbol { | ||||
|     pub fn new(declared_name: &str, is_mutable: bool, identifier: Option<&Identifier>) -> Self { | ||||
|         VariableSymbol { | ||||
|             declared_name: declared_name.to_string(), | ||||
|             is_mutable, | ||||
|             definition: identifier.map(SourceDefinition::from_identifier), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SymbolInner for VariableSymbol { | ||||
|     fn declared_name(&self) -> &str { | ||||
|         self.declared_name.as_str() | ||||
|     } | ||||
| 
 | ||||
|     fn definition(&self) -> Option<SourceDefinition> { | ||||
|         self.definition.clone() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for VariableSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("VariableSymbol") | ||||
|             .field("declared_name", &self.declared_name) | ||||
|             .field("is_mutable", &self.is_mutable) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Class Member */ | ||||
| 
 | ||||
| pub struct ClassMemberSymbol { | ||||
|     declared_name: String, | ||||
|     is_field: bool, | ||||
|     definition: Option<SourceDefinition>, | ||||
| } | ||||
| 
 | ||||
| impl ClassMemberSymbol { | ||||
|     pub fn new(declared_name: &str, is_field: bool, definition: Option<SourceDefinition>) -> Self { | ||||
|         ClassMemberSymbol { | ||||
|             declared_name: declared_name.to_string(), | ||||
|             is_field, | ||||
|             definition, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl SymbolInner for ClassMemberSymbol { | ||||
|     fn declared_name(&self) -> &str { | ||||
|         self.declared_name.as_str() | ||||
|     } | ||||
| 
 | ||||
|     fn definition(&self) -> Option<SourceDefinition> { | ||||
|         self.definition.clone() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for ClassMemberSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("ClassMemberSymbol") | ||||
|             .field("declared_name", &self.declared_name) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| @ -1,53 +0,0 @@ | ||||
| use crate::name_analysis::symbol::source_definition::SourceDefinition; | ||||
| use std::fmt::{Debug, Formatter}; | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct ClassMemberSymbol { | ||||
|     is_public: bool, | ||||
|     is_mut: bool, | ||||
|     declared_name: String, | ||||
|     source_definition: Option<SourceDefinition>, | ||||
| } | ||||
| 
 | ||||
| impl ClassMemberSymbol { | ||||
|     pub fn new( | ||||
|         is_public: bool, | ||||
|         is_mut: bool, | ||||
|         declared_name: &str, | ||||
|         source_definition: Option<SourceDefinition>, | ||||
|     ) -> Self { | ||||
|         Self { | ||||
|             is_public, | ||||
|             is_mut, | ||||
|             declared_name: declared_name.to_string(), | ||||
|             source_definition, | ||||
|         } | ||||
|     } | ||||
|     
 | ||||
|     pub fn is_public(&self) -> bool { | ||||
|         self.is_public | ||||
|     } | ||||
|     
 | ||||
|     pub fn is_mut(&self) -> bool { | ||||
|         self.is_mut | ||||
|     } | ||||
| 
 | ||||
|     pub fn declared_name(&self) -> &str { | ||||
|         &self.declared_name | ||||
|     } | ||||
| 
 | ||||
|     pub fn source_definition(&self) -> Option<&SourceDefinition> { | ||||
|         self.source_definition.as_ref() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for ClassMemberSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("ClassMemberSymbol") | ||||
|             .field("is_public", &self.is_public) | ||||
|             .field("is_mut", &self.is_mut) | ||||
|             .field("declared_name", &self.declared_name) | ||||
|             .field("source_definition", &self.source_definition) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| @ -1,101 +0,0 @@ | ||||
| use crate::name_analysis::symbol::parameter_symbol::ParameterSymbol; | ||||
| use crate::name_analysis::symbol::source_definition::SourceDefinition; | ||||
| use crate::name_analysis::symbol::type_symbol::ConcreteTypeSymbol; | ||||
| use std::fmt::{Debug, Formatter}; | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct FunctionSymbol { | ||||
|     fqn: String, | ||||
|     declared_name: String, | ||||
|     is_public: bool, | ||||
|     is_platform: bool, | ||||
|     source_definition: Option<SourceDefinition>, | ||||
|     parameters: Vec<ParameterSymbol>, | ||||
|     return_type: Option<ConcreteTypeSymbol>, // todo: can we use TypeSymbol?
 | ||||
| } | ||||
| 
 | ||||
| impl FunctionSymbol { | ||||
|     pub fn without_parameters_or_return_type( | ||||
|         fqn: &str, | ||||
|         declared_name: &str, | ||||
|         is_public: bool, | ||||
|         is_platform: bool, | ||||
|         source_definition: Option<SourceDefinition>, | ||||
|     ) -> FunctionSymbol { | ||||
|         FunctionSymbol { | ||||
|             fqn: fqn.to_string(), | ||||
|             declared_name: declared_name.to_string(), | ||||
|             is_public, | ||||
|             is_platform, | ||||
|             source_definition, | ||||
|             parameters: Vec::new(), | ||||
|             return_type: None, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn with_parameters(self, parameters: Vec<ParameterSymbol>) -> Self { | ||||
|         Self { | ||||
|             fqn: self.fqn, | ||||
|             declared_name: self.declared_name, | ||||
|             is_public: self.is_public, | ||||
|             is_platform: self.is_platform, | ||||
|             source_definition: self.source_definition, | ||||
|             parameters, | ||||
|             return_type: self.return_type, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn with_return_type(self, return_type: ConcreteTypeSymbol) -> Self { | ||||
|         Self { | ||||
|             fqn: self.fqn, | ||||
|             declared_name: self.declared_name, | ||||
|             is_public: self.is_public, | ||||
|             is_platform: self.is_platform, | ||||
|             source_definition: self.source_definition, | ||||
|             parameters: self.parameters, | ||||
|             return_type: Some(return_type), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn fqn(&self) -> &str { | ||||
|         &self.fqn | ||||
|     } | ||||
| 
 | ||||
|     pub fn declared_name(&self) -> &str { | ||||
|         &self.declared_name | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_public(&self) -> bool { | ||||
|         self.is_public | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_platform(&self) -> bool { | ||||
|         self.is_platform | ||||
|     } | ||||
| 
 | ||||
|     pub fn source_definition(&self) -> Option<&SourceDefinition> { | ||||
|         self.source_definition.as_ref() | ||||
|     } | ||||
| 
 | ||||
|     pub fn parameters(&self) -> &[ParameterSymbol] { | ||||
|         &self.parameters | ||||
|     } | ||||
| 
 | ||||
|     pub fn return_type(&self) -> Option<&ConcreteTypeSymbol> { | ||||
|         self.return_type.as_ref() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for FunctionSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("FunctionSymbol") | ||||
|             .field("fqn", &self.fqn) | ||||
|             .field("declared_name", &self.declared_name) | ||||
|             .field("is_public", &self.is_public) | ||||
|             .field("is_platform", &self.is_platform) | ||||
|             .field("parameters", &self.parameters) | ||||
|             .field("return_type", &self.return_type) | ||||
|             .field("source_definition", &self.source_definition) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| @ -1,46 +0,0 @@ | ||||
| pub(crate) mod class_member_symbol; | ||||
| pub(crate) mod function_symbol; | ||||
| pub(crate) mod module_symbol; | ||||
| pub(crate) mod parameter_symbol; | ||||
| pub(crate) mod source_definition; | ||||
| pub(crate) mod type_symbol; | ||||
| pub(crate) mod use_symbol; | ||||
| pub(crate) mod variable_symbol; | ||||
| 
 | ||||
| use crate::name_analysis::symbol::use_symbol::{ConcreteUseSymbol, StarUseSymbol}; | ||||
| use class_member_symbol::ClassMemberSymbol; | ||||
| use function_symbol::FunctionSymbol; | ||||
| use module_symbol::ModuleSymbol; | ||||
| use parameter_symbol::ParameterSymbol; | ||||
| use source_definition::SourceDefinition; | ||||
| use std::fmt::{Debug, Display}; | ||||
| use std::ops::Deref; | ||||
| use type_symbol::TypeSymbol; | ||||
| use variable_symbol::VariableSymbol; | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| pub enum Symbol { | ||||
|     ConcreteUse(ConcreteUseSymbol), | ||||
|     StarUse(StarUseSymbol), | ||||
|     Module(ModuleSymbol), | ||||
|     Type(TypeSymbol), | ||||
|     Function(FunctionSymbol), | ||||
|     Parameter(ParameterSymbol), | ||||
|     Variable(VariableSymbol), | ||||
|     ClassMember(ClassMemberSymbol), | ||||
| } | ||||
| 
 | ||||
| impl Symbol { | ||||
|     pub fn definition(&self) -> Option<&SourceDefinition> { | ||||
|         match self { | ||||
|             Symbol::ConcreteUse(concrete) => concrete.source_definition(), | ||||
|             Symbol::StarUse(star) => star.source_definition(), | ||||
|             Symbol::Module(module) => module.source_definition(), | ||||
|             Symbol::Type(type_symbol) => type_symbol.source_definition(), | ||||
|             Symbol::Function(function_symbol) => function_symbol.source_definition(), | ||||
|             Symbol::Parameter(parameter_symbol) => parameter_symbol.source_definition(), | ||||
|             Symbol::Variable(variable_symbol) => variable_symbol.source_definition(), | ||||
|             Symbol::ClassMember(class_member_symbol) => class_member_symbol.source_definition(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,53 +0,0 @@ | ||||
| use crate::name_analysis::symbol::source_definition::SourceDefinition; | ||||
| use std::fmt::{Debug, Formatter}; | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct ModuleSymbol { | ||||
|     fqn: String, | ||||
|     declared_name: String, | ||||
|     is_public: bool, | ||||
|     source_definition: Option<SourceDefinition>, | ||||
| } | ||||
| 
 | ||||
| impl ModuleSymbol { | ||||
|     pub fn new( | ||||
|         fqn: &str, | ||||
|         declared_name: &str, | ||||
|         is_public: bool, | ||||
|         source_definition: Option<SourceDefinition>, | ||||
|     ) -> Self { | ||||
|         Self { | ||||
|             fqn: fqn.to_string(), | ||||
|             declared_name: declared_name.to_string(), | ||||
|             is_public, | ||||
|             source_definition, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn fqn(&self) -> &str { | ||||
|         &self.fqn | ||||
|     } | ||||
| 
 | ||||
|     pub fn declared_name(&self) -> &str { | ||||
|         &self.declared_name | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_public(&self) -> bool { | ||||
|         self.is_public | ||||
|     } | ||||
| 
 | ||||
|     pub fn source_definition(&self) -> Option<&SourceDefinition> { | ||||
|         self.source_definition.as_ref() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for ModuleSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("ModuleSymbol") | ||||
|             .field("fqn", &self.fqn) | ||||
|             .field("declared_name", &self.declared_name) | ||||
|             .field("is_public", &self.is_public) | ||||
|             .field("source_definition", &self.source_definition) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| @ -1,34 +0,0 @@ | ||||
| use crate::name_analysis::symbol::source_definition::SourceDefinition; | ||||
| use std::fmt::{Debug, Formatter}; | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct ParameterSymbol { | ||||
|     declared_name: String, | ||||
|     source_definition: Option<SourceDefinition>, | ||||
| } | ||||
| 
 | ||||
| impl ParameterSymbol { | ||||
|     pub fn new(declared_name: &str, source_definition: Option<SourceDefinition>) -> Self { | ||||
|         ParameterSymbol { | ||||
|             declared_name: declared_name.to_string(), | ||||
|             source_definition, | ||||
|         } | ||||
|     } | ||||
|     
 | ||||
|     pub fn declared_name(&self) -> &str { | ||||
|         &self.declared_name | ||||
|     } | ||||
|     
 | ||||
|     pub fn source_definition(&self) -> Option<&SourceDefinition> { | ||||
|         self.source_definition.as_ref() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for ParameterSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("ParameterSymbol") | ||||
|             .field("declared_name", &self.declared_name) | ||||
|             .field("source_definition", &self.source_definition) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| @ -1,41 +0,0 @@ | ||||
| use std::cell::RefCell; | ||||
| use std::range::Range; | ||||
| use std::rc::Rc; | ||||
| use crate::ast::node::{Identifier, Operator, UseStatement}; | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct SourceDefinition { | ||||
|     file_id: usize, | ||||
|     range: Range<usize>, | ||||
| } | ||||
| 
 | ||||
| impl SourceDefinition { | ||||
|     pub fn from_identifier(identifier: &Identifier) -> Self { | ||||
|         SourceDefinition { | ||||
|             file_id: identifier.file_id(), | ||||
|             range: identifier.range(), | ||||
|         } | ||||
|     } | ||||
|     
 | ||||
|     pub fn from_use_statement(use_statement: &UseStatement) -> Self { | ||||
|         Self { | ||||
|             file_id: use_statement.file_id(), | ||||
|             range: use_statement.range(), | ||||
|         } | ||||
|     } | ||||
|     
 | ||||
|     pub fn from_operator(operator: &Operator) -> Self { | ||||
|         Self { | ||||
|             file_id: operator.file_id(), | ||||
|             range: operator.range(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn file_id(&self) -> usize { | ||||
|         self.file_id | ||||
|     } | ||||
| 
 | ||||
|     pub fn range(&self) -> Range<usize> { | ||||
|         self.range.clone() | ||||
|     } | ||||
| } | ||||
| @ -1,121 +0,0 @@ | ||||
| use crate::name_analysis::symbol::source_definition::SourceDefinition; | ||||
| use std::fmt::{Debug, Formatter}; | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub enum TypeSymbol { | ||||
|     Concrete(ConcreteTypeSymbol), | ||||
|     Generic(GenericTypeSymbol), | ||||
| } | ||||
| 
 | ||||
| impl TypeSymbol { | ||||
|     pub fn declared_name(&self) -> &str { | ||||
|         match self { | ||||
|             TypeSymbol::Concrete(t) => t.declared_name(), | ||||
|             TypeSymbol::Generic(t) => t.declared_name(), | ||||
|         } | ||||
|     } | ||||
|     
 | ||||
|     pub fn source_definition(&self) -> Option<&SourceDefinition> { | ||||
|         match self { | ||||
|             TypeSymbol::Concrete(t) => t.source_definition(), | ||||
|             TypeSymbol::Generic(t) => t.source_definition(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct ConcreteTypeSymbol { | ||||
|     fqn: String, | ||||
|     declared_name: String, | ||||
|     is_public: bool, | ||||
|     kind: ConcreteTypeSymbolKind, | ||||
|     source_definition: Option<SourceDefinition>, | ||||
| } | ||||
| 
 | ||||
| impl ConcreteTypeSymbol { | ||||
|     pub fn new( | ||||
|         fqn: &str, | ||||
|         declared_name: &str, | ||||
|         is_public: bool, | ||||
|         kind: ConcreteTypeSymbolKind, | ||||
|         source_definition: Option<SourceDefinition>, | ||||
|     ) -> Self { | ||||
|         Self { | ||||
|             fqn: fqn.to_string(), | ||||
|             declared_name: declared_name.to_string(), | ||||
|             is_public, | ||||
|             kind, | ||||
|             source_definition | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn fqn(&self) -> &str { | ||||
|         &self.fqn | ||||
|     } | ||||
| 
 | ||||
|     pub fn is_public(&self) -> bool { | ||||
|         self.is_public | ||||
|     } | ||||
|     
 | ||||
|     pub fn kind(&self) -> &ConcreteTypeSymbolKind { | ||||
|         &self.kind | ||||
|     } | ||||
|     
 | ||||
|     pub fn declared_name(&self) -> &str { | ||||
|         &self.declared_name | ||||
|     } | ||||
|     
 | ||||
|     pub fn source_definition(&self) -> Option<&SourceDefinition> { | ||||
|         self.source_definition.as_ref() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for ConcreteTypeSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("TypeSymbol") | ||||
|             .field("fqn", &self.fqn) | ||||
|             .field("declared_name", &self.declared_name) | ||||
|             .field("is_public", &self.is_public) | ||||
|             .field("kind", &self.kind) | ||||
|             .field("source_definition", &self.source_definition) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub enum ConcreteTypeSymbolKind { | ||||
|     Interface, | ||||
|     Class | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct GenericTypeSymbol { | ||||
|     declared_name: String, | ||||
|     source_definition: Option<SourceDefinition>, | ||||
| } | ||||
| 
 | ||||
| impl GenericTypeSymbol { | ||||
|     pub fn new(declared_name: &str, source_definition: Option<SourceDefinition>) -> Self { | ||||
|         GenericTypeSymbol { | ||||
|             declared_name: declared_name.to_string(), | ||||
|             source_definition, | ||||
|         } | ||||
|     } | ||||
|     
 | ||||
|     pub fn declared_name(&self) -> &str { | ||||
|         &self.declared_name | ||||
|     } | ||||
|     
 | ||||
|     pub fn source_definition(&self) -> Option<&SourceDefinition> { | ||||
|         self.source_definition.as_ref() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for GenericTypeSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("GenericTypeSymbol") | ||||
|             .field("declared_name", &self.declared_name) | ||||
|             .field("source_definition", &self.source_definition) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| @ -1,77 +0,0 @@ | ||||
| use crate::name_analysis::symbol::source_definition::SourceDefinition; | ||||
| use std::fmt::{Debug, Formatter}; | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct ConcreteUseSymbol { | ||||
|     base_fqn: String, | ||||
|     declared_name: String, | ||||
|     source_definition: Option<SourceDefinition>, | ||||
| } | ||||
| 
 | ||||
| impl ConcreteUseSymbol { | ||||
|     pub fn new( | ||||
|         base_fqn: &str, | ||||
|         declared_name: &str, | ||||
|         source_definition: Option<SourceDefinition>, | ||||
|     ) -> Self { | ||||
|         Self { | ||||
|             base_fqn: base_fqn.to_string(), | ||||
|             declared_name: declared_name.to_string(), | ||||
|             source_definition, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn base_fqn(&self) -> &str { | ||||
|         &self.base_fqn | ||||
|     } | ||||
| 
 | ||||
|     pub fn declared_name(&self) -> &str { | ||||
|         &self.declared_name | ||||
|     } | ||||
| 
 | ||||
|     pub fn source_definition(&self) -> Option<&SourceDefinition> { | ||||
|         self.source_definition.as_ref() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for ConcreteUseSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("ConcreteUseStatementSymbol") | ||||
|             .field("base_fqn", &self.base_fqn) | ||||
|             .field("declared_name", &self.declared_name) | ||||
|             .field("source_definition", &self.source_definition) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct StarUseSymbol { | ||||
|     base_fqn: String, | ||||
|     source_definition: Option<SourceDefinition>, | ||||
| } | ||||
| 
 | ||||
| impl StarUseSymbol { | ||||
|     pub fn new(base_fqn: &str, source_definition: Option<SourceDefinition>) -> Self { | ||||
|         Self { | ||||
|             base_fqn: base_fqn.to_string(), | ||||
|             source_definition, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn base_fqn(&self) -> &str { | ||||
|         &self.base_fqn | ||||
|     } | ||||
| 
 | ||||
|     pub fn source_definition(&self) -> Option<&SourceDefinition> { | ||||
|         self.source_definition.as_ref() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for StarUseSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("StarUseStatementSymbol") | ||||
|             .field("base_fqn", &self.base_fqn) | ||||
|             .field("source_definition", &self.source_definition) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
| @ -1,41 +0,0 @@ | ||||
| use crate::name_analysis::symbol::source_definition::SourceDefinition; | ||||
| use std::fmt::{Debug, Formatter}; | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct VariableSymbol { | ||||
|     declared_name: String, | ||||
|     is_mutable: bool, | ||||
|     source_definition: Option<SourceDefinition>, | ||||
| } | ||||
| 
 | ||||
| impl VariableSymbol { | ||||
|     pub fn new(declared_name: &str, is_mutable: bool, source_definition: Option<SourceDefinition>) -> Self { | ||||
|         VariableSymbol { | ||||
|             declared_name: declared_name.to_string(), | ||||
|             is_mutable, | ||||
|             source_definition, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn declared_name(&self) -> &str { | ||||
|         &self.declared_name | ||||
|     } | ||||
|     
 | ||||
|     pub fn is_mutable(&self) -> bool { | ||||
|         self.is_mutable | ||||
|     } | ||||
|     
 | ||||
|     pub fn source_definition(&self) -> Option<&SourceDefinition> { | ||||
|         self.source_definition.as_ref() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Debug for VariableSymbol { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         f.debug_struct("VariableSymbol") | ||||
|             .field("declared_name", &self.declared_name) | ||||
|             .field("is_mutable", &self.is_mutable) | ||||
|             .field("source_definition", &self.source_definition) | ||||
|             .finish() | ||||
|     } | ||||
| } | ||||
							
								
								
									
										468
									
								
								src/name_analysis/symbol_table.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										468
									
								
								src/name_analysis/symbol_table.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,468 @@ | ||||
| use crate::name_analysis::symbol::*; | ||||
| use crate::name_analysis::symbol_table::SymbolInsertError::SymbolAlreadyDefined; | ||||
| use crate::name_analysis::symbol_table::SymbolLookupError::NoDefinition; | ||||
| use std::cell::RefCell; | ||||
| use std::collections::HashMap; | ||||
| use std::fmt::Display; | ||||
| use std::ops::Deref; | ||||
| use std::rc::Rc; | ||||
| 
 | ||||
| /* Scope */ | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| struct Scope { | ||||
|     parent: Option<usize>, | ||||
|     use_statement_symbols: HashMap<String, Rc<RefCell<UseStatementSymbol>>>, | ||||
|     module_symbols: HashMap<String, Rc<ModuleSymbol>>, | ||||
|     type_symbols: HashMap<String, Rc<TypeSymbol>>, | ||||
|     function_symbols: HashMap<String, Rc<RefCell<FunctionSymbol>>>, | ||||
|     parameter_symbols: HashMap<String, Rc<ParameterSymbol>>, | ||||
|     variable_symbols: HashMap<String, Rc<VariableSymbol>>, | ||||
|     class_member_symbols: HashMap<String, Rc<ClassMemberSymbol>>, | ||||
|     debug_name: String, | ||||
| } | ||||
| 
 | ||||
| impl Scope { | ||||
|     pub fn new(parent: Option<usize>, debug_name: String) -> Scope { | ||||
|         Scope { | ||||
|             parent, | ||||
|             use_statement_symbols: HashMap::new(), | ||||
|             module_symbols: HashMap::new(), | ||||
|             type_symbols: HashMap::new(), | ||||
|             function_symbols: HashMap::new(), | ||||
|             parameter_symbols: HashMap::new(), | ||||
|             variable_symbols: HashMap::new(), | ||||
|             class_member_symbols: HashMap::new(), | ||||
|             debug_name, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn get_module_symbol_by_declared_name(&self, name: &str) -> Option<Rc<ModuleSymbol>> { | ||||
|         for module_symbol in self.module_symbols.values() { | ||||
|             if module_symbol.declared_name() == name { | ||||
|                 return Some(module_symbol.clone()); | ||||
|             } | ||||
|         } | ||||
|         None | ||||
|     } | ||||
| 
 | ||||
|     fn get_type_symbol_by_declared_name(&self, declared_name: &str) -> Option<Rc<TypeSymbol>> { | ||||
|         if let Some(type_symbol) = self.type_symbols.get(declared_name) { | ||||
|             Some(type_symbol.clone()) | ||||
|         } else { | ||||
|             for use_statement_symbol in self.use_statement_symbols.values() { | ||||
|                 let borrowed = use_statement_symbol.borrow(); | ||||
|                 if borrowed.declared_name() == declared_name { | ||||
|                     if let Some(referenced_symbol) = borrowed.referenced_symbol() { | ||||
|                         match *referenced_symbol { | ||||
|                             Symbol::Type(type_symbol) => return Some(type_symbol.clone()), | ||||
|                             _ => continue, | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             None | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn get_type_symbol_by_fqn(&self, fqn: &str) -> Option<Rc<TypeSymbol>> { | ||||
|         self.type_symbols | ||||
|             .values() | ||||
|             .find(|s| match s.deref().deref() { | ||||
|                 TypeSymbol::Concrete(cts) => cts.fqn() == fqn, | ||||
|                 _ => false, | ||||
|             }) | ||||
|             .cloned() | ||||
|     } | ||||
| 
 | ||||
|     fn get_usable_symbol_by_fqn(&self, fqn: &str) -> Option<Symbol> { | ||||
|         for function_symbol in self.function_symbols.values() { | ||||
|             if function_symbol.borrow().fqn() == fqn { | ||||
|                 return Some(Symbol::Function(function_symbol.clone())); | ||||
|             } | ||||
|         } | ||||
|         for type_symbol in self.type_symbols.values() { | ||||
|             match type_symbol.deref() { | ||||
|                 TypeSymbol::Concrete(concrete_type_symbol) => { | ||||
|                     if concrete_type_symbol.fqn() == fqn { | ||||
|                         return Some(Symbol::Type(type_symbol.clone())); | ||||
|                     } | ||||
|                 } | ||||
|                 _ => continue, | ||||
|             } | ||||
|         } | ||||
|         None | ||||
|     } | ||||
| 
 | ||||
|     fn get_usable_symbol_by_declared_name(&self, declared_name: &str) -> Option<Symbol> { | ||||
|         for function_symbol in self.function_symbols.values() { | ||||
|             if function_symbol.borrow().declared_name() == declared_name { | ||||
|                 return Some(Symbol::Function(function_symbol.clone())); | ||||
|             } | ||||
|         } | ||||
|         for type_symbol in self.type_symbols.values() { | ||||
|             if type_symbol.declared_name() == declared_name { | ||||
|                 return Some(Symbol::Type(type_symbol.clone())); | ||||
|             } | ||||
|         } | ||||
|         None | ||||
|     } | ||||
| 
 | ||||
|     fn get_value_symbol_by_declared_name(&self, declared_name: &str) -> Option<Symbol> { | ||||
|         for variable_symbol in self.variable_symbols.values() { | ||||
|             if variable_symbol.declared_name() == declared_name { | ||||
|                 return Some(Symbol::Variable(variable_symbol.clone())); | ||||
|             } | ||||
|         } | ||||
|         for parameter_symbol in self.parameter_symbols.values() { | ||||
|             if parameter_symbol.declared_name() == declared_name { | ||||
|                 return Some(Symbol::Parameter(parameter_symbol.clone())); | ||||
|             } | ||||
|         } | ||||
|         None | ||||
|     } | ||||
| 
 | ||||
|     fn get_expressible_by_declared_name(&self, declared_name: &str) -> Option<Symbol> { | ||||
|         self.variable_symbols | ||||
|             .get(declared_name) | ||||
|             .map(|s| Symbol::Variable(s.clone())) | ||||
|             .or_else(|| { | ||||
|                 self.parameter_symbols | ||||
|                     .get(declared_name) | ||||
|                     .map(|p| Symbol::Parameter(p.clone())) | ||||
|             }) | ||||
|             .or_else(|| { | ||||
|                 self.class_member_symbols | ||||
|                     .get(declared_name) | ||||
|                     .map(|cms| Symbol::ClassMember(cms.clone())) | ||||
|             }) | ||||
|             .or_else(|| { | ||||
|                 self.function_symbols | ||||
|                     .get(declared_name) | ||||
|                     .map(|f| Symbol::Function(f.clone())) | ||||
|             }) | ||||
|             .or_else(|| { | ||||
|                 self.type_symbols | ||||
|                     .get(declared_name) | ||||
|                     .map(|t| Symbol::Type(t.clone())) | ||||
|             }) | ||||
|             .or_else(|| { | ||||
|                 self.use_statement_symbols | ||||
|                     .get(declared_name) | ||||
|                     .map(|us| Symbol::UseStatement(us.clone())) | ||||
|             }) | ||||
|     } | ||||
| 
 | ||||
|     fn get_expressible_by_fqn(&self, fqn: &str) -> Option<Symbol> { | ||||
|         self.function_symbols | ||||
|             .values() | ||||
|             .find(|fs| fs.borrow().fqn() == fqn) | ||||
|             .map(|f| Symbol::Function(f.clone())) | ||||
|             .or_else(|| { | ||||
|                 self.get_type_symbol_by_fqn(fqn) | ||||
|                     .map(|ts| Symbol::Type(ts.clone())) | ||||
|             }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /* Symbol table */ | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum SymbolInsertError { | ||||
|     SymbolAlreadyDefined(Symbol), | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum SymbolLookupError { | ||||
|     NoDefinition, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub struct SymbolTable { | ||||
|     scopes: Vec<Scope>, | ||||
|     current_scope_id: usize, | ||||
| } | ||||
| 
 | ||||
| /// Contains a vec of scopes, like a flattened tree
 | ||||
| impl SymbolTable { | ||||
|     pub fn new() -> Self { | ||||
|         let mut t = SymbolTable { | ||||
|             scopes: vec![Scope::new(None, String::from("GlobalScope"))], | ||||
|             current_scope_id: 0, | ||||
|         }; | ||||
|         t | ||||
|     } | ||||
| 
 | ||||
|     pub fn current_scope_id(&self) -> usize { | ||||
|         self.current_scope_id | ||||
|     } | ||||
| 
 | ||||
|     pub fn scopes(&self) -> &Vec<Scope> { | ||||
|         &self.scopes | ||||
|     } | ||||
| 
 | ||||
|     pub fn push_scope(&mut self, debug_name: &str) { | ||||
|         let id = self.scopes.len(); | ||||
|         self.scopes.push(Scope::new( | ||||
|             Some(self.current_scope_id), | ||||
|             debug_name.to_string(), | ||||
|         )); | ||||
|         self.current_scope_id = id; | ||||
|     } | ||||
| 
 | ||||
|     pub fn pop_scope(&mut self) { | ||||
|         if let Some(parent_id) = self.scopes[self.current_scope_id].parent { | ||||
|             self.current_scope_id = parent_id; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn insert_use_statement_symbol( | ||||
|         &mut self, | ||||
|         use_statement_symbol: UseStatementSymbol, | ||||
|     ) -> Result<Rc<RefCell<UseStatementSymbol>>, SymbolInsertError> { | ||||
|         let current_scope = self.scopes.get_mut(self.current_scope_id).unwrap(); | ||||
|         if let Some(defined_symbol) = | ||||
|             current_scope.get_usable_symbol_by_declared_name(use_statement_symbol.declared_name()) | ||||
|         { | ||||
|             Err(SymbolAlreadyDefined(defined_symbol)) | ||||
|         } else { | ||||
|             let declared_name = use_statement_symbol.declared_name().to_string(); | ||||
|             let to_insert = Rc::new(RefCell::new(use_statement_symbol)); | ||||
|             let to_return = to_insert.clone(); | ||||
|             current_scope | ||||
|                 .use_statement_symbols | ||||
|                 .insert(declared_name, to_insert); | ||||
|             Ok(to_return) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn insert_module_symbol( | ||||
|         &mut self, | ||||
|         module_symbol: ModuleSymbol, | ||||
|     ) -> Result<(), SymbolInsertError> { | ||||
|         let current_scope = self.scopes.get_mut(self.current_scope_id).unwrap(); | ||||
|         if let Some(defined_symbol) = | ||||
|             current_scope.get_module_symbol_by_declared_name(module_symbol.declared_name()) | ||||
|         { | ||||
|             Err(SymbolAlreadyDefined(Symbol::Module(defined_symbol.clone()))) | ||||
|         } else { | ||||
|             current_scope.module_symbols.insert( | ||||
|                 module_symbol.declared_name().to_string(), | ||||
|                 Rc::new(module_symbol), | ||||
|             ); | ||||
|             Ok(()) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn insert_type_symbol(&mut self, type_symbol: TypeSymbol) -> Result<(), SymbolInsertError> { | ||||
|         let current_scope = self.scopes.get_mut(self.current_scope_id).unwrap(); | ||||
|         if let Some(defined_symbol) = | ||||
|             current_scope.get_usable_symbol_by_declared_name(type_symbol.declared_name()) | ||||
|         { | ||||
|             Err(SymbolAlreadyDefined(defined_symbol)) | ||||
|         } else { | ||||
|             current_scope.type_symbols.insert( | ||||
|                 type_symbol.declared_name().to_string(), | ||||
|                 Rc::new(type_symbol), | ||||
|             ); | ||||
|             Ok(()) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn insert_function_symbol( | ||||
|         &mut self, | ||||
|         function_symbol: FunctionSymbol, | ||||
|     ) -> Result<Rc<RefCell<FunctionSymbol>>, SymbolInsertError> { | ||||
|         let current_scope = self.scopes.get_mut(self.current_scope_id).unwrap(); | ||||
|         if let Some(defined_symbol) = | ||||
|             current_scope.get_usable_symbol_by_declared_name(function_symbol.declared_name()) | ||||
|         { | ||||
|             Err(SymbolAlreadyDefined(defined_symbol)) | ||||
|         } else { | ||||
|             let declared_name = function_symbol.declared_name().to_string(); | ||||
|             let to_insert = Rc::new(RefCell::new(function_symbol)); | ||||
|             let to_return = to_insert.clone(); | ||||
|             current_scope | ||||
|                 .function_symbols | ||||
|                 .insert(declared_name, to_insert); | ||||
|             Ok(to_return) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn insert_parameter_symbol( | ||||
|         &mut self, | ||||
|         parameter_symbol: ParameterSymbol, | ||||
|     ) -> Result<Rc<ParameterSymbol>, SymbolInsertError> { | ||||
|         let current_scope = self.scopes.get_mut(self.current_scope_id).unwrap(); | ||||
|         if let Some(defined_symbol) = | ||||
|             current_scope.get_value_symbol_by_declared_name(parameter_symbol.declared_name()) | ||||
|         { | ||||
|             Err(SymbolAlreadyDefined(defined_symbol)) | ||||
|         } else { | ||||
|             let to_insert = Rc::new(parameter_symbol); | ||||
|             let to_return = to_insert.clone(); | ||||
|             current_scope | ||||
|                 .parameter_symbols | ||||
|                 .insert(to_insert.declared_name().to_string(), to_insert); | ||||
|             Ok(to_return) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn insert_variable_symbol( | ||||
|         &mut self, | ||||
|         variable_symbol: VariableSymbol, | ||||
|     ) -> Result<Rc<VariableSymbol>, SymbolInsertError> { | ||||
|         let current_scope = self.scopes.get_mut(self.current_scope_id).unwrap(); | ||||
|         if let Some(defined_symbol) = | ||||
|             current_scope.get_value_symbol_by_declared_name(variable_symbol.declared_name()) | ||||
|         { | ||||
|             Err(SymbolAlreadyDefined(defined_symbol)) | ||||
|         } else { | ||||
|             let declared_name = variable_symbol.declared_name().to_string(); | ||||
|             let to_insert = Rc::new(variable_symbol); | ||||
|             let to_return = to_insert.clone(); | ||||
|             current_scope | ||||
|                 .variable_symbols | ||||
|                 .insert(declared_name, to_insert); | ||||
|             Ok(to_return) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn insert_class_member_symbol( | ||||
|         &mut self, | ||||
|         class_member_symbol: ClassMemberSymbol, | ||||
|     ) -> Result<(), SymbolInsertError> { | ||||
|         let current_scope = self.scopes.get_mut(self.current_scope_id).unwrap(); | ||||
|         if let Some(defined_symbol) = | ||||
|             current_scope.get_expressible_by_declared_name(class_member_symbol.declared_name()) | ||||
|         { | ||||
|             Err(SymbolAlreadyDefined(defined_symbol)) | ||||
|         } else { | ||||
|             current_scope.class_member_symbols.insert( | ||||
|                 class_member_symbol.declared_name().to_string(), | ||||
|                 Rc::new(class_member_symbol), | ||||
|             ); | ||||
|             Ok(()) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn lookup_type_by_declared_name( | ||||
|         &self, | ||||
|         declared_name: &str, | ||||
|         scope_id: usize, | ||||
|     ) -> Result<Rc<TypeSymbol>, SymbolLookupError> { | ||||
|         let mut scope_opt = Some(&self.scopes[scope_id]); | ||||
|         while let Some(scope) = scope_opt { | ||||
|             if let Some(symbol) = scope.get_type_symbol_by_declared_name(declared_name) { | ||||
|                 return Ok(symbol); | ||||
|             } | ||||
|             scope_opt = if let Some(parent_id) = scope.parent { | ||||
|                 Some(&self.scopes[parent_id]) | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
|         } | ||||
|         Err(NoDefinition) | ||||
|     } | ||||
| 
 | ||||
|     pub fn lookup_type_by_fqn( | ||||
|         &self, | ||||
|         fqn: &str, | ||||
|         scope_id: usize, | ||||
|     ) -> Result<Rc<TypeSymbol>, SymbolLookupError> { | ||||
|         let mut scope_opt = Some(&self.scopes[scope_id]); | ||||
|         while let Some(scope) = scope_opt { | ||||
|             if let Some(symbol) = scope.get_type_symbol_by_fqn(fqn) { | ||||
|                 return Ok(symbol); | ||||
|             } | ||||
|             scope_opt = if let Some(parent_id) = scope.parent { | ||||
|                 Some(&self.scopes[parent_id]) | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
|         } | ||||
|         Err(NoDefinition) | ||||
|     } | ||||
| 
 | ||||
|     pub fn lookup_usable_by_fqn( | ||||
|         &self, | ||||
|         fully_qualified_name: &str, | ||||
|         scope_id: usize, | ||||
|     ) -> Result<Symbol, SymbolLookupError> { | ||||
|         for scope in &self.scopes { | ||||
|             if let Some(symbol) = scope.get_usable_symbol_by_fqn(fully_qualified_name) { | ||||
|                 return Ok(symbol); | ||||
|             } | ||||
|         } | ||||
|         Err(NoDefinition) | ||||
|     } | ||||
| 
 | ||||
|     pub fn lookup_expressible_by_declared_name( | ||||
|         &self, | ||||
|         declared_name: &str, | ||||
|         scope_id: usize, | ||||
|     ) -> Result<Symbol, SymbolLookupError> { | ||||
|         let mut scope_opt = Some(&self.scopes[scope_id]); | ||||
|         while let Some(scope) = scope_opt { | ||||
|             if let Some(symbol) = scope.get_expressible_by_declared_name(declared_name) { | ||||
|                 return Ok(symbol); | ||||
|             } | ||||
|             scope_opt = if let Some(parent_id) = scope.parent { | ||||
|                 Some(&self.scopes[parent_id]) | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
|         } | ||||
|         Err(NoDefinition) | ||||
|     } | ||||
| 
 | ||||
|     pub fn lookup_expressible_by_fqn( | ||||
|         &self, | ||||
|         fqn: &str, | ||||
|         scope_id: usize, | ||||
|     ) -> Result<Symbol, SymbolLookupError> { | ||||
|         let mut scope_opt = Some(&self.scopes[scope_id]); | ||||
|         while let Some(scope) = scope_opt { | ||||
|             if let Some(symbol) = scope.get_expressible_by_fqn(fqn) { | ||||
|                 return Ok(symbol); | ||||
|             } | ||||
|             scope_opt = if let Some(parent_id) = scope.parent { | ||||
|                 Some(&self.scopes[parent_id]) | ||||
|             } else { | ||||
|                 None | ||||
|             } | ||||
|         } | ||||
|         Err(NoDefinition) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl Display for SymbolTable { | ||||
|     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | ||||
|         writeln!(f, "SymbolTable(current_scope = {})", self.current_scope_id)?; | ||||
|         for (i, scope) in self.scopes.iter().enumerate() { | ||||
|             writeln!(f, "----Scope {} {}----", i, scope.debug_name)?; | ||||
|             for symbol in scope.use_statement_symbols.values() { | ||||
|                 writeln!(f, "{:#?}", symbol.borrow())?; | ||||
|             } | ||||
|             for symbol in scope.module_symbols.values() { | ||||
|                 writeln!(f, "{:#?}", symbol)?; | ||||
|             } | ||||
|             for symbol in scope.type_symbols.values() { | ||||
|                 writeln!(f, "{:#?}", symbol)?; | ||||
|             } | ||||
|             for symbol in scope.function_symbols.values() { | ||||
|                 writeln!(f, "{:#?}", symbol.borrow())?; | ||||
|             } | ||||
|             for symbol in scope.parameter_symbols.values() { | ||||
|                 writeln!(f, "{:#?}", symbol)?; | ||||
|             } | ||||
|             for symbol in scope.variable_symbols.values() { | ||||
|                 writeln!(f, "{:#?}", symbol)?; | ||||
|             } | ||||
|             for symbol in scope.class_member_symbols.values() { | ||||
|                 writeln!(f, "{:#?}", symbol)?; | ||||
|             } | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user