Files
lux/src/parser.rs
Brandon Lucas bc60f1c8f1 fix: improve error message for bare 'run' expressions at top level
When users write `run main() with {}` at top level instead of
`let _ = run main() with {}`, provide a helpful error message
explaining the correct syntax instead of the generic "Expected
declaration" error.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-16 23:40:15 -05:00

2796 lines
86 KiB
Rust

//! Parser for the Lux language
#![allow(dead_code)]
use crate::ast::*;
use crate::diagnostics::{Diagnostic, ErrorCode, Severity};
use crate::lexer::{LexError, Lexer, StringPart, Token, TokenKind};
use std::fmt;
/// Parser error
#[derive(Debug, Clone)]
pub struct ParseError {
pub message: String,
pub span: Span,
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Parse error at {}-{}: {}",
self.span.start, self.span.end, self.message
)
}
}
impl ParseError {
/// Convert to a rich diagnostic for Elm-style error display
pub fn to_diagnostic(&self) -> Diagnostic {
let (code, title, hints) = categorize_parse_error(&self.message);
Diagnostic {
severity: Severity::Error,
code,
title,
message: self.message.clone(),
span: self.span,
hints,
expected_type: None,
actual_type: None,
secondary_spans: Vec::new(),
}
}
}
/// Categorize parse errors to provide better titles, hints, and error codes
fn categorize_parse_error(message: &str) -> (Option<ErrorCode>, String, Vec<String>) {
let message_lower = message.to_lowercase();
if message_lower.contains("unexpected") && message_lower.contains("expected") {
(
Some(ErrorCode::E0101),
"Unexpected Token".to_string(),
vec!["Check for missing or misplaced punctuation.".to_string()],
)
} else if message_lower.contains("expected") && message_lower.contains("expression") {
(
Some(ErrorCode::E0101),
"Missing Expression".to_string(),
vec!["An expression was expected here.".to_string()],
)
} else if message_lower.contains("expected") && message_lower.contains(":") {
(
Some(ErrorCode::E0104),
"Missing Type Annotation".to_string(),
vec!["A type annotation is required here.".to_string()],
)
} else if message_lower.contains("unclosed") || message_lower.contains("unterminated") {
(
Some(ErrorCode::E0102),
"Unclosed Delimiter".to_string(),
vec![
"Check for matching opening and closing brackets.".to_string(),
"Make sure all strings are properly closed with quotes.".to_string(),
],
)
} else if message_lower.contains("invalid") && message_lower.contains("literal") {
(
Some(ErrorCode::E0103),
"Invalid Literal".to_string(),
vec!["Check the format of the literal value.".to_string()],
)
} else if message_lower.contains("invalid") && message_lower.contains("operator") {
(
Some(ErrorCode::E0105),
"Invalid Operator".to_string(),
vec!["Check the operator syntax.".to_string()],
)
} else if message_lower.contains("invalid") {
(
Some(ErrorCode::E0100),
"Invalid Syntax".to_string(),
vec!["Check the syntax of this construct.".to_string()],
)
} else if message_lower.contains("identifier") {
(
Some(ErrorCode::E0100),
"Invalid Identifier".to_string(),
vec!["Identifiers must start with a letter and contain only letters, numbers, and underscores.".to_string()],
)
} else {
(Some(ErrorCode::E0100), "Parse Error".to_string(), vec![])
}
}
impl From<LexError> for ParseError {
fn from(err: LexError) -> Self {
ParseError {
message: err.message,
span: err.span,
}
}
}
/// The parser
pub struct Parser {
tokens: Vec<Token>,
pos: usize,
eof_token: Token,
}
impl Parser {
pub fn new(tokens: Vec<Token>) -> Self {
Self {
tokens,
pos: 0,
eof_token: Token::new(TokenKind::Eof, Span::default()),
}
}
pub fn parse_source(source: &str) -> Result<Program, ParseError> {
let tokens = Lexer::new(source).tokenize()?;
let mut parser = Parser::new(tokens);
parser.parse_program()
}
/// Parse a complete program
pub fn parse_program(&mut self) -> Result<Program, ParseError> {
let mut imports = Vec::new();
let mut declarations = Vec::new();
self.skip_newlines();
// Parse imports first (they must come before declarations)
while self.check(TokenKind::Import) {
imports.push(self.parse_import()?);
self.skip_newlines();
}
while !self.is_at_end() {
declarations.push(self.parse_declaration()?);
self.skip_newlines();
}
Ok(Program {
imports,
declarations,
})
}
/// Parse an import declaration
fn parse_import(&mut self) -> Result<ImportDecl, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Import)?;
// Parse module path: foo/bar/baz
let mut segments = vec![self.parse_ident()?];
while self.check(TokenKind::Slash) {
self.advance();
segments.push(self.parse_ident()?);
}
let path_span = start.merge(self.previous_span());
let path = ModulePath {
segments,
span: path_span,
};
// Check for wildcard import: import foo.*
let wildcard = if self.check(TokenKind::Dot) && self.peek_next_kind() == TokenKind::Star {
self.advance(); // .
self.advance(); // *
true
} else {
false
};
// Check for selective import: import foo.{a, b, c}
let items = if !wildcard
&& self.check(TokenKind::Dot)
&& self.peek_next_kind() == TokenKind::LBrace
{
self.advance(); // .
self.advance(); // {
let mut items = Vec::new();
while !self.check(TokenKind::RBrace) {
items.push(self.parse_ident()?);
if !self.check(TokenKind::RBrace) {
self.expect(TokenKind::Comma)?;
}
}
self.expect(TokenKind::RBrace)?;
Some(items)
} else {
None
};
// Check for alias: import foo as bar
let alias = if self.check(TokenKind::As) {
self.advance();
Some(self.parse_ident()?)
} else {
None
};
let span = start.merge(self.previous_span());
Ok(ImportDecl {
path,
alias,
items,
wildcard,
span,
})
}
/// Parse a top-level declaration
fn parse_declaration(&mut self) -> Result<Declaration, ParseError> {
self.skip_newlines();
// Collect any doc comments before the declaration
let doc = self.collect_doc_comments();
// Check for visibility modifier
let visibility = if self.check(TokenKind::Pub) {
self.advance();
Visibility::Public
} else {
Visibility::Private
};
match self.peek_kind() {
TokenKind::Fn => Ok(Declaration::Function(self.parse_function_decl(visibility, doc)?)),
TokenKind::Effect => Ok(Declaration::Effect(self.parse_effect_decl(doc)?)),
TokenKind::Handler => Ok(Declaration::Handler(self.parse_handler_decl()?)),
TokenKind::Type => Ok(Declaration::Type(self.parse_type_decl(visibility, doc)?)),
TokenKind::Let => Ok(Declaration::Let(self.parse_let_decl(visibility, doc)?)),
TokenKind::Trait => Ok(Declaration::Trait(self.parse_trait_decl(visibility, doc)?)),
TokenKind::Impl => Ok(Declaration::Impl(self.parse_impl_decl()?)),
TokenKind::Run => Err(self.error("Bare 'run' expressions are not allowed at top level. Use 'let _ = run ...' or 'let result = run ...'")),
_ => Err(self.error("Expected declaration (fn, effect, handler, type, trait, impl, or let)")),
}
}
/// Collect consecutive doc comments into a single string
fn collect_doc_comments(&mut self) -> Option<String> {
let mut docs = Vec::new();
while let TokenKind::DocComment(content) = self.peek_kind() {
docs.push(content.clone());
self.advance();
self.skip_newlines();
}
if docs.is_empty() {
None
} else {
Some(docs.join("\n"))
}
}
/// Parse a function declaration
fn parse_function_decl(&mut self, visibility: Visibility, doc: Option<String>) -> Result<FunctionDecl, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Fn)?;
let name = self.parse_ident()?;
// Optional type parameters
let type_params = if self.check(TokenKind::Lt) {
self.parse_type_params()?
} else {
Vec::new()
};
self.expect(TokenKind::LParen)?;
let params = self.parse_params()?;
self.expect(TokenKind::RParen)?;
// Return type
self.expect(TokenKind::Colon)?;
let return_type = self.parse_type()?;
// Optional effects
let effects = if self.check(TokenKind::With) {
self.advance();
self.parse_effect_list()?
} else {
Vec::new()
};
// Optional behavioral properties: is pure, is total, etc.
let properties = self.parse_behavioral_properties()?;
// Optional where clauses
let where_clauses = self.parse_where_clauses()?;
self.expect(TokenKind::Eq)?;
self.skip_newlines();
let body = self.parse_expr()?;
let span = start.merge(body.span());
Ok(FunctionDecl {
visibility,
doc,
name,
type_params,
params,
return_type,
effects,
properties,
where_clauses,
body,
span,
})
}
/// Parse effect declaration
fn parse_effect_decl(&mut self, doc: Option<String>) -> Result<EffectDecl, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Effect)?;
let name = self.parse_ident()?;
let type_params = if self.check(TokenKind::Lt) {
self.parse_type_params()?
} else {
Vec::new()
};
self.expect(TokenKind::LBrace)?;
self.skip_newlines();
let mut operations = Vec::new();
while !self.check(TokenKind::RBrace) {
operations.push(self.parse_effect_op()?);
self.skip_newlines();
// Optional comma or newline separator
if self.check(TokenKind::Comma) {
self.advance();
}
self.skip_newlines();
}
let end = self.current_span();
self.expect(TokenKind::RBrace)?;
Ok(EffectDecl {
doc,
name,
type_params,
operations,
span: start.merge(end),
})
}
/// Parse an effect operation
fn parse_effect_op(&mut self) -> Result<EffectOp, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Fn)?;
let name = self.parse_ident()?;
self.expect(TokenKind::LParen)?;
let params = self.parse_params()?;
self.expect(TokenKind::RParen)?;
self.expect(TokenKind::Colon)?;
let return_type = self.parse_type()?;
let span = start.merge(self.previous_span());
Ok(EffectOp {
name,
params,
return_type,
span,
})
}
/// Parse handler declaration
fn parse_handler_decl(&mut self) -> Result<HandlerDecl, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Handler)?;
let name = self.parse_ident()?;
// Optional parameters
let params = if self.check(TokenKind::LParen) {
self.advance();
let p = self.parse_params()?;
self.expect(TokenKind::RParen)?;
p
} else {
Vec::new()
};
self.expect(TokenKind::Colon)?;
let effect = self.parse_ident()?;
self.expect(TokenKind::LBrace)?;
self.skip_newlines();
let mut implementations = Vec::new();
while !self.check(TokenKind::RBrace) {
implementations.push(self.parse_handler_impl()?);
self.skip_newlines();
}
let end = self.current_span();
self.expect(TokenKind::RBrace)?;
Ok(HandlerDecl {
name,
params,
effect,
implementations,
span: start.merge(end),
})
}
/// Parse handler implementation
fn parse_handler_impl(&mut self) -> Result<HandlerImpl, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Fn)?;
let op_name = self.parse_ident()?;
self.expect(TokenKind::LParen)?;
let mut params = Vec::new();
while !self.check(TokenKind::RParen) {
params.push(self.parse_ident()?);
if !self.check(TokenKind::RParen) {
self.expect(TokenKind::Comma)?;
}
}
self.expect(TokenKind::RParen)?;
// Optional resume parameter
let resume = if self.check(TokenKind::With) {
self.advance();
Some(self.parse_ident()?)
} else {
None
};
self.expect(TokenKind::Eq)?;
self.skip_newlines();
let body = self.parse_expr()?;
let span = start.merge(body.span());
Ok(HandlerImpl {
op_name,
params,
resume,
body,
span,
})
}
/// Parse type declaration
fn parse_type_decl(&mut self, visibility: Visibility, doc: Option<String>) -> Result<TypeDecl, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Type)?;
let name = self.parse_ident()?;
let type_params = if self.check(TokenKind::Lt) {
self.parse_type_params()?
} else {
Vec::new()
};
// Check for version annotation: type User @v2 { ... }
let version = if self.check(TokenKind::At) {
Some(self.parse_version()?)
} else {
None
};
// Check what kind of type definition
let definition = if self.check(TokenKind::LBrace) {
// Record type
self.advance();
self.skip_newlines();
let fields = self.parse_record_fields()?;
self.skip_newlines();
// Check for migrations inside the record: from @v1 = { ... }
let migrations = self.parse_migrations()?;
self.expect(TokenKind::RBrace)?;
(TypeDef::Record(fields), migrations)
} else if self.check(TokenKind::Eq) {
self.advance();
self.skip_newlines();
if self.check(TokenKind::Pipe) {
// Enum type - with leading | for variants
(TypeDef::Enum(self.parse_variants()?), Vec::new())
} else if self.is_single_line_enum() {
// Single-line enum without leading pipe: type Status = Ok | Error
(TypeDef::Enum(self.parse_single_line_variants()?), Vec::new())
} else {
// Type alias - any type expression
(TypeDef::Alias(self.parse_type()?), Vec::new())
}
} else {
return Err(self.error("Expected '=' or '{' in type declaration"));
};
let span = start.merge(self.previous_span());
Ok(TypeDecl {
visibility,
doc,
name,
type_params,
version,
definition: definition.0,
migrations: definition.1,
span,
})
}
/// Parse let declaration
fn parse_let_decl(&mut self, visibility: Visibility, doc: Option<String>) -> Result<LetDecl, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Let)?;
// Allow underscore as wildcard pattern (discards the value)
let name = if self.check(TokenKind::Underscore) {
let span = self.current_span();
self.advance();
Ident::new("_".to_string(), span)
} else {
self.parse_ident()?
};
let typ = if self.check(TokenKind::Colon) {
self.advance();
Some(self.parse_type()?)
} else {
None
};
self.expect(TokenKind::Eq)?;
self.skip_newlines();
let value = self.parse_expr()?;
let span = start.merge(value.span());
Ok(LetDecl {
visibility,
doc,
name,
typ,
value,
span,
})
}
/// Parse trait declaration: trait Show { fn show(self): String }
fn parse_trait_decl(&mut self, visibility: Visibility, doc: Option<String>) -> Result<TraitDecl, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Trait)?;
let name = self.parse_ident()?;
// Optional type parameters: trait Functor<F> { ... }
let type_params = if self.check(TokenKind::Lt) {
self.parse_type_params()?
} else {
Vec::new()
};
// Optional super traits: trait Ord: Eq { ... }
let super_traits = if self.check(TokenKind::Colon) {
self.advance();
self.parse_trait_bounds()?
} else {
Vec::new()
};
self.expect(TokenKind::LBrace)?;
self.skip_newlines();
let mut methods = Vec::new();
while !self.check(TokenKind::RBrace) {
methods.push(self.parse_trait_method()?);
self.skip_newlines();
if self.check(TokenKind::Comma) {
self.advance();
}
self.skip_newlines();
}
let end = self.current_span();
self.expect(TokenKind::RBrace)?;
Ok(TraitDecl {
visibility,
doc,
name,
type_params,
super_traits,
methods,
span: start.merge(end),
})
}
/// Parse a trait method signature
fn parse_trait_method(&mut self) -> Result<TraitMethod, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Fn)?;
let name = self.parse_ident()?;
// Optional type parameters
let type_params = if self.check(TokenKind::Lt) {
self.parse_type_params()?
} else {
Vec::new()
};
self.expect(TokenKind::LParen)?;
let params = self.parse_params()?;
self.expect(TokenKind::RParen)?;
self.expect(TokenKind::Colon)?;
let return_type = self.parse_type()?;
// Optional default implementation
let default_impl = if self.check(TokenKind::Eq) {
self.advance();
self.skip_newlines();
Some(self.parse_expr()?)
} else {
None
};
let span = start.merge(self.previous_span());
Ok(TraitMethod {
name,
type_params,
params,
return_type,
default_impl,
span,
})
}
/// Parse trait bounds: Eq + Ord + Show
fn parse_trait_bounds(&mut self) -> Result<Vec<TraitBound>, ParseError> {
let mut bounds = Vec::new();
loop {
bounds.push(self.parse_trait_bound()?);
if self.check(TokenKind::Plus) {
self.advance();
} else {
break;
}
}
Ok(bounds)
}
/// Parse a single trait bound: Show, Functor<F>
fn parse_trait_bound(&mut self) -> Result<TraitBound, ParseError> {
let start = self.current_span();
let trait_name = self.parse_ident()?;
let type_args = if self.check(TokenKind::Lt) {
self.advance();
let mut args = Vec::new();
while !self.check(TokenKind::Gt) {
args.push(self.parse_type()?);
if !self.check(TokenKind::Gt) {
self.expect(TokenKind::Comma)?;
}
}
self.expect(TokenKind::Gt)?;
args
} else {
Vec::new()
};
let span = start.merge(self.previous_span());
Ok(TraitBound {
trait_name,
type_args,
span,
})
}
/// Parse impl declaration: impl Show for Int { ... }
fn parse_impl_decl(&mut self) -> Result<ImplDecl, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Impl)?;
// Optional type parameters: impl<T> Show for List<T> { ... }
let type_params = if self.check(TokenKind::Lt) {
self.parse_type_params()?
} else {
Vec::new()
};
// Parse the trait name
let trait_name = self.parse_ident()?;
// Optional type arguments for the trait: impl Functor<List> for ...
let trait_args = if self.check(TokenKind::Lt) {
self.advance();
let mut args = Vec::new();
while !self.check(TokenKind::Gt) {
args.push(self.parse_type()?);
if !self.check(TokenKind::Gt) {
self.expect(TokenKind::Comma)?;
}
}
self.expect(TokenKind::Gt)?;
args
} else {
Vec::new()
};
self.expect(TokenKind::For)?;
let target_type = self.parse_type()?;
// Optional where clause with trait constraints
let constraints = if self.check(TokenKind::Where) {
self.parse_trait_constraints()?
} else {
Vec::new()
};
self.expect(TokenKind::LBrace)?;
self.skip_newlines();
let mut methods = Vec::new();
while !self.check(TokenKind::RBrace) {
methods.push(self.parse_impl_method()?);
self.skip_newlines();
if self.check(TokenKind::Comma) {
self.advance();
}
self.skip_newlines();
}
let end = self.current_span();
self.expect(TokenKind::RBrace)?;
Ok(ImplDecl {
type_params,
constraints,
trait_name,
trait_args,
target_type,
methods,
span: start.merge(end),
})
}
/// Parse trait constraints in a where clause: where T: Show, U: Eq + Ord
fn parse_trait_constraints(&mut self) -> Result<Vec<TraitConstraint>, ParseError> {
let mut constraints = Vec::new();
while self.check(TokenKind::Where) {
self.advance();
let start = self.current_span();
let type_param = self.parse_ident()?;
self.expect(TokenKind::Colon)?;
let bounds = self.parse_trait_bounds()?;
let span = start.merge(self.previous_span());
constraints.push(TraitConstraint {
type_param,
bounds,
span,
});
if self.check(TokenKind::Comma) {
self.advance();
}
}
Ok(constraints)
}
/// Parse an impl method
fn parse_impl_method(&mut self) -> Result<ImplMethod, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Fn)?;
let name = self.parse_ident()?;
self.expect(TokenKind::LParen)?;
let params = self.parse_params()?;
self.expect(TokenKind::RParen)?;
// Optional return type (infer if not provided)
let return_type = if self.check(TokenKind::Colon) {
self.advance();
Some(self.parse_type()?)
} else {
None
};
self.expect(TokenKind::Eq)?;
self.skip_newlines();
let body = self.parse_expr()?;
let span = start.merge(body.span());
Ok(ImplMethod {
name,
params,
return_type,
body,
span,
})
}
/// Parse type parameters <A, B, C>
fn parse_type_params(&mut self) -> Result<Vec<Ident>, ParseError> {
self.expect(TokenKind::Lt)?;
let mut params = Vec::new();
while !self.check(TokenKind::Gt) {
params.push(self.parse_ident()?);
if !self.check(TokenKind::Gt) {
self.expect(TokenKind::Comma)?;
}
}
self.expect(TokenKind::Gt)?;
Ok(params)
}
/// Parse function parameters
fn parse_params(&mut self) -> Result<Vec<Parameter>, ParseError> {
let mut params = Vec::new();
while !self.check(TokenKind::RParen) {
let start = self.current_span();
let name = self.parse_ident()?;
self.expect(TokenKind::Colon)?;
let typ = self.parse_type()?;
let span = start.merge(self.previous_span());
params.push(Parameter { name, typ, span });
if !self.check(TokenKind::RParen) {
self.expect(TokenKind::Comma)?;
}
}
Ok(params)
}
/// Parse effect list { Effect1, Effect2 }
fn parse_effect_list(&mut self) -> Result<Vec<Ident>, ParseError> {
self.expect(TokenKind::LBrace)?;
let mut effects = Vec::new();
while !self.check(TokenKind::RBrace) {
effects.push(self.parse_ident()?);
if !self.check(TokenKind::RBrace) {
self.expect(TokenKind::Comma)?;
}
}
self.expect(TokenKind::RBrace)?;
Ok(effects)
}
/// Parse behavioral properties: is pure, is total, is idempotent, etc.
fn parse_behavioral_properties(&mut self) -> Result<Vec<BehavioralProperty>, ParseError> {
let mut properties = Vec::new();
while self.check(TokenKind::Is) || self.check(TokenKind::Assume) {
let is_assumed = self.check(TokenKind::Assume);
self.advance(); // consume 'is' or 'assume'
if is_assumed {
// 'assume' must be followed by 'is'
if !self.check(TokenKind::Is) {
return Err(ParseError {
message: "Expected 'is' after 'assume'".to_string(),
span: self.current_span(),
});
}
self.advance(); // consume 'is'
}
let property = self.parse_single_property()?;
properties.push(property);
// Optional comma for multiple properties: is pure, is total
if self.check(TokenKind::Comma) {
self.advance();
}
}
Ok(properties)
}
/// Parse a single behavioral property keyword
fn parse_single_property(&mut self) -> Result<BehavioralProperty, ParseError> {
let span = self.current_span();
match self.peek_kind() {
TokenKind::Pure => {
self.advance();
Ok(BehavioralProperty::Pure)
}
TokenKind::Total => {
self.advance();
Ok(BehavioralProperty::Total)
}
TokenKind::Idempotent => {
self.advance();
Ok(BehavioralProperty::Idempotent)
}
TokenKind::Deterministic => {
self.advance();
Ok(BehavioralProperty::Deterministic)
}
TokenKind::Commutative => {
self.advance();
Ok(BehavioralProperty::Commutative)
}
_ => Err(ParseError {
message: "Expected behavioral property: pure, total, idempotent, deterministic, or commutative".to_string(),
span,
}),
}
}
/// Parse where clauses: where F is pure, where result > 0
fn parse_where_clauses(&mut self) -> Result<Vec<WhereClause>, ParseError> {
let mut clauses = Vec::new();
while self.check(TokenKind::Where) {
self.advance(); // consume 'where'
let span = self.current_span();
// Check if it's a property constraint: where F is pure
// or a result refinement: where result > 0
if self.check_ident() {
let ident = self.parse_ident()?;
if self.check(TokenKind::Is) {
self.advance(); // consume 'is'
let property = self.parse_single_property()?;
clauses.push(WhereClause::PropertyConstraint {
type_param: ident,
property,
span,
});
} else {
// This is a result refinement starting with an identifier
// For now, we'll parse it as a simple expression
// Put the identifier back by creating an expression
let predicate = self.parse_refinement_with_ident(ident)?;
clauses.push(WhereClause::ResultRefinement {
predicate: Box::new(predicate),
span,
});
}
} else {
return Err(ParseError {
message: "Expected identifier after 'where'".to_string(),
span,
});
}
// Optional comma for multiple where clauses
if self.check(TokenKind::Comma) {
self.advance();
}
}
Ok(clauses)
}
/// Parse a refinement expression that starts with an already-parsed identifier
fn parse_refinement_with_ident(&mut self, ident: Ident) -> Result<Expr, ParseError> {
// Start with the identifier as a variable
let mut left = Expr::Var(ident);
// Parse the rest as a comparison expression
if let Some(op) = self.try_parse_comparison_op() {
let right = self.parse_primary_expr()?;
let span = left.span().merge(right.span());
left = Expr::BinaryOp {
op,
left: Box::new(left),
right: Box::new(right),
span,
};
}
Ok(left)
}
/// Try to parse a comparison operator
fn try_parse_comparison_op(&mut self) -> Option<BinaryOp> {
match self.peek_kind() {
TokenKind::Lt => {
self.advance();
Some(BinaryOp::Lt)
}
TokenKind::Le => {
self.advance();
Some(BinaryOp::Le)
}
TokenKind::Gt => {
self.advance();
Some(BinaryOp::Gt)
}
TokenKind::Ge => {
self.advance();
Some(BinaryOp::Ge)
}
TokenKind::EqEq => {
self.advance();
Some(BinaryOp::Eq)
}
TokenKind::Ne => {
self.advance();
Some(BinaryOp::Ne)
}
_ => None,
}
}
/// Check if the current token is an identifier
fn check_ident(&self) -> bool {
matches!(self.peek_kind(), TokenKind::Ident(_))
}
/// Parse a type expression
fn parse_type(&mut self) -> Result<TypeExpr, ParseError> {
// Function type: fn(A, B): C with {E}
if self.check(TokenKind::Fn) {
return self.parse_function_type();
}
// Tuple or parenthesized type
if self.check(TokenKind::LParen) {
return self.parse_tuple_or_paren_type();
}
// Record type
if self.check(TokenKind::LBrace) {
return self.parse_record_type();
}
// Named type (possibly with type arguments)
let name = self.parse_ident()?;
// Check for type arguments
let base_type = if self.check(TokenKind::Lt) {
self.advance();
let mut args = Vec::new();
while !self.check(TokenKind::Gt) {
args.push(self.parse_type()?);
if !self.check(TokenKind::Gt) {
self.expect(TokenKind::Comma)?;
}
}
self.expect(TokenKind::Gt)?;
TypeExpr::App(Box::new(TypeExpr::Named(name)), args)
} else {
TypeExpr::Named(name)
};
// Check for version constraint: Type @v1, Type @v2+, Type @latest
if self.check(TokenKind::At) {
let constraint = self.parse_version_constraint()?;
Ok(TypeExpr::Versioned {
base: Box::new(base_type),
constraint,
})
} else {
Ok(base_type)
}
}
fn parse_function_type(&mut self) -> Result<TypeExpr, ParseError> {
self.expect(TokenKind::Fn)?;
self.expect(TokenKind::LParen)?;
let mut params = Vec::new();
while !self.check(TokenKind::RParen) {
params.push(self.parse_type()?);
if !self.check(TokenKind::RParen) {
self.expect(TokenKind::Comma)?;
}
}
self.expect(TokenKind::RParen)?;
self.expect(TokenKind::Colon)?;
let return_type = Box::new(self.parse_type()?);
let effects = if self.check(TokenKind::With) {
self.advance();
self.parse_effect_list()?
} else {
Vec::new()
};
Ok(TypeExpr::Function {
params,
return_type,
effects,
})
}
fn parse_tuple_or_paren_type(&mut self) -> Result<TypeExpr, ParseError> {
self.expect(TokenKind::LParen)?;
if self.check(TokenKind::RParen) {
self.advance();
return Ok(TypeExpr::Unit);
}
let first = self.parse_type()?;
if self.check(TokenKind::Comma) {
// Tuple type
let mut elements = vec![first];
while self.check(TokenKind::Comma) {
self.advance();
if self.check(TokenKind::RParen) {
break;
}
elements.push(self.parse_type()?);
}
self.expect(TokenKind::RParen)?;
Ok(TypeExpr::Tuple(elements))
} else {
// Parenthesized type
self.expect(TokenKind::RParen)?;
Ok(first)
}
}
fn parse_record_type(&mut self) -> Result<TypeExpr, ParseError> {
self.expect(TokenKind::LBrace)?;
self.skip_newlines();
let fields = self.parse_record_fields()?;
self.expect(TokenKind::RBrace)?;
Ok(TypeExpr::Record(fields))
}
fn parse_record_fields(&mut self) -> Result<Vec<RecordField>, ParseError> {
let mut fields = Vec::new();
while !self.check(TokenKind::RBrace) && !self.check(TokenKind::From) {
let start = self.current_span();
let name = self.parse_ident()?;
self.expect(TokenKind::Colon)?;
let typ = self.parse_type()?;
let span = start.merge(self.previous_span());
fields.push(RecordField { name, typ, span });
self.skip_newlines();
if self.check(TokenKind::Comma) {
self.advance();
}
self.skip_newlines();
}
Ok(fields)
}
/// Parse a version annotation: @v1, @v2, etc.
fn parse_version(&mut self) -> Result<Version, ParseError> {
let start = self.current_span();
self.expect(TokenKind::At)?;
// Expect an identifier like "v1", "v2"
let ident = self.parse_ident()?;
// Parse the version number from the identifier (e.g., "v1" -> 1)
if !ident.name.starts_with('v') {
return Err(ParseError {
message: format!("Expected version like @v1, @v2, got @{}", ident.name),
span: ident.span,
});
}
let version_str = &ident.name[1..];
let number = version_str.parse::<u32>().map_err(|_| ParseError {
message: format!("Invalid version number: {}", version_str),
span: ident.span,
})?;
let span = start.merge(ident.span);
Ok(Version::new(number, span))
}
/// Parse a version constraint: @v1, @v1+, @latest
fn parse_version_constraint(&mut self) -> Result<VersionConstraint, ParseError> {
let start = self.current_span();
self.expect(TokenKind::At)?;
// Check for @latest
if self.check(TokenKind::Latest) {
self.advance();
let span = start.merge(self.previous_span());
return Ok(VersionConstraint::Latest(span));
}
// Parse version identifier (v1, v2, etc.)
let ident = self.parse_ident()?;
if !ident.name.starts_with('v') {
return Err(ParseError {
message: format!(
"Expected version like @v1, @v2, or @latest, got @{}",
ident.name
),
span: ident.span,
});
}
let version_str = &ident.name[1..];
let number = version_str.parse::<u32>().map_err(|_| ParseError {
message: format!("Invalid version number: {}", version_str),
span: ident.span,
})?;
let span = start.merge(ident.span);
let version = Version::new(number, span);
// Check for + (at least this version)
if self.check(TokenKind::Plus) {
self.advance();
Ok(VersionConstraint::AtLeast(version))
} else {
Ok(VersionConstraint::Exact(version))
}
}
/// Parse migrations: from @v1 = { ... }, from @v2 = { ... }
fn parse_migrations(&mut self) -> Result<Vec<Migration>, ParseError> {
let mut migrations = Vec::new();
while self.check(TokenKind::From) {
let start = self.current_span();
self.advance(); // consume 'from'
let from_version = self.parse_version()?;
self.expect(TokenKind::Eq)?;
self.skip_newlines();
// Parse the migration body (an expression, typically a record literal)
let body = self.parse_expr()?;
let span = start.merge(body.span());
migrations.push(Migration {
from_version,
body,
span,
});
self.skip_newlines();
// Optional comma between migrations
if self.check(TokenKind::Comma) {
self.advance();
}
self.skip_newlines();
}
Ok(migrations)
}
/// Check if we're looking at a single-line enum (no leading pipe)
/// e.g., type Status = Ok | Error
fn is_single_line_enum(&self) -> bool {
// Look for pattern: Ident (LParen|LBrace)? Pipe
// We need to peek ahead without consuming tokens
if !matches!(&self.peek().kind, TokenKind::Ident(_)) {
return false;
}
// Check if there's a | somewhere on this logical line
// This is a simple heuristic - look at the next few tokens
let mut pos = self.pos;
let mut found_ident = false;
let mut depth = 0;
while pos < self.tokens.len() {
match &self.tokens[pos].kind {
TokenKind::Ident(_) if !found_ident => found_ident = true,
TokenKind::LParen | TokenKind::LBrace => depth += 1,
TokenKind::RParen | TokenKind::RBrace => {
if depth > 0 {
depth -= 1;
}
}
TokenKind::Pipe if depth == 0 => return true,
TokenKind::Newline | TokenKind::Eof => return false,
_ => {}
}
pos += 1;
}
false
}
/// Parse single-line variants without leading pipe
fn parse_single_line_variants(&mut self) -> Result<Vec<Variant>, ParseError> {
let mut variants = Vec::new();
loop {
let start = self.current_span();
let name = self.parse_ident()?;
let fields = if self.check(TokenKind::LParen) {
// Tuple variant
self.advance();
let mut types = Vec::new();
while !self.check(TokenKind::RParen) {
types.push(self.parse_type()?);
if !self.check(TokenKind::RParen) {
self.expect(TokenKind::Comma)?;
}
}
self.expect(TokenKind::RParen)?;
VariantFields::Tuple(types)
} else if self.check(TokenKind::LBrace) {
// Record variant
self.advance();
self.skip_newlines();
let fields = self.parse_record_fields()?;
self.expect(TokenKind::RBrace)?;
VariantFields::Record(fields)
} else {
VariantFields::Unit
};
let span = start.merge(self.previous_span());
variants.push(Variant { name, fields, span });
// Check for more variants
if self.check(TokenKind::Pipe) {
self.advance();
self.skip_newlines();
} else {
break;
}
}
Ok(variants)
}
fn parse_variants(&mut self) -> Result<Vec<Variant>, ParseError> {
let mut variants = Vec::new();
// Skip leading pipe
if self.check(TokenKind::Pipe) {
self.advance();
}
loop {
let start = self.current_span();
let name = self.parse_ident()?;
let fields = if self.check(TokenKind::LParen) {
// Tuple variant
self.advance();
let mut types = Vec::new();
while !self.check(TokenKind::RParen) {
types.push(self.parse_type()?);
if !self.check(TokenKind::RParen) {
self.expect(TokenKind::Comma)?;
}
}
self.expect(TokenKind::RParen)?;
VariantFields::Tuple(types)
} else if self.check(TokenKind::LBrace) {
// Record variant
self.advance();
self.skip_newlines();
let fields = self.parse_record_fields()?;
self.expect(TokenKind::RBrace)?;
VariantFields::Record(fields)
} else {
VariantFields::Unit
};
let span = start.merge(self.previous_span());
variants.push(Variant { name, fields, span });
self.skip_newlines();
if self.check(TokenKind::Pipe) {
self.advance();
self.skip_newlines();
} else {
break;
}
}
Ok(variants)
}
fn peek_is_variant(&self) -> bool {
matches!(self.peek_kind(), TokenKind::Ident(_))
}
/// Parse an expression
fn parse_expr(&mut self) -> Result<Expr, ParseError> {
self.parse_pipe_expr()
}
/// Parse pipe expressions: a |> b |> c
fn parse_pipe_expr(&mut self) -> Result<Expr, ParseError> {
let mut expr = self.parse_or_expr()?;
while self.check(TokenKind::PipeGt) {
let start = expr.span();
self.advance();
self.skip_newlines();
let right = self.parse_or_expr()?;
let span = start.merge(right.span());
// Transform a |> f into f(a)
expr = Expr::Call {
func: Box::new(right),
args: vec![expr],
span,
};
}
Ok(expr)
}
/// Parse or expressions: a || b
fn parse_or_expr(&mut self) -> Result<Expr, ParseError> {
let mut expr = self.parse_and_expr()?;
while self.check(TokenKind::Or) {
let start = expr.span();
self.advance();
self.skip_newlines();
let right = self.parse_and_expr()?;
let span = start.merge(right.span());
expr = Expr::BinaryOp {
op: BinaryOp::Or,
left: Box::new(expr),
right: Box::new(right),
span,
};
}
Ok(expr)
}
/// Parse and expressions: a && b
fn parse_and_expr(&mut self) -> Result<Expr, ParseError> {
let mut expr = self.parse_equality_expr()?;
while self.check(TokenKind::And) {
let start = expr.span();
self.advance();
self.skip_newlines();
let right = self.parse_equality_expr()?;
let span = start.merge(right.span());
expr = Expr::BinaryOp {
op: BinaryOp::And,
left: Box::new(expr),
right: Box::new(right),
span,
};
}
Ok(expr)
}
/// Parse equality expressions: a == b, a != b
fn parse_equality_expr(&mut self) -> Result<Expr, ParseError> {
let mut expr = self.parse_comparison_expr()?;
loop {
let op = match self.peek_kind() {
TokenKind::EqEq => BinaryOp::Eq,
TokenKind::Ne => BinaryOp::Ne,
_ => break,
};
let start = expr.span();
self.advance();
self.skip_newlines();
let right = self.parse_comparison_expr()?;
let span = start.merge(right.span());
expr = Expr::BinaryOp {
op,
left: Box::new(expr),
right: Box::new(right),
span,
};
}
Ok(expr)
}
/// Parse comparison expressions: a < b, a <= b, etc.
fn parse_comparison_expr(&mut self) -> Result<Expr, ParseError> {
let mut expr = self.parse_additive_expr()?;
loop {
let op = match self.peek_kind() {
TokenKind::Lt => BinaryOp::Lt,
TokenKind::Le => BinaryOp::Le,
TokenKind::Gt => BinaryOp::Gt,
TokenKind::Ge => BinaryOp::Ge,
_ => break,
};
let start = expr.span();
self.advance();
self.skip_newlines();
let right = self.parse_additive_expr()?;
let span = start.merge(right.span());
expr = Expr::BinaryOp {
op,
left: Box::new(expr),
right: Box::new(right),
span,
};
}
Ok(expr)
}
/// Parse additive expressions: a + b, a - b
fn parse_additive_expr(&mut self) -> Result<Expr, ParseError> {
let mut expr = self.parse_multiplicative_expr()?;
loop {
let op = match self.peek_kind() {
TokenKind::Plus => BinaryOp::Add,
TokenKind::Minus => BinaryOp::Sub,
_ => break,
};
let start = expr.span();
self.advance();
self.skip_newlines();
let right = self.parse_multiplicative_expr()?;
let span = start.merge(right.span());
expr = Expr::BinaryOp {
op,
left: Box::new(expr),
right: Box::new(right),
span,
};
}
Ok(expr)
}
/// Parse multiplicative expressions: a * b, a / b, a % b
fn parse_multiplicative_expr(&mut self) -> Result<Expr, ParseError> {
let mut expr = self.parse_unary_expr()?;
loop {
let op = match self.peek_kind() {
TokenKind::Star => BinaryOp::Mul,
TokenKind::Slash => BinaryOp::Div,
TokenKind::Percent => BinaryOp::Mod,
_ => break,
};
let start = expr.span();
self.advance();
self.skip_newlines();
let right = self.parse_unary_expr()?;
let span = start.merge(right.span());
expr = Expr::BinaryOp {
op,
left: Box::new(expr),
right: Box::new(right),
span,
};
}
Ok(expr)
}
/// Parse unary expressions: -a, !a
fn parse_unary_expr(&mut self) -> Result<Expr, ParseError> {
let op = match self.peek_kind() {
TokenKind::Minus => UnaryOp::Neg,
TokenKind::Not => UnaryOp::Not,
_ => return self.parse_call_expr(),
};
let start = self.current_span();
self.advance();
let operand = self.parse_unary_expr()?;
let span = start.merge(operand.span());
Ok(Expr::UnaryOp {
op,
operand: Box::new(operand),
span,
})
}
/// Parse call and field access expressions
fn parse_call_expr(&mut self) -> Result<Expr, ParseError> {
let mut expr = self.parse_primary_expr()?;
loop {
if self.check(TokenKind::LParen) {
// Function call
let start = expr.span();
self.advance();
let args = self.parse_args()?;
self.expect(TokenKind::RParen)?;
let span = start.merge(self.previous_span());
expr = Expr::Call {
func: Box::new(expr),
args,
span,
};
} else if self.check(TokenKind::Dot) {
let start = expr.span();
self.advance();
let field = self.parse_ident()?;
// Check if this is an effect operation: Effect.operation(args)
if self.check(TokenKind::LParen) {
if let Expr::Var(effect) = expr {
self.advance();
let args = self.parse_args()?;
self.expect(TokenKind::RParen)?;
let span = start.merge(self.previous_span());
expr = Expr::EffectOp {
effect,
operation: field,
args,
span,
};
continue;
}
}
let span = start.merge(field.span);
expr = Expr::Field {
object: Box::new(expr),
field,
span,
};
} else {
break;
}
}
Ok(expr)
}
fn parse_args(&mut self) -> Result<Vec<Expr>, ParseError> {
let mut args = Vec::new();
while !self.check(TokenKind::RParen) {
args.push(self.parse_expr()?);
if !self.check(TokenKind::RParen) {
self.expect(TokenKind::Comma)?;
}
}
Ok(args)
}
/// Parse primary expressions
fn parse_primary_expr(&mut self) -> Result<Expr, ParseError> {
let token = self.peek().clone();
match &token.kind {
// Literals
TokenKind::Int(n) => {
let n = *n;
self.advance();
Ok(Expr::Literal(Literal {
kind: LiteralKind::Int(n),
span: token.span,
}))
}
TokenKind::Float(f) => {
let f = *f;
self.advance();
Ok(Expr::Literal(Literal {
kind: LiteralKind::Float(f),
span: token.span,
}))
}
TokenKind::String(s) => {
let s = s.clone();
self.advance();
Ok(Expr::Literal(Literal {
kind: LiteralKind::String(s),
span: token.span,
}))
}
TokenKind::InterpolatedString(parts) => {
let parts = parts.clone();
let span = token.span;
self.advance();
self.desugar_interpolated_string(&parts, span)
}
TokenKind::Char(c) => {
let c = *c;
self.advance();
Ok(Expr::Literal(Literal {
kind: LiteralKind::Char(c),
span: token.span,
}))
}
TokenKind::Bool(b) => {
let b = *b;
self.advance();
Ok(Expr::Literal(Literal {
kind: LiteralKind::Bool(b),
span: token.span,
}))
}
// Identifiers
TokenKind::Ident(_) => {
let ident = self.parse_ident()?;
Ok(Expr::Var(ident))
}
// Keywords that start expressions
TokenKind::If => self.parse_if_expr(),
TokenKind::Match => self.parse_match_expr(),
TokenKind::Let => self.parse_let_expr(),
TokenKind::Fn => self.parse_lambda_expr(),
TokenKind::Run => self.parse_run_expr(),
TokenKind::Resume => self.parse_resume_expr(),
// Delimiters
TokenKind::LParen => self.parse_tuple_or_paren_expr(),
TokenKind::LBrace => self.parse_block_or_record_expr(),
TokenKind::LBracket => self.parse_list_expr(),
_ => Err(self.error(&format!("Unexpected token: {}", token.kind))),
}
}
fn parse_if_expr(&mut self) -> Result<Expr, ParseError> {
let start = self.current_span();
self.expect(TokenKind::If)?;
let condition = Box::new(self.parse_expr()?);
self.expect(TokenKind::Then)?;
self.skip_newlines();
let then_branch = Box::new(self.parse_expr()?);
self.skip_newlines();
// Else is optional - if missing, synthesize a Unit value
let else_branch = if self.check(TokenKind::Else) {
self.expect(TokenKind::Else)?;
self.skip_newlines();
Box::new(self.parse_expr()?)
} else {
// No else clause - use Unit as the else branch
Box::new(Expr::Literal(Literal {
kind: LiteralKind::Unit,
span: then_branch.span(),
}))
};
let span = start.merge(else_branch.span());
Ok(Expr::If {
condition,
then_branch,
else_branch,
span,
})
}
fn parse_match_expr(&mut self) -> Result<Expr, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Match)?;
let scrutinee = Box::new(self.parse_expr()?);
self.expect(TokenKind::LBrace)?;
self.skip_newlines();
let mut arms = Vec::new();
while !self.check(TokenKind::RBrace) {
arms.push(self.parse_match_arm()?);
self.skip_newlines();
if self.check(TokenKind::Comma) {
self.advance();
}
self.skip_newlines();
}
let end = self.current_span();
self.expect(TokenKind::RBrace)?;
Ok(Expr::Match {
scrutinee,
arms,
span: start.merge(end),
})
}
fn parse_match_arm(&mut self) -> Result<MatchArm, ParseError> {
let start = self.current_span();
let pattern = self.parse_pattern()?;
let guard = if self.check(TokenKind::If) {
self.advance();
Some(self.parse_expr()?)
} else {
None
};
self.expect(TokenKind::Arrow)?;
self.skip_newlines();
let body = self.parse_expr()?;
let span = start.merge(body.span());
Ok(MatchArm {
pattern,
guard,
body,
span,
})
}
fn parse_pattern(&mut self) -> Result<Pattern, ParseError> {
let token = self.peek().clone();
match &token.kind {
TokenKind::Underscore => {
self.advance();
Ok(Pattern::Wildcard(token.span))
}
TokenKind::Int(n) => {
let n = *n;
self.advance();
Ok(Pattern::Literal(Literal {
kind: LiteralKind::Int(n),
span: token.span,
}))
}
TokenKind::String(s) => {
let s = s.clone();
self.advance();
Ok(Pattern::Literal(Literal {
kind: LiteralKind::String(s),
span: token.span,
}))
}
TokenKind::Bool(b) => {
let b = *b;
self.advance();
Ok(Pattern::Literal(Literal {
kind: LiteralKind::Bool(b),
span: token.span,
}))
}
TokenKind::Ident(name) => {
// Check if it starts with uppercase (constructor) or lowercase (variable)
if name.chars().next().map_or(false, |c| c.is_uppercase()) {
self.parse_constructor_pattern()
} else {
let ident = self.parse_ident()?;
Ok(Pattern::Var(ident))
}
}
TokenKind::LParen => self.parse_tuple_pattern(),
TokenKind::LBrace => self.parse_record_pattern(),
_ => Err(self.error("Expected pattern")),
}
}
fn parse_constructor_pattern(&mut self) -> Result<Pattern, ParseError> {
let start = self.current_span();
let name = self.parse_ident()?;
if self.check(TokenKind::LParen) {
self.advance();
let mut fields = Vec::new();
while !self.check(TokenKind::RParen) {
fields.push(self.parse_pattern()?);
if !self.check(TokenKind::RParen) {
self.expect(TokenKind::Comma)?;
}
}
self.expect(TokenKind::RParen)?;
let span = start.merge(self.previous_span());
Ok(Pattern::Constructor { name, fields, span })
} else {
let span = name.span;
Ok(Pattern::Constructor {
name,
fields: Vec::new(),
span,
})
}
}
fn parse_tuple_pattern(&mut self) -> Result<Pattern, ParseError> {
let start = self.current_span();
self.expect(TokenKind::LParen)?;
let mut elements = Vec::new();
while !self.check(TokenKind::RParen) {
elements.push(self.parse_pattern()?);
if !self.check(TokenKind::RParen) {
self.expect(TokenKind::Comma)?;
}
}
self.expect(TokenKind::RParen)?;
let span = start.merge(self.previous_span());
Ok(Pattern::Tuple { elements, span })
}
fn parse_record_pattern(&mut self) -> Result<Pattern, ParseError> {
let start = self.current_span();
self.expect(TokenKind::LBrace)?;
self.skip_newlines();
let mut fields = Vec::new();
while !self.check(TokenKind::RBrace) {
let name = self.parse_ident()?;
let pattern = if self.check(TokenKind::Colon) {
self.advance();
self.parse_pattern()?
} else {
Pattern::Var(name.clone())
};
fields.push((name, pattern));
self.skip_newlines();
if self.check(TokenKind::Comma) {
self.advance();
}
self.skip_newlines();
}
self.expect(TokenKind::RBrace)?;
let span = start.merge(self.previous_span());
Ok(Pattern::Record { fields, span })
}
fn parse_let_expr(&mut self) -> Result<Expr, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Let)?;
// Allow underscore as wildcard pattern (discards the value)
let name = if self.check(TokenKind::Underscore) {
let span = self.current_span();
self.advance();
Ident::new("_".to_string(), span)
} else {
self.parse_ident()?
};
let typ = if self.check(TokenKind::Colon) {
self.advance();
Some(self.parse_type()?)
} else {
None
};
self.expect(TokenKind::Eq)?;
self.skip_newlines();
let value = Box::new(self.parse_expr()?);
// Check for semicolon (let-in style)
self.skip_newlines();
if self.check(TokenKind::Semi) {
self.advance();
self.skip_newlines();
let body = Box::new(self.parse_expr()?);
let span = start.merge(body.span());
Ok(Expr::Let {
name,
typ,
value,
body,
span,
})
} else {
// Let as expression (returns unit)
let span = start.merge(value.span());
let body = Box::new(Expr::Literal(Literal {
kind: LiteralKind::Unit,
span: Span::default(),
}));
Ok(Expr::Let {
name,
typ,
value,
body,
span,
})
}
}
fn parse_lambda_expr(&mut self) -> Result<Expr, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Fn)?;
self.expect(TokenKind::LParen)?;
let params = self.parse_lambda_params()?;
self.expect(TokenKind::RParen)?;
let return_type = if self.check(TokenKind::Colon) {
self.advance();
Some(Box::new(self.parse_type()?))
} else {
None
};
let effects = if self.check(TokenKind::With) {
self.advance();
self.parse_effect_list()?
} else {
Vec::new()
};
self.expect(TokenKind::Arrow)?;
self.skip_newlines();
let body = Box::new(self.parse_expr()?);
let span = start.merge(body.span());
Ok(Expr::Lambda {
params,
return_type,
effects,
body,
span,
})
}
fn parse_lambda_params(&mut self) -> Result<Vec<Parameter>, ParseError> {
let mut params = Vec::new();
while !self.check(TokenKind::RParen) {
let start = self.current_span();
let name = self.parse_ident()?;
let typ = if self.check(TokenKind::Colon) {
self.advance();
self.parse_type()?
} else {
// Infer type later
TypeExpr::Named(Ident::new("_", Span::default()))
};
let span = start.merge(self.previous_span());
params.push(Parameter { name, typ, span });
if !self.check(TokenKind::RParen) {
self.expect(TokenKind::Comma)?;
}
}
Ok(params)
}
fn parse_run_expr(&mut self) -> Result<Expr, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Run)?;
let expr = Box::new(self.parse_call_expr()?);
self.expect(TokenKind::With)?;
self.expect(TokenKind::LBrace)?;
self.skip_newlines();
let mut handlers = Vec::new();
while !self.check(TokenKind::RBrace) {
let effect = self.parse_ident()?;
self.expect(TokenKind::Eq)?;
let handler = self.parse_expr()?;
handlers.push((effect, handler));
self.skip_newlines();
if self.check(TokenKind::Comma) {
self.advance();
}
self.skip_newlines();
}
let end = self.current_span();
self.expect(TokenKind::RBrace)?;
Ok(Expr::Run {
expr,
handlers,
span: start.merge(end),
})
}
fn parse_resume_expr(&mut self) -> Result<Expr, ParseError> {
let start = self.current_span();
self.expect(TokenKind::Resume)?;
self.expect(TokenKind::LParen)?;
let value = Box::new(self.parse_expr()?);
self.expect(TokenKind::RParen)?;
let span = start.merge(self.previous_span());
Ok(Expr::Resume { value, span })
}
fn parse_tuple_or_paren_expr(&mut self) -> Result<Expr, ParseError> {
let start = self.current_span();
self.expect(TokenKind::LParen)?;
if self.check(TokenKind::RParen) {
self.advance();
return Ok(Expr::Literal(Literal {
kind: LiteralKind::Unit,
span: start.merge(self.previous_span()),
}));
}
let first = self.parse_expr()?;
if self.check(TokenKind::Comma) {
// Tuple
let mut elements = vec![first];
while self.check(TokenKind::Comma) {
self.advance();
if self.check(TokenKind::RParen) {
break;
}
elements.push(self.parse_expr()?);
}
self.expect(TokenKind::RParen)?;
let span = start.merge(self.previous_span());
Ok(Expr::Tuple { elements, span })
} else {
// Parenthesized expression
self.expect(TokenKind::RParen)?;
Ok(first)
}
}
fn parse_block_or_record_expr(&mut self) -> Result<Expr, ParseError> {
let start = self.current_span();
self.expect(TokenKind::LBrace)?;
self.skip_newlines();
// Empty block
if self.check(TokenKind::RBrace) {
self.advance();
return Ok(Expr::Literal(Literal {
kind: LiteralKind::Unit,
span: start.merge(self.previous_span()),
}));
}
// Check if it's a record (ident: expr) or block
if matches!(self.peek_kind(), TokenKind::Ident(_)) {
let lookahead = self.tokens.get(self.pos + 1).map(|t| &t.kind);
if matches!(lookahead, Some(TokenKind::Colon)) {
return self.parse_record_expr_rest(start);
}
}
// It's a block
self.parse_block_rest(start)
}
fn parse_record_expr_rest(&mut self, start: Span) -> Result<Expr, ParseError> {
let mut fields = Vec::new();
while !self.check(TokenKind::RBrace) {
let name = self.parse_ident()?;
self.expect(TokenKind::Colon)?;
let value = self.parse_expr()?;
fields.push((name, value));
self.skip_newlines();
if self.check(TokenKind::Comma) {
self.advance();
}
self.skip_newlines();
}
self.expect(TokenKind::RBrace)?;
let span = start.merge(self.previous_span());
Ok(Expr::Record { fields, span })
}
fn parse_block_rest(&mut self, start: Span) -> Result<Expr, ParseError> {
let mut statements = Vec::new();
loop {
self.skip_newlines();
if self.check(TokenKind::RBrace) {
break;
}
if self.check(TokenKind::Let) {
// Let statement
let let_start = self.current_span();
self.advance();
// Allow underscore as wildcard pattern (discards the value)
let name = if self.check(TokenKind::Underscore) {
let span = self.current_span();
self.advance();
Ident::new("_".to_string(), span)
} else {
self.parse_ident()?
};
let typ = if self.check(TokenKind::Colon) {
self.advance();
Some(self.parse_type()?)
} else {
None
};
self.expect(TokenKind::Eq)?;
self.skip_newlines();
let value = self.parse_expr()?;
let span = let_start.merge(value.span());
statements.push(Statement::Let {
name,
typ,
value,
span,
});
} else {
// Expression statement
let expr = self.parse_expr()?;
statements.push(Statement::Expr(expr));
}
self.skip_newlines();
// Optional semicolon
if self.check(TokenKind::Semi) {
self.advance();
}
self.skip_newlines();
}
// The last statement is the result
let result = if let Some(Statement::Expr(expr)) = statements.pop() {
Box::new(expr)
} else {
Box::new(Expr::Literal(Literal {
kind: LiteralKind::Unit,
span: Span::default(),
}))
};
self.expect(TokenKind::RBrace)?;
let span = start.merge(self.previous_span());
Ok(Expr::Block {
statements,
result,
span,
})
}
fn parse_list_expr(&mut self) -> Result<Expr, ParseError> {
let start = self.current_span();
self.expect(TokenKind::LBracket)?;
self.skip_newlines();
let mut elements = Vec::new();
while !self.check(TokenKind::RBracket) {
elements.push(self.parse_expr()?);
self.skip_newlines();
if !self.check(TokenKind::RBracket) {
self.expect(TokenKind::Comma)?;
self.skip_newlines();
}
}
self.expect(TokenKind::RBracket)?;
let span = start.merge(self.previous_span());
Ok(Expr::List { elements, span })
}
/// Desugar an interpolated string into concatenation with toString calls
/// "Hello, {name}!" becomes "Hello, " + toString(name) + "!"
fn desugar_interpolated_string(
&mut self,
parts: &[StringPart],
span: Span,
) -> Result<Expr, ParseError> {
let mut exprs: Vec<Expr> = Vec::new();
for part in parts {
match part {
StringPart::Literal(s) => {
exprs.push(Expr::Literal(Literal {
kind: LiteralKind::String(s.clone()),
span,
}));
}
StringPart::Expr(expr_text) => {
// Parse the expression text
let lexer = Lexer::new(expr_text);
let tokens = lexer.tokenize().map_err(|e| ParseError {
message: format!("Lexer error in interpolation: {}", e.message),
span,
})?;
let mut parser = Parser::new(tokens);
let inner_expr = parser.parse_expr().map_err(|e| ParseError {
message: format!("Parse error in interpolation: {}", e.message),
span,
})?;
// Wrap the expression in toString() call
let to_string_call = Expr::Call {
func: Box::new(Expr::Var(Ident::new("toString".to_string(), span))),
args: vec![inner_expr],
span,
};
exprs.push(to_string_call);
}
}
}
// Chain all expressions with + operator
if exprs.is_empty() {
return Ok(Expr::Literal(Literal {
kind: LiteralKind::String(String::new()),
span,
}));
}
let mut result = exprs.remove(0);
for expr in exprs {
result = Expr::BinaryOp {
op: BinaryOp::Add,
left: Box::new(result),
right: Box::new(expr),
span,
};
}
Ok(result)
}
// Helper methods
fn parse_ident(&mut self) -> Result<Ident, ParseError> {
let token = self.peek().clone();
match &token.kind {
TokenKind::Ident(name) => {
let name = name.clone();
self.advance();
Ok(Ident::new(name, token.span))
}
_ => Err(self.error("Expected identifier")),
}
}
fn peek(&self) -> &Token {
self.tokens.get(self.pos).unwrap_or(&self.eof_token)
}
fn peek_kind(&self) -> TokenKind {
self.peek().kind.clone()
}
fn peek_next(&self) -> &Token {
self.tokens.get(self.pos + 1).unwrap_or(&self.eof_token)
}
fn peek_next_kind(&self) -> TokenKind {
self.peek_next().kind.clone()
}
fn advance(&mut self) -> &Token {
if !self.is_at_end() {
self.pos += 1;
}
self.tokens.get(self.pos - 1).unwrap()
}
fn check(&self, kind: TokenKind) -> bool {
std::mem::discriminant(&self.peek_kind()) == std::mem::discriminant(&kind)
}
fn expect(&mut self, kind: TokenKind) -> Result<&Token, ParseError> {
if self.check(kind.clone()) {
Ok(self.advance())
} else {
Err(self.error(&format!("Expected {}, found {}", kind, self.peek_kind())))
}
}
fn is_at_end(&self) -> bool {
matches!(self.peek_kind(), TokenKind::Eof)
}
fn skip_newlines(&mut self) {
while self.check(TokenKind::Newline) {
self.advance();
}
}
fn current_span(&self) -> Span {
self.peek().span
}
fn previous_span(&self) -> Span {
self.tokens
.get(self.pos.saturating_sub(1))
.map(|t| t.span)
.unwrap_or_default()
}
fn error(&self, message: &str) -> ParseError {
ParseError {
message: message.to_string(),
span: self.current_span(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_function() {
let source = "fn add(a: Int, b: Int): Int = a + b";
let program = Parser::parse_source(source).unwrap();
assert_eq!(program.declarations.len(), 1);
}
#[test]
fn test_parse_effect() {
let source = r#"
effect Console {
fn print(msg: String): Unit
fn read(): String
}
"#;
let program = Parser::parse_source(source).unwrap();
assert_eq!(program.declarations.len(), 1);
}
#[test]
fn test_parse_if_expr() {
let source = "fn max(a: Int, b: Int): Int = if a > b then a else b";
let program = Parser::parse_source(source).unwrap();
assert_eq!(program.declarations.len(), 1);
}
#[test]
fn test_parse_lambda() {
let source = "let f = fn(x: Int): Int => x + 1";
let program = Parser::parse_source(source).unwrap();
assert_eq!(program.declarations.len(), 1);
}
#[test]
fn test_parse_import() {
let source = r#"
import std/list
fn foo(): Int = 42
"#;
let program = Parser::parse_source(source).unwrap();
assert_eq!(program.imports.len(), 1);
assert_eq!(program.imports[0].path.segments.len(), 2);
assert_eq!(program.imports[0].path.segments[0].name, "std");
assert_eq!(program.imports[0].path.segments[1].name, "list");
assert_eq!(program.declarations.len(), 1);
}
#[test]
fn test_parse_import_alias() {
let source = "import std/list as L";
let program = Parser::parse_source(source).unwrap();
assert_eq!(program.imports.len(), 1);
assert!(program.imports[0].alias.is_some());
assert_eq!(program.imports[0].alias.as_ref().unwrap().name, "L");
}
#[test]
fn test_parse_import_selective() {
let source = "import std/list.{map, filter, fold}";
let program = Parser::parse_source(source).unwrap();
assert_eq!(program.imports.len(), 1);
assert!(program.imports[0].items.is_some());
let items = program.imports[0].items.as_ref().unwrap();
assert_eq!(items.len(), 3);
assert_eq!(items[0].name, "map");
assert_eq!(items[1].name, "filter");
assert_eq!(items[2].name, "fold");
}
#[test]
fn test_parse_pub_function() {
let source = "pub fn add(a: Int, b: Int): Int = a + b";
let program = Parser::parse_source(source).unwrap();
assert_eq!(program.declarations.len(), 1);
if let Declaration::Function(f) = &program.declarations[0] {
assert_eq!(f.visibility, Visibility::Public);
} else {
panic!("Expected function declaration");
}
}
#[test]
fn test_parse_pub_let() {
let source = "pub let x = 42";
let program = Parser::parse_source(source).unwrap();
assert_eq!(program.declarations.len(), 1);
if let Declaration::Let(l) = &program.declarations[0] {
assert_eq!(l.visibility, Visibility::Public);
} else {
panic!("Expected let declaration");
}
}
#[test]
fn test_parse_private_by_default() {
let source = "fn foo(): Int = 42";
let program = Parser::parse_source(source).unwrap();
if let Declaration::Function(f) = &program.declarations[0] {
assert_eq!(f.visibility, Visibility::Private);
} else {
panic!("Expected function declaration");
}
}
// Schema Evolution tests
#[test]
fn test_parse_versioned_type() {
let source = "type User @v1 { name: String, email: String }";
let program = Parser::parse_source(source).unwrap();
assert_eq!(program.declarations.len(), 1);
if let Declaration::Type(t) = &program.declarations[0] {
assert_eq!(t.name.name, "User");
assert!(t.version.is_some());
assert_eq!(t.version.unwrap().number, 1);
} else {
panic!("Expected type declaration");
}
}
#[test]
fn test_parse_versioned_type_v2() {
let source = "type User @v2 { name: String, email: String, age: Int }";
let program = Parser::parse_source(source).unwrap();
if let Declaration::Type(t) = &program.declarations[0] {
assert!(t.version.is_some());
assert_eq!(t.version.unwrap().number, 2);
} else {
panic!("Expected type declaration");
}
}
#[test]
fn test_parse_versioned_type_expr() {
let source = "let user: User @v2 = x";
let program = Parser::parse_source(source).unwrap();
if let Declaration::Let(l) = &program.declarations[0] {
if let Some(TypeExpr::Versioned { base, constraint }) = &l.typ {
if let TypeExpr::Named(name) = base.as_ref() {
assert_eq!(name.name, "User");
} else {
panic!("Expected Named type");
}
if let VersionConstraint::Exact(v) = constraint {
assert_eq!(v.number, 2);
} else {
panic!("Expected Exact version constraint");
}
} else {
panic!("Expected Versioned type expression");
}
} else {
panic!("Expected let declaration");
}
}
#[test]
fn test_parse_version_at_least() {
let source = "let user: User @v2+ = x";
let program = Parser::parse_source(source).unwrap();
if let Declaration::Let(l) = &program.declarations[0] {
if let Some(TypeExpr::Versioned { constraint, .. }) = &l.typ {
if let VersionConstraint::AtLeast(v) = constraint {
assert_eq!(v.number, 2);
} else {
panic!("Expected AtLeast version constraint");
}
} else {
panic!("Expected Versioned type expression");
}
} else {
panic!("Expected let declaration");
}
}
#[test]
fn test_parse_version_latest() {
let source = "let user: User @latest = x";
let program = Parser::parse_source(source).unwrap();
if let Declaration::Let(l) = &program.declarations[0] {
if let Some(TypeExpr::Versioned { constraint, .. }) = &l.typ {
assert!(matches!(constraint, VersionConstraint::Latest(_)));
} else {
panic!("Expected Versioned type expression");
}
} else {
panic!("Expected let declaration");
}
}
#[test]
fn test_parse_type_with_migration() {
let source = r#"
type User @v2 {
name: String,
email: String,
age: Int,
from @v1 = { name: old.name, email: old.email, age: 0 }
}
"#;
let program = Parser::parse_source(source).unwrap();
if let Declaration::Type(t) = &program.declarations[0] {
assert_eq!(t.name.name, "User");
assert_eq!(t.version.unwrap().number, 2);
assert_eq!(t.migrations.len(), 1);
assert_eq!(t.migrations[0].from_version.number, 1);
} else {
panic!("Expected type declaration");
}
}
// ============ Behavioral Properties Tests ============
#[test]
fn test_parse_function_is_pure() {
let source = "fn add(a: Int, b: Int): Int is pure = a + b";
let program = Parser::parse_source(source).unwrap();
if let Declaration::Function(f) = &program.declarations[0] {
assert_eq!(f.name.name, "add");
assert_eq!(f.properties.len(), 1);
assert_eq!(f.properties[0], BehavioralProperty::Pure);
} else {
panic!("Expected function declaration");
}
}
#[test]
fn test_parse_function_multiple_properties() {
let source = "fn double(x: Int): Int is pure, is total = x * 2";
let program = Parser::parse_source(source).unwrap();
if let Declaration::Function(f) = &program.declarations[0] {
assert_eq!(f.name.name, "double");
assert_eq!(f.properties.len(), 2);
assert!(f.properties.contains(&BehavioralProperty::Pure));
assert!(f.properties.contains(&BehavioralProperty::Total));
} else {
panic!("Expected function declaration");
}
}
#[test]
fn test_parse_function_with_effects_and_properties() {
let source = "fn log(msg: String): Unit with {Logger} is deterministic = Logger.log(msg)";
let program = Parser::parse_source(source).unwrap();
if let Declaration::Function(f) = &program.declarations[0] {
assert_eq!(f.name.name, "log");
assert_eq!(f.effects.len(), 1);
assert_eq!(f.effects[0].name, "Logger");
assert_eq!(f.properties.len(), 1);
assert_eq!(f.properties[0], BehavioralProperty::Deterministic);
} else {
panic!("Expected function declaration");
}
}
#[test]
fn test_parse_function_idempotent() {
let source = "fn normalize(s: String): String is idempotent = s";
let program = Parser::parse_source(source).unwrap();
if let Declaration::Function(f) = &program.declarations[0] {
assert_eq!(f.properties.len(), 1);
assert_eq!(f.properties[0], BehavioralProperty::Idempotent);
} else {
panic!("Expected function declaration");
}
}
#[test]
fn test_parse_function_commutative() {
let source = "fn add(a: Int, b: Int): Int is commutative = a + b";
let program = Parser::parse_source(source).unwrap();
if let Declaration::Function(f) = &program.declarations[0] {
assert_eq!(f.properties.len(), 1);
assert_eq!(f.properties[0], BehavioralProperty::Commutative);
} else {
panic!("Expected function declaration");
}
}
#[test]
fn test_parse_where_property_constraint() {
let source = "fn retry(action: F): Int where F is idempotent = 0";
let program = Parser::parse_source(source).unwrap();
if let Declaration::Function(f) = &program.declarations[0] {
assert_eq!(f.name.name, "retry");
assert_eq!(f.where_clauses.len(), 1);
if let WhereClause::PropertyConstraint {
type_param,
property,
..
} = &f.where_clauses[0]
{
assert_eq!(type_param.name, "F");
assert_eq!(*property, BehavioralProperty::Idempotent);
} else {
panic!("Expected PropertyConstraint");
}
} else {
panic!("Expected function declaration");
}
}
#[test]
fn test_parse_where_result_refinement() {
let source = "fn abs(x: Int): Int where result >= 0 = if x < 0 then 0 - x else x";
let program = Parser::parse_source(source).unwrap();
if let Declaration::Function(f) = &program.declarations[0] {
assert_eq!(f.name.name, "abs");
assert_eq!(f.where_clauses.len(), 1);
if let WhereClause::ResultRefinement { predicate, .. } = &f.where_clauses[0] {
// Check that the predicate is a binary comparison
if let Expr::BinaryOp { op, .. } = predicate.as_ref() {
assert_eq!(*op, BinaryOp::Ge);
} else {
panic!("Expected BinaryOp in refinement");
}
} else {
panic!("Expected ResultRefinement");
}
} else {
panic!("Expected function declaration");
}
}
#[test]
fn test_parse_all_behavioral_features() {
// Single-line version to avoid newline issues
let source = "fn process(f: F, x: Int): Int with {Logger} is pure, is total where F is deterministic = f(x)";
let program = Parser::parse_source(source).unwrap();
if let Declaration::Function(func) = &program.declarations[0] {
assert_eq!(func.name.name, "process");
assert_eq!(func.effects.len(), 1);
assert_eq!(func.properties.len(), 2);
assert!(func.properties.contains(&BehavioralProperty::Pure));
assert!(func.properties.contains(&BehavioralProperty::Total));
assert_eq!(func.where_clauses.len(), 1);
} else {
panic!("Expected function declaration");
}
}
}