Skip to content

Commit

Permalink
Started implementing parsing for general purpose generics / compile-t…
Browse files Browse the repository at this point in the history
…ime arguments
  • Loading branch information
IsaacShelton committed Sep 10, 2024
1 parent cc2fc3f commit 4694414
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 78 deletions.
8 changes: 8 additions & 0 deletions src/ast/datatype/generics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use super::Type;
use crate::ast::Expr;

#[derive(Clone, Debug)]
pub enum CompileTimeArgument {
Type(Type),
Expr(Expr),
}
2 changes: 2 additions & 0 deletions src/ast/datatype/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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::*;
Expand Down
27 changes: 27 additions & 0 deletions src/parser/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down Expand Up @@ -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)?;
}
Expand Down
71 changes: 44 additions & 27 deletions src/parser/parse_expr/primary/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,37 +66,54 @@ impl<'a, I: Inflow<Token>> 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,
Expand Down
12 changes: 10 additions & 2 deletions src/parser/parse_expr/primary/structure_literal.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand All @@ -12,8 +14,14 @@ impl<'a, I: Inflow<Token>> 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<Expr, ParseError> {
// 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();

Expand Down
147 changes: 98 additions & 49 deletions src/parser/parse_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
};

Expand All @@ -28,6 +29,48 @@ impl<'a, I: Inflow<Token>> Parser<'a, I> {
});
};

let generics = self.parse_generics()?;
self.parse_type_from_parts(identifier, generics, source)
}

pub fn parse_generics(&mut self) -> Result<Vec<CompileTimeArgument>, 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<CompileTimeArgument>,
source: Source,
) -> Result<Type, ParseError> {
let type_kind = match identifier.as_str() {
"bool" => Ok(TypeKind::Boolean),
"char" => Ok(TypeKind::char()),
Expand All @@ -52,39 +95,38 @@ impl<'a, I: Inflow<Token>> 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())),
}?;
Expand All @@ -96,14 +138,15 @@ impl<'a, I: Inflow<Token>> 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<I: Inflow<Token>>(
parser: &mut Parser<I>,
closer: &Token,
closer: Token,
column_offset: u32,
) {
if parser.input.eat(TokenKind::Assign) {
Expand All @@ -118,36 +161,42 @@ impl<'a, I: Inflow<Token>> 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,
}
}
}

0 comments on commit 4694414

Please sign in to comment.