From 4a32f0d2f4cc8fccd213b323c79b777300e41465 Mon Sep 17 00:00:00 2001 From: Soispha Date: Sun, 24 Mar 2024 19:35:09 +0100 Subject: [PATCH] feat(parser): Add support for parsing function pointer types Notably these diverge from rust, by requiring argument names. This is a deliberate design decision, as it help users to guess what's going to be passed into their callback. --- trixy-parser/docs/grammar.ebnf | 2 +- trixy-parser/src/command_spec/checked.rs | 101 +++++++++++--- trixy-parser/src/command_spec/unchecked.rs | 84 ++++++++--- trixy-parser/src/parsing/checked/mod.rs | 154 +++++++++++++++++---- trixy-parser/src/parsing/unchecked/mod.rs | 49 +++++-- 5 files changed, 309 insertions(+), 81 deletions(-) diff --git a/trixy-parser/docs/grammar.ebnf b/trixy-parser/docs/grammar.ebnf index 2e6c098..9435ee9 100644 --- a/trixy-parser/docs/grammar.ebnf +++ b/trixy-parser/docs/grammar.ebnf @@ -35,7 +35,7 @@ Namespace = {DocComment} {Attribute} "mod" Identifier "{" {Function | Namespace Structure = {DocComment} {Attribute} "struct" Identifier "{" [DocNamedType {"," DocNamedType } [","]] "}"; Enumeration = {DocComment} {Attribute} "enum" Identifier "{" [DocIdentifier {"," DocIdentifier} [","]] "}"; -Type = Identifier ["<" Type {"," Type} ">"]; +Type = Identifier ["<" Type {"," Type} ">"] | "fn" "(" [NamedType {"," NamedType }] ")" [ "->" Type ]; StringLiteral = ["r"] "\"" {ANYTHING} "\"" | "r" "#" {"#"} "\"" {ANYTHING} "#" {"#"} "\""; Identifier = (CHARACTER | "_") { NUMBER | CHARACTER | "_" } ; diff --git a/trixy-parser/src/command_spec/checked.rs b/trixy-parser/src/command_spec/checked.rs index 23625ce..078e5dc 100644 --- a/trixy-parser/src/command_spec/checked.rs +++ b/trixy-parser/src/command_spec/checked.rs @@ -25,7 +25,7 @@ use std::fmt::{Display, Write}; use crate::lexing::TokenKind; -use super::unchecked; +use super::unchecked::{self, DeriveValue}; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] pub enum Variant { @@ -103,34 +103,95 @@ pub struct Function { } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] -pub struct Type { - pub identifier: Identifier, - pub generic_args: Vec, +pub enum Type { + Typical { + identifier: Identifier, + generic_args: Vec, + }, + Function { + inputs: Vec, + output: Option>, + }, } -impl Display for Type { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let ident = &self.identifier.name; - f.write_str(ident)?; - if !self.generic_args.is_empty() { - f.write_char('<')?; - let mut first_run = true; - for arg in &self.generic_args { - if !first_run { - f.write_str(", ")?; +impl Type { + pub fn print_generics(&self) -> String { + let mut output = String::new(); + self.display_generics(&mut output) + .expect("This should fail"); + output + } + pub fn display_generics(&self, f: &mut T) -> std::fmt::Result { + match self { + Type::Typical { + identifier: _, + generic_args, + } => { + if !generic_args.is_empty() { + f.write_char('<')?; + let mut first_run = true; + for arg in generic_args { + if !first_run { + f.write_str(", ")?; + } else { + first_run = false; + } + write!(f, "{}", arg)?; + } + f.write_char('>') } else { - first_run = false; + f.write_str("") } - write!(f, "{}", arg)?; } - f.write_char('>') - } else { - f.write_str("") + Type::Function { .. } => { + unimplemented!("This function should only be called for the 'Typical' enum variant") + } } } } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +impl Display for Type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Type::Typical { + identifier, + generic_args: _, + } => { + f.write_str(&identifier.name)?; + self.display_generics(f) + } + Type::Function { inputs, output } => { + f.write_str("fn(")?; + if !inputs.is_empty() { + f.write_str( + &inputs + .iter() + .map(|ty| ty.to_string()) + .collect::>() + .join(", "), + )?; + } + + f.write_str(")")?; + if let Some(output) = output { + f.write_str("-> ")?; + f.write_str(output.to_string().as_str())?; + } + Ok(()) + } + } + } +} + +impl Display for NamedType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.name.name)?; + f.write_str(": ")?; + f.write_str(self.r#type.to_string().as_str()) + } +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] pub struct NamedType { pub name: Identifier, pub r#type: Type, diff --git a/trixy-parser/src/command_spec/unchecked.rs b/trixy-parser/src/command_spec/unchecked.rs index 7712b87..648a670 100644 --- a/trixy-parser/src/command_spec/unchecked.rs +++ b/trixy-parser/src/command_spec/unchecked.rs @@ -25,7 +25,7 @@ use std::fmt::{Display, Write}; -use crate::lexing::{Token, TokenSpan}; +use crate::lexing::{Token, TokenKind, TokenSpan}; #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)] pub struct CommandSpec { @@ -173,33 +173,77 @@ pub struct NamedType { } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] -pub struct Type { - pub identifier: Token, // Will later become an Identifier - pub generic_args: Vec, +pub enum Type { + Typical { + identifier: Token, // Will later become an Identifier + generic_args: Vec, + }, + Function { + inputs: Vec, + output: Option>, + }, +} + +impl Display for NamedType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let ident = match self.name.kind() { + crate::lexing::TokenKind::Identifier(ident) => ident, + _ => panic!("Tried to display a non identifier token in the Type display implementation. This is a bug"), + }; + f.write_str(ident)?; + f.write_str(": ")?; + f.write_str(self.r#type.to_string().as_str()) + } } impl Display for Type { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let ident = match self.identifier.kind() { - crate::lexing::TokenKind::Identifier(ident) => ident, - _ => panic!("Tried to display a non identifier token in the Type display implementation. This is a bug"), - }; + match self { + Type::Typical { + identifier, + generic_args, + } => { + let ident = match identifier.kind() { + crate::lexing::TokenKind::Identifier(ident) => ident, + _ => panic!("Tried to display a non identifier token in the Type display implementation. This is a bug"), + }; - f.write_str(ident)?; - if !self.generic_args.is_empty() { - f.write_char('<')?; - let mut first_run = true; - for arg in &self.generic_args { - if !first_run { - f.write_str(", ")?; + f.write_str(ident)?; + if !generic_args.is_empty() { + f.write_char('<')?; + let mut first_run = true; + for arg in generic_args { + if !first_run { + f.write_str(", ")?; + } else { + first_run = false; + } + write!(f, "{}", arg)?; + } + f.write_char('>') } else { - first_run = false; + f.write_str("") } - write!(f, "{}", arg)?; } - f.write_char('>') - } else { - f.write_str("") + Type::Function { inputs, output } => { + f.write_str("fn(")?; + if !inputs.is_empty() { + f.write_str( + &inputs + .iter() + .map(|ty| ty.to_string()) + .collect::>() + .join(", "), + )?; + } + + f.write_str(")")?; + if let Some(output) = output { + f.write_str("-> ")?; + f.write_str(output.to_string().as_str())?; + } + Ok(()) + } } } } diff --git a/trixy-parser/src/parsing/checked/mod.rs b/trixy-parser/src/parsing/checked/mod.rs index 9a3163c..8f35790 100644 --- a/trixy-parser/src/parsing/checked/mod.rs +++ b/trixy-parser/src/parsing/checked/mod.rs @@ -286,25 +286,101 @@ impl Parser { }) } - fn process_type(&mut self, mut r#type: UncheckedType) -> Result { - let identifier: Identifier = - mem::take(&mut r#type.identifier.kind).to_identifier(Variant::Void); + fn process_type(&mut self, r#type: UncheckedType) -> Result { + match r#type { + UncheckedType::Typical { + identifier, + generic_args, + } => self.process_typical_type(identifier, generic_args), + UncheckedType::Function { inputs, output } => { + self.process_function_type(inputs, output) + } + } + } + fn process_typical_type( + &mut self, + mut type_identifier: Token, + generic_args: Vec, + ) -> Result { + fn match_to_vector_struct(matches: Vec<&UncheckedStructure>) -> Vec { + matches + .first() + .expect("This exists") + .namespaces + .iter() + .map(|token| match token.kind() { + TokenKind::Identifier(ident) => Identifier { + name: ident.to_owned(), + variant: Variant::Namespace, + }, + _ => unreachable!("This should not be here"), + }) + .collect() + } + fn match_to_vector_enum(matches: Vec<&UncheckedEnumeration>) -> Vec { + matches + .first() + .expect("This exists") + .namespaces + .iter() + .map(|token| match token.kind() { + TokenKind::Identifier(ident) => Identifier { + name: ident.to_owned(), + variant: Variant::Namespace, + }, + _ => unreachable!("This should not be here"), + }) + .collect() + } + + let identifier: Identifier = + mem::take(&mut type_identifier.kind).to_identifier(Variant::Void); let variant: Variant; - if self + + let matched_structures: Vec<_> = self .structures .iter() - .map(|r#struct| (r#struct.identifier.kind.clone()).to_identifier(Variant::Void)) - .any(|ident| ident == identifier) - { - variant = Variant::Structure; - } else if self + .filter(|r#struct| { + (r#struct.identifier.kind.clone()).to_identifier(Variant::Void) == identifier + }) + .collect(); + let matched_enumerations: Vec<_> = self .enumerations .iter() - .map(|r#enum| (r#enum.identifier.kind.clone()).to_identifier(Variant::Void)) - .any(|ident| ident == identifier) - { - variant = Variant::Enumeration; + .filter(|r#enum| { + (r#enum.identifier.kind.clone()).to_identifier(Variant::Void) == identifier + }) + .collect(); + + if !matched_structures.is_empty() { + if matched_structures.len() != 1 { + return Err(error::ParsingError::StructureCopied { + structure: identifier, + span: *matched_structures + .first() + .expect("Exists") + .identifier + .span(), + }); + } + variant = Variant::Structure { + namespace: match_to_vector_struct(matched_structures), + }; + } else if !matched_enumerations.is_empty() { + if matched_enumerations.len() != 1 { + return Err(error::ParsingError::EnumerationCopied { + enumeration: identifier, + span: *matched_enumerations + .first() + .expect("Exists") + .identifier + .span(), + }); + } + variant = Variant::Enumeration { + namespace: match_to_vector_enum(matched_enumerations), + }; } else if BASE_TYPES .iter() .any(|(ident, _)| ident == &identifier.name) @@ -313,7 +389,7 @@ impl Parser { } else { return Err(ParsingError::TypeNotDeclared { r#type: identifier, - span: r#type.identifier.span, + span: type_identifier.span, }); } let identifier: Identifier = Identifier { @@ -338,43 +414,63 @@ impl Parser { if !fitting_types .iter() - .any(|generic_number| *generic_number == &r#type.generic_args.len()) + .any(|generic_number| *generic_number == &generic_args.len()) { - if r#type.generic_args.len() < **min_fitting_type { + if generic_args.len() < **min_fitting_type { return Err(ParsingError::NotEnoughGenericArgs { expected_min: **min_fitting_type, - got: r#type.generic_args.len(), + got: generic_args.len(), r#type: identifier, - span: r#type.identifier.span, + span: type_identifier.span, }); - } else if r#type.generic_args.len() > **max_fitting_type { + } else if generic_args.len() > **max_fitting_type { return Err(ParsingError::TooManyGenericArgs { expected_max: **max_fitting_type, - got: r#type.generic_args.len(), + got: generic_args.len(), r#type: identifier, - span: r#type.identifier.span, + span: type_identifier.span, }); } } - } else if fitting_types.is_empty() && !r#type.generic_args.is_empty() { + } else if fitting_types.is_empty() && !generic_args.is_empty() { // Self declared types can not have generic arguments return Err(ParsingError::TooManyGenericArgs { expected_max: 0, - got: r#type.generic_args.len(), + got: generic_args.len(), r#type: identifier, - span: r#type.identifier.span, + span: type_identifier.span, }); } } - let mut generic_args = vec![]; - for generic_arg in r#type.generic_args { - generic_args.push(self.process_type(generic_arg)?); + let mut new_generic_args: Vec = vec![]; + for generic_arg in generic_args { + new_generic_args.push(self.process_type(generic_arg)?); } - Ok(Type { + Ok(Type::Typical { identifier, - generic_args, + generic_args: new_generic_args, + }) + } + fn process_function_type( + &mut self, + inputs: Vec, + output: Option>, + ) -> Result { + let inputs = inputs + .into_iter() + .map(|input| self.process_named_type(input)) + .collect::, ParsingError>>()?; + + let mut new_output = None; + if let Some(output) = output { + new_output = Some(Box::new(self.process_type(*output)?)); + } + + Ok(Type::Function { + inputs, + output: new_output, }) } } diff --git a/trixy-parser/src/parsing/unchecked/mod.rs b/trixy-parser/src/parsing/unchecked/mod.rs index a244fae..43dcb7d 100644 --- a/trixy-parser/src/parsing/unchecked/mod.rs +++ b/trixy-parser/src/parsing/unchecked/mod.rs @@ -122,24 +122,51 @@ impl Parser { } fn parse_type(&mut self) -> Result { - let identifier = self.expect(token![Ident])?; + if self.expect_peek(token![Ident]) { + let identifier = self.expect(token![Ident])?; + + let mut generic_args = vec![]; + if self.expect_peek(token![<]) { + self.expect(token![<])?; + if self.expect_peek(token![Ident]) { + generic_args.push(self.parse_type()?); + } + while self.expect_peek(token![Comma]) { + self.expect(token![Comma])?; + generic_args.push(self.parse_type()?); + } + self.expect(token![>])?; + } + Ok(Type::Typical { + identifier, + generic_args, + }) + } else { + // we got a function type + self.expect(token![fn])?; + self.expect(token![CurvedBracketOpen])?; + let mut inputs = vec![]; - let mut generic_args = vec![]; - if self.expect_peek(token![<]) { - self.expect(token![<])?; if self.expect_peek(token![Ident]) { - generic_args.push(self.parse_type()?); + inputs.push(self.parse_named_type()?); } while self.expect_peek(token![Comma]) { self.expect(token![Comma])?; - generic_args.push(self.parse_type()?); + inputs.push(self.parse_named_type()?); } - self.expect(token![>])?; + + self.expect(token![CurvedBracketClose])?; + let mut output_type = None; + if self.expect_peek(token![->]) { + self.expect(token![->])?; + output_type = Some(Box::new(self.parse_type()?)); + } + + Ok(Type::Function { + inputs, + output: output_type, + }) } - Ok(Type { - identifier, - generic_args, - }) } fn parse_bracket_string_literal(&mut self) -> Result {