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.
This commit is contained in:
Benedikt Peetz 2024-03-24 19:35:09 +01:00
parent 21b0ce1c01
commit f73d3aa5d7
Signed by: bpeetz
GPG Key ID: A5E94010C3A642AD
5 changed files with 309 additions and 81 deletions

View File

@ -35,7 +35,7 @@ Namespace = {DocComment} {Attribute} "mod" Identifier "{" {Function | Namespace
Structure = {DocComment} {Attribute} "struct" Identifier "{" [DocNamedType {"," DocNamedType } [","]] "}"; Structure = {DocComment} {Attribute} "struct" Identifier "{" [DocNamedType {"," DocNamedType } [","]] "}";
Enumeration = {DocComment} {Attribute} "enum" Identifier "{" [DocIdentifier {"," DocIdentifier} [","]] "}"; Enumeration = {DocComment} {Attribute} "enum" Identifier "{" [DocIdentifier {"," DocIdentifier} [","]] "}";
Type = Identifier ["<" Type {"," Type} ">"]; Type = Identifier ["<" Type {"," Type} ">"] | "fn" "(" [NamedType {"," NamedType }] ")" [ "->" Type ];
StringLiteral = ["r"] "\"" {ANYTHING} "\"" | "r" "#" {"#"} "\"" {ANYTHING} "#" {"#"} "\""; StringLiteral = ["r"] "\"" {ANYTHING} "\"" | "r" "#" {"#"} "\"" {ANYTHING} "#" {"#"} "\"";
Identifier = (CHARACTER | "_") { NUMBER | CHARACTER | "_" } ; Identifier = (CHARACTER | "_") { NUMBER | CHARACTER | "_" } ;

View File

@ -25,7 +25,7 @@ use std::fmt::{Display, Write};
use crate::lexing::TokenKind; use crate::lexing::TokenKind;
use super::unchecked; use super::unchecked::{self, DeriveValue};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub enum Variant { pub enum Variant {
@ -103,34 +103,95 @@ pub struct Function {
} }
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Type { pub enum Type {
pub identifier: Identifier, Typical {
pub generic_args: Vec<Type>, identifier: Identifier,
generic_args: Vec<Type>,
},
Function {
inputs: Vec<NamedType>,
output: Option<Box<Type>>,
},
} }
impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let ident = &self.identifier.name;
f.write_str(ident)?; impl Type {
if !self.generic_args.is_empty() { pub fn print_generics(&self) -> String {
f.write_char('<')?; let mut output = String::new();
let mut first_run = true; self.display_generics(&mut output)
for arg in &self.generic_args { .expect("This should fail");
if !first_run { output
f.write_str(", ")?; }
pub fn display_generics<T: Write>(&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 { } else {
first_run = false; f.write_str("")
} }
write!(f, "{}", arg)?;
} }
f.write_char('>') Type::Function { .. } => {
} else { unimplemented!("This function should only be called for the 'Typical' enum variant")
f.write_str("") }
} }
} }
} }
#[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::<Vec<_>>()
.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 struct NamedType {
pub name: Identifier, pub name: Identifier,
pub r#type: Type, pub r#type: Type,

View File

@ -25,7 +25,7 @@
use std::fmt::{Display, Write}; use std::fmt::{Display, Write};
use crate::lexing::{Token, TokenSpan}; use crate::lexing::{Token, TokenKind, TokenSpan};
#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct CommandSpec { pub struct CommandSpec {
@ -173,33 +173,77 @@ pub struct NamedType {
} }
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct Type { pub enum Type {
pub identifier: Token, // Will later become an Identifier Typical {
pub generic_args: Vec<Type>, identifier: Token, // Will later become an Identifier
generic_args: Vec<Type>,
},
Function {
inputs: Vec<NamedType>,
output: Option<Box<Type>>,
},
}
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 { impl Display for Type {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let ident = match self.identifier.kind() { match self {
crate::lexing::TokenKind::Identifier(ident) => ident, Type::Typical {
_ => panic!("Tried to display a non identifier token in the Type display implementation. This is a bug"), 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)?; f.write_str(ident)?;
if !self.generic_args.is_empty() { if !generic_args.is_empty() {
f.write_char('<')?; f.write_char('<')?;
let mut first_run = true; let mut first_run = true;
for arg in &self.generic_args { for arg in generic_args {
if !first_run { if !first_run {
f.write_str(", ")?; f.write_str(", ")?;
} else {
first_run = false;
}
write!(f, "{}", arg)?;
}
f.write_char('>')
} else { } else {
first_run = false; f.write_str("")
} }
write!(f, "{}", arg)?;
} }
f.write_char('>') Type::Function { inputs, output } => {
} else { f.write_str("fn(")?;
f.write_str("") if !inputs.is_empty() {
f.write_str(
&inputs
.iter()
.map(|ty| ty.to_string())
.collect::<Vec<_>>()
.join(", "),
)?;
}
f.write_str(")")?;
if let Some(output) = output {
f.write_str("-> ")?;
f.write_str(output.to_string().as_str())?;
}
Ok(())
}
} }
} }
} }

