diff --git a/ast-generator/src/ast_node/struct_ast_node.rs b/ast-generator/src/ast_node/struct_ast_node.rs index 7ff4ff4..a6d9aae 100644 --- a/ast-generator/src/ast_node/struct_ast_node.rs +++ b/ast-generator/src/ast_node/struct_ast_node.rs @@ -36,8 +36,8 @@ pub fn make_struct_ast_node_impl(spec: &StructSpec) -> TokenStream { } } MemberChildBuild::Boolean(_) => None, - MemberChildBuild::Special(_) => None, }, + StructChild::Special(_) => None, }) .filter(Option::is_some) .map(Option::unwrap) diff --git a/ast-generator/src/build_fn/struct_build_fn.rs b/ast-generator/src/build_fn/struct_build_fn.rs index 2ce5585..3fdcef6 100644 --- a/ast-generator/src/build_fn/struct_build_fn.rs +++ b/ast-generator/src/build_fn/struct_build_fn.rs @@ -1,8 +1,44 @@ use crate::deserialize::util::{make_build_fn_name, make_build_pair}; -use crate::spec::struct_spec::{MemberChildBuild, NodeMemberBuild, SpecialMemberBuild, StructChild, StructSpec, VecChild, VecChildBuild}; +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 { + 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::>() +} + fn make_vec_child_holder(vec_child: &VecChild) -> TokenStream { let child_ident = format_ident!("{}", vec_child.name()); match vec_child.build() { @@ -44,8 +80,8 @@ fn make_child_holder(child_spec: &StructChild) -> Option { Some(make_node_child_holder(member_child.name(), node_child)) } MemberChildBuild::Boolean(_) => Some(make_boolean_child_holder(member_child.name())), - MemberChildBuild::Special(_) => None, }, + StructChild::Special(_) => None, } } @@ -111,9 +147,9 @@ fn make_rule_matcher(child_spec: &StructChild) -> Option { Rule::#rule_ident => { #action } }) } - MemberChildBuild::Special(_) => None } - } + }, + StructChild::Special(_) => None } } @@ -146,6 +182,11 @@ fn make_boolean_member_child_arg(name: &str) -> TokenStream { 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, pair_ident: &Ident) -> Option { match child_spec { StructChild::SkipChild(_) => None, @@ -159,22 +200,8 @@ fn make_child_arg(child_spec: &StructChild, pair_ident: &Ident) -> Option { Some(make_boolean_member_child_arg(member_child.name())) }, - MemberChildBuild::Special(special_member_build) => { - match special_member_build { - SpecialMemberBuild::FileId => { - Some(quote! { file_id }) - } - SpecialMemberBuild::Range => { - Some(quote! { - Range { - start: #pair_ident.as_span().start(), - end: #pair_ident.as_span().end(), - } - }) - } - } - }, }, + StructChild::Special(special_child) => Some(make_special_child_arg(special_child)), } } @@ -198,6 +225,10 @@ 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() @@ -224,6 +255,10 @@ pub fn make_struct_build_fn(build_spec: &StructSpec) -> TokenStream { quote! { fn #build_fn_ident(file_id: usize, #pair_ident: Pair) -> #return_type_ident { + #preamble + + #(#special_children;)* + #(#child_holders;)* #iter_stream diff --git a/ast-generator/src/deserialize/struct_spec.rs b/ast-generator/src/deserialize/struct_spec.rs index 998492b..bae37c8 100644 --- a/ast-generator/src/deserialize/struct_spec.rs +++ b/ast-generator/src/deserialize/struct_spec.rs @@ -56,12 +56,6 @@ fn deserialize_member_build(child_name: &str, rule: &str, props: &Yaml) -> Membe } else { panic!("Expected 'on' in 'boolean' in 'build' in {}", child_name); } - } else if props["special"].is_hash() { - match props["special"]["kind"].as_str().unwrap() { - "file_id" => MemberChildBuild::Special(SpecialMemberBuild::FileId), - "range" => MemberChildBuild::Special(SpecialMemberBuild::Range), - _ => panic!(), - } } else { panic!( "Expected one of 'node', 'boolean', or 'special' in 'build' in {}", @@ -98,6 +92,14 @@ fn deserialize_member_child(child_name: &str, props: &Yaml) -> StructChild { } } +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"]) @@ -105,6 +107,8 @@ fn deserialize_hash_child(name: &str, props: &Yaml) -> StructChild { 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); } diff --git a/ast-generator/src/pretty_print.rs b/ast-generator/src/pretty_print.rs index af08ffe..5698857 100644 --- a/ast-generator/src/pretty_print.rs +++ b/ast-generator/src/pretty_print.rs @@ -231,8 +231,8 @@ fn make_struct_p2_impl(struct_build_spec: &StructSpec) -> TokenStream { writer.writeln_indented(&format!(#format_string, self.#child_ident()))?; }) } - MemberChildBuild::Special(_) => None }, + StructChild::Special(_) => None }) .filter(Option::is_some) .map(Option::unwrap) diff --git a/ast-generator/src/spec/struct_spec.rs b/ast-generator/src/spec/struct_spec.rs index 0b6db54..e8a593e 100644 --- a/ast-generator/src/spec/struct_spec.rs +++ b/ast-generator/src/spec/struct_spec.rs @@ -26,6 +26,25 @@ 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 { @@ -160,7 +179,6 @@ impl MemberChild { pub enum MemberChildBuild { Node(NodeMemberBuild), Boolean(BooleanMemberBuild), - Special(SpecialMemberBuild), } #[derive(Debug)] @@ -215,7 +233,30 @@ pub enum BooleanMemberBuildOn { } #[derive(Debug)] -pub enum SpecialMemberBuild { +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, } diff --git a/ast-generator/src/type_gen/struct_type.rs b/ast-generator/src/type_gen/struct_type.rs index 55fa714..72d3f1f 100644 --- a/ast-generator/src/type_gen/struct_type.rs +++ b/ast-generator/src/type_gen/struct_type.rs @@ -1,6 +1,6 @@ use crate::spec::struct_spec::{ - MemberChild, MemberChildBuild, SpecialMemberBuild, StructChild, StructSpec, VecChild, - VecChildBuild, + MemberChild, MemberChildBuild, SpecialChild, SpecialChildKind, StructChild, StructSpec, + VecChild, VecChildBuild, }; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; @@ -76,22 +76,26 @@ fn make_member_child_accessors(member_child: &MemberChild) -> TokenStream { } } } - MemberChildBuild::Special(special_member_build) => match special_member_build { - SpecialMemberBuild::FileId => { - quote! { - pub fn file_id(&self) -> usize { - self.file_id - } + } +} + +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 } } - SpecialMemberBuild::Range => { - quote! { - pub fn range(&self) -> Range { - self.range - } + } + SpecialChildKind::Range => { + quote! { + pub fn #child_ident(&self) -> Range { + self.#child_ident } } - }, + } } } @@ -100,6 +104,7 @@ fn make_accessors(child: &StructChild) -> Option { 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)), } } @@ -108,6 +113,7 @@ fn make_member_ident(child: &StructChild) -> Option { 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())), } } @@ -135,10 +141,6 @@ fn make_member_child_type_ident(member_child: &MemberChild) -> TokenStream { MemberChildBuild::Boolean(_) => { quote! { bool } } - MemberChildBuild::Special(special_member_build) => match special_member_build { - SpecialMemberBuild::FileId => quote! { usize }, - SpecialMemberBuild::Range => quote! { Range }, - }, } } @@ -156,6 +158,17 @@ fn make_member_child_annotated_member(member_child: &MemberChild) -> TokenStream } } +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 }, + }; + quote! { + #child_ident: #child_type_ident + } +} + fn make_annotated_member(child: &StructChild) -> Option { match child { StructChild::SkipChild(_) => None, @@ -163,6 +176,9 @@ fn make_annotated_member(child: &StructChild) -> Option { StructChild::MemberChild(member_child) => { Some(make_member_child_annotated_member(member_child)) } + StructChild::Special(special_child) => { + Some(make_special_child_annotated_member(special_child)) + } } } diff --git a/src/parser/ast.schema.yaml b/src/parser/ast.schema.yaml index 95126ea..38525a0 100644 --- a/src/parser/ast.schema.yaml +++ b/src/parser/ast.schema.yaml @@ -88,6 +88,7 @@ $defs: - $ref: "#/$defs/StructChildSkipHash" - $ref: "#/$defs/StructChildVecHash" - $ref: "#/$defs/StructChildMemberHash" + - $ref: "#/$defs/StructChildSpecialHash" StructChildSkipHash: type: object additionalProperties: false @@ -148,7 +149,6 @@ $defs: oneOf: - $ref: "#/$defs/StructChildMemberBuildNodeHash" - $ref: "#/$defs/StructChildMemberBuildBooleanHash" - - $ref: "#/$defs/StructChildMemberBuildSpecialHash" StructChildMemberBuildNodeHash: type: object additionalProperties: false @@ -191,7 +191,7 @@ $defs: - rule_present required: - on - StructChildMemberBuildSpecialHash: + StructChildSpecialHash: type: object additionalProperties: false description: A special member to be built. diff --git a/src/parser/ast.yaml b/src/parser/ast.yaml index e472297..d4c4db2 100644 --- a/src/parser/ast.yaml +++ b/src/parser/ast.yaml @@ -215,15 +215,11 @@ UseStatement: member: rule: UseStatementSuffix - file_id: - member: - build: - special: - kind: file_id + special: + kind: file_id - range: - member: - build: - special: - kind: range + special: + kind: range UseStatementPrefix: struct: children: