From 4694414dcf903db0e37b5a59ddb560936a2ae350 Mon Sep 17 00:00:00 2001 From: IsaacShelton Date: Mon, 9 Sep 2024 22:12:42 -0500 Subject: [PATCH] Started implementing parsing for general purpose generics / compile-time arguments --- src/ast/datatype/generics.rs | 8 + src/ast/datatype/mod.rs | 2 + src/parser/error.rs | 27 ++++ src/parser/parse_expr/primary/mod.rs | 71 +++++---- .../parse_expr/primary/structure_literal.rs | 12 +- src/parser/parse_type.rs | 147 ++++++++++++------ 6 files changed, 189 insertions(+), 78 deletions(-) create mode 100644 src/ast/datatype/generics.rs diff --git a/src/ast/datatype/generics.rs b/src/ast/datatype/generics.rs new file mode 100644 index 00000000..29dced83 --- /dev/null +++ b/src/ast/datatype/generics.rs @@ -0,0 +1,8 @@ +use super::Type; +use crate::ast::Expr; + +#[derive(Clone, Debug)] +pub enum CompileTimeArgument { + Type(Type), + Expr(Expr), +} diff --git a/src/ast/datatype/mod.rs b/src/ast/datatype/mod.rs index 0ecf6ff6..8f2e2004 100644 --- a/src/ast/datatype/mod.rs +++ b/src/ast/datatype/mod.rs @@ -2,6 +2,7 @@ mod c_integer; mod description; mod fixed_array; mod function_pointer; +mod generics; mod kind; mod nameless_enumeration; mod nameless_structure; @@ -12,6 +13,7 @@ pub use c_integer::*; pub use description::*; pub use fixed_array::*; pub use function_pointer::*; +pub use generics::*; pub use kind::*; pub use nameless_enumeration::*; pub use nameless_structure::*; diff --git a/src/parser/error.rs b/src/parser/error.rs index cd910398..aac0d803 100644 --- a/src/parser/error.rs +++ b/src/parser/error.rs @@ -75,6 +75,15 @@ pub enum ParseErrorKind { message: String, }, CannotCallFunctionsAtGlobalScope, + IncorrectNumberOfTypeParametersFor { + name: String, + got: usize, + expected: usize, + }, + ExpectedTypeParameterToBeType { + name: String, + word_for_nth: String, + }, Other { message: String, }, @@ -180,6 +189,24 @@ impl Display for ParseErrorKind { ParseErrorKind::CannotCallFunctionsAtGlobalScope => { write!(f, "Cannot call functions at global scope")?; } + ParseErrorKind::IncorrectNumberOfTypeParametersFor { + name, + got, + expected, + } => { + write!( + f, + "Incorrect number of type parameters for '{}' (got {}, expected {})", + name, got, expected + )?; + } + ParseErrorKind::ExpectedTypeParameterToBeType { name, word_for_nth } => { + write!( + f, + "Expected {} type parameter to '{}' to be a type", + word_for_nth, name + )?; + } ParseErrorKind::Other { message } | ParseErrorKind::Lexical { message } => { write!(f, "{}", message)?; } diff --git a/src/parser/parse_expr/primary/mod.rs b/src/parser/parse_expr/primary/mod.rs index b3d166b2..b99e9ce3 100644 --- a/src/parser/parse_expr/primary/mod.rs +++ b/src/parser/parse_expr/primary/mod.rs @@ -66,37 +66,54 @@ impl<'a, I: Inflow> Parser<'a, I> { TokenKind::StructKeyword | TokenKind::UnionKeyword | TokenKind::EnumKeyword => { self.parse_structure_literal() } - TokenKind::Identifier(_) => match self.input.peek_nth(1).kind { - TokenKind::Namespace => self.parse_enum_member_literal(), - TokenKind::OpenAngle => self.parse_structure_literal(), - TokenKind::OpenCurly => { - let peek = &self.input.peek_nth(2).kind; - - if peek.is_extend() || peek.is_colon() { - self.parse_structure_literal() - } else { - let next_three = - array_last::<3, 5, _>(self.input.peek_n()).map(|token| &token.kind); - - match &next_three[..] { - [TokenKind::Identifier(_), TokenKind::Colon, ..] - | [TokenKind::Newline, TokenKind::Identifier(_), TokenKind::Colon, ..] => { - self.parse_structure_literal() + TokenKind::Identifier(_) => { + // TODO: CLEANUP: This should be cleaned up once we have proper + // namespaces and generic parsing that applies to all cases + + match self.input.peek_nth(1).kind { + TokenKind::Namespace => self.parse_enum_member_literal(), + TokenKind::OpenAngle => { + let name = self.input.eat_identifier().unwrap(); + let generics = self.parse_generics()?; + + if !generics.is_empty() { + todo!("generics in expressions not implemented yet"); + } + + let ast_type = self.parse_type_from_parts(name, vec![], source)?; + self.parse_structure_literal_with(ast_type) + } + TokenKind::OpenCurly => { + let peek = &self.input.peek_nth(2).kind; + + if peek.is_extend() || peek.is_colon() { + self.parse_structure_literal() + } else { + let next_three = + array_last::<3, 5, _>(self.input.peek_n()).map(|token| &token.kind); + + match &next_three[..] { + [TokenKind::Identifier(_), TokenKind::Colon, ..] + | [TokenKind::Newline, TokenKind::Identifier(_), TokenKind::Colon, ..] => { + self.parse_structure_literal() + } + _ => Ok(Expr::new( + ExprKind::Variable( + self.input.advance().kind.unwrap_identifier(), + ), + source, + )), } - _ => Ok(Expr::new( - ExprKind::Variable(self.input.advance().kind.unwrap_identifier()), - source, - )), } } + TokenKind::OpenParen => self.parse_call(), + TokenKind::DeclareAssign => self.parse_declare_assign(), + _ => Ok(Expr::new( + ExprKind::Variable(self.input.advance().kind.unwrap_identifier()), + source, + )), } - TokenKind::OpenParen => self.parse_call(), - TokenKind::DeclareAssign => self.parse_declare_assign(), - _ => Ok(Expr::new( - ExprKind::Variable(self.input.advance().kind.unwrap_identifier()), - source, - )), - }, + } TokenKind::Not | TokenKind::BitComplement | TokenKind::Subtract => { let operator = match kind { TokenKind::Not => UnaryOperator::Not, diff --git a/src/parser/parse_expr/primary/structure_literal.rs b/src/parser/parse_expr/primary/structure_literal.rs index c8a04e7c..219d212b 100644 --- a/src/parser/parse_expr/primary/structure_literal.rs +++ b/src/parser/parse_expr/primary/structure_literal.rs @@ -1,6 +1,8 @@ use super::Parser; use crate::{ - ast::{ConformBehavior, Expr, ExprKind, FieldInitializer, FillBehavior, StructureLiteral}, + ast::{ + ConformBehavior, Expr, ExprKind, FieldInitializer, FillBehavior, StructureLiteral, Type, + }, inflow::Inflow, parser::error::ParseError, token::{Token, TokenKind}, @@ -12,8 +14,14 @@ impl<'a, I: Inflow> Parser<'a, I> { // ^ let ast_type = self.parse_type(None::<&str>, Some("for type of struct literal"))?; - let source = ast_type.source; + self.parse_structure_literal_with(ast_type) + } + pub fn parse_structure_literal_with(&mut self, ast_type: Type) -> Result { + // Type { x: VALUE, b: VALUE, c: VALUE, :d, :e, ..SPECIFIER } + // ^ + + let source = ast_type.source; self.parse_token(TokenKind::OpenCurly, Some("to begin struct literal"))?; self.ignore_newlines(); diff --git a/src/parser/parse_type.rs b/src/parser/parse_type.rs index daecc253..9cbe0e0f 100644 --- a/src/parser/parse_type.rs +++ b/src/parser/parse_type.rs @@ -3,8 +3,9 @@ use super::{ Parser, }; use crate::{ - ast::{FixedArray, Type, TypeKind}, + ast::{CompileTimeArgument, Type, TypeKind}, inflow::Inflow, + source_files::Source, token::{Token, TokenKind}, }; @@ -28,6 +29,48 @@ impl<'a, I: Inflow> Parser<'a, I> { }); }; + let generics = self.parse_generics()?; + self.parse_type_from_parts(identifier, generics, source) + } + + pub fn parse_generics(&mut self) -> Result, ParseError> { + let mut generics = vec![]; + + if !self.input.eat(TokenKind::OpenAngle) { + return Ok(generics); + } + + loop { + if self.parse_type_parameters_close().is_some() { + break; + } else if self.input.peek_is(TokenKind::EndOfFile) { + // TODO: Improve error message + return Err(self.unexpected_token_is_next()); + } + + if !generics.is_empty() && !self.input.eat(TokenKind::Comma) { + // TODO: Improve error message + return Err(self.unexpected_token_is_next()); + } + + generics.push(if self.input.peek().could_start_type() { + CompileTimeArgument::Type( + self.parse_type(None::<&str>, Some("for compile time argument"))?, + ) + } else { + CompileTimeArgument::Expr(self.parse_expr()?) + }); + } + + Ok(generics) + } + + pub fn parse_type_from_parts( + &mut self, + identifier: String, + generics: Vec, + source: Source, + ) -> Result { let type_kind = match identifier.as_str() { "bool" => Ok(TypeKind::Boolean), "char" => Ok(TypeKind::char()), @@ -52,39 +95,38 @@ impl<'a, I: Inflow> Parser<'a, I> { "f32" | "float" => Ok(TypeKind::f32()), "f64" | "double" => Ok(TypeKind::f64()), "void" => Ok(TypeKind::Void), - "ptr" => Ok(TypeKind::Pointer(Box::new( - if self.input.eat(TokenKind::OpenAngle) { - let inner = self.parse_type(None::<&str>, None::<&str>)?; - self.parse_type_parameters_close()?; - inner + "ptr" => { + if generics.len() == 1 { + if let CompileTimeArgument::Type(inner) = generics.into_iter().next().unwrap() { + Ok(TypeKind::Pointer(Box::new(inner))) + } else { + Err(ParseError { + kind: ParseErrorKind::ExpectedTypeParameterToBeType { + name: identifier, + word_for_nth: "first".into(), + }, + source, + }) + } } else { - TypeKind::Void.at(source) - }, - ))), - "array" => { - if !self.input.eat(TokenKind::OpenAngle) { - return Err(ParseError { - kind: ParseErrorKind::ExpectedTypeParameters, + Err(ParseError { + kind: ParseErrorKind::IncorrectNumberOfTypeParametersFor { + name: identifier, + expected: 1, + got: generics.len(), + }, source, - }); - } - - let count = self.parse_expr()?; - - if !self.input.eat(TokenKind::Comma) { - return Err(ParseError { - kind: ParseErrorKind::ExpectedCommaInTypeParameters, - source: self.source_here(), - }); + }) } + } + "array" => { + // TODO: Update fixed array type to use compile time arguments + todo!("array<$N, $T> not updated yet to use compile time arguments"); - let inner = self.parse_type(None::<&str>, None::<&str>)?; - self.parse_type_parameters_close()?; - - Ok(TypeKind::FixedArray(Box::new(FixedArray { - ast_type: inner, - count, - }))) + // Ok(TypeKind::FixedArray(Box::new(FixedArray { + // ast_type: inner, + // count, + // }))) } identifier => Ok(TypeKind::Named(identifier.into())), }?; @@ -96,14 +138,15 @@ impl<'a, I: Inflow> Parser<'a, I> { /// This function may partially consume tokens, so be /// aware that any previously peeked tokens may no longer be in /// the same lookahead position after calling this function. - fn parse_type_parameters_close(&mut self) -> Result<(), ParseError> { - let closer = self.input.advance(); + fn parse_type_parameters_close(&mut self) -> Option<()> { + let closer = self.input.peek(); + let source = closer.source; /// Sub-function for properly handling trailing `=` signs /// resulting from partially consuming '>'-like tokens. fn merge_trailing_equals>( parser: &mut Parser, - closer: &Token, + closer: Token, column_offset: u32, ) { if parser.input.eat(TokenKind::Assign) { @@ -118,36 +161,42 @@ impl<'a, I: Inflow> Parser<'a, I> { } match &closer.kind { - TokenKind::GreaterThan => Ok(()), + TokenKind::GreaterThan => { + self.input.advance(); + Some(()) + } TokenKind::RightShift => { + self.input.advance(); self.input - .unadvance(TokenKind::GreaterThan.at(closer.source.shift_column(1))); - Ok(()) + .unadvance(TokenKind::GreaterThan.at(source.shift_column(1))); + Some(()) } TokenKind::LogicalRightShift => { + self.input.advance(); self.input - .unadvance(TokenKind::RightShift.at(closer.source.shift_column(1))); - Ok(()) + .unadvance(TokenKind::RightShift.at(source.shift_column(1))); + Some(()) } TokenKind::RightShiftAssign => { - merge_trailing_equals(self, &closer, 2); - + let closer = self.input.advance(); + merge_trailing_equals(self, closer, 2); self.input - .unadvance(TokenKind::GreaterThan.at(closer.source.shift_column(1))); - Ok(()) + .unadvance(TokenKind::GreaterThan.at(source.shift_column(1))); + Some(()) } TokenKind::LogicalRightShiftAssign => { - merge_trailing_equals(self, &closer, 3); - + let closer = self.input.advance(); + merge_trailing_equals(self, closer, 3); self.input - .unadvance(TokenKind::RightShift.at(closer.source.shift_column(1))); - Ok(()) + .unadvance(TokenKind::RightShift.at(source.shift_column(1))); + Some(()) } TokenKind::GreaterThanEq => { - merge_trailing_equals(self, &closer, 1); - Ok(()) + let closer = self.input.advance(); + merge_trailing_equals(self, closer, 1); + Some(()) } - _ => Err(self.unexpected_token(&closer)), + _ => None, } } }