View File

@ -286,25 +286,101 @@ impl Parser {
}) })
} }
fn process_type(&mut self, mut r#type: UncheckedType) -> Result<Type, ParsingError> { fn process_type(&mut self, r#type: UncheckedType) -> Result<Type, ParsingError> {
let identifier: Identifier = match r#type {
mem::take(&mut r#type.identifier.kind).to_identifier(Variant::Void); 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<UncheckedType>,
) -> Result<Type, ParsingError> {
fn match_to_vector_struct(matches: Vec<&UncheckedStructure>) -> Vec<Identifier> {
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<Identifier> {
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; let variant: Variant;
if self
let matched_structures: Vec<_> = self
.structures .structures
.iter() .iter()
.map(|r#struct| (r#struct.identifier.kind.clone()).to_identifier(Variant::Void)) .filter(|r#struct| {
.any(|ident| ident == identifier) (r#struct.identifier.kind.clone()).to_identifier(Variant::Void) == identifier
{ })
variant = Variant::Structure; .collect();
} else if self let matched_enumerations: Vec<_> = self
.enumerations .enumerations
.iter() .iter()
.map(|r#enum| (r#enum.identifier.kind.clone()).to_identifier(Variant::Void)) .filter(|r#enum| {
.any(|ident| ident == identifier) (r#enum.identifier.kind.clone()).to_identifier(Variant::Void) == identifier
{ })
variant = Variant::Enumeration; .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 } else if BASE_TYPES
.iter() .iter()
.any(|(ident, _)| ident == &identifier.name) .any(|(ident, _)| ident == &identifier.name)
@ -313,7 +389,7 @@ impl Parser {
} else { } else {
return Err(ParsingError::TypeNotDeclared { return Err(ParsingError::TypeNotDeclared {
r#type: identifier, r#type: identifier,
span: r#type.identifier.span, span: type_identifier.span,
}); });
} }
let identifier: Identifier = Identifier { let identifier: Identifier = Identifier {
@ -338,43 +414,63 @@ impl Parser {
if !fitting_types if !fitting_types
.iter() .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 { return Err(ParsingError::NotEnoughGenericArgs {
expected_min: **min_fitting_type, expected_min: **min_fitting_type,
got: r#type.generic_args.len(), got: generic_args.len(),
r#type: identifier, 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 { return Err(ParsingError::TooManyGenericArgs {
expected_max: **max_fitting_type, expected_max: **max_fitting_type,
got: r#type.generic_args.len(), got: generic_args.len(),
r#type: identifier, 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 // Self declared types can not have generic arguments
return Err(ParsingError::TooManyGenericArgs { return Err(ParsingError::TooManyGenericArgs {
expected_max: 0, expected_max: 0,
got: r#type.generic_args.len(), got: generic_args.len(),
r#type: identifier, r#type: identifier,
span: r#type.identifier.span, span: type_identifier.span,
}); });
} }
} }
let mut generic_args = vec![]; let mut new_generic_args: Vec<checked::Type> = vec![];
for generic_arg in r#type.generic_args { for generic_arg in generic_args {
generic_args.push(self.process_type(generic_arg)?); new_generic_args.push(self.process_type(generic_arg)?);
} }
Ok(Type { Ok(Type::Typical {
identifier, identifier,
generic_args, generic_args: new_generic_args,
})
}
fn process_function_type(
&mut self,
inputs: Vec<UncheckedNamedType>,
output: Option<Box<UncheckedType>>,
) -> Result<Type, ParsingError> {
let inputs = inputs
.into_iter()
.map(|input| self.process_named_type(input))
.collect::<Result<Vec<NamedType>, 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,
}) })
} }
} }

View File

@ -122,24 +122,51 @@ impl Parser {
} }
fn parse_type(&mut self) -> Result<Type, ParsingError> { fn parse_type(&mut self) -> Result<Type, ParsingError> {
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]) { if self.expect_peek(token![Ident]) {
generic_args.push(self.parse_type()?); inputs.push(self.parse_named_type()?);
} }
while self.expect_peek(token![Comma]) { while self.expect_peek(token![Comma]) {
self.expect(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<StringLiteral, ParsingError> { fn parse_bracket_string_literal(&mut self) -> Result<StringLiteral, ParsingError> {