From a96745c29289f6f9c49a9464e08b6f6e58a55b8b Mon Sep 17 00:00:00 2001 From: Filip Tibell Date: Wed, 22 Feb 2023 12:35:49 +0100 Subject: [PATCH] Support referenced types in parser type stringification --- packages/cli/src/gen/definitions/parser.rs | 45 ++++++--- .../cli/src/gen/definitions/type_info_ext.rs | 94 +++++++++++++------ 2 files changed, 97 insertions(+), 42 deletions(-) diff --git a/packages/cli/src/gen/definitions/parser.rs b/packages/cli/src/gen/definitions/parser.rs index 25caa4a..5739c4e 100644 --- a/packages/cli/src/gen/definitions/parser.rs +++ b/packages/cli/src/gen/definitions/parser.rs @@ -1,3 +1,5 @@ +use std::collections::{BTreeMap, HashMap}; + use anyhow::{Context, Result}; use full_moon::{ ast::{ @@ -20,16 +22,19 @@ struct DefinitionsParserItem { type_info: TypeInfo, } -#[derive(Debug, Default, Clone)] +#[derive(Debug, Clone)] pub struct DefinitionsParser { - found_top_level_items: Vec, + found_top_level_items: BTreeMap, + found_top_level_types: HashMap, found_top_level_declares: Vec, } impl DefinitionsParser { pub fn new() -> Self { Self { - ..Default::default() + found_top_level_items: BTreeMap::new(), + found_top_level_types: HashMap::new(), + found_top_level_declares: Vec::new(), } } @@ -62,7 +67,8 @@ impl DefinitionsParser { .map(|cap| cap[1].to_string()) .collect(); // Parse contents into top-level parser items for later use - let mut found_top_level_items = Vec::new(); + let mut found_top_level_items = BTreeMap::new(); + let mut found_top_level_types = HashMap::new(); let ast = full_moon::parse(&resulting_contents).context("Failed to parse type definitions")?; for stmt in ast.nodes().stmts() { @@ -73,15 +79,21 @@ impl DefinitionsParser { Stmt::TypeDeclaration(typ) => Some((typ, typ.type_token())), _ => None, } { - found_top_level_items.push(DefinitionsParserItem { - name: declaration.type_name().token().to_string(), - comment: find_token_moonwave_comment(token_reference), - type_info: declaration.type_definition().clone(), - }); + let name = declaration.type_name().token().to_string(); + found_top_level_items.insert( + name.clone(), + DefinitionsParserItem { + name: name.clone(), + comment: find_token_moonwave_comment(token_reference), + type_info: declaration.type_definition().clone(), + }, + ); + found_top_level_types.insert(name, declaration.type_definition().clone()); } } // Store results self.found_top_level_items = found_top_level_items; + self.found_top_level_types = found_top_level_types; self.found_top_level_declares = found_declares; Ok(()) } @@ -96,7 +108,10 @@ impl DefinitionsParser { if let Some(comment) = item.comment { builder = builder.with_children(&parse_moonwave_style_comment(&comment)); } - if let Some(args) = item.type_info.extract_args_normalized() { + if let Some(args) = item + .type_info + .extract_args_normalized(&self.found_top_level_types) + { builder = builder.with_arg_types(&args); } if let TypeInfo::Table { fields, .. } = item.type_info { @@ -122,11 +137,11 @@ impl DefinitionsParser { */ #[allow(clippy::unnecessary_wraps)] pub fn drain(&mut self) -> Result> { - let mut top_level_items = self.found_top_level_items.drain(..).collect::>(); - let results = top_level_items - .drain(..) - .map(|visitor_item| self.convert_parser_item_into_doc_item(visitor_item)) - .collect(); + let mut results = Vec::new(); + for top_level_item in self.found_top_level_items.values() { + results.push(self.convert_parser_item_into_doc_item(top_level_item.clone())); + } + self.found_top_level_items = BTreeMap::new(); self.found_top_level_declares = Vec::new(); Ok(results) } diff --git a/packages/cli/src/gen/definitions/type_info_ext.rs b/packages/cli/src/gen/definitions/type_info_ext.rs index 36fba71..0354a6d 100644 --- a/packages/cli/src/gen/definitions/type_info_ext.rs +++ b/packages/cli/src/gen/definitions/type_info_ext.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use full_moon::{ ast::types::{TypeArgument, TypeInfo}, tokenizer::{Symbol, Token, TokenReference, TokenType}, @@ -8,9 +10,16 @@ use super::kind::DefinitionsItemKind; pub(crate) trait TypeInfoExt { fn is_fn(&self) -> bool; fn parse_definitions_kind(&self) -> DefinitionsItemKind; - fn stringify_simple(&self, parent_typ: Option<&TypeInfo>) -> String; + fn stringify_simple( + &self, + parent_typ: Option<&TypeInfo>, + type_lookup_table: &HashMap, + ) -> String; fn extract_args(&self, base: Vec) -> Vec; - fn extract_args_normalized(&self) -> Option>; + fn extract_args_normalized( + &self, + type_lookup_table: &HashMap, + ) -> Option>; } impl TypeInfoExt for TypeInfo { @@ -90,44 +99,68 @@ impl TypeInfoExt for TypeInfo { * `{ TypeName }` * `"string-literal"` */ - fn stringify_simple(&self, parent_typ: Option<&TypeInfo>) -> String { + fn stringify_simple( + &self, + parent_typ: Option<&TypeInfo>, + type_lookup_table: &HashMap, + ) -> String { match self { TypeInfo::Array { type_info, .. } => { - format!("{{ {} }}", type_info.as_ref().stringify_simple(Some(self))) + format!( + "{{ {} }}", + type_info + .as_ref() + .stringify_simple(Some(self), type_lookup_table) + ) } - TypeInfo::Basic(tok) => match parent_typ { - Some(TypeInfo::Callback { generics, .. }) => { - if let Some(generics) = generics { - // If the function that contains this arg has generic and a - // generic is the same as this token, we stringify it as any - if generics - .generics() - .iter() - .any(|g| g.to_string() == tok.token().to_string()) - { - "any".to_string() - } else { - tok.token().to_string() - } - } else { - tok.token().to_string() + TypeInfo::Basic(tok) => { + let tok_str = tok.token().to_string(); + let mut any_str = None; + // If the function that contains this arg has generic and a + // generic is the same as this token, we stringify it as any + if let Some(TypeInfo::Callback { + generics: Some(callback_generics), + .. + }) = parent_typ + { + if callback_generics + .generics() + .iter() + .any(|g| g.to_string() == tok_str) + { + any_str = Some("any".to_string()); } } - _ => tok.token().to_string(), - }, + // Also check if we got a referenced type, meaning that it + // exists in the lookup table of global types passed to us + if let Some(any_str) = any_str { + any_str + } else if let Some(referenced_typ) = type_lookup_table.get(&tok_str) { + referenced_typ.stringify_simple(None, type_lookup_table) + } else { + tok_str + } + } TypeInfo::String(str) => str.token().to_string(), TypeInfo::Boolean(_) => "boolean".to_string(), TypeInfo::Callback { .. } => "function".to_string(), TypeInfo::Optional { base, .. } => { - format!("{}?", base.as_ref().stringify_simple(Some(self))) + format!( + "{}?", + base.as_ref() + .stringify_simple(Some(self), type_lookup_table) + ) } TypeInfo::Table { .. } => "table".to_string(), TypeInfo::Union { left, right, .. } => { format!( "{} {} {}", - left.as_ref().stringify_simple(Some(self)), + left.as_ref() + .stringify_simple(Some(self), type_lookup_table), Symbol::Pipe, - right.as_ref().stringify_simple(Some(self)) + right + .as_ref() + .stringify_simple(Some(self), type_lookup_table) ) } // TODO: Stringify custom table types properly, these show up as basic tokens @@ -156,13 +189,20 @@ impl TypeInfoExt for TypeInfo { } } - fn extract_args_normalized(&self) -> Option> { + fn extract_args_normalized( + &self, + type_lookup_table: &HashMap, + ) -> Option> { if self.is_fn() { let separator = format!(" {} ", Symbol::Pipe); let args_stringified_not_normalized = self .extract_args(Vec::new()) .iter() - .map(|type_arg| type_arg.type_info().stringify_simple(Some(self))) + .map(|type_arg| { + type_arg + .type_info() + .stringify_simple(Some(self), type_lookup_table) + }) .collect::>(); let mut args_stringified = Vec::new(); for arg_string in args_stringified_not_normalized {