- Tuple index: `pair.0`, `pair.1` syntax across parser, typechecker, interpreter, C/JS backends, formatter, linter, and symbol table - Multi-line function args: allow newlines inside argument lists - Fix effect unification for callback parameters (empty expected effects means "no constraint", not "must be pure") Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2828 lines
87 KiB
Rust
2828 lines
87 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, total, idempotent, etc.
|
|
/// Supports: `is pure`, `is pure is total`, `is pure, total`, `is pure, is total`
|
|
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);
|
|
|
|
// After first property, allow comma-separated list without repeating 'is'
|
|
while self.check(TokenKind::Comma) {
|
|
self.advance(); // consume comma
|
|
// Allow optional 'is' after comma: `is pure, is total` or `is pure, total`
|
|
if self.check(TokenKind::Is) {
|
|
self.advance();
|
|
}
|
|
let property = self.parse_single_property()?;
|
|
properties.push(property);
|
|
}
|
|
}
|
|
|
|
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();
|
|
|
|
// Check for tuple index access: expr.0, expr.1, etc.
|
|
if let TokenKind::Int(n) = self.peek_kind() {
|
|
let index = n as usize;
|
|
self.advance();
|
|
let span = start.merge(self.previous_span());
|
|
expr = Expr::TupleIndex {
|
|
object: Box::new(expr),
|
|
index,
|
|
span,
|
|
};
|
|
continue;
|
|
}
|
|
|
|
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();
|
|
self.skip_newlines();
|
|
|
|
while !self.check(TokenKind::RParen) {
|
|
args.push(self.parse_expr()?);
|
|
self.skip_newlines();
|
|
if !self.check(TokenKind::RParen) {
|
|
self.expect(TokenKind::Comma)?;
|
|
self.skip_newlines();
|
|
}
|
|
}
|
|
|
|
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::Char(c) => {
|
|
let c = *c;
|
|
self.advance();
|
|
Ok(Pattern::Literal(Literal {
|
|
kind: LiteralKind::Char(c),
|
|
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");
|
|
}
|
|
}
|
|
}
|