mod error;
pub use error::{ParserError, ParserResult};
use crate::ast::{
AstNode, BinaryOp, EnumVariant, EnumVariantField, ExpressionKind, ExpressionNode, Field,
FunctionNode, ImportSpec, LiteralNode, MatchArm, Param, PatternNode, Precedence, PrimitiveType,
SpanExt, Spanned, StatementKind, StatementNode, TraitBound, TraitRef, TypeKind, TypeNode,
UnaryOp,
};
use crate::lexer::{Span, Token, TokenType};
#[derive(Debug)]
pub struct Parser<'a> {
tokens: Vec<&'a Token>,
current: usize,
pub errors: Vec<ParserError>,
loop_depth: usize,
}
impl<'a> Parser<'a> {
pub fn new(tokens: &'a [Token]) -> Self {
Self {
tokens: tokens
.iter()
.filter(|&token| {
!matches!(
token.token_type,
TokenType::LineComment(_) | TokenType::MultilineComment(_)
)
})
.collect(),
current: 0,
errors: Vec::new(),
loop_depth: 0,
}
}
fn advance(&mut self) -> &Token {
if !self.is_at_end() {
self.current += 1;
}
self.previous()
}
fn previous(&self) -> &Token {
self.tokens[(self.current - 1).max(0)]
}
fn is_statement_starter(&self, token_type: &TokenType) -> bool {
matches!(
token_type,
TokenType::Auto
| TokenType::Const
| TokenType::Func
| TokenType::Class
| TokenType::Interface
| TokenType::Enum
| TokenType::If
| TokenType::While
| TokenType::For
| TokenType::Match
| TokenType::Break
| TokenType::Continue
| TokenType::Return
| TokenType::OpenBrace
| TokenType::CloseBrace
)
}
fn check_statement_termination(&self) -> ParserResult<()> {
if self.current >= self.tokens.len() {
return Ok(());
}
let token = &self.tokens[self.current];
if matches!(
token.token_type,
TokenType::CloseBrace | TokenType::Eof | TokenType::NewLine
) {
return Ok(());
}
if self.current + 1 < self.tokens.len() {
let next_token = &self.tokens[self.current + 1];
if self.is_statement_starter(&next_token.token_type)
&& !matches!(token.token_type, TokenType::NewLine)
&& next_token.token_type != TokenType::Eof
{
return Err(ParserError::new(
"Expected newline before statement".to_string(),
next_token.span,
));
}
}
Ok(())
}
pub fn parse(&mut self) -> Result<Vec<AstNode>, (Vec<AstNode>, Vec<ParserError>)> {
let mut nodes = Vec::new();
while !self.is_at_end() {
if self.is_at_end() {
break;
}
let start_position = self.current;
match self.declaration() {
Ok(Some(decl)) => {
nodes.push(decl);
let should_check_termination = matches!(
nodes.last().expect("nodes should not be empty after push"),
AstNode::Statement(stmt) if !matches!(
stmt.kind,
StatementKind::If { .. } | StatementKind::While { .. } |
StatementKind::For { .. } | StatementKind::Match { .. } |
StatementKind::Block(_)
)
);
if should_check_termination && let Err(e) = self.check_statement_termination() {
self.errors.push(e);
self.synchronize();
}
let _ = self.skip_newlines();
}
Ok(None) => {
if self.current == start_position {
self.advance();
}
}
Err(e) => {
self.errors.push(e);
self.synchronize();
}
}
let _ = self.skip_newlines();
}
let all_errors = std::mem::take(&mut self.errors);
if all_errors.is_empty() {
Ok(nodes)
} else {
Err((nodes, all_errors))
}
}
fn declaration(&mut self) -> ParserResult<Option<AstNode>> {
let _ = self.skip_newlines();
if self.is_at_end() {
return Ok(None);
}
let result = self.parse_declaration_content();
while self.matches(&[TokenType::NewLine]) {}
if let Err(ref e) = result {
self.errors.push(e.clone());
if !self.is_at_end() {
return self.declaration();
}
return Ok(None);
}
result
}
fn parse_declaration_content(&mut self) -> ParserResult<Option<AstNode>> {
if self.check(TokenType::Auto) {
self.auto_declaration().map(Some)
} else if self.check(TokenType::Const) {
self.const_declaration().map(Some)
} else if self.check(TokenType::Common) {
self.consume();
self.function_declaration(true).map(Some)
} else if self.check(TokenType::Func) {
self.function_declaration(false).map(Some)
} else if let TokenType::Id(_) = &self.peek().token_type {
self.parse_id_start_declaration()
} else if self.check(TokenType::Class) {
self.class_declaration().map(Some)
} else if self.check(TokenType::Interface) {
self.interface_declaration().map(Some)
} else if self.check(TokenType::Enum) {
self.enum_declaration().map(Some)
} else if self.check(TokenType::Import) {
self.import_declaration().map(Some)
} else {
self.statement().map(Some)
}
}
fn parse_id_start_declaration(&mut self) -> ParserResult<Option<AstNode>> {
let start = self.current;
if self.parse_type().is_ok() {
self.parse_typed_or_statement(start)
} else {
self.current = start;
self.statement().map(Some)
}
}
fn parse_typed_or_statement(&mut self, start: usize) -> ParserResult<Option<AstNode>> {
if let TokenType::Id(_) = &self.peek().token_type {
let next = self.current + 1;
if next < self.tokens.len() && self.tokens[next].token_type == TokenType::Eq {
self.current = start;
self.parse_typed_declaration_with_recovery()
} else {
self.current = start;
self.statement().map(Some)
}
} else {
self.current = start;
self.statement().map(Some)
}
}
fn parse_typed_declaration_with_recovery(&mut self) -> ParserResult<Option<AstNode>> {
match self.typed_declaration() {
Ok(node) => Ok(Some(node)),
Err(e)
if matches!(
e.message.as_str(),
"must be terminated with a newline" | "expected newline after statement"
) =>
{
self.errors.push(e);
self.synchronize();
Ok(None)
}
Err(e) => Err(e),
}
}
fn auto_declaration(&mut self) -> ParserResult<AstNode> {
let start_span = self.peek().span;
self.current += 1;
let name = self.consume_identifier("Expected variable name after 'auto'")?;
let name_span = self.tokens[self.current - 1].span;
self.consume_token(TokenType::Eq, "Expected '=' after variable name")?;
let value = self.parse_expression()?;
self.check_no_postfix_increment_decrement(&value)?;
if !self.is_in_block() {
if self.current < self.tokens.len() {
let next_token = &self.tokens[self.current];
if self.is_statement_starter(&next_token.token_type)
&& !matches!(self.tokens[self.current - 1].token_type, TokenType::NewLine)
{
return Err(ParserError::new(
"Expected newline after statement".to_string(),
next_token.span,
));
}
}
} else {
let _ = self.skip_newlines();
}
let span = start_span.combine(&value.span);
Ok(AstNode::Statement(StatementNode {
kind: StatementKind::AutoDecl(
name,
TypeNode {
kind: TypeKind::Auto,
span: name_span,
},
value,
),
span,
}))
}
fn const_declaration(&mut self) -> ParserResult<AstNode> {
let start_span = self.peek().span;
self.current += 1;
let type_node = self.parse_type()?;
let name = self.consume_identifier("Expected constant name after type")?;
self.consume_token(TokenType::Eq, "Expected '=' after constant name")?;
let value = self.parse_expression()?;
self.check_no_postfix_increment_decrement(&value)?;
let span = start_span.combine(&value.span);
Ok(AstNode::Statement(StatementNode {
kind: StatementKind::ConstDecl(name, type_node, value),
span,
}))
}
fn typed_declaration(&mut self) -> ParserResult<AstNode> {
let start_span = self.peek().span;
let type_node = self.parse_type()?;
let name = self.consume_identifier("Expected variable name after type")?;
self.consume_token(TokenType::Eq, "Expected '=' after variable name")?;
let value = self.parse_expression()?;
self.check_no_postfix_increment_decrement(&value)?;
let span = start_span.combine(&value.span);
Ok(AstNode::Statement(StatementNode {
kind: StatementKind::TypedDecl(name, type_node, value),
span,
}))
}
fn class_declaration(&mut self) -> ParserResult<AstNode> {
let start_span = self.tokens[self.current].span;
self.consume_token(TokenType::Class, "Expected 'class' keyword")?;
let name = self.consume_identifier("Expected class name")?;
let type_params = self.parse_type_params_list()?;
let traits = self.parse_trait_list()?;
self.consume_token(TokenType::OpenBrace, "Expected '{' after class header")?;
let (fields, methods) = self.parse_class_body(&type_params, start_span)?;
let end_span =
self.consume_token(TokenType::CloseBrace, "Expected '}' after class body")?;
let full_span = start_span.combine(&end_span);
Ok(AstNode::Class {
name,
type_params,
traits,
fields,
methods,
span: full_span,
})
}
fn parse_type_params_list(&mut self) -> ParserResult<Vec<(String, Vec<TraitBound>)>> {
if !self.matches(&[TokenType::Lt]) {
return Ok(Vec::new());
}
let mut params = Vec::new();
loop {
let param = self.consume_identifier("Expected type parameter name")?;
let bounds = self.parse_trait_bounds()?;
params.push((param, bounds));
if !self.matches(&[TokenType::Comma]) {
break;
}
}
self.consume_token(TokenType::Gt, "Expected '>' after type parameters")?;
Ok(params)
}
fn parse_trait_bounds(&mut self) -> ParserResult<Vec<TraitBound>> {
if !self.matches(&[TokenType::Is]) {
return Ok(Vec::new());
}
let mut bounds = Vec::new();
loop {
let bound_name = self.consume_identifier("Expected trait name in bound")?;
let type_args = self.parse_optional_type_args()?;
bounds.push(TraitBound {
name: bound_name,
type_params: type_args,
});
if !self.matches(&[TokenType::Ref]) {
break;
}
}
Ok(bounds)
}
fn parse_trait_list(&mut self) -> ParserResult<Vec<TraitRef>> {
if !self.matches(&[TokenType::Is]) {
return Ok(Vec::new());
}
let mut traits_list = Vec::new();
loop {
let trait_name = self.consume_identifier("Expected trait name")?;
let type_args = self.parse_optional_type_args()?;
traits_list.push(TraitRef {
name: trait_name,
type_args,
});
if !self.matches(&[TokenType::Comma]) {
break;
}
}
Ok(traits_list)
}
fn parse_optional_type_args(&mut self) -> ParserResult<Vec<TypeNode>> {
if self.matches(&[TokenType::Lt]) {
let args = self.parse_type_arguments()?;
self.consume_token(TokenType::Gt, "Expected '>' after type arguments")?;
Ok(args)
} else {
Ok(Vec::new())
}
}
fn parse_class_body(
&mut self,
type_params: &[(String, Vec<TraitBound>)],
start_span: Span,
) -> ParserResult<(Vec<Field>, Vec<FunctionNode>)> {
let mut fields = Vec::new();
let mut methods = Vec::new();
while !self.check(TokenType::CloseBrace) && !self.is_at_end() {
self.parse_class_member(type_params, start_span, &mut fields, &mut methods)?;
}
Ok((fields, methods))
}
fn parse_class_member(
&mut self,
type_params: &[(String, Vec<TraitBound>)],
start_span: Span,
fields: &mut Vec<Field>,
methods: &mut Vec<FunctionNode>,
) -> ParserResult<()> {
match self.peek().token_type {
TokenType::Func => {
let name_span = self.peek_ahead(1).map(|t| t.span);
let func_node = self.function_declaration(false)?;
if let AstNode::Function(func) = func_node {
if func.name == "new" {
self.errors.push(ParserError::new(
"'new' is reserved for class constructors and cannot be defined as a class method",
name_span.unwrap_or(func.span),
));
return Ok(());
}
methods.push(func);
} else {
return Err(ParserError::new("Expected function in class", start_span));
}
}
TokenType::Common => {
self.consume();
let name_span = self.peek_ahead(1).map(|t| t.span);
let func_node = self.function_declaration(true)?;
if let AstNode::Function(func) = func_node {
if func.name == "new" {
self.errors.push(ParserError::new(
"'new' is reserved for class constructors and cannot be defined as a class method",
name_span.unwrap_or(func.span),
));
return Ok(());
}
methods.push(func);
} else {
return Err(ParserError::new("Expected function in class", start_span));
}
}
TokenType::Id(_) | TokenType::Const => {
let field = self.parse_field_declaration(type_params)?;
fields.push(field);
}
TokenType::NewLine => {
self.consume_token(TokenType::NewLine, "Expected newline")?;
}
_ => {
let token_desc = Self::describe_token(&self.peek().token_type);
return Err(ParserError::with_help(
format!(
"Expected field or method declaration in class body, found {}",
token_desc
),
self.peek().span,
"Class bodies can only contain field declarations (e.g., 'int x = 0') and method declarations (e.g., 'func foo() returns void { ... }')",
));
}
}
Ok(())
}
fn interface_declaration(&mut self) -> ParserResult<AstNode> {
let start_span = self.tokens[self.current].span;
self.consume_token(TokenType::Interface, "Expected 'interface' keyword")?;
let name = self.consume_identifier("Expected interface name")?;
let type_params = self.parse_interface_type_params()?;
self.consume_token(TokenType::OpenBrace, "Expected '{' after interface header")?;
let (fields, methods) = self.parse_interface_body(&type_params, start_span)?;
let end_span =
self.consume_token(TokenType::CloseBrace, "Expected '}' after interface body")?;
let full_span = start_span.combine(&end_span);
Ok(AstNode::Interface {
name,
type_params,
fields,
methods,
span: full_span,
})
}
fn parse_interface_type_params(&mut self) -> ParserResult<Vec<(String, Vec<TraitBound>)>> {
if !self.matches(&[TokenType::Lt]) {
return Ok(Vec::new());
}
let mut params = Vec::new();
if !self.check(TokenType::Gt) {
loop {
let param = self.consume_identifier("Expected type parameter name")?;
let bounds = self.parse_colon_trait_bounds()?;
params.push((param, bounds));
if !self.matches(&[TokenType::Comma]) {
break;
}
}
}
self.consume_token(TokenType::Gt, "Expected '>' after type parameters")?;
Ok(params)
}
fn parse_colon_trait_bounds(&mut self) -> ParserResult<Vec<TraitBound>> {
if !self.matches(&[TokenType::Colon]) {
return Ok(Vec::new());
}
let mut bounds = Vec::new();
loop {
let bound_name = self.consume_identifier("Expected trait name in bound")?;
let type_args = self.parse_optional_type_args()?;
bounds.push(TraitBound {
name: bound_name,
type_params: type_args,
});
if !self.matches(&[TokenType::Plus]) {
break;
}
}
Ok(bounds)
}
fn parse_interface_body(
&mut self,
type_params: &[(String, Vec<TraitBound>)],
start_span: Span,
) -> ParserResult<(Vec<Field>, Vec<FunctionNode>)> {
let mut fields = Vec::new();
let mut methods = Vec::new();
while !self.is_at_end() {
self.skip_newlines();
if self.check(TokenType::CloseBrace) {
break;
}
self.parse_interface_member(type_params, start_span, &mut fields, &mut methods)?;
}
Ok((fields, methods))
}
fn parse_interface_member(
&mut self,
type_params: &[(String, Vec<TraitBound>)],
start_span: Span,
fields: &mut Vec<Field>,
methods: &mut Vec<FunctionNode>,
) -> ParserResult<()> {
match self.peek().token_type {
TokenType::Func => {
let method = self.parse_interface_method(start_span)?;
methods.push(method);
}
TokenType::Id(_) | TokenType::Const => {
let field = self.parse_field_declaration(type_params)?;
fields.push(field);
}
TokenType::NewLine => {
self.consume_token(TokenType::NewLine, "Expected newline")?;
}
_ => {
return Err(ParserError::new(
"Expected field or function declaration in interface",
self.peek().span,
));
}
}
Ok(())
}
fn parse_interface_method(&mut self, start_span: Span) -> ParserResult<FunctionNode> {
self.consume();
let name = self.consume_identifier("Expected method name")?;
let type_params = self.parse_simple_type_params()?;
self.consume_token(TokenType::OpenParen, "Expected '(' after method name")?;
let params = self.parse_param_list()?;
self.consume_token(TokenType::CloseParen, "Expected ')' after parameters")?;
let return_type = self.parse_optional_return_type()?;
Ok(FunctionNode {
name,
type_params,
params,
return_type,
body: vec![],
span: start_span,
is_common: false,
})
}
fn parse_simple_type_params(&mut self) -> ParserResult<Vec<(String, Vec<TraitBound>)>> {
if !self.matches(&[TokenType::Lt]) {
return Ok(Vec::new());
}
let mut params = Vec::new();
if !self.check(TokenType::Gt) {
loop {
let param = self.consume_identifier("Expected type parameter name")?;
params.push((param, Vec::new()));
if !self.matches(&[TokenType::Comma]) {
break;
}
}
}
self.consume_token(TokenType::Gt, "Expected '>' after type parameters")?;
Ok(params)
}
fn parse_param_list(&mut self) -> ParserResult<Vec<Param>> {
let mut params = Vec::new();
if !self.check(TokenType::CloseParen) {
loop {
let param_type = self.parse_type()?;
let param_name = self.consume_identifier("Expected parameter name")?;
params.push(Param {
name: param_name,
type_: param_type,
default_value: None,
});
if !self.matches(&[TokenType::Comma]) {
break;
}
}
}
Ok(params)
}
fn parse_optional_return_type(&mut self) -> ParserResult<TypeNode> {
if self.matches(&[TokenType::Minus, TokenType::Gt]) || self.matches(&[TokenType::Returns]) {
self.parse_type()
} else {
Ok(TypeNode {
kind: TypeKind::Primitive(PrimitiveType::Void),
span: self.peek().span,
})
}
}
fn enum_declaration(&mut self) -> ParserResult<AstNode> {
let start_span = self.tokens[self.current].span;
self.consume_token(TokenType::Enum, "Expected 'enum' keyword")?;
let name = self.consume_identifier("Expected enum name")?;
let type_params = self.parse_colon_type_params()?;
self.consume_token(TokenType::OpenBrace, "Expected '{' after enum header")?;
let variants = self.parse_enum_variants()?;
let end_span =
self.consume_token(TokenType::CloseBrace, "Expected '}' after enum variants")?;
let full_span = start_span.combine(&end_span);
Ok(AstNode::Enum {
name,
type_params,
variants,
span: full_span,
})
}
fn parse_colon_type_params(&mut self) -> ParserResult<Vec<(String, Vec<TraitBound>)>> {
if !self.matches(&[TokenType::Lt]) {
return Ok(Vec::new());
}
let mut params = Vec::new();
if !self.check(TokenType::Gt) {
loop {
let param = self.consume_identifier("Expected type parameter name")?;
let bounds = self.parse_colon_trait_bounds()?;
params.push((param, bounds));
if !self.matches(&[TokenType::Comma]) {
break;
}
}
}
self.consume_token(TokenType::Gt, "Expected '>' after type parameters")?;
Ok(params)
}
fn parse_enum_variants(&mut self) -> ParserResult<Vec<EnumVariant>> {
let mut variants = Vec::new();
self.skip_newlines();
while !self.check(TokenType::CloseBrace) && !self.is_at_end() {
let variant = self.parse_single_enum_variant()?;
variants.push(variant);
if !self.matches(&[TokenType::Comma]) {
self.skip_newlines();
break;
}
self.skip_newlines();
if self.check(TokenType::CloseBrace) {
break;
}
}
Ok(variants)
}
fn parse_single_enum_variant(&mut self) -> ParserResult<EnumVariant> {
let variant_name = self.consume_identifier("Expected variant name")?;
let data = self.parse_enum_variant_data()?;
Ok(EnumVariant {
name: variant_name,
data,
})
}
fn parse_enum_variant_data(&mut self) -> ParserResult<Option<Vec<EnumVariantField>>> {
if !self.matches(&[TokenType::OpenParen]) {
return Ok(None);
}
let mut fields = Vec::new();
if !self.check(TokenType::CloseParen) {
loop {
let field_type = self.parse_type()?;
let field_name = if let TokenType::Id(name) = self.peek().token_type.clone() {
self.advance();
Some(name)
} else {
None
};
fields.push((field_name, field_type));
if !self.matches(&[TokenType::Comma]) {
break;
}
}
}
self.consume_token(TokenType::CloseParen, "Expected ')' after variant data")?;
Ok(Some(fields))
}
fn import_declaration(&mut self) -> ParserResult<AstNode> {
let start_span = self.consume_token(TokenType::Import, "Expected 'import' keyword")?;
let module_path = self.parse_module_path()?;
let spec = self.parse_import_spec(&module_path)?;
let end_span = self.previous().span;
Ok(AstNode::Statement(StatementNode {
kind: StatementKind::Import { module_path, spec },
span: start_span.combine(&end_span),
}))
}
fn parse_import_spec(&mut self, module_path: &str) -> ParserResult<ImportSpec> {
if self.matches(&[TokenType::Dot]) {
self.parse_dot_import_spec()
} else {
self.parse_module_import_spec(module_path)
}
}
fn parse_dot_import_spec(&mut self) -> ParserResult<ImportSpec> {
if self.matches(&[TokenType::Star]) {
Ok(ImportSpec::Wildcard)
} else if self.matches(&[TokenType::OpenParen]) {
self.parse_import_items()
} else {
let item = self.consume_identifier("Expected item name after '.'")?;
let alias = if self.matches(&[TokenType::As]) {
Some(self.consume_identifier("Expected alias after 'as'")?)
} else {
None
};
Ok(ImportSpec::Item { item, alias })
}
}
fn parse_module_import_spec(&mut self, module_path: &str) -> ParserResult<ImportSpec> {
let alias = if self.matches(&[TokenType::As]) {
let alias_name = self.consume_identifier("Expected alias after 'as'")?;
if alias_name == "_" {
None
} else {
Some(alias_name)
}
} else {
Some(
module_path
.split('.')
.next_back()
.unwrap_or(module_path)
.to_string(),
)
};
Ok(ImportSpec::Module { alias })
}
fn parse_module_path(&mut self) -> ParserResult<String> {
let mut parts = Vec::new();
if self.matches(&[TokenType::Dot]) {
parts.push(".".to_string());
if self.matches(&[TokenType::Slash]) {
parts.push("".to_string()); } else if self.matches(&[TokenType::Dot]) {
parts.push(".".to_string());
self.consume_token(TokenType::Slash, "Expected '/' after '..'")?;
parts.push("".to_string());
}
} else if self.matches(&[TokenType::Slash]) {
parts.push("".to_string()); }
parts.push(self.consume_identifier("Expected module path")?);
while self.check(TokenType::Dot) {
let next = self.peek_ahead(1);
if next.is_some_and(|t| matches!(t.token_type, TokenType::Star | TokenType::OpenParen))
{
break;
}
if next.is_some_and(|t| matches!(t.token_type, TokenType::Id(_))) {
let after_identifier = self.peek_ahead(2);
if !after_identifier.is_some_and(|t| matches!(t.token_type, TokenType::Dot)) {
break;
}
}
self.advance(); parts.push(self.consume_identifier("Expected module name after '.'")?);
}
Ok(parts.join("."))
}
fn parse_import_items(&mut self) -> ParserResult<ImportSpec> {
let mut items = Vec::new();
if !self.check(TokenType::CloseParen) {
loop {
let item = self.consume_identifier("Expected item name")?;
let alias = if self.matches(&[TokenType::As]) {
Some(self.consume_identifier("Expected alias after 'as'")?)
} else {
None
};
items.push((item, alias));
if !self.matches(&[TokenType::Comma]) {
break;
}
self.skip_newlines();
}
}
self.consume_token(TokenType::CloseParen, "Expected ')' after import items")?;
Ok(ImportSpec::Items { items })
}
fn peek_ahead(&self, n: usize) -> Option<&Token> {
self.tokens.get(self.current + n).copied()
}
fn function_declaration(&mut self, is_common: bool) -> ParserResult<AstNode> {
let start_span = self.peek().span;
self.consume_token(TokenType::Func, "Expected 'func' keyword")?;
let name = self.consume_identifier("Expected function name")?;
let type_params = self.parse_is_type_params()?;
self.consume_token(TokenType::OpenParen, "Expected '(' after function name")?;
let params = self.parse_function_params()?;
self.consume_token(TokenType::CloseParen, "Expected ')' after parameters")?;
let return_type = self.parse_required_return_type()?;
self.skip_newlines();
let body_statements = self.parse_function_body(start_span)?;
let end_span = body_statements.last().map(|s| s.span).unwrap_or(start_span);
let span = start_span.combine(&end_span);
Ok(AstNode::Function(FunctionNode {
name,
type_params,
params,
return_type,
body: body_statements,
span,
is_common,
}))
}
fn parse_is_type_params(&mut self) -> ParserResult<Vec<(String, Vec<TraitBound>)>> {
if !self.matches(&[TokenType::Lt]) {
return Ok(Vec::new());
}
let mut params = Vec::new();
if !self.check(TokenType::Gt) {
loop {
let param = self.consume_identifier("Expected type parameter name")?;
let bounds = self.parse_ref_trait_bounds()?;
params.push((param, bounds));
if !self.matches(&[TokenType::Comma]) {
break;
}
}
}
self.consume_token(TokenType::Gt, "Expected '>' after type parameters")?;
Ok(params)
}
fn parse_ref_trait_bounds(&mut self) -> ParserResult<Vec<TraitBound>> {
if !self.matches(&[TokenType::Is]) {
return Ok(Vec::new());
}
let mut bounds = Vec::new();
loop {
let bound_name = self.consume_identifier("Expected trait name in bound")?;
let type_args = self.parse_optional_type_args()?;
bounds.push(TraitBound {
name: bound_name,
type_params: type_args,
});
if !self.matches(&[TokenType::Ref]) {
break;
}
}
Ok(bounds)
}
fn parse_function_params(&mut self) -> ParserResult<Vec<Param>> {
let mut params = Vec::new();
if !self.check(TokenType::CloseParen) {
let mut has_default = false;
loop {
let param = self.parse_single_param(&mut has_default)?;
params.push(param);
if !self.matches(&[TokenType::Comma]) {
break;
}
}
}
Ok(params)
}
fn parse_single_param(&mut self, has_default: &mut bool) -> ParserResult<Param> {
let param_type = self.parse_type()?;
let param_name = self.consume_identifier("Expected parameter name")?;
let default_value = self.parse_param_default(has_default)?;
Ok(Param {
name: param_name,
type_: param_type,
default_value,
})
}
fn parse_param_default(
&mut self,
has_default: &mut bool,
) -> ParserResult<Option<ExpressionNode>> {
if !self.matches(&[TokenType::Eq]) {
if *has_default {
return Err(ParserError::with_help(
"Required parameter cannot follow a parameter with a default value",
self.previous().span,
"Move all parameters with default values to the end of the parameter list",
));
}
return Ok(None);
}
let default_expr = self.parse_expression()?;
if !Self::is_literal_expression(&default_expr) {
return Err(ParserError::with_help(
"Default parameter values must be literals",
default_expr.span,
"Only literal values (int, float, string, bool, char) are allowed as default parameter values. Example: func foo(int x = 10) returns void { ... }",
));
}
*has_default = true;
Ok(Some(default_expr))
}
fn parse_required_return_type(&mut self) -> ParserResult<TypeNode> {
if self.matches(&[TokenType::Returns]) {
self.parse_type()
} else {
Err(ParserError::with_help(
format!(
"Expected 'returns' before return type, found {}",
Self::describe_token(&self.peek().token_type)
),
self.peek().span,
"All functions must declare a return type. Use 'returns void' for functions that return nothing. Example: func foo() returns int { ... }",
))
}
}
fn parse_function_body(&mut self, start_span: Span) -> ParserResult<Vec<StatementNode>> {
let body = self.block()?;
match body {
AstNode::Statement(StatementNode {
kind: StatementKind::Block(block),
..
}) => Ok(block),
_ => Err(ParserError::new(
"Expected block statement for function body",
start_span,
)),
}
}
fn statement(&mut self) -> ParserResult<AstNode> {
let result = if self.matches(&[TokenType::If]) {
self.if_statement()
} else if self.matches(&[TokenType::While]) {
self.while_statement()
} else if self.matches(&[TokenType::For]) {
self.for_statement()
} else if self.matches(&[TokenType::Match]) {
self.match_statement()
} else if self.matches(&[TokenType::Break]) {
self.break_statement()
} else if self.matches(&[TokenType::Continue]) {
self.continue_statement()
} else if self.matches(&[TokenType::Return]) {
self.return_statement()
} else if self.matches(&[TokenType::Func]) {
self.function_declaration(false)
} else if self.looks_like_typed_decl() {
self.typed_declaration()
} else if self.check(TokenType::OpenBrace) {
self.block()
} else {
self.expression_statement()
}?;
if !self.is_in_block()
&& !matches!(
&result,
AstNode::Statement(stmt) if matches!(
stmt.kind,
StatementKind::If { .. } | StatementKind::While { .. } | StatementKind::For { .. } | StatementKind::Match { .. } | StatementKind::Function { .. }
)
)
&& let Err(e) = self.check_statement_termination()
{
self.errors.push(e);
}
Ok(result)
}
fn looks_like_typed_decl(&self) -> bool {
let n = self.tokens.len();
let i = self.current;
if let Some(j) = self.skip_type(i)
&& j < n
&& let TokenType::Id(_) = self.tokens[j].token_type
&& j + 1 < n
&& self.tokens[j + 1].token_type == TokenType::Eq
{
return true;
}
false
}
fn skip_type(&self, mut i: usize) -> Option<usize> {
let n = self.tokens.len();
if i >= n {
return None;
}
match self.tokens[i].token_type {
TokenType::Ref => {
i += 1;
self.skip_type(i)
}
TokenType::OpenParen => self.skip_function_type(i, n),
TokenType::Id(_) => self.skip_identifier_type(i, n),
_ => None,
}
}
fn skip_function_type(&self, mut i: usize, n: usize) -> Option<usize> {
i += 1;
let mut depth = 1usize;
while i < n && depth > 0 {
match self.tokens[i].token_type {
TokenType::OpenParen => depth += 1,
TokenType::CloseParen => depth -= 1,
_ => {}
}
i += 1;
}
if depth != 0 {
return None;
}
if i >= n || self.tokens[i].token_type != TokenType::Returns {
return None;
}
i += 1;
self.skip_type(i)
}
fn skip_identifier_type(&self, mut i: usize, n: usize) -> Option<usize> {
i += 1;
if i < n && self.tokens[i].token_type == TokenType::Lt {
i += 1;
let mut depth = 1usize;
while i < n && depth > 0 {
match self.tokens[i].token_type {
TokenType::Lt => depth += 1,
TokenType::Gt => depth -= 1,
_ => {}
}
i += 1;
}
if depth != 0 {
return None;
}
}
Some(i)
}
fn if_statement(&mut self) -> ParserResult<AstNode> {
let start_span = self.tokens[self.current - 1].span;
let condition = self.parse_expression()?;
self.check_no_postfix_increment_decrement(&condition)?;
self.skip_newlines();
let then_block = match self.block()? {
AstNode::Statement(StatementNode {
kind: StatementKind::Block(block),
..
}) => block,
_ => {
return Err(ParserError::new(
"Expected block after if condition",
self.peek().span,
));
}
};
self.skip_newlines();
let (else_block, end_span) = self.parse_else_branch(start_span, &then_block)?;
let span = start_span.combine(&end_span);
Ok(AstNode::Statement(StatementNode {
kind: StatementKind::If {
cond: condition,
then_block,
else_block,
},
span,
}))
}
fn parse_else_branch(
&mut self,
start_span: Span,
then_block: &[StatementNode],
) -> ParserResult<(Option<Vec<StatementNode>>, Span)> {
if !self.matches(&[TokenType::Else]) {
let end_span = then_block.last().map(|s| s.span).unwrap_or(start_span);
return Ok((None, end_span));
}
self.skip_newlines();
if self.matches(&[TokenType::If]) {
self.parse_else_if_branch()
} else {
self.parse_else_block()
}
}
fn parse_else_if_branch(&mut self) -> ParserResult<(Option<Vec<StatementNode>>, Span)> {
let nested = self.if_statement()?;
match nested {
AstNode::Statement(stmt) => {
let end_span = stmt.span;
Ok((Some(vec![stmt]), end_span))
}
_ => Err(ParserError::new(
"Expected statement after else if",
self.previous().span,
)),
}
}
fn parse_else_block(&mut self) -> ParserResult<(Option<Vec<StatementNode>>, Span)> {
if !self.check(TokenType::OpenBrace) {
return Err(ParserError::new(
"Expected '{' after else",
self.peek().span,
));
}
let else_block = match self.block()? {
AstNode::Statement(StatementNode {
kind: StatementKind::Block(block),
..
}) => block,
_ => {
return Err(ParserError::new(
"Expected block after else",
self.peek().span,
));
}
};
let end_span = else_block
.last()
.map(|s| s.span)
.unwrap_or_else(|| self.tokens[self.current - 1].span);
Ok((Some(else_block), end_span))
}
fn while_statement(&mut self) -> ParserResult<AstNode> {
let start_span = self.tokens[self.current].span;
let condition = self.parse_expression()?;
self.check_no_postfix_increment_decrement(&condition)?;
self.skip_newlines();
self.loop_depth += 1;
let body = self.block()?;
self.loop_depth -= 1;
let body_statements = match body {
AstNode::Statement(stmt) => match stmt.kind {
StatementKind::Block(block) => block,
_ => vec![stmt],
},
_ => {
return Err(ParserError::new(
"Expected statement after while condition",
start_span,
));
}
};
let end_span = body_statements.last().map(|s| s.span).unwrap_or(start_span);
let span = start_span.combine(&end_span);
Ok(AstNode::Statement(StatementNode {
kind: StatementKind::While {
cond: condition,
body: body_statements,
},
span,
}))
}
fn for_statement(&mut self) -> ParserResult<AstNode> {
let start_span = self.tokens[self.current].span;
let var_type = self.parse_type()?;
let var = self.consume_identifier("Expected variable name")?;
self.consume_token(TokenType::In, "Expected 'in' after variable")?;
let iter = self.parse_expression()?;
self.check_no_postfix_increment_decrement(&iter)?;
self.skip_newlines();
self.loop_depth += 1;
let body = if self.check(TokenType::OpenBrace) {
self.block()?
} else {
self.statement()?
};
self.loop_depth -= 1;
let body_statements = match body {
AstNode::Statement(stmt) => match stmt.kind {
StatementKind::Block(block) => block,
_ => vec![stmt],
},
_ => {
return Err(ParserError::new(
"Expected statement after for loop",
start_span,
));
}
};
let end_span = body_statements.last().map(|s| s.span).unwrap_or(start_span);
let span = start_span.combine(&end_span);
Ok(AstNode::Statement(StatementNode {
kind: StatementKind::For {
var,
var_type,
iter,
body: body_statements,
},
span,
}))
}
fn match_statement(&mut self) -> ParserResult<AstNode> {
let start_span = self.tokens[self.current].span;
let expr = self.parse_expression()?;
self.check_no_postfix_increment_decrement(&expr)?;
self.consume_token(TokenType::OpenBrace, "Expected '{' after match expression")?;
self.skip_newlines();
let mut arms = Vec::new();
while !self.check(TokenType::CloseBrace) && !self.is_at_end() {
arms.push(self.parse_match_arm(start_span)?);
self.skip_newlines();
if self.matches(&[TokenType::Comma]) {
self.skip_newlines();
if self.check(TokenType::CloseBrace) {
break;
}
}
}
let end_span =
self.consume_token(TokenType::CloseBrace, "Expected '}' after match arms")?;
Ok(AstNode::Statement(StatementNode {
kind: StatementKind::Match { expr, arms },
span: start_span.combine(&end_span),
}))
}
fn parse_match_arm(&mut self, start_span: Span) -> ParserResult<MatchArm> {
let pattern = self.parse_pattern()?;
let guard = if self.matches(&[TokenType::If]) {
Some(self.parse_expression()?)
} else {
None
};
self.skip_newlines();
let body = self.parse_match_arm_body(start_span)?;
Ok(MatchArm {
pattern,
guard,
body,
})
}
fn parse_match_arm_body(&mut self, start_span: Span) -> ParserResult<Vec<StatementNode>> {
let node = if self.check(TokenType::OpenBrace) {
self.block()?
} else if self.matches(&[TokenType::Colon]) {
self.skip_newlines();
if self.check(TokenType::OpenBrace) {
self.block()?
} else {
self.statement()?
}
} else {
self.statement()?
};
match node {
AstNode::Statement(stmt) => match stmt.kind {
StatementKind::Block(block) => Ok(block),
_ => Ok(vec![stmt]),
},
_ => Err(ParserError::new(
"Expected statement for match arm body",
start_span,
)),
}
}
fn parse_pattern(&mut self) -> ParserResult<PatternNode> {
match &self.peek().token_type {
TokenType::None => {
self.current += 1; Ok(PatternNode::EnumVariant {
name: "none".to_string(),
args: vec![],
})
}
TokenType::Id(name) => {
let name_clone = name.clone();
self.current += 1; self.parse_pattern_identifier_or_variant(name_clone)
}
TokenType::Underscore => {
self.current += 1; Ok(PatternNode::Wildcard)
}
TokenType::OpenBracket => self.parse_list_pattern(),
_ => self.parse_literal_pattern(),
}
}
fn return_statement(&mut self) -> ParserResult<AstNode> {
let start_span = self.tokens[self.current].span;
let value = if self.is_at_end()
|| self.check(TokenType::NewLine)
|| self.check(TokenType::CloseBrace)
{
None
} else {
let expr = self.parse_expression()?;
self.check_no_postfix_increment_decrement(&expr)?;
Some(expr)
};
let end_span = value.as_ref().map_or(start_span, |v| v.span);
Ok(AstNode::Statement(StatementNode {
kind: StatementKind::Return(value),
span: start_span.combine(&end_span),
}))
}
fn break_statement(&mut self) -> ParserResult<AstNode> {
let start_span = self.tokens[self.current].span;
if self.loop_depth == 0 {
return Err(ParserError::with_help(
"Cannot use 'break' outside of a loop",
start_span,
"'break' can only be used inside a 'for' or 'while' loop",
));
}
Ok(AstNode::Statement(StatementNode {
kind: StatementKind::Break,
span: start_span,
}))
}
fn continue_statement(&mut self) -> ParserResult<AstNode> {
let start_span = self.tokens[self.current].span;
if self.loop_depth == 0 {
return Err(ParserError::with_help(
"Cannot use 'continue' outside of a loop",
start_span,
"'continue' can only be used inside a 'for' or 'while' loop",
));
}
Ok(AstNode::Statement(StatementNode {
kind: StatementKind::Continue,
span: start_span,
}))
}
fn skip_newlines(&mut self) -> usize {
let mut count = 0;
while self.matches(&[TokenType::NewLine]) {
count += 1;
}
count
}
fn block(&mut self) -> ParserResult<AstNode> {
let start_span = self.consume_token(TokenType::OpenBrace, "Expected '{' before block")?;
self.skip_newlines();
if self.matches(&[TokenType::CloseBrace]) {
return Ok(AstNode::Statement(StatementNode {
kind: StatementKind::Block(Vec::new()),
span: start_span.combine(&self.previous().span),
}));
}
let statements = self.parse_block_statements_loop()?;
let end_span = if self.check(TokenType::CloseBrace) {
self.consume_token(TokenType::CloseBrace, "Expected '}' after block")?
} else {
return Err(ParserError::new(
"Expected '}' after block".to_string(),
self.peek().span,
));
};
let stmts: Vec<StatementNode> = statements
.into_iter()
.filter_map(|node| node.into_statement())
.collect();
Ok(AstNode::Statement(StatementNode {
kind: StatementKind::Block(stmts),
span: start_span.combine(&end_span),
}))
}
fn parse_block_statements_loop(&mut self) -> ParserResult<Vec<AstNode>> {
let mut statements = Vec::new();
while !self.check(TokenType::CloseBrace) && !self.is_at_end() {
self.skip_newlines();
if self.check(TokenType::CloseBrace) {
break;
}
self.parse_block_statement(&mut statements)?;
}
Ok(statements)
}
fn parse_block_statement(&mut self, statements: &mut Vec<AstNode>) -> ParserResult<()> {
match self.declaration() {
Ok(Some(decl)) => {
statements.push(decl);
self.skip_newlines();
}
Ok(None) => {
let current_pos = self.current;
if current_pos == self.current {
self.advance();
}
}
Err(e) => {
self.errors.push(e);
self.synchronize();
let current_pos = self.current;
if self.current == current_pos && !self.is_at_end() {
self.advance();
}
}
}
Ok(())
}
fn is_in_block(&self) -> bool {
let mut brace_count: usize = 0;
for i in (0..self.current).rev() {
match self.tokens[i].token_type {
TokenType::CloseBrace => brace_count += 1,
TokenType::OpenBrace => {
if brace_count == 0 {
return true;
}
brace_count = brace_count.saturating_sub(1);
}
_ => {}
}
}
false
}
fn expression_statement(&mut self) -> ParserResult<AstNode> {
let expr = self.parse_expression()?;
let has_newline = self.check(TokenType::NewLine);
if !self.is_in_block() && !has_newline && self.current < self.tokens.len() {
let next_token = &self.tokens[self.current];
if self.is_statement_starter(&next_token.token_type) {
return Err(ParserError::new(
"Expected newline before statement".to_string(),
next_token.span,
));
}
}
let _ = self.skip_newlines();
let span = *expr.span();
self.validate_postfix_in_statement(&expr)?;
Ok(AstNode::Statement(StatementNode {
kind: StatementKind::Expression(expr),
span,
}))
}
fn validate_postfix_in_statement(&self, expr: &ExpressionNode) -> ParserResult<()> {
if let ExpressionKind::Unary { op, postfix, .. } = &expr.kind
&& *postfix
&& matches!(op, UnaryOp::Incr | UnaryOp::Decr)
{
return Ok(());
}
self.check_no_postfix_increment_decrement(expr)
}
#[allow(clippy::only_used_in_recursion)]
fn check_no_postfix_increment_decrement(&self, expr: &ExpressionNode) -> ParserResult<()> {
self.check_no_postfix_increment_decrement_kind(&expr.kind, expr.span)
}
fn check_no_postfix_increment_decrement_kind(
&self,
kind: &ExpressionKind,
span: Span,
) -> ParserResult<()> {
match kind {
ExpressionKind::Unary {
op,
expr: inner,
postfix,
..
} => self.check_unary_postfix_nesting(op, *postfix, inner, span),
ExpressionKind::Binary { left, right, .. } => {
self.check_no_postfix_increment_decrement(left)?;
self.check_no_postfix_increment_decrement(right)
}
ExpressionKind::Call { func, args } => {
self.check_no_postfix_increment_decrement(func)?;
self.check_no_postfix_increment_decrement_all(args)
}
ExpressionKind::FieldAccess { expr: inner, .. } => {
self.check_no_postfix_increment_decrement(inner)
}
ExpressionKind::ListAccess { expr: inner, index } => {
self.check_no_postfix_increment_decrement(inner)?;
self.check_no_postfix_increment_decrement(index)
}
ExpressionKind::ListLiteral(elems) | ExpressionKind::SetLiteral(elems) => {
self.check_no_postfix_increment_decrement_all(elems)
}
ExpressionKind::MapLiteral { entries, .. } => {
self.check_no_postfix_increment_decrement_map_entries(entries)
}
ExpressionKind::If {
cond,
then_expr,
else_expr,
} => {
self.check_no_postfix_increment_decrement(cond)?;
self.check_no_postfix_increment_decrement(then_expr)?;
self.check_no_postfix_increment_decrement(else_expr)
}
ExpressionKind::Lambda { body, .. } => {
self.check_no_postfix_increment_decrement_lambda_body(body)
}
_ => Ok(()),
}
}
fn check_unary_postfix_nesting(
&self,
op: &UnaryOp,
postfix: bool,
inner: &ExpressionNode,
span: Span,
) -> ParserResult<()> {
if postfix && matches!(op, UnaryOp::Incr | UnaryOp::Decr) {
return Err(ParserError::with_help(
"Increment/Decrement operator can only be used as a standalone statement",
span,
"Expressions like 'x + y++' are not supported. Use 'y++' as a separate statement before the expression.",
));
}
self.check_no_postfix_increment_decrement(inner)
}
fn check_no_postfix_increment_decrement_all(
&self,
expressions: &[ExpressionNode],
) -> ParserResult<()> {
for expression in expressions {
self.check_no_postfix_increment_decrement(expression)?;
}
Ok(())
}
fn check_no_postfix_increment_decrement_map_entries(
&self,
entries: &[(ExpressionNode, ExpressionNode)],
) -> ParserResult<()> {
for (key, value) in entries {
self.check_no_postfix_increment_decrement(key)?;
self.check_no_postfix_increment_decrement(value)?;
}
Ok(())
}
fn check_no_postfix_increment_decrement_lambda_body(
&self,
body: &[StatementNode],
) -> ParserResult<()> {
for stmt in body {
if let StatementKind::Expression(expr) = &stmt.kind {
self.check_no_postfix_increment_decrement(expr)?;
}
}
Ok(())
}
fn parse_named_type_with_builtin_support(
&mut self,
name: String,
start_span: Span,
) -> ParserResult<TypeNode> {
if let Ok(prim_type) =
PrimitiveType::parse(Token::new(TokenType::Id(name.clone()), start_span))
{
return Ok(TypeNode {
kind: TypeKind::Primitive(prim_type),
span: start_span,
});
}
if let Some(node) = self.parse_container_type(&name, start_span)? {
return Ok(node);
}
let type_args = if self.matches(&[TokenType::Lt]) {
let args = self.parse_type_arguments()?;
self.consume_token(TokenType::Gt, "Expected '>' after type arguments")?;
args
} else {
Vec::new()
};
if name == "dyn" && !type_args.is_empty() {
return Ok(TypeNode {
kind: TypeKind::TraitObject(Box::new(type_args[0].clone())),
span: start_span,
});
}
Ok(TypeNode {
kind: TypeKind::Named(name, type_args),
span: start_span,
})
}
fn parse_container_type(
&mut self,
name: &str,
start_span: Span,
) -> ParserResult<Option<TypeNode>> {
if !matches!(name, "list" | "map" | "set" | "tuple") {
return Ok(None);
}
if !self.matches(&[TokenType::Lt]) {
return Ok(None);
}
let node = match name {
"list" => {
let element_type = self.parse_type()?;
self.consume_token(TokenType::Gt, "Expected '>' after list element type")?;
TypeNode {
kind: TypeKind::List(Box::new(element_type)),
span: start_span,
}
}
"map" => {
let key_type = self.parse_type()?;
self.consume_token(
TokenType::Comma,
"Expected ',' between key and value types in map",
)?;
let value_type = self.parse_type()?;
self.consume_token(TokenType::Gt, "Expected '>' after map value type")?;
TypeNode {
kind: TypeKind::Map(Box::new(key_type), Box::new(value_type)),
span: start_span,
}
}
"set" => {
let element_type = self.parse_type()?;
self.consume_token(TokenType::Gt, "Expected '>' after set element type")?;
TypeNode {
kind: TypeKind::Set(Box::new(element_type)),
span: start_span,
}
}
"tuple" => {
let left_type = self.parse_type()?;
self.consume_token(TokenType::Comma, "Expected ',' in tuple type")?;
let right_type = self.parse_type()?;
self.consume_token(TokenType::Gt, "Expected '>' after tuple type")?;
TypeNode {
kind: TypeKind::Tuple(Box::new(left_type), Box::new(right_type)),
span: start_span,
}
}
_ => unreachable!("guarded by matches!"),
};
Ok(Some(node))
}
fn parse_pattern_identifier_or_variant(&mut self, name: String) -> ParserResult<PatternNode> {
if self.matches(&[TokenType::OpenParen]) {
let mut args = Vec::new();
if !self.check(TokenType::CloseParen) {
loop {
args.push(self.parse_pattern()?);
if !self.matches(&[TokenType::Comma]) {
break;
}
self.skip_newlines();
}
}
self.consume_token(
TokenType::CloseParen,
"Expected ')' after enum variant arguments",
)?;
return Ok(PatternNode::EnumVariant { name, args });
}
Ok(PatternNode::Identifier(name))
}
fn parse_list_pattern(&mut self) -> ParserResult<PatternNode> {
self.current += 1; self.skip_newlines();
let mut elements = Vec::new();
let mut rest = None;
if !self.check(TokenType::CloseBracket) {
loop {
self.skip_newlines();
if self.check(TokenType::DotDot) {
self.current += 1; rest = Some(Box::new(self.parse_pattern()?));
self.skip_newlines();
break;
}
elements.push(self.parse_pattern()?);
self.skip_newlines();
if !self.matches(&[TokenType::Comma]) {
break;
}
self.skip_newlines();
}
}
self.consume_token(TokenType::CloseBracket, "Expected ']' after list pattern")?;
Ok(PatternNode::List { elements, rest })
}
fn parse_literal_pattern(&mut self) -> ParserResult<PatternNode> {
let token = self.consume();
match &token.token_type {
TokenType::Int(n) => Ok(PatternNode::Literal(LiteralNode::Integer(*n))),
TokenType::Float(f) => Ok(PatternNode::Literal(LiteralNode::Float(*f))),
TokenType::Bool(b) => Ok(PatternNode::Literal(LiteralNode::Boolean(*b))),
TokenType::Char(c) => Ok(PatternNode::Literal(LiteralNode::Char(*c))),
TokenType::Str(s) => Ok(PatternNode::Literal(LiteralNode::String(s.clone()))),
_ => Err(ParserError::from_token("Expected pattern", token)),
}
}
fn parse_type(&mut self) -> ParserResult<TypeNode> {
if self.matches(&[TokenType::Ref]) {
let start_span = self.previous().span;
let referenced_type = self.parse_type()?;
return Ok(TypeNode {
kind: TypeKind::Reference(Box::new(referenced_type)),
span: Span {
row_start: start_span.row_start,
col_start: start_span.col_start,
row_end: self.previous().span.row_end,
col_end: self.previous().span.col_end,
},
});
}
let token = &self.tokens[self.current];
let start_span = token.span;
self.current += 1;
match token.token_type {
TokenType::Id(ref name) => {
self.parse_named_type_with_builtin_support(name.clone(), start_span)
}
TokenType::Func => {
self.consume_token(
TokenType::OpenParen,
"Expected '(' after 'func' in function type",
)?;
let mut param_types = Vec::new();
if !self.check(TokenType::CloseParen) {
loop {
param_types.push(self.parse_type()?);
if !self.matches(&[TokenType::Comma]) {
break;
}
self.skip_newlines();
}
}
self.consume_token(TokenType::CloseParen, "Expected ')' after parameter types")?;
self.consume_token(TokenType::Returns, "Expected 'returns' in function type")?;
let return_type = Box::new(self.parse_type()?);
Ok(TypeNode {
kind: TypeKind::Function {
params: param_types,
returns: return_type,
},
span: start_span,
})
}
_ => Err(ParserError::from_token("Expected type", token)),
}
}
fn parse_type_arguments(&mut self) -> ParserResult<Vec<TypeNode>> {
let mut args = Vec::new();
while !self.check(TokenType::Gt) && !self.is_at_end() {
let arg = self.parse_type()?;
args.push(arg);
if !self.matches(&[TokenType::Comma]) {
break;
}
self.skip_newlines();
}
Ok(args)
}
fn synchronize(&mut self) {
while !self.is_at_end() {
match self.peek().token_type {
TokenType::NewLine => {
self.current += 1;
return;
}
TokenType::Func
| TokenType::Auto
| TokenType::Const
| TokenType::Class
| TokenType::Interface
| TokenType::Enum
| TokenType::Import => {
return;
}
TokenType::If
| TokenType::Else
| TokenType::While
| TokenType::For
| TokenType::Match
| TokenType::Return => {
return;
}
TokenType::OpenBrace | TokenType::CloseBrace => {
return;
}
TokenType::OpenParen | TokenType::CloseParen => {
return;
}
TokenType::OpenBracket | TokenType::CloseBracket => {
return;
}
_ => {
self.current += 1;
}
}
}
}
pub fn is_at_end(&self) -> bool {
self.current >= self.tokens.len()
}
fn peek(&self) -> &Token {
if self.is_at_end() {
if let Some(last_token) = self.tokens.last() {
last_token
} else {
static EOF_TOKEN: Token = Token {
token_type: TokenType::Eof,
span: Span {
row_start: 1,
row_end: None,
col_start: 1,
col_end: None,
},
};
&EOF_TOKEN
}
} else {
self.tokens[self.current]
}
}
fn consume(&mut self) -> &Token {
let ret = &self.tokens[self.current];
self.current += 1;
ret
}
pub fn parse_expression(&mut self) -> ParserResult<ExpressionNode> {
self.parse_precedence(Precedence::Assignment)
}
fn parse_precedence(&mut self, min_precedence: Precedence) -> ParserResult<ExpressionNode> {
let left = self.parse_unary()?;
let mut value = Box::new(left);
while let Some(op_token) = self.peek_operator() {
let op_precedence = self.get_operator_precedence(&op_token)?;
if op_precedence < min_precedence {
break;
}
let _ = self.consume_operator();
let next_precedence = if op_token.is_right_associative() {
op_precedence
} else {
op_precedence.next_higher()
};
let right = self.parse_precedence(next_precedence)?;
let left_span = *value.span();
let right_span = *right.span();
let left_expr = value.clone();
let op_span = self.previous().span;
let new_value = ExpressionNode {
kind: ExpressionKind::Binary {
left: left_expr,
op: op_token,
op_span,
right: Box::new(right),
},
span: left_span.combine(&right_span),
};
*value = new_value;
}
Ok(*value)
}
fn parse_unary(&mut self) -> ParserResult<ExpressionNode> {
if let Some(op_token) = self.consume_if_unary_operator() {
if matches!(op_token.token_type, TokenType::Incr | TokenType::Decr) {
return Err(ParserError::with_help(
"Increment/Decrement operator can only be used in the postfix position",
op_token.span,
"Place the operator after the variable: 'x++' or 'x--' instead of '++x' or '--x'",
));
}
let expr = self.parse_precedence(Precedence::Unary)?;
let expr_span = *expr.span();
Ok(ExpressionNode {
kind: ExpressionKind::Unary {
op: UnaryOp::parse(op_token.clone())?,
op_span: op_token.span,
expr: Box::new(expr),
postfix: false,
},
span: op_token.span.combine(&expr_span),
})
} else {
let expr = self.parse_primary()?;
self.parse_postfix_operators(expr)
}
}
fn parse_collection_literal(&mut self, start_span: Span) -> ParserResult<ExpressionNode> {
self.skip_newlines();
if self.check(TokenType::CloseBrace) {
return self.parse_empty_collection(start_span);
}
let first_expr = self.parse_expression()?;
if self.matches(&[TokenType::Colon]) {
self.parse_map_literal(start_span, first_expr)
} else {
self.parse_set_literal(start_span, first_expr)
}
}
fn parse_empty_collection(&mut self, start_span: Span) -> ParserResult<ExpressionNode> {
let end_span =
self.consume_token(TokenType::CloseBrace, "Expected '}' after collection")?;
Ok(ExpressionNode {
kind: ExpressionKind::SetLiteral(vec![]),
span: start_span.combine(&end_span),
})
}
fn parse_map_literal(
&mut self,
start_span: Span,
first_key: ExpressionNode,
) -> ParserResult<ExpressionNode> {
let first_value = self.parse_expression()?;
let mut entries = vec![(first_key, first_value)];
self.parse_collection_entries(&mut entries, true)?;
let end_span =
self.consume_token(TokenType::CloseBrace, "Expected '}' after collection")?;
Ok(ExpressionNode {
kind: ExpressionKind::MapLiteral {
key_type: Box::new(TypeNode {
kind: TypeKind::Auto,
span: start_span,
}),
value_type: Box::new(TypeNode {
kind: TypeKind::Auto,
span: start_span,
}),
entries,
},
span: start_span.combine(&end_span),
})
}
fn parse_set_literal(
&mut self,
start_span: Span,
first_elem: ExpressionNode,
) -> ParserResult<ExpressionNode> {
let mut elements = vec![first_elem];
self.parse_set_entries(&mut elements)?;
let end_span =
self.consume_token(TokenType::CloseBrace, "Expected '}' after collection")?;
Ok(ExpressionNode {
kind: ExpressionKind::SetLiteral(elements),
span: start_span.combine(&end_span),
})
}
fn parse_set_entries(&mut self, elements: &mut Vec<ExpressionNode>) -> ParserResult<()> {
loop {
self.skip_newlines();
if !self.has_comma_or_entry()? {
break;
}
let elem = self.parse_expression()?;
elements.push(elem);
}
Ok(())
}
fn parse_collection_entries(
&mut self,
entries: &mut Vec<(ExpressionNode, ExpressionNode)>,
is_map: bool,
) -> ParserResult<()> {
loop {
self.skip_newlines();
if !self.has_comma_or_entry()? {
break;
}
if is_map {
let key = self.parse_expression()?;
self.consume_token(TokenType::Colon, "Expected ':' after map key")?;
let value = self.parse_expression()?;
entries.push((key, value));
}
}
Ok(())
}
fn has_comma_or_entry(&mut self) -> ParserResult<bool> {
if self.matches(&[TokenType::Comma]) {
if self.check(TokenType::CloseBrace) {
return Ok(false);
}
self.skip_newlines();
return Ok(true);
}
self.skip_newlines_after_comma()
}
fn skip_newlines_after_comma(&mut self) -> ParserResult<bool> {
let mut i = 0;
let mut found_newlines = false;
while let Some(t) = self.peek_ahead(i) {
if t.token_type == TokenType::NewLine {
found_newlines = true;
i += 1;
} else {
break;
}
}
if found_newlines
&& self
.peek_ahead(i)
.is_some_and(|t| t.token_type == TokenType::Comma)
&& self.peek_ahead(i + 1).is_some_and(|t| {
!matches!(
t.token_type,
TokenType::CloseBrace | TokenType::NewLine | TokenType::Comma
)
})
{
for _ in 0..=i {
self.current += 1;
}
self.skip_newlines();
Ok(true)
} else {
Ok(false)
}
}
fn parse_parenthesized_or_tuple_expression(
&mut self,
start_span: Span,
) -> ParserResult<ExpressionNode> {
self.skip_newlines();
if self.check(TokenType::CloseParen) {
self.consume_token(TokenType::CloseParen, "Expected ')' after expression")?;
return Err(ParserError::with_help(
"Tuple must have exactly 2 elements, found empty parentheses",
start_span.combine(&self.previous().span),
"Tuples are created with two elements: (value1, value2). Example: auto pair = (1, \"hello\")",
));
}
let first_expr = self.parse_expression()?;
self.skip_newlines();
if self.matches(&[TokenType::Comma]) {
self.skip_newlines();
if self.check(TokenType::CloseParen) {
self.consume_token(TokenType::CloseParen, "Expected ')' after tuple elements")?;
return Err(ParserError::with_help(
"Tuple must have exactly 2 elements, found only 1",
start_span.combine(&self.previous().span),
"Tuples require exactly two elements: (value1, value2). A trailing comma after a single value is not allowed.",
));
}
let second_expr = self.parse_expression()?;
self.skip_newlines();
self.consume_token(TokenType::CloseParen, "Expected ')' after tuple elements")?;
let tuple_expr = ExpressionNode {
kind: ExpressionKind::TupleLiteral(vec![first_expr, second_expr]),
span: start_span.combine(&self.previous().span),
};
return self.parse_postfix_operators(tuple_expr);
}
self.consume_token(TokenType::CloseParen, "Expected ')' after expression")?;
self.parse_postfix_operators(first_expr)
}
fn parse_list_literal_expression(&mut self, start_span: Span) -> ParserResult<ExpressionNode> {
let mut elements = Vec::new();
self.skip_newlines();
if !self.check(TokenType::CloseBracket) {
loop {
elements.push(self.parse_expression()?);
self.skip_newlines();
if self.matches(&[TokenType::Comma]) {
if self.check(TokenType::CloseBracket) {
break;
}
self.skip_newlines();
} else if !self.consume_newline_delimited_comma() {
break;
}
}
}
self.skip_newlines();
let expr = ExpressionNode {
kind: ExpressionKind::ListLiteral(elements),
span: start_span.combine(&self.consume_list_literal_close_span()?),
};
self.parse_postfix_operators(expr)
}
fn consume_list_literal_close_span(&mut self) -> ParserResult<Span> {
self.consume_token(TokenType::CloseBracket, "Expected ']' after list elements")
}
fn consume_newline_delimited_comma(&mut self) -> bool {
let mut i = 0;
let mut found_newlines = false;
while let Some(t) = self.peek_ahead(i) {
if t.token_type == TokenType::NewLine {
found_newlines = true;
i += 1;
} else {
break;
}
}
if !found_newlines {
return false;
}
let comma_after_newlines = self
.peek_ahead(i)
.is_some_and(|t| t.token_type == TokenType::Comma);
let has_expression_after_comma = self.peek_ahead(i + 1).is_some_and(|t| {
!matches!(
t.token_type,
TokenType::CloseBracket | TokenType::NewLine | TokenType::Comma
)
});
if !(comma_after_newlines && has_expression_after_comma) {
return false;
}
for _ in 0..=i {
self.current += 1;
}
self.skip_newlines();
true
}
fn parse_lambda_expression(&mut self, start_span: Span) -> ParserResult<ExpressionNode> {
self.consume_token(TokenType::OpenParen, "Expected '(' after 'func' in lambda")?;
let mut params = Vec::new();
if !self.check(TokenType::CloseParen) {
loop {
let param_type = self.parse_type()?;
let param_name = self.consume_identifier("Expected parameter name")?;
if self.matches(&[TokenType::Eq]) {
return Err(ParserError::with_help(
"Default arguments are not supported in lambda expressions",
self.previous().span,
"Lambda parameters cannot have default values. Define a named function instead if you need default parameters.",
));
}
params.push(Param {
name: param_name,
type_: param_type,
default_value: None,
});
if !self.matches(&[TokenType::Comma]) {
break;
}
self.skip_newlines();
}
}
self.consume_token(TokenType::CloseParen, "Expected ')' after parameters")?;
let return_type = if self.matches(&[TokenType::Returns]) {
self.parse_type()?
} else {
return Err(ParserError::with_help(
format!(
"Expected 'returns' after lambda parameters, found {}",
Self::describe_token(&self.peek().token_type)
),
self.peek().span,
"Lambda expressions require an explicit return type. Example: func(int x) returns int { return x + 1 }",
));
};
let body = self.block()?;
let body_statements = match body {
AstNode::Statement(stmt) => match stmt.kind {
StatementKind::Block(block) => block,
_ => vec![stmt],
},
_ => {
return Err(ParserError::new(
"Expected block statement for lambda body",
start_span,
));
}
};
let end_span = body_statements.last().map(|s| s.span).unwrap_or(start_span);
let expr = ExpressionNode {
kind: ExpressionKind::Lambda {
params,
return_type,
body: body_statements,
},
span: start_span.combine(&end_span),
};
self.parse_postfix_operators(expr)
}
fn parse_if_expression(&mut self, token_span: Span) -> ParserResult<ExpressionNode> {
let cond = self.parse_expression()?;
self.consume_token(TokenType::OpenBrace, "Expected '{' after if condition")?;
let then_expr = self.parse_expression()?;
self.consume_token(TokenType::CloseBrace, "Expected '}' after then expression")?;
self.consume_token(TokenType::Else, "Expected 'else' after then branch")?;
self.consume_token(TokenType::OpenBrace, "Expected '{' after else")?;
let else_expr = self.parse_expression()?;
self.consume_token(TokenType::CloseBrace, "Expected '}' after else expression")?;
let span = token_span.combine(&self.previous().span);
Ok(ExpressionNode {
kind: ExpressionKind::If {
cond: Box::new(cond),
then_expr: Box::new(then_expr),
else_expr: Box::new(else_expr),
},
span,
})
}
fn parse_postfixed_primary(
&mut self,
kind: ExpressionKind,
token_span: Span,
) -> ParserResult<ExpressionNode> {
let expr = ExpressionNode {
kind,
span: token_span,
};
self.parse_postfix_operators(expr)
}
fn unexpected_primary_error(&self, token_type: TokenType, token_span: Span) -> ParserError {
let token_desc = Self::describe_token(&token_type);
if matches!(token_type, TokenType::Match) {
return ParserError::with_help(
"match cannot be used as an expression; it can only be used as a statement",
token_span,
"Use 'match' as a standalone statement with 'return' in each arm, or use an if/else expression for inline conditionals.",
);
}
if matches!(token_type, TokenType::Return) {
return ParserError::with_help(
"Unexpected 'return' statement",
token_span,
"'return' can only be used inside a function body",
);
}
ParserError::from_token(
format!("Expected expression, found {}", token_desc),
&Token {
token_type,
span: token_span,
},
)
}
fn parse_primary(&mut self) -> ParserResult<ExpressionNode> {
if self.is_at_end() {
let error_span = self.tokens.last().map(|t| t.span).unwrap_or_else(|| Span {
row_start: 1,
row_end: None,
col_start: 1,
col_end: None,
});
return Err(ParserError::new(
"Expected expression, found end of input",
error_span,
));
}
let token = self.consume();
let token_type = token.token_type.clone();
let token_span = token.span;
match token_type {
TokenType::OpenParen => self.parse_parenthesized_or_tuple_expression(token_span),
TokenType::Int(n) => self.parse_postfixed_primary(
ExpressionKind::Literal(LiteralNode::Integer(n)),
token_span,
),
TokenType::Bool(b) => self.parse_postfixed_primary(
ExpressionKind::Literal(LiteralNode::Boolean(b)),
token_span,
),
TokenType::None => self.parse_postfixed_primary(ExpressionKind::None, token_span),
TokenType::Char(c) => self
.parse_postfixed_primary(ExpressionKind::Literal(LiteralNode::Char(c)), token_span),
TokenType::Str(s) => self.parse_postfixed_primary(
ExpressionKind::Literal(LiteralNode::String(s)),
token_span,
),
TokenType::Float(f) => self.parse_postfixed_primary(
ExpressionKind::Literal(LiteralNode::Float(f)),
token_span,
),
TokenType::OpenBracket => self.parse_list_literal_expression(token_span),
TokenType::OpenBrace => self.parse_collection_literal(token_span),
TokenType::Func => self.parse_lambda_expression(token_span),
TokenType::If => self.parse_if_expression(token_span),
TokenType::Id(id) => {
self.parse_postfixed_primary(ExpressionKind::Identifier(id.clone()), token_span)
}
_ => Err(self.unexpected_primary_error(token_type, token_span)),
}
}
fn parse_call_postfix(&mut self, expr: ExpressionNode) -> ParserResult<ExpressionNode> {
let mut args = Vec::new();
if !self.check(TokenType::CloseParen) {
loop {
self.skip_newlines();
args.push(self.parse_expression()?);
self.skip_newlines();
if !self.matches(&[TokenType::Comma]) {
break;
}
}
}
let end_span = self.consume_token(TokenType::CloseParen, "Expected ')' after arguments")?;
let expr_span = *expr.span();
Ok(ExpressionNode {
kind: ExpressionKind::Call {
func: Box::new(expr),
args,
},
span: expr_span.combine(&end_span),
})
}
fn parse_field_access_postfix(&mut self, expr: ExpressionNode) -> ParserResult<ExpressionNode> {
let field = self.consume_identifier("Expected field name after '.'")?;
let expr_span = *expr.span();
let field_span = self.tokens[self.current - 1].span;
Ok(ExpressionNode {
kind: ExpressionKind::FieldAccess {
expr: Box::new(expr),
field,
},
span: expr_span.combine(&field_span),
})
}
fn parse_index_postfix(&mut self, expr: ExpressionNode) -> ParserResult<ExpressionNode> {
let index = self.parse_expression()?;
let end_span = self.consume_token(TokenType::CloseBracket, "Expected ']' after index")?;
let expr_span = *expr.span();
Ok(ExpressionNode {
kind: ExpressionKind::ListAccess {
expr: Box::new(expr),
index: Box::new(index),
},
span: expr_span.combine(&end_span),
})
}
fn should_consume_generic_type_args(&self, expr: &ExpressionNode) -> Option<(String, bool)> {
let generic_target_name = self.generic_target_name(expr)?;
let gt_idx = self.find_matching_generic_gt_index()?;
let should_consume = self.should_consume_generics_for_target(&generic_target_name, gt_idx);
Some((generic_target_name, should_consume))
}
fn generic_target_name(&self, expr: &ExpressionNode) -> Option<String> {
match &expr.kind {
ExpressionKind::Identifier(id) => Some(id.clone()),
ExpressionKind::FieldAccess { expr, field } => {
if let ExpressionKind::Identifier(base) = &expr.kind {
Some(format!("{}.{}", base, field))
} else {
Some(field.clone())
}
}
_ => None,
}
}
fn find_matching_generic_gt_index(&self) -> Option<usize> {
let mut i = self.current + 1;
let mut depth = 1usize;
while i < self.tokens.len() {
match self.tokens[i].token_type {
TokenType::Lt => depth += 1,
TokenType::Gt => {
depth -= 1;
if depth == 0 {
return Some(i);
}
}
TokenType::Eof
| TokenType::NewLine
| TokenType::OpenBrace
| TokenType::CloseBrace => {
return None;
}
_ => {}
}
i += 1;
}
None
}
fn should_consume_generics_for_target(&self, generic_target_name: &str, gt_idx: usize) -> bool {
if let Some(next) = self.tokens.get(gt_idx + 1) {
matches!(
next.token_type,
TokenType::Dot
| TokenType::OpenParen
| TokenType::CloseParen
| TokenType::Eq
| TokenType::NewLine
| TokenType::Eof
) || generic_target_name
.chars()
.next()
.is_some_and(|c| c.is_ascii_uppercase())
} else {
true
}
}
fn parse_generic_postfix(
&mut self,
expr: ExpressionNode,
) -> ParserResult<Option<ExpressionNode>> {
let Some((name, should_consume_generics)) = self.should_consume_generic_type_args(&expr)
else {
return Ok(None);
};
if !should_consume_generics {
return Ok(None);
}
let _ = self.matches(&[TokenType::Lt]);
let type_args = self.parse_type_arguments()?;
let end_span = self.consume_token(TokenType::Gt, "Expected '>' after type arguments")?;
Ok(Some(ExpressionNode {
kind: ExpressionKind::GenericType(name, type_args),
span: expr.span.combine(&end_span),
}))
}
fn parse_postfix_update(&mut self, expr: ExpressionNode, op: UnaryOp) -> ExpressionNode {
let expr_span = *expr.span();
let op_span = self.previous().span;
ExpressionNode {
kind: ExpressionKind::Unary {
op,
op_span,
expr: Box::new(expr),
postfix: true,
},
span: expr_span.combine(&op_span),
}
}
fn skip_newline_gap_for_postfix(&mut self) -> bool {
let mut lookahead = self.current;
let mut found_newlines = false;
while let Some(token) = self.tokens.get(lookahead) {
if token.token_type == TokenType::NewLine {
found_newlines = true;
lookahead += 1;
} else {
break;
}
}
if found_newlines && let Some(next) = self.tokens.get(lookahead) {
match next.token_type {
TokenType::Dot | TokenType::OpenParen | TokenType::OpenBracket => {
self.current = lookahead;
return true;
}
_ => {}
}
}
false
}
fn try_parse_postfix_operator(
&mut self,
expr: ExpressionNode,
) -> ParserResult<Option<ExpressionNode>> {
if self.matches(&[TokenType::OpenParen]) {
return self.parse_call_postfix(expr).map(Some);
}
if self.matches(&[TokenType::Dot]) {
return self.parse_field_access_postfix(expr).map(Some);
}
if self.matches(&[TokenType::OpenBracket]) {
return self.parse_index_postfix(expr).map(Some);
}
if self.check(TokenType::Lt) {
return self
.parse_generic_postfix(expr)
.map(|maybe_expr| maybe_expr.or(None));
}
if self.matches(&[TokenType::Incr]) {
return Ok(Some(self.parse_postfix_update(expr, UnaryOp::Incr)));
}
if self.matches(&[TokenType::Decr]) {
return Ok(Some(self.parse_postfix_update(expr, UnaryOp::Decr)));
}
Ok(None)
}
fn parse_postfix_operators(
&mut self,
mut expr: ExpressionNode,
) -> ParserResult<ExpressionNode> {
loop {
if let Some(next_expr) = self.try_parse_postfix_operator(expr.clone())? {
expr = next_expr;
continue;
}
if self.check(TokenType::Lt) {
break;
}
if self.skip_newline_gap_for_postfix() {
continue;
}
break;
}
Ok(expr)
}
fn matches(&mut self, types: &[TokenType]) -> bool {
if self.is_at_end() {
return false;
}
let token = self.peek();
for ty in types {
if token.token_type == *ty {
if token.token_type == TokenType::Eof {
return false;
}
self.current += 1;
return true;
}
}
false
}
fn check(&self, ty: TokenType) -> bool {
if self.is_at_end() {
return false;
}
self.peek().token_type == ty
}
fn consume_token(&mut self, expected: TokenType, error_msg: &str) -> ParserResult<Span> {
if self.is_at_end() {
return Err(ParserError::new(
format!("{}, but reached end of file", error_msg),
self.tokens.last().map(|t| t.span).unwrap_or_else(|| Span {
row_start: 1,
row_end: None,
col_start: 1,
col_end: None,
}),
));
}
let token = &self.tokens[self.current];
if token.token_type == expected {
self.current += 1;
Ok(token.span)
} else {
let found_desc = Self::describe_token(&token.token_type);
Err(ParserError::new(
format!("{}, found {}", error_msg, found_desc),
token.span,
))
}
}
fn consume_identifier(&mut self, error_msg: &str) -> ParserResult<String> {
if self.is_at_end() {
return Err(ParserError::new(
format!("{}, but reached end of file", error_msg),
self.peek().span,
));
}
match &self.peek().token_type {
TokenType::Id(name) => {
let name_clone = name.clone();
self.current += 1;
Ok(name_clone)
}
TokenType::Underscore => {
let name_clone = "_".to_string();
self.current += 1;
Ok(name_clone)
}
_ => {
let found_desc = Self::describe_token(&self.peek().token_type);
Err(ParserError::new(
format!("{}, found {}", error_msg, found_desc),
self.peek().span,
))
}
}
}
fn describe_token(token_type: &TokenType) -> String {
match token_type {
TokenType::Auto => "'auto' keyword".to_string(),
TokenType::Func => "'func' keyword".to_string(),
TokenType::Returns => "'returns' keyword".to_string(),
TokenType::Return => "'return' keyword".to_string(),
TokenType::Class => "'class' keyword".to_string(),
TokenType::Interface => "'interface' keyword".to_string(),
TokenType::Enum => "'enum' keyword".to_string(),
TokenType::If => "'if' keyword".to_string(),
TokenType::Else => "'else' keyword".to_string(),
TokenType::For => "'for' keyword".to_string(),
TokenType::While => "'while' keyword".to_string(),
TokenType::Match => "'match' keyword".to_string(),
TokenType::Const => "'const' keyword".to_string(),
TokenType::Import => "'import' keyword".to_string(),
TokenType::Break => "'break' keyword".to_string(),
TokenType::Continue => "'continue' keyword".to_string(),
TokenType::In => "'in' keyword".to_string(),
TokenType::Is => "'is' keyword".to_string(),
TokenType::As => "'as' keyword".to_string(),
TokenType::Common => "'common' keyword".to_string(),
TokenType::None => "'none' keyword".to_string(),
TokenType::OpenBrace => "'{'".to_string(),
TokenType::CloseBrace => "'}'".to_string(),
TokenType::OpenParen => "'('".to_string(),
TokenType::CloseParen => "')'".to_string(),
TokenType::OpenBracket => "'['".to_string(),
TokenType::CloseBracket => "']'".to_string(),
TokenType::Dot => "'.'".to_string(),
TokenType::DotDot => "'..'".to_string(),
TokenType::Comma => "','".to_string(),
TokenType::Colon => "':'".to_string(),
TokenType::Eq => "'='".to_string(),
TokenType::Plus => "'+'".to_string(),
TokenType::Minus => "'-'".to_string(),
TokenType::Star => "'*'".to_string(),
TokenType::Slash => "'/'".to_string(),
TokenType::Percent => "'%'".to_string(),
TokenType::Lt => "'<'".to_string(),
TokenType::Gt => "'>'".to_string(),
TokenType::EqEq => "'=='".to_string(),
TokenType::NotEq => "'!='".to_string(),
TokenType::Bang => "'!'".to_string(),
TokenType::And => "'&&'".to_string(),
TokenType::Or => "'||'".to_string(),
TokenType::Id(name) => format!("identifier '{}'", name),
TokenType::Int(n) => format!("integer literal '{}'", n),
TokenType::Float(n) => format!("float literal '{}'", n),
TokenType::Str(s) => format!("string literal \"{}\"", s),
TokenType::Bool(b) => format!("boolean literal '{}'", b),
TokenType::Char(c) => format!("character literal '{}'", c),
TokenType::Eof => "end of file".to_string(),
TokenType::NewLine => "newline".to_string(),
TokenType::StarStar => "'**'".to_string(),
TokenType::Le => "'<='".to_string(),
TokenType::Ge => "'>='".to_string(),
TokenType::Incr => "'++'".to_string(),
TokenType::Decr => "'--'".to_string(),
TokenType::PlusEq => "'+='".to_string(),
TokenType::MinusEq => "'-='".to_string(),
TokenType::StarEq => "'*='".to_string(),
TokenType::SlashEq => "'/='".to_string(),
TokenType::PercentEq => "'%='".to_string(),
TokenType::Ref => "'&'".to_string(),
TokenType::Underscore => "'_'".to_string(),
_ => format!("{:?}", token_type),
}
}
fn consume_operator(&mut self) -> Option<BinaryOp> {
if self.is_at_end() {
return None;
}
let token = self.peek();
let result = match token.token_type {
TokenType::Plus => Some(BinaryOp::Add),
TokenType::Minus => Some(BinaryOp::Subtract),
TokenType::Star => Some(BinaryOp::Multiply),
TokenType::Slash => Some(BinaryOp::Divide),
TokenType::Percent => Some(BinaryOp::Modulo),
TokenType::StarStar => Some(BinaryOp::Exponent),
TokenType::Eq => Some(BinaryOp::Assign),
TokenType::EqEq => Some(BinaryOp::Equal),
TokenType::NotEq => Some(BinaryOp::NotEqual),
TokenType::Lt => Some(BinaryOp::Less),
TokenType::Gt => Some(BinaryOp::Greater),
TokenType::Le => Some(BinaryOp::LessEqual),
TokenType::Ge => Some(BinaryOp::GreaterEqual),
TokenType::And => Some(BinaryOp::LogicalAnd),
TokenType::Or => Some(BinaryOp::LogicalOr),
TokenType::In => Some(BinaryOp::In),
TokenType::PlusEq => Some(BinaryOp::AddAssign),
TokenType::MinusEq => Some(BinaryOp::SubtractAssign),
TokenType::StarEq => Some(BinaryOp::MultiplyAssign),
TokenType::SlashEq => Some(BinaryOp::DivideAssign),
TokenType::PercentEq => Some(BinaryOp::ModuloAssign),
_ => None,
};
if result.is_some() {
self.current += 1;
}
result
}
fn peek_operator(&self) -> Option<BinaryOp> {
if self.is_at_end() {
return None;
}
match self.peek().token_type {
TokenType::Plus => Some(BinaryOp::Add),
TokenType::Minus => Some(BinaryOp::Subtract),
TokenType::Star => Some(BinaryOp::Multiply),
TokenType::Slash => Some(BinaryOp::Divide),
TokenType::Percent => Some(BinaryOp::Modulo),
TokenType::StarStar => Some(BinaryOp::Exponent),
TokenType::Eq => Some(BinaryOp::Assign),
TokenType::EqEq => Some(BinaryOp::Equal),
TokenType::NotEq => Some(BinaryOp::NotEqual),
TokenType::Lt => Some(BinaryOp::Less),
TokenType::Gt => Some(BinaryOp::Greater),
TokenType::Le => Some(BinaryOp::LessEqual),
TokenType::Ge => Some(BinaryOp::GreaterEqual),
TokenType::And => Some(BinaryOp::LogicalAnd),
TokenType::Or => Some(BinaryOp::LogicalOr),
TokenType::In => Some(BinaryOp::In),
TokenType::PlusEq => Some(BinaryOp::AddAssign),
TokenType::MinusEq => Some(BinaryOp::SubtractAssign),
TokenType::StarEq => Some(BinaryOp::MultiplyAssign),
TokenType::SlashEq => Some(BinaryOp::DivideAssign),
TokenType::PercentEq => Some(BinaryOp::ModuloAssign),
_ => None,
}
}
fn consume_if_unary_operator(&mut self) -> Option<Token> {
if self.is_at_end() {
return None;
}
let token = self.peek();
match &token.token_type {
TokenType::Minus
| TokenType::Bang
| TokenType::Ref
| TokenType::Incr
| TokenType::Decr
| TokenType::Star => {
let token_clone = token.clone();
self.current += 1;
Some(token_clone)
}
_ => None,
}
}
fn get_operator_precedence(&self, op: &BinaryOp) -> ParserResult<Precedence> {
let precedence = match op {
BinaryOp::Assign
| BinaryOp::AddAssign
| BinaryOp::SubtractAssign
| BinaryOp::MultiplyAssign
| BinaryOp::DivideAssign
| BinaryOp::ModuloAssign => Precedence::Assignment,
BinaryOp::LogicalOr => Precedence::Or,
BinaryOp::LogicalAnd => Precedence::And,
BinaryOp::In => Precedence::Comparison,
BinaryOp::Equal | BinaryOp::NotEqual => Precedence::Equality,
BinaryOp::Less | BinaryOp::LessEqual | BinaryOp::Greater | BinaryOp::GreaterEqual => {
Precedence::Comparison
}
BinaryOp::Add | BinaryOp::Subtract => Precedence::Term,
BinaryOp::Multiply | BinaryOp::Divide | BinaryOp::Modulo => Precedence::Factor,
BinaryOp::Exponent => Precedence::Exponent,
};
Ok(precedence)
}
fn parse_field_declaration(
&mut self,
type_param_names: &[(String, Vec<TraitBound>)],
) -> ParserResult<Field> {
let is_const = if self.check(TokenType::Const) {
self.consume();
true
} else {
false
};
let field_type = self.parse_type()?;
let field_name = self.consume_identifier("Expected field name")?;
let default_value = if self.matches(&[TokenType::Eq]) {
let expr = self.parse_primary()?;
if !Self::is_literal_expression(&expr) {
return Err(ParserError::with_help(
"Field default values must be literals",
expr.span,
"Only literal values (int, float, string, bool, char) are allowed as field defaults. Example: int count = 0",
));
}
Some(expr)
} else {
None
};
if is_const && default_value.is_none() {
return Err(ParserError::with_help(
"Const fields must have a default value",
self.previous().span,
"Add a default value to the const field. Example: const int MAX_SIZE = 100",
));
}
let is_generic_param = Self::is_field_generic_param(&field_type, type_param_names);
Ok(Field {
name: field_name,
type_: field_type,
is_generic_param,
is_const,
default_value,
})
}
fn is_field_generic_param(
field_type: &TypeNode,
type_param_names: &[(String, Vec<TraitBound>)],
) -> bool {
match &field_type.kind {
TypeKind::Named(name, type_args) => {
type_args.is_empty()
&& type_param_names
.iter()
.any(|(param_name, _)| param_name == name)
}
_ => false,
}
}
fn is_literal_expression(expr: &ExpressionNode) -> bool {
matches!(expr.kind, ExpressionKind::Literal(_))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lexer::{Lexer, Token};
use crate::source::Source;
use std::rc::Rc;
#[derive(Debug)]
struct TestParser {
parser: Parser<'static>,
}
impl TestParser {
fn new(source: &str) -> Self {
let mut src = Source::from_test_str(source);
let tokens = collect_tokens(&mut src);
let tokens_rc = Rc::new(tokens);
let tokens_ptr = Rc::into_raw(tokens_rc.clone());
let tokens_ref = unsafe { &*tokens_ptr };
let parser = Parser::new(tokens_ref);
Self { parser }
}
fn parse(&mut self) -> Result<Vec<AstNode>, (Vec<AstNode>, Vec<ParserError>)> {
self.parser.parse()
}
}
fn collect_tokens(source: &mut Source) -> Vec<Token> {
let input = source.input.clone();
println!("Collecting tokens from source:\n{}", input);
let mut lexer = Lexer::new(source);
let mut tokens = Vec::new();
loop {
match lexer.next_token() {
Ok(token) => {
if token.token_type == TokenType::Eof {
break;
}
tokens.push(token);
}
Err(e) => {
panic!("Lexer error: {}", e);
}
}
}
tokens
}
fn create_parser(source: &str) -> TestParser {
TestParser::new(source)
}
fn parse_expr(source: &str) -> ExpressionNode {
let mut test_parser = create_parser(source);
test_parser.parser.parse_expression().unwrap()
}
fn parse_stmts(source: &str) -> Vec<StatementNode> {
let mut test_parser = create_parser(source);
match test_parser.parse() {
Ok(nodes) => collect_statements(nodes),
Err((nodes, errors)) => {
eprintln!("Parse errors: {:?}", errors);
collect_statements(nodes)
}
}
}
fn collect_statements(nodes: Vec<AstNode>) -> Vec<StatementNode> {
nodes
.into_iter()
.filter_map(AstNode::into_statement)
.collect()
}
fn assert_auto_decl_literal_statement(
stmt: &StatementNode,
expected_row: usize,
expected_value: &LiteralNode,
) {
if let StatementKind::AutoDecl(_, _, expr) = &stmt.kind {
assert_eq!(stmt.span.row_start, expected_row);
assert!(stmt.span.col_start > 0);
assert_eq!(expr.span.row_start, expected_row);
assert!(expr.span.col_start > 0);
if let ExpressionKind::Literal(lit) = &expr.kind {
assert_eq!(lit, expected_value);
} else {
panic!("Expected literal expression, got {:?}", expr.kind);
}
} else {
panic!("Expected AutoDecl statement, got {:?}", stmt.kind);
}
}
#[test]
fn test_span_propagation() {
let input = r#"
auto x = 42
auto y = "hello"
"#;
let stmts = parse_stmts(input);
assert!(!stmts.is_empty(), "No statements were parsed");
assert!(stmts.len() >= 2, "Expected at least 2 statements");
assert_auto_decl_literal_statement(&stmts[0], 2, &LiteralNode::Integer(42));
assert_auto_decl_literal_statement(&stmts[1], 3, &LiteralNode::String("hello".to_string()));
}
#[test]
fn test_binary_expression_span() {
let input = "1 + 2 * 3";
let expr = parse_expr(input);
if let ExpressionKind::Binary {
left, op: _, right, ..
} = &expr.kind
{
assert_eq!(expr.span.row_start, 1);
assert_eq!(expr.span.col_start, 1);
assert_eq!(expr.span.col_end, Some(10));
assert_eq!(left.span.row_start, 1);
assert_eq!(left.span.col_start, 1);
assert_eq!(right.span.row_start, 1);
assert!(right.span.col_start > left.span.col_start);
} else {
panic!("Expected binary expression, got {:?}", expr.kind);
}
}
#[test]
fn test_function_call_span() {
let input = "add(1, 2 + 3)";
let expr = parse_expr(input);
if let ExpressionKind::Call { func, args } = &expr.kind {
assert_eq!(expr.span.row_start, 1);
assert_eq!(expr.span.col_start, 1);
assert_eq!(func.span.row_start, 1);
assert_eq!(func.span.col_start, 1);
assert_eq!(args.len(), 2);
assert_eq!(args[0].span.row_start, 1);
assert_eq!(args[0].span.col_start, 5);
if let ExpressionKind::Binary {
left, op: _, right, ..
} = &args[1].kind
{
assert_eq!(left.span.row_start, 1);
assert_eq!(left.span.col_start, 8);
assert_eq!(right.span.row_start, 1);
assert_eq!(right.span.col_start, 12);
} else {
panic!(
"Expected binary expression as second argument, got {:?}",
args[1].kind
);
}
} else {
panic!("Expected function call expression, got {:?}", expr.kind);
}
}
#[test]
fn test_newline_termination() {
let stmts = parse_stmts("auto x = 1\n");
assert_eq!(stmts.len(), 1);
let source = "auto x = 1\nauto y = 2\n";
let mut test_parser = create_parser(source);
let result = test_parser.parse();
match result {
Ok(nodes) => {
println!("Successfully parsed {} nodes", nodes.len());
for (i, node) in nodes.iter().enumerate() {
println!("Node {}: {:?}", i, node);
}
assert_eq!(nodes.len(), 2);
}
Err((nodes, errors)) => {
println!(
"Parse failed with nodes: {:?} and errors: {:?}",
nodes, errors
);
panic!("Failed to parse multiple statements with newlines");
}
}
let stmts = parse_stmts("auto x = 1\n\nauto y = 2\n");
assert_eq!(stmts.len(), 2);
}
#[test]
fn test_missing_newline_error() {
let source = "auto x = 1 auto y = 2";
let mut test_parser = create_parser(source);
let result = test_parser.parse();
match result {
Ok(nodes) => {
panic!(
"Expected an error about missing newline, but got successful parse with nodes: {:?}",
nodes
);
}
Err((_, errors)) => {
assert!(!errors.is_empty(), "Expected errors but got none");
let has_newline_error = errors.iter().any(|e| {
let msg_lower = e.message.to_lowercase();
msg_lower.contains("expected newline after statement")
|| msg_lower.contains("missing newline")
|| msg_lower.contains("expected newline")
});
if !has_newline_error {
panic!(
"Expected an error about missing newline, but got: {:?}",
errors
);
}
}
}
}
#[test]
fn test_variable_declaration() {
let stmts = parse_stmts("auto x = 42\n");
assert_eq!(stmts.len(), 1);
match &stmts[0].kind {
StatementKind::AutoDecl(name, _, expr) => {
assert_eq!(name, "x");
assert!(matches!(expr.kind, ExpressionKind::Literal(_)));
}
_ => panic!("Expected auto variable declaration"),
}
let stmts = parse_stmts("int x = 42\n");
assert_eq!(stmts.len(), 1);
let stmts = parse_stmts("const int PI = 3.14159\n");
assert_eq!(stmts.len(), 1);
}
#[test]
fn test_expressions() {
let stmts = parse_stmts("1 + 2 * 3\n");
assert_eq!(stmts.len(), 1);
assert!(matches!(&stmts[0].kind, StatementKind::Expression { .. }));
let expr = parse_expr("(1 + 2) * 3");
assert!(matches!(expr.kind, ExpressionKind::Binary { .. }));
}
#[test]
fn test_block_statements() {
let block_expr = parse_stmts("{\n auto x = 1\n auto y = 2\n}\n");
assert!(
!block_expr.is_empty(),
"Failed to parse block expression with newlines"
);
let single_stmt = parse_stmts("auto x = 1\n");
assert!(
!single_stmt.is_empty(),
"Failed to parse single statement with newline"
);
let multi_stmt = parse_stmts("auto x = 1\n\nauto y = 2\n");
assert_eq!(
multi_stmt.len(),
2,
"Expected 2 statements with newline separation"
);
let block_with_empty_lines = parse_stmts("{\n auto x = 1\n \n auto y = 2\n}\n");
assert!(
!block_with_empty_lines.is_empty(),
"Failed to parse block with empty lines"
);
}
#[test]
fn test_control_flow() {
let stmts = parse_stmts("if x {\n auto y = 1\n}\n");
assert!(!stmts.is_empty());
}
#[test]
fn test_function_declaration() {
let mut test_parser = create_parser("func add(int a, int b) returns int {\n a + b\n}\n");
let result = test_parser.parse();
match result {
Ok(nodes) => {
assert!(!nodes.is_empty());
if let AstNode::Function(func) = &nodes[0] {
assert_eq!(func.name, "add");
assert_eq!(func.params.len(), 2);
assert_eq!(
func.return_type.kind,
TypeKind::Primitive(PrimitiveType::Int)
);
} else {
panic!("Expected a function node, got {:?}", nodes[0]);
}
}
Err((nodes, errors)) => {
if !nodes.is_empty()
&& let AstNode::Function(func) = &nodes[0]
{
assert_eq!(func.name, "add");
assert_eq!(func.params.len(), 2);
assert_eq!(
func.return_type.kind,
TypeKind::Primitive(PrimitiveType::Int)
);
return;
}
panic!("Failed to parse function: {:?}", errors);
}
}
let mut test_parser = create_parser("fn double(int x) {\n return x * 2\n}\n");
let result = test_parser.parse();
match result {
Ok(nodes) => {
assert!(!nodes.is_empty(), "Expected at least one statement");
}
Err((nodes, errors)) => {
if nodes.is_empty() {
panic!("Failed to parse function: {:?}", errors);
}
}
}
let mut test_parser =
create_parser("fn greet(string name, int times = 1) {\n return 0\n}\n");
let result = test_parser.parse();
match result {
Ok(nodes) => {
assert!(!nodes.is_empty(), "Expected at least one statement");
}
Err((nodes, errors)) => {
if nodes.is_empty() {
panic!("Failed to parse function: {:?}", errors);
}
}
}
}
#[test]
fn test_error_recovery() {
let source = r#"
let x = 5
auto y = 10
func foo() {
return 42
}
= 99
let z = 20 30
auto valid1 = 100
let result = (5 + 10
func bar() {
return
auto x = 42
}
auto valid2 = 200
struct BadStruct {
field1: InvalidType
field2: int
}
auto valid3 = 300
"#;
let mut parser = create_parser(source);
let result = parser.parse();
if let Err((nodes, errors)) = result {
assert!(!errors.is_empty(), "Expected at least one error");
assert!(!nodes.is_empty(), "Expected some valid nodes to be parsed");
println!("Successfully parsed {} nodes despite errors", nodes.len());
assert!(
nodes.len() >= 2,
"Expected at least 2 valid nodes to be parsed"
);
let mut found_expected_error = false;
for error in &errors {
println!("Found error: {} at {:?}", error.message, error.span);
if error.message.contains("unknown escape sequence")
|| error.message.contains("Expected expression")
{
found_expected_error = true;
}
assert!(
!error.message.is_empty(),
"Expected a non-empty error message"
);
}
assert!(
found_expected_error,
"Expected to find an error about invalid syntax"
);
} else {
panic!("Expected parsing to fail with errors");
}
let source = "auto x = 10 + * 5 - / 3\nauto y = 20\n";
let mut parser = create_parser(source);
let result = parser.parse();
assert!(result.is_err());
}
}