feat: Add support for type parsing.

This commit adds simple primitive type parsing, it also adds
a new type of expression called the cast expression.
This commit is contained in:
2026-03-12 12:50:17 +01:00
parent 93f08d1944
commit 4e2df32e36
5 changed files with 132 additions and 12 deletions

View File

@@ -60,6 +60,11 @@ pub enum ExpressionKind<P: Phase> {
expr: Box<Expression<P>>, expr: Box<Expression<P>>,
index: Box<Expression<P>>, index: Box<Expression<P>>,
}, },
Cast {
expr: Box<Expression<P>>,
ty: Box<Type<P>>,
},
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -123,3 +128,29 @@ pub enum BinaryOp {
/// Member Access /// Member Access
Dot, Dot,
} }
pub type ParsedType = Type<Parsed>;
#[derive(Debug, PartialEq)]
pub struct Type<P: Phase> {
pub kind: TypeKind,
pub span: Span,
pub extra: P::ExtraData,
}
#[derive(Debug, PartialEq)]
pub enum TypeKind {
I8,
I16,
I32,
I64,
U8,
U16,
U32,
U64,
Bool,
Named(String),
}

View File

@@ -71,14 +71,20 @@ impl<'src> Lexer<'src> {
match &self.source[start..self.position] { match &self.source[start..self.position] {
"and" => TokenKind::KwAnd, "and" => TokenKind::KwAnd,
"or" => TokenKind::KwOr, "or" => TokenKind::KwOr,
"as" => TokenKind::KwAs,
"u8" => TokenKind::TyU8, "u8" => TokenKind::TyU8,
"u16" => TokenKind::TyU16, "u16" => TokenKind::TyU16,
"u32" => TokenKind::TyU32, "u32" => TokenKind::TyU32,
"u64" => TokenKind::TyU64, "u64" => TokenKind::TyU64,
"i8" => TokenKind::TyI8, "i8" => TokenKind::TyI8,
"i16" => TokenKind::TyI16, "i16" => TokenKind::TyI16,
"i32" => TokenKind::TyI32, "i32" => TokenKind::TyI32,
"i64" => TokenKind::TyI64, "i64" => TokenKind::TyI64,
"bool" => TokenKind::TyBool,
"true" | "false" => TokenKind::LitBool, "true" | "false" => TokenKind::LitBool,
_ => TokenKind::Identifier, _ => TokenKind::Identifier,
} }

View File

@@ -63,6 +63,55 @@ impl<'src> Parser<'src> {
} }
} }
/// Skips [Token]s until we reach a neutral statement boundary, so that
/// subsequent statements can still be parsed cleanly.
fn synchronize(&mut self) {
while let Some(peek) = self.peek() {
match peek.kind {
// Consume the `;` and stop
TokenKind::Semi => {
self.advance();
break;
}
// Stop before these
TokenKind::RCurly => break,
_ => _ = self.advance(),
}
}
}
pub fn parse_type(&mut self) -> Result<ast::ParsedType, Diagnostic> {
let peek = self.peek_no_eof()?;
let kind = match peek.kind {
TokenKind::TyU8 => ast::TypeKind::U8,
TokenKind::TyU16 => ast::TypeKind::U16,
TokenKind::TyU32 => ast::TypeKind::U32,
TokenKind::TyU64 => ast::TypeKind::U64,
TokenKind::TyI8 => ast::TypeKind::I8,
TokenKind::TyI16 => ast::TypeKind::I16,
TokenKind::TyI32 => ast::TypeKind::I32,
TokenKind::TyI64 => ast::TypeKind::I64,
TokenKind::TyBool => ast::TypeKind::Bool,
TokenKind::Identifier => ast::TypeKind::Named(peek.text.to_string()),
_ => return Err(Diagnostic::new(Severity::Error, "expected a type")),
};
let span = self.advance().span;
Ok(ast::ParsedType {
kind,
span,
extra: (),
})
}
/// Parses an [ast::Expression] using the pratt parsing algorithm. /// Parses an [ast::Expression] using the pratt parsing algorithm.
pub fn parse_expression(&mut self, min_bp: u8) -> Result<ast::ParsedExpression, Diagnostic> { pub fn parse_expression(&mut self, min_bp: u8) -> Result<ast::ParsedExpression, Diagnostic> {
let peek_token = self.peek_no_eof()?; let peek_token = self.peek_no_eof()?;
@@ -95,6 +144,7 @@ impl<'src> Parser<'src> {
left = match peek_token.kind { left = match peek_token.kind {
TokenKind::LParen => self.parse_call_expr(left)?, TokenKind::LParen => self.parse_call_expr(left)?,
TokenKind::LBracket => self.parse_index_expr(left)?, TokenKind::LBracket => self.parse_index_expr(left)?,
TokenKind::KwAs => self.parse_cast_expr(left)?,
_ => unreachable!(), _ => unreachable!(),
}; };
@@ -177,7 +227,17 @@ impl<'src> Parser<'src> {
extra: (), extra: (),
}) })
} }
TokenKind::LParen => {
let open_paren = self.advance();
let inner = self.parse_expression(0)?;
let close_paren = self.expect(TokenKind::RParen)?;
Ok(ast::ParsedExpression {
kind: inner.kind,
span: open_paren.span.extend(close_paren.span),
extra: (),
})
}
_ => Err(Diagnostic::new( _ => Err(Diagnostic::new(
Severity::Error, Severity::Error,
format!( format!(
@@ -242,6 +302,26 @@ impl<'src> Parser<'src> {
extra: (), extra: (),
}) })
} }
/// Parses an [ast::ExpressionKind::Cast] expression.
fn parse_cast_expr(
&mut self,
expr: ast::ParsedExpression,
) -> Result<ast::ParsedExpression, Diagnostic> {
self.expect(TokenKind::KwAs)?;
let ty = self.parse_type()?;
let span = expr.span.extend(ty.span);
Ok(ast::ParsedExpression {
kind: ast::ExpressionKind::Cast {
expr: Box::new(expr),
ty: Box::new(ty),
},
span,
extra: (),
})
}
} }
fn infix_binding_power(kind: TokenKind) -> Option<(u8, u8, ast::BinaryOp)> { fn infix_binding_power(kind: TokenKind) -> Option<(u8, u8, ast::BinaryOp)> {
@@ -273,7 +353,7 @@ fn infix_binding_power(kind: TokenKind) -> Option<(u8, u8, ast::BinaryOp)> {
TokenKind::Slash => (70, 71, ast::BinaryOp::Div), TokenKind::Slash => (70, 71, ast::BinaryOp::Div),
TokenKind::Percent => (70, 71, ast::BinaryOp::Rem), TokenKind::Percent => (70, 71, ast::BinaryOp::Rem),
TokenKind::Dot => (90, 91, ast::BinaryOp::Dot), TokenKind::Dot => (100, 101, ast::BinaryOp::Dot),
_ => return None, _ => return None,
}) })
@@ -293,8 +373,9 @@ fn prefix_binding_power(kind: TokenKind) -> Option<(u8, ast::UnaryOp)> {
fn postfix_binding_power(kind: TokenKind) -> Option<u8> { fn postfix_binding_power(kind: TokenKind) -> Option<u8> {
Some(match kind { Some(match kind {
TokenKind::LParen => 90, TokenKind::LParen => 100,
TokenKind::LBracket => 90, TokenKind::LBracket => 100,
TokenKind::KwAs => 90,
_ => return None, _ => return None,
}) })

View File

@@ -68,16 +68,18 @@ define_tokens! {
// -- Keywords -- // -- Keywords --
KwAnd => "`and`", KwAnd => "`and`",
KwOr => "`or`", KwOr => "`or`",
KwAs => "`as`",
// -- Type Keywords -- // -- Type Keywords --
TyU8 => "`u8`", TyU8 => "`u8`",
TyU16 => "`u16`", TyU16 => "`u16`",
TyU32 => "`u32`", TyU32 => "`u32`",
TyU64 => "`u64`", TyU64 => "`u64`",
TyI8 => "`i8`", TyI8 => "`i8`",
TyI16 => "`i16`", TyI16 => "`i16`",
TyI32 => "`i32`", TyI32 => "`i32`",
TyI64 => "`i64`", TyI64 => "`i64`",
TyBool => "`bool`",
// -- Arithmetic Operators -- // -- Arithmetic Operators --
Plus => "`+`", Plus => "`+`",

View File

@@ -1 +1 @@
foo.bar(12, 3) - 5 foo.bar - 5 as i32