1763 lines
64 KiB
Rust
1763 lines
64 KiB
Rust
use crate::ast::*;
|
|
use crate::lexer::Token;
|
|
|
|
use std::iter::Peekable;
|
|
use std::ops::Range;
|
|
use std::vec::IntoIter;
|
|
|
|
type TokenIter = Peekable<IntoIter<(Token, Range<usize>)>>;
|
|
|
|
pub struct Parser {
|
|
pub file: String,
|
|
pub tokens: TokenIter,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ParseError {
|
|
pub message: String,
|
|
pub span: Span,
|
|
}
|
|
|
|
impl Parser {
|
|
pub fn new(file: String, tokens: Vec<(Token, Range<usize>)>) -> Self {
|
|
Parser {
|
|
file,
|
|
tokens: tokens.into_iter().peekable(),
|
|
}
|
|
}
|
|
|
|
// Parse the entire file into a list of AST nodes
|
|
pub fn parse(&mut self) -> Result<Vec<ASTNode>, ParseError> {
|
|
let mut nodes = Vec::new();
|
|
|
|
while self.peek().is_some() {
|
|
nodes.push(self.parse_top_level()?);
|
|
}
|
|
|
|
Ok(nodes)
|
|
}
|
|
|
|
fn peek(&mut self) -> Option<&Token> {
|
|
self.tokens.peek().map(|(token, _)| token)
|
|
}
|
|
|
|
fn peek_span(&mut self) -> Option<Range<usize>> {
|
|
self.tokens.peek().map(|(_, span)| span.clone())
|
|
}
|
|
|
|
fn next(&mut self) -> Option<(Token, Range<usize>)> {
|
|
self.tokens.next()
|
|
}
|
|
|
|
fn expect(&mut self, expected: Token) -> Result<Range<usize>, ParseError> {
|
|
match self.next() {
|
|
Some((token, span))
|
|
if std::mem::discriminant(&token) == std::mem::discriminant(&expected) =>
|
|
{
|
|
Ok(span)
|
|
}
|
|
Some((token, span)) => Err(ParseError {
|
|
message: format!("Expected {:?}, found {:?}", expected, token),
|
|
span: Span::new(&span, self.file.clone()),
|
|
}),
|
|
None => Err(ParseError {
|
|
message: format!("Expected {:?}, found EOF", expected),
|
|
span: Span::new(&(0..0), self.file.clone()),
|
|
}),
|
|
}
|
|
}
|
|
|
|
fn error<T>(&self, msg: String, span: Range<usize>) -> Result<T, ParseError> {
|
|
Err(ParseError {
|
|
message: msg,
|
|
span: Span::new(&span, self.file.clone()),
|
|
})
|
|
}
|
|
|
|
fn parse_top_level(&mut self) -> Result<ASTNode, ParseError> {
|
|
let mut attributes = Vec::new();
|
|
|
|
// Parse any leading attributes
|
|
while matches!(self.peek(), Some(Token::At)) {
|
|
attributes.push(self.parse_attribute()?);
|
|
}
|
|
|
|
let start = self.peek_span().unwrap_or(0..0).start;
|
|
let token = self.peek().cloned();
|
|
match token {
|
|
Some(Token::KeywordUse) => {
|
|
self.next();
|
|
let path = match self.next() {
|
|
Some((Token::String(s), _)) => s,
|
|
Some((_, span)) => {
|
|
return self.error("Expected string after 'use'".to_string(), span);
|
|
}
|
|
None => {
|
|
return self.error("Expected string after 'use'".to_string(), start..start);
|
|
}
|
|
};
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(ASTNode {
|
|
kind: ASTNodeKind::Use(path),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes,
|
|
})
|
|
}
|
|
Some(Token::KeywordFn) => {
|
|
self.next();
|
|
let func = self.parse_function()?;
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(ASTNode {
|
|
kind: ASTNodeKind::Function(func),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes,
|
|
})
|
|
}
|
|
Some(Token::KeywordStruct) => {
|
|
self.next();
|
|
let struct_def = self.parse_struct()?;
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(ASTNode {
|
|
kind: ASTNodeKind::Struct(struct_def),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes,
|
|
})
|
|
}
|
|
Some(Token::KeywordEnum) => {
|
|
self.next();
|
|
let enum_def = self.parse_enum()?;
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(ASTNode {
|
|
kind: ASTNodeKind::Enum(enum_def),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes,
|
|
})
|
|
}
|
|
Some(Token::KeywordImpl) => {
|
|
self.next();
|
|
let impl_def = self.parse_impl()?;
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(ASTNode {
|
|
kind: ASTNodeKind::Impl(impl_def),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes,
|
|
})
|
|
}
|
|
Some(Token::KeywordTrait) => {
|
|
self.next();
|
|
let trait_def = self.parse_trait()?;
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(ASTNode {
|
|
kind: ASTNodeKind::Trait(trait_def),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes,
|
|
})
|
|
}
|
|
Some(Token::KeywordExtern) => {
|
|
self.next();
|
|
let extern_def = self.parse_extern()?;
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(ASTNode {
|
|
kind: ASTNodeKind::Extern(extern_def),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes,
|
|
})
|
|
}
|
|
Some(Token::KeywordLoad) => {
|
|
self.next();
|
|
let load_def = self.parse_load()?;
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(ASTNode {
|
|
kind: ASTNodeKind::Load(load_def),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes,
|
|
})
|
|
}
|
|
Some(token) => {
|
|
let span = self.peek_span().unwrap_or(start..start);
|
|
self.error(format!("Unexpected token at top level: {:?}", token), span)
|
|
}
|
|
None => self.error("Unexpected EOF".to_string(), start..start),
|
|
}
|
|
}
|
|
|
|
fn parse_attribute(&mut self) -> Result<Attribute, ParseError> {
|
|
self.expect(Token::At)?;
|
|
let start = self.peek_span().unwrap_or(0..0).start;
|
|
let name = match self.next() {
|
|
Some((Token::Variable(name), _)) => name,
|
|
Some((_, span)) => return self.error("Expected attribute name".to_string(), span),
|
|
None => return self.error("Expected attribute name".to_string(), start..start),
|
|
};
|
|
|
|
// Parentheses are optional
|
|
let mut args = vec![];
|
|
if matches!(self.peek(), Some(Token::LParen)) {
|
|
self.next();
|
|
loop {
|
|
let token = self.peek().cloned();
|
|
match token {
|
|
Some(Token::RParen) => {
|
|
self.next();
|
|
break;
|
|
}
|
|
Some(Token::String(s)) => {
|
|
self.next();
|
|
args.push(AttributeArg::Literal(s));
|
|
}
|
|
Some(Token::Variable(id)) => {
|
|
self.next();
|
|
let next_token = self.peek().cloned();
|
|
if matches!(next_token, Some(Token::Assign)) {
|
|
self.next();
|
|
match self.next() {
|
|
Some((Token::Variable(val), _)) => {
|
|
args.push(AttributeArg::KeyValue(id, val))
|
|
}
|
|
Some((_, span)) => {
|
|
return self.error("Expected value after =".to_string(), span);
|
|
}
|
|
None => {
|
|
return self
|
|
.error("Expected value after =".to_string(), start..start);
|
|
}
|
|
}
|
|
} else {
|
|
args.push(AttributeArg::Value(id));
|
|
}
|
|
}
|
|
Some(token) => {
|
|
let span = self.peek_span().unwrap_or(start..start);
|
|
return self
|
|
.error(format!("Unexpected token in attribute: {:?}", token), span);
|
|
}
|
|
None => {
|
|
return self.error("Expected attribute argument".to_string(), start..start);
|
|
}
|
|
}
|
|
let next_token = self.peek().cloned();
|
|
if matches!(next_token, Some(Token::Comma)) {
|
|
self.next();
|
|
} else if matches!(next_token, Some(Token::RParen)) {
|
|
// ok
|
|
} else {
|
|
{
|
|
let span = self.peek_span().unwrap_or(start..start);
|
|
return self.error("Expected , or )".to_string(), span);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Attribute {
|
|
name,
|
|
args,
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
})
|
|
}
|
|
|
|
fn parse_function(&mut self) -> Result<Function, ParseError> {
|
|
let start = self.peek_span().unwrap_or(0..0).start;
|
|
let name = match self.next() {
|
|
Some((Token::Variable(n), _)) => n,
|
|
Some((_, span)) => return self.error("Expected function name".to_string(), span),
|
|
None => return self.error("Expected function name".to_string(), start..start),
|
|
};
|
|
|
|
// Parse type parameters if present
|
|
let parameters = if matches!(self.peek(), Some(Token::Less)) {
|
|
self.next();
|
|
self.parse_parameters()?
|
|
} else {
|
|
Vec::new()
|
|
};
|
|
|
|
// Parse function arguments
|
|
self.expect(Token::LParen)?;
|
|
let mut args = Vec::new();
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::RParen)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
|
|
let arg_name = match self.next() {
|
|
Some((Token::Variable(n), _)) => n,
|
|
Some((_, span)) => return self.error("Expected argument name".to_string(), span),
|
|
None => return self.error("Expected argument name".to_string(), start..start),
|
|
};
|
|
|
|
let arg_type = if matches!(self.peek(), Some(Token::Colon)) {
|
|
self.next();
|
|
Some(self.parse_type_annot()?)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
args.push((arg_name, arg_type));
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
} else if !matches!(self.peek(), Some(Token::RParen)) {
|
|
let span = self.peek_span().unwrap_or(start..start);
|
|
return self.error("Expected , or )".to_string(), span);
|
|
}
|
|
}
|
|
|
|
// Parse return type if present
|
|
let return_type = if matches!(self.peek(), Some(Token::Arrow)) {
|
|
self.next();
|
|
Some(self.parse_type_annot()?)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
// Parse body expression
|
|
let body = self.parse_expr()?;
|
|
|
|
Ok(Function {
|
|
name,
|
|
parameters,
|
|
args,
|
|
return_type,
|
|
body,
|
|
})
|
|
}
|
|
|
|
fn parse_struct(&mut self) -> Result<Struct, ParseError> {
|
|
let start = self.peek_span().unwrap_or(0..0).start;
|
|
let name = match self.next() {
|
|
Some((Token::Variable(n), _)) => n,
|
|
Some((_, span)) => return self.error("Expected struct name".to_string(), span),
|
|
None => return self.error("Expected struct name".to_string(), start..start),
|
|
};
|
|
|
|
// Parse type parameters if present
|
|
let parameters = if matches!(self.peek(), Some(Token::Less)) {
|
|
self.next();
|
|
self.parse_parameters()?
|
|
} else {
|
|
Vec::new()
|
|
};
|
|
|
|
// Parse fields
|
|
let mut fields = Vec::new();
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::KeywordEnd)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
|
|
let field_start = self.peek_span().unwrap_or(0..0).start;
|
|
let field_name = match self.next() {
|
|
Some((Token::Variable(n), _)) => n,
|
|
Some((_, span)) => return self.error("Expected field name".to_string(), span),
|
|
None => return self.error("Expected field name".to_string(), start..start),
|
|
};
|
|
|
|
self.expect(Token::Colon)?;
|
|
let field_type = self.parse_type_annot()?;
|
|
let field_end = self.peek_span().unwrap_or(field_start..field_start).start;
|
|
|
|
fields.push(Field {
|
|
name: field_name,
|
|
field_type,
|
|
span: Span::new(&(field_start..field_end), self.file.clone()),
|
|
});
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
|
|
Ok(Struct {
|
|
name,
|
|
parameters,
|
|
fields,
|
|
})
|
|
}
|
|
|
|
fn parse_enum(&mut self) -> Result<Enum, ParseError> {
|
|
let start = self.peek_span().unwrap_or(0..0).start;
|
|
let name = match self.next() {
|
|
Some((Token::Variable(n), _)) => n,
|
|
Some((_, span)) => return self.error("Expected enum name".to_string(), span),
|
|
None => return self.error("Expected enum name".to_string(), start..start),
|
|
};
|
|
|
|
// Parse type parameters if present
|
|
let parameters = if matches!(self.peek(), Some(Token::Less)) {
|
|
self.next();
|
|
self.parse_parameters()?
|
|
} else {
|
|
Vec::new()
|
|
};
|
|
|
|
// Parse variants
|
|
let mut variants = Vec::new();
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::KeywordEnd)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
|
|
let variant_start = self.peek_span().unwrap_or(0..0).start;
|
|
let variant_name = match self.next() {
|
|
Some((Token::Variable(n), _)) => n,
|
|
Some((_, span)) => return self.error("Expected variant name".to_string(), span),
|
|
None => return self.error("Expected variant name".to_string(), start..start),
|
|
};
|
|
|
|
let mut fields = Vec::new();
|
|
if matches!(self.peek(), Some(Token::LParen)) {
|
|
self.next();
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::RParen)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
fields.push(self.parse_type_annot()?);
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
}
|
|
|
|
let variant_end = self
|
|
.peek_span()
|
|
.unwrap_or(variant_start..variant_start)
|
|
.start;
|
|
variants.push(Variant {
|
|
name: variant_name,
|
|
fields,
|
|
span: Span::new(&(variant_start..variant_end), self.file.clone()),
|
|
});
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
|
|
Ok(Enum {
|
|
name,
|
|
parameters,
|
|
variants,
|
|
})
|
|
}
|
|
|
|
fn parse_impl(&mut self) -> Result<Impl, ParseError> {
|
|
let start = self.peek_span().unwrap_or(0..0).start;
|
|
|
|
// Parse impl target as a type (could be generic like Option<int>)
|
|
let target_type = self.parse_type_annot()?;
|
|
|
|
// Extract the base type name from the type annotation
|
|
let target = match target_type {
|
|
TypeAnnot::Var(name) => name,
|
|
TypeAnnot::Cons(name, _) => name,
|
|
_ => {
|
|
return self.error(
|
|
"Expected type name for impl target".to_string(),
|
|
start..start,
|
|
);
|
|
}
|
|
};
|
|
|
|
// Parse optional trait name
|
|
let trait_name = if matches!(self.peek(), Some(Token::Colon)) {
|
|
self.next();
|
|
match self.next() {
|
|
Some((Token::Variable(n), _)) => Some(n),
|
|
Some((_, span)) => return self.error("Expected trait name".to_string(), span),
|
|
None => return self.error("Expected trait name".to_string(), start..start),
|
|
}
|
|
} else {
|
|
None
|
|
};
|
|
|
|
// Parse methods
|
|
let mut methods = Vec::new();
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::KeywordEnd)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
|
|
self.expect(Token::KeywordFn)?;
|
|
methods.push(self.parse_function()?);
|
|
}
|
|
|
|
Ok(Impl {
|
|
target,
|
|
trait_name,
|
|
methods,
|
|
})
|
|
}
|
|
|
|
fn parse_trait(&mut self) -> Result<Trait, ParseError> {
|
|
let start = self.peek_span().unwrap_or(0..0).start;
|
|
let name = match self.next() {
|
|
Some((Token::Variable(n), _)) => n,
|
|
Some((_, span)) => return self.error("Expected trait name".to_string(), span),
|
|
None => return self.error("Expected trait name".to_string(), start..start),
|
|
};
|
|
|
|
// Parse type parameters if present
|
|
let parameters = if matches!(self.peek(), Some(Token::Less)) {
|
|
self.next();
|
|
self.parse_parameters()?
|
|
} else {
|
|
Vec::new()
|
|
};
|
|
|
|
// Parse methods
|
|
let mut methods = Vec::new();
|
|
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::KeywordEnd)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
|
|
if matches!(self.peek(), Some(Token::KeywordFn)) {
|
|
self.next();
|
|
methods.push(self.parse_function_signature()?);
|
|
|
|
// Optional comma between methods
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
Ok(Trait {
|
|
name,
|
|
methods,
|
|
parameters,
|
|
associated_types: Vec::new(),
|
|
})
|
|
}
|
|
|
|
fn parse_extern(&mut self) -> Result<Extern, ParseError> {
|
|
let start = self.peek_span().unwrap_or(0..0).start;
|
|
let name = match self.next() {
|
|
Some((Token::Variable(n), _)) => n,
|
|
Some((_, span)) => return self.error("Expected extern name".to_string(), span),
|
|
None => return self.error("Expected extern name".to_string(), start..start),
|
|
};
|
|
|
|
// Parse argument types (with optional parameter names)
|
|
self.expect(Token::LParen)?;
|
|
let mut args = Vec::new();
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::RParen)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
|
|
args.push(self.parse_type_annot()?);
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
|
|
// Parse return type
|
|
self.expect(Token::Arrow)?;
|
|
let return_type = self.parse_type_annot()?;
|
|
|
|
// Parse from clause
|
|
self.expect(Token::KeywordFrom)?;
|
|
let from = match self.next() {
|
|
Some((Token::Variable(n), _)) => n,
|
|
Some((_, span)) => return self.error("Expected library identifier".to_string(), span),
|
|
None => return self.error("Expected library identifier".to_string(), start..start),
|
|
};
|
|
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Extern {
|
|
name,
|
|
args,
|
|
return_type,
|
|
from,
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
})
|
|
}
|
|
|
|
fn parse_load(&mut self) -> Result<Load, ParseError> {
|
|
let start = self.peek_span().unwrap_or(0..0).start;
|
|
let library = match self.next() {
|
|
Some((Token::String(s), _)) => s,
|
|
Some((_, span)) => return self.error("Expected library name".to_string(), span),
|
|
None => return self.error("Expected library name".to_string(), start..start),
|
|
};
|
|
|
|
self.expect(Token::KeywordAs)?;
|
|
let alias = match self.next() {
|
|
Some((Token::Variable(a), _)) => a,
|
|
Some((_, span)) => return self.error("Expected alias".to_string(), span),
|
|
None => return self.error("Expected alias".to_string(), start..start),
|
|
};
|
|
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Load {
|
|
library,
|
|
alias,
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
})
|
|
}
|
|
|
|
fn parse_parameters(&mut self) -> Result<Vec<Parameter>, ParseError> {
|
|
let start = self.peek_span().unwrap_or(0..0).start;
|
|
let mut params = Vec::new();
|
|
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::Greater)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
|
|
let param_start = self.peek_span().unwrap_or(0..0).start;
|
|
let param_name = match self.next() {
|
|
Some((Token::Variable(n), _)) => n,
|
|
Some((_, span)) => return self.error("Expected parameter name".to_string(), span),
|
|
None => return self.error("Expected parameter name".to_string(), start..start),
|
|
};
|
|
|
|
let bounds = if matches!(self.peek(), Some(Token::Colon)) {
|
|
self.next();
|
|
self.parse_trait_bounds()?
|
|
} else {
|
|
Vec::new()
|
|
};
|
|
|
|
let kind = if matches!(self.peek(), Some(Token::Colon)) {
|
|
self.next();
|
|
Some(self.parse_kind()?)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let param_end = self.peek_span().unwrap_or(param_start..param_start).end;
|
|
params.push(Parameter {
|
|
name: param_name,
|
|
bounds,
|
|
kind,
|
|
span: Span::new(&(param_start..param_end), self.file.clone()),
|
|
});
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
|
|
Ok(params)
|
|
}
|
|
|
|
fn parse_trait_bounds(&mut self) -> Result<Vec<String>, ParseError> {
|
|
let mut bounds = Vec::new();
|
|
loop {
|
|
match self.next() {
|
|
Some((Token::Variable(n), _)) => bounds.push(n),
|
|
Some((_, span)) => return self.error("Expected trait name".to_string(), span),
|
|
None => return self.error("Expected trait name".to_string(), 0..0),
|
|
}
|
|
|
|
if !matches!(self.peek(), Some(Token::Plus)) {
|
|
break;
|
|
}
|
|
self.next();
|
|
}
|
|
|
|
Ok(bounds)
|
|
}
|
|
|
|
fn parse_kind(&mut self) -> Result<Kind, ParseError> {
|
|
if matches!(self.peek(), Some(Token::Mul)) {
|
|
self.next();
|
|
Ok(Kind::Star)
|
|
} else {
|
|
let k1 = Box::new(self.parse_kind()?);
|
|
self.expect(Token::Arrow)?;
|
|
let k2 = Box::new(self.parse_kind()?);
|
|
Ok(Kind::Arrow(k1, k2))
|
|
}
|
|
}
|
|
|
|
fn parse_type_annot(&mut self) -> Result<TypeAnnot, ParseError> {
|
|
let start = self.peek_span().unwrap_or(0..0).start;
|
|
|
|
// Check for function type: fn (args)->ret
|
|
if matches!(self.peek(), Some(Token::KeywordFn)) {
|
|
self.next();
|
|
self.expect(Token::LParen)?;
|
|
let mut arg_types = Vec::new();
|
|
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::RParen)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
arg_types.push(self.parse_type_annot()?);
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
|
|
self.expect(Token::Arrow)?;
|
|
let ret_type = Box::new(self.parse_type_annot()?);
|
|
return Ok(TypeAnnot::Function(arg_types, ret_type));
|
|
}
|
|
|
|
let mut base_type = match self.next() {
|
|
Some((Token::Variable(n), _)) => TypeAnnot::Cons(n, vec![]),
|
|
Some((Token::KeywordBool, _)) => TypeAnnot::Cons("bool".to_string(), vec![]),
|
|
Some((Token::KeywordInt, _)) => TypeAnnot::Cons("int".to_string(), vec![]),
|
|
Some((Token::KeywordFloat, _)) => TypeAnnot::Cons("float".to_string(), vec![]),
|
|
Some((Token::KeywordString, _)) => TypeAnnot::Cons("string".to_string(), vec![]),
|
|
Some((Token::LParen, _)) => {
|
|
// Check for unit type: ()
|
|
if matches!(self.peek(), Some(Token::RParen)) {
|
|
self.next();
|
|
return Ok(TypeAnnot::Cons("unit".to_string(), vec![]));
|
|
}
|
|
|
|
let mut types = Vec::new();
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::RParen)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
types.push(self.parse_type_annot()?);
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
|
|
// Single element in parens is not a tuple, unwrap it
|
|
if types.len() == 1 {
|
|
types.pop().unwrap()
|
|
} else {
|
|
TypeAnnot::Tuple(types)
|
|
}
|
|
}
|
|
Some((Token::LBracket, _)) => {
|
|
let inner = self.parse_type_annot()?;
|
|
self.expect(Token::RBracket)?;
|
|
TypeAnnot::Array(Box::new(inner))
|
|
}
|
|
Some((Token::Bang, _)) => TypeAnnot::Cons("never".to_string(), vec![]),
|
|
Some((_, span)) => return self.error("Expected type name".to_string(), span),
|
|
None => return self.error("Expected type name".to_string(), start..start),
|
|
};
|
|
|
|
// Parse type arguments if present
|
|
if matches!(self.peek(), Some(Token::Less)) {
|
|
self.next();
|
|
let mut args = Vec::new();
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::Greater)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
args.push(self.parse_type_annot()?);
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
base_type = match base_type {
|
|
TypeAnnot::Cons(name, _) => TypeAnnot::Cons(name, args),
|
|
_ => {
|
|
return self.error("Expected type name for generic".to_string(), start..start);
|
|
}
|
|
};
|
|
}
|
|
|
|
// Parse array types
|
|
while matches!(self.peek(), Some(Token::LBracket)) {
|
|
self.next();
|
|
self.expect(Token::RBracket)?;
|
|
base_type = TypeAnnot::Array(Box::new(base_type));
|
|
}
|
|
|
|
Ok(base_type)
|
|
}
|
|
|
|
fn parse_function_signature(&mut self) -> Result<FunctionSignature, ParseError> {
|
|
let start = self.peek_span().unwrap_or(0..0).start;
|
|
let name = match self.next() {
|
|
Some((Token::Variable(n), _)) => n,
|
|
Some((_, span)) => return self.error("Expected function name".to_string(), span),
|
|
None => return self.error("Expected function name".to_string(), start..start),
|
|
};
|
|
|
|
self.expect(Token::LParen)?;
|
|
let mut params = Vec::new();
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::RParen)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
|
|
let param_start = self.peek_span().unwrap_or(0..0).start;
|
|
let param_name = match self.next() {
|
|
Some((Token::Variable(n), _)) => n,
|
|
Some((_, span)) => return self.error("Expected parameter name".to_string(), span),
|
|
None => return self.error("Expected parameter name".to_string(), start..start),
|
|
};
|
|
|
|
// Parameters in trait methods may have type annotations
|
|
if matches!(self.peek(), Some(Token::Colon)) {
|
|
self.next();
|
|
let _param_type = self.parse_type_annot()?;
|
|
}
|
|
|
|
let param_end = self.peek_span().unwrap_or(param_start..param_start).end;
|
|
params.push(Parameter {
|
|
name: param_name,
|
|
bounds: Vec::new(),
|
|
kind: None,
|
|
span: Span::new(&(param_start..param_end), self.file.clone()),
|
|
});
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
|
|
self.expect(Token::Arrow)?;
|
|
let return_type = self.parse_type_annot()?;
|
|
|
|
Ok(FunctionSignature {
|
|
name,
|
|
params,
|
|
return_type,
|
|
})
|
|
}
|
|
|
|
fn parse_expr(&mut self) -> Result<Expr, ParseError> {
|
|
let mut attributes = Vec::new();
|
|
|
|
// Parse any leading attributes
|
|
while matches!(self.peek(), Some(Token::At)) {
|
|
attributes.push(self.parse_attribute()?);
|
|
}
|
|
|
|
let mut expr = self.parse_assignment()?;
|
|
expr.attributes = attributes;
|
|
Ok(expr)
|
|
}
|
|
|
|
fn parse_range_expr(&mut self) -> Result<Expr, ParseError> {
|
|
let left = self.parse_or_expr()?;
|
|
|
|
if matches!(self.peek(), Some(Token::DotDot)) {
|
|
let start = left.span.start;
|
|
self.next();
|
|
let right = self.parse_or_expr()?;
|
|
let end = right.span.end;
|
|
Ok(Expr {
|
|
kind: ExprKind::Range(Box::new(left), Box::new(right)),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
} else {
|
|
Ok(left)
|
|
}
|
|
}
|
|
|
|
fn parse_assignment(&mut self) -> Result<Expr, ParseError> {
|
|
let left = self.parse_range_expr()?;
|
|
|
|
if matches!(self.peek(), Some(Token::Assign)) {
|
|
let start = left.span.start;
|
|
self.next();
|
|
let right = self.parse_assignment()?;
|
|
let end = right.span.end;
|
|
Ok(Expr {
|
|
kind: ExprKind::Assign(Box::new(left), Box::new(right)),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
} else {
|
|
Ok(left)
|
|
}
|
|
}
|
|
|
|
fn parse_or_expr(&mut self) -> Result<Expr, ParseError> {
|
|
let mut left = self.parse_and_expr()?;
|
|
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::Or)) {
|
|
let start = left.span.start;
|
|
self.next();
|
|
let right = self.parse_and_expr()?;
|
|
let end = right.span.end;
|
|
left = Expr {
|
|
kind: ExprKind::BinOp(Box::new(left), BinOp::Or, Box::new(right)),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
};
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
Ok(left)
|
|
}
|
|
|
|
fn parse_and_expr(&mut self) -> Result<Expr, ParseError> {
|
|
let mut left = self.parse_eq_expr()?;
|
|
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::And)) {
|
|
let start = left.span.start;
|
|
self.next();
|
|
let right = self.parse_eq_expr()?;
|
|
let end = right.span.end;
|
|
left = Expr {
|
|
kind: ExprKind::BinOp(Box::new(left), BinOp::And, Box::new(right)),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
};
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
Ok(left)
|
|
}
|
|
|
|
fn parse_eq_expr(&mut self) -> Result<Expr, ParseError> {
|
|
let mut left = self.parse_comp_expr()?;
|
|
|
|
loop {
|
|
let op = match self.peek() {
|
|
Some(Token::Eq) => BinOp::Eq,
|
|
Some(Token::NotEq) => BinOp::Neq,
|
|
_ => break,
|
|
};
|
|
let start = left.span.start;
|
|
self.next();
|
|
let right = self.parse_comp_expr()?;
|
|
let end = right.span.end;
|
|
left = Expr {
|
|
kind: ExprKind::BinOp(Box::new(left), op, Box::new(right)),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
};
|
|
}
|
|
|
|
Ok(left)
|
|
}
|
|
|
|
fn parse_comp_expr(&mut self) -> Result<Expr, ParseError> {
|
|
let mut left = self.parse_add_expr()?;
|
|
|
|
loop {
|
|
let op = match self.peek() {
|
|
Some(Token::Less) => BinOp::Lt,
|
|
Some(Token::Greater) => BinOp::Gt,
|
|
Some(Token::LessEq) => BinOp::Leq,
|
|
Some(Token::GreaterEq) => BinOp::Geq,
|
|
_ => break,
|
|
};
|
|
let start = left.span.start;
|
|
self.next();
|
|
let right = self.parse_add_expr()?;
|
|
let end = right.span.end;
|
|
left = Expr {
|
|
kind: ExprKind::BinOp(Box::new(left), op, Box::new(right)),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
};
|
|
}
|
|
|
|
Ok(left)
|
|
}
|
|
|
|
fn parse_add_expr(&mut self) -> Result<Expr, ParseError> {
|
|
let mut left = self.parse_mul_expr()?;
|
|
|
|
loop {
|
|
let op = match self.peek() {
|
|
Some(Token::Plus) => BinOp::Add,
|
|
Some(Token::Minus) => BinOp::Sub,
|
|
_ => break,
|
|
};
|
|
let start = left.span.start;
|
|
self.next();
|
|
let right = self.parse_mul_expr()?;
|
|
let end = right.span.end;
|
|
left = Expr {
|
|
kind: ExprKind::BinOp(Box::new(left), op, Box::new(right)),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
};
|
|
}
|
|
|
|
Ok(left)
|
|
}
|
|
|
|
fn parse_mul_expr(&mut self) -> Result<Expr, ParseError> {
|
|
let mut left = self.parse_unary_expr()?;
|
|
|
|
loop {
|
|
let op = match self.peek() {
|
|
Some(Token::Mul) => BinOp::Mul,
|
|
Some(Token::Div) => BinOp::Div,
|
|
Some(Token::Mod) => BinOp::Mod,
|
|
_ => break,
|
|
};
|
|
let start = left.span.start;
|
|
self.next();
|
|
let right = self.parse_unary_expr()?;
|
|
let end = right.span.end;
|
|
left = Expr {
|
|
kind: ExprKind::BinOp(Box::new(left), op, Box::new(right)),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
};
|
|
}
|
|
|
|
Ok(left)
|
|
}
|
|
|
|
fn parse_unary_expr(&mut self) -> Result<Expr, ParseError> {
|
|
let start = self.peek_span().unwrap_or(0..0).start;
|
|
match self.peek() {
|
|
Some(Token::Not) => {
|
|
self.next();
|
|
let expr = self.parse_unary_expr()?;
|
|
let end = expr.span.end;
|
|
Ok(Expr {
|
|
kind: ExprKind::UnOp(UnOp::Not, Box::new(expr)),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
Some(Token::Minus) => {
|
|
self.next();
|
|
let expr = self.parse_unary_expr()?;
|
|
let end = expr.span.end;
|
|
Ok(Expr {
|
|
kind: ExprKind::UnOp(UnOp::Neg, Box::new(expr)),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
_ => self.parse_postfix_expr(),
|
|
}
|
|
}
|
|
|
|
fn parse_postfix_expr(&mut self) -> Result<Expr, ParseError> {
|
|
let mut expr = self.parse_primary_expr()?;
|
|
|
|
loop {
|
|
match self.peek() {
|
|
Some(Token::LParen) => {
|
|
// Function call
|
|
let start = expr.span.start;
|
|
self.next();
|
|
let mut args = Vec::new();
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::RParen)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
args.push(self.parse_expr()?);
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
let end = self.peek_span().unwrap_or(expr.span.end..expr.span.end).end;
|
|
expr = Expr {
|
|
kind: ExprKind::Call(Box::new(expr), args),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
};
|
|
}
|
|
Some(Token::LBracket) => {
|
|
// Index
|
|
let start = expr.span.start;
|
|
self.next();
|
|
let index = self.parse_expr()?;
|
|
self.expect(Token::RBracket)?;
|
|
let end = self.peek_span().unwrap_or(expr.span.end..expr.span.end).end;
|
|
expr = Expr {
|
|
kind: ExprKind::Index(Box::new(expr), Box::new(index)),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
};
|
|
}
|
|
Some(Token::Dot) => {
|
|
// Field access
|
|
let start = expr.span.start;
|
|
self.next();
|
|
let field = match self.next() {
|
|
Some((Token::Variable(f), _)) => f,
|
|
Some((_, span)) => {
|
|
return self.error("Expected field name".to_string(), span);
|
|
}
|
|
None => return self.error("Expected field name".to_string(), start..start),
|
|
};
|
|
let end = self.peek_span().unwrap_or(expr.span.end..expr.span.end).end;
|
|
expr = Expr {
|
|
kind: ExprKind::Dot(Box::new(expr), field),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
};
|
|
}
|
|
Some(Token::OptionalChain) => {
|
|
// Optional chain
|
|
let start = expr.span.start;
|
|
self.next();
|
|
let field = match self.next() {
|
|
Some((Token::Variable(f), _)) => f,
|
|
Some((_, span)) => {
|
|
return self.error("Expected field name".to_string(), span);
|
|
}
|
|
None => return self.error("Expected field name".to_string(), start..start),
|
|
};
|
|
let end = self.peek_span().unwrap_or(expr.span.end..expr.span.end).end;
|
|
expr = Expr {
|
|
kind: ExprKind::OptionalChain(Some(Box::new(expr)), field),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
};
|
|
}
|
|
Some(Token::Unwrap) => {
|
|
// Early return / unwrap
|
|
let start = expr.span.start;
|
|
self.next();
|
|
let end = self.peek_span().unwrap_or(expr.span.end..expr.span.end).end;
|
|
expr = Expr {
|
|
kind: ExprKind::EarlyReturn(Some(Box::new(expr))),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
};
|
|
}
|
|
Some(Token::KeywordAs) => {
|
|
// Cast
|
|
let start = expr.span.start;
|
|
self.next();
|
|
let type_annot = self.parse_type_annot()?;
|
|
let end = self.peek_span().unwrap_or(expr.span.end..expr.span.end).end;
|
|
expr = Expr {
|
|
kind: ExprKind::Cast(Box::new(expr), type_annot),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
};
|
|
}
|
|
_ => break,
|
|
}
|
|
}
|
|
|
|
Ok(expr)
|
|
}
|
|
|
|
fn parse_primary_expr(&mut self) -> Result<Expr, ParseError> {
|
|
let start = self.peek_span().unwrap_or(0..0).start;
|
|
match self.peek().cloned() {
|
|
Some(Token::Int(n)) => {
|
|
self.next();
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Expr {
|
|
kind: ExprKind::Int(n),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
Some(Token::Float(f)) => {
|
|
self.next();
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Expr {
|
|
kind: ExprKind::Float(f),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
Some(Token::Bool(b)) => {
|
|
self.next();
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Expr {
|
|
kind: ExprKind::Bool(b),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
Some(Token::String(s)) => {
|
|
self.next();
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Expr {
|
|
kind: ExprKind::String(s),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
Some(Token::Variable(name)) => {
|
|
self.next();
|
|
|
|
// Check for struct literal or enum variant
|
|
if matches!(self.peek(), Some(Token::LBrace)) {
|
|
// Struct literal
|
|
self.next();
|
|
let mut fields = Vec::new();
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::RBrace)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
|
|
let field_name = match self.next() {
|
|
Some((Token::Variable(f), _)) => f,
|
|
Some((_, span)) => {
|
|
return self.error("Expected field name".to_string(), span);
|
|
}
|
|
None => {
|
|
return self.error("Expected field name".to_string(), start..start);
|
|
}
|
|
};
|
|
|
|
self.expect(Token::Colon)?;
|
|
let field_expr = self.parse_expr()?;
|
|
fields.push((field_name, field_expr));
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Expr {
|
|
kind: ExprKind::StructLit(name, fields),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
} else if matches!(self.peek(), Some(Token::Access)) {
|
|
// Enum variant
|
|
self.next();
|
|
let variant = match self.next() {
|
|
Some((Token::Variable(v), _)) => v,
|
|
Some((_, span)) => {
|
|
return self.error("Expected variant name".to_string(), span);
|
|
}
|
|
None => {
|
|
return self.error("Expected variant name".to_string(), start..start);
|
|
}
|
|
};
|
|
|
|
let mut args = Vec::new();
|
|
if matches!(self.peek(), Some(Token::LParen)) {
|
|
self.next();
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::RParen)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
args.push(self.parse_expr()?);
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
}
|
|
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Expr {
|
|
kind: ExprKind::EnumLit(name, variant, args),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
} else {
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Expr {
|
|
kind: ExprKind::Variable(name),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
}
|
|
Some(Token::LParen) => {
|
|
self.next();
|
|
if matches!(self.peek(), Some(Token::RParen)) {
|
|
// Empty tuple
|
|
self.next();
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Expr {
|
|
kind: ExprKind::Tuple(vec![]),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
} else {
|
|
let first = self.parse_expr()?;
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
// Tuple
|
|
let mut elements = vec![first];
|
|
self.next();
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::RParen)) {
|
|
break;
|
|
}
|
|
elements.push(self.parse_expr()?);
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
self.expect(Token::RParen)?;
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Expr {
|
|
kind: ExprKind::Tuple(elements),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
} else {
|
|
self.expect(Token::RParen)?;
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Expr {
|
|
kind: first.kind,
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: first.attributes,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
Some(Token::LBracket) => {
|
|
self.next();
|
|
let mut elements = Vec::new();
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::RBracket)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
elements.push(self.parse_expr()?);
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Expr {
|
|
kind: ExprKind::Array(elements),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
Some(Token::KeywordLet) => {
|
|
self.next();
|
|
|
|
// Parse binding kind (mut, uniq, once) - comes AFTER let
|
|
let binding_kind = match self.peek() {
|
|
Some(Token::KeywordMut) => {
|
|
self.next();
|
|
BindingKind::Mutable
|
|
}
|
|
Some(Token::KeywordUniq) => {
|
|
self.next();
|
|
BindingKind::Affine
|
|
}
|
|
Some(Token::KeywordOnce) => {
|
|
self.next();
|
|
BindingKind::Linear
|
|
}
|
|
_ => BindingKind::Default,
|
|
};
|
|
|
|
// Now parse the variable name
|
|
let var_name = match self.next() {
|
|
Some((Token::Variable(n), _)) => n,
|
|
Some((_, span)) => {
|
|
return self.error("Expected variable name".to_string(), span);
|
|
}
|
|
None => return self.error("Expected variable name".to_string(), start..start),
|
|
};
|
|
|
|
// Parse optional type annotation
|
|
let type_annot = if matches!(self.peek(), Some(Token::Colon)) {
|
|
self.next();
|
|
Some(self.parse_type_annot()?)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
self.expect(Token::Assign)?;
|
|
let expr = self.parse_expr()?;
|
|
let end = expr.span.end;
|
|
Ok(Expr {
|
|
kind: ExprKind::Let(var_name, binding_kind, type_annot, Box::new(expr)),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
Some(Token::KeywordIf) => {
|
|
self.next();
|
|
let cond = self.parse_expr()?;
|
|
let then_expr = self.parse_expr()?;
|
|
let else_expr = if matches!(self.peek(), Some(Token::KeywordElse)) {
|
|
self.next();
|
|
Some(Box::new(self.parse_expr()?))
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let end = else_expr
|
|
.as_ref()
|
|
.map(|e| e.span.end)
|
|
.unwrap_or(then_expr.span.end);
|
|
|
|
Ok(Expr {
|
|
kind: ExprKind::If(Box::new(cond), Box::new(then_expr), else_expr),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
Some(Token::KeywordMatch) => {
|
|
self.next();
|
|
let expr = self.parse_expr()?;
|
|
let mut arms = Vec::new();
|
|
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::KeywordEnd)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
|
|
let pattern = self.parse_pattern()?;
|
|
self.expect(Token::FatArrow)?;
|
|
let body = self.parse_expr()?;
|
|
arms.push((pattern, body));
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Expr {
|
|
kind: ExprKind::Match(Box::new(expr), arms),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
Some(Token::KeywordWhile) => {
|
|
self.next();
|
|
let cond = self.parse_expr()?;
|
|
let body = self.parse_expr()?;
|
|
let end = body.span.end;
|
|
|
|
Ok(Expr {
|
|
kind: ExprKind::While(Box::new(cond), Box::new(body)),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
Some(Token::KeywordFor) => {
|
|
self.next();
|
|
let var = match self.next() {
|
|
Some((Token::Variable(v), _)) => v,
|
|
Some((_, span)) => {
|
|
return self.error("Expected variable name".to_string(), span);
|
|
}
|
|
None => return self.error("Expected variable name".to_string(), start..start),
|
|
};
|
|
self.expect(Token::KeywordIn)?;
|
|
let iterable = self.parse_expr()?;
|
|
let body = self.parse_expr()?;
|
|
let end = body.span.end;
|
|
|
|
Ok(Expr {
|
|
kind: ExprKind::For(var, Box::new(iterable), Box::new(body)),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
Some(Token::KeywordDo) => {
|
|
self.next();
|
|
let mut exprs = Vec::new();
|
|
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::KeywordEnd)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
exprs.push(self.parse_expr()?);
|
|
|
|
if matches!(self.peek(), Some(Token::Semicolon)) {
|
|
self.next();
|
|
}
|
|
}
|
|
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Expr {
|
|
kind: ExprKind::Do(exprs),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
Some(Token::KeywordLambda) => {
|
|
let start = self.peek_span().unwrap_or(0..0).start;
|
|
self.next();
|
|
self.expect(Token::LParen)?;
|
|
let mut params = Vec::new();
|
|
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::RParen)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
|
|
let param_name = match self.next() {
|
|
Some((Token::Variable(p), _)) => p,
|
|
Some((_, span)) => {
|
|
return self.error("Expected parameter name".to_string(), span);
|
|
}
|
|
None => {
|
|
return self.error("Expected parameter name".to_string(), start..start);
|
|
}
|
|
};
|
|
|
|
// Check for optional type annotation
|
|
let param_type = if matches!(self.peek(), Some(Token::Colon)) {
|
|
self.next(); // consume ':'
|
|
Some(self.parse_type_annot()?)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
params.push((param_name, param_type));
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
|
|
let body = self.parse_expr()?;
|
|
let end = body.span.end;
|
|
|
|
Ok(Expr {
|
|
kind: ExprKind::Lambda(params, Box::new(body)),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
Some(Token::KeywordReturn) => {
|
|
self.next();
|
|
let expr = if self.is_expr_end() {
|
|
None
|
|
} else {
|
|
Some(Box::new(self.parse_expr()?))
|
|
};
|
|
|
|
let end = expr
|
|
.as_ref()
|
|
.map(|e| e.span.end)
|
|
.unwrap_or(self.peek_span().unwrap_or(start..start).end);
|
|
|
|
Ok(Expr {
|
|
kind: ExprKind::Return(expr),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
Some(Token::KeywordBreak) => {
|
|
self.next();
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Expr {
|
|
kind: ExprKind::Break,
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
Some(Token::KeywordContinue) => {
|
|
self.next();
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Expr {
|
|
kind: ExprKind::Continue,
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
attributes: Vec::new(),
|
|
})
|
|
}
|
|
Some(token) => {
|
|
let span = self.peek_span().unwrap_or(start..start);
|
|
self.error(format!("Unexpected token: {:?}", token), span)
|
|
}
|
|
None => self.error("Unexpected EOF".to_string(), start..start),
|
|
}
|
|
}
|
|
|
|
fn parse_pattern(&mut self) -> Result<Pattern, ParseError> {
|
|
let start = self.peek_span().unwrap_or(0..0).start;
|
|
|
|
match self.peek().cloned() {
|
|
Some(Token::Variable(name)) => {
|
|
self.next();
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
|
|
// Check for struct or enum pattern
|
|
if matches!(self.peek(), Some(Token::LBrace)) {
|
|
// Struct pattern
|
|
self.next();
|
|
let mut fields = Vec::new();
|
|
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::RBrace)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
|
|
let field_name = match self.next() {
|
|
Some((Token::Variable(f), _)) => f,
|
|
Some((_, span)) => {
|
|
return self.error("Expected field name".to_string(), span);
|
|
}
|
|
None => {
|
|
return self.error("Expected field name".to_string(), start..start);
|
|
}
|
|
};
|
|
|
|
self.expect(Token::Colon)?;
|
|
let pattern = self.parse_pattern()?;
|
|
fields.push((field_name, pattern));
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Pattern {
|
|
kind: PatternKind::Struct(name, fields),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
})
|
|
} else if matches!(self.peek(), Some(Token::Access)) {
|
|
// Enum pattern
|
|
self.next();
|
|
let variant = match self.next() {
|
|
Some((Token::Variable(v), _)) => v,
|
|
Some((_, span)) => {
|
|
return self.error("Expected variant name".to_string(), span);
|
|
}
|
|
None => {
|
|
return self.error("Expected variant name".to_string(), start..start);
|
|
}
|
|
};
|
|
|
|
let mut patterns = Vec::new();
|
|
if matches!(self.peek(), Some(Token::LParen)) {
|
|
self.next();
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::RParen)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
patterns.push(self.parse_pattern()?);
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
}
|
|
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Pattern {
|
|
kind: PatternKind::Enum(name, variant, patterns),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
})
|
|
} else {
|
|
Ok(Pattern {
|
|
kind: PatternKind::Variable(name),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
})
|
|
}
|
|
}
|
|
Some(Token::Union) => {
|
|
self.next();
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Pattern {
|
|
kind: PatternKind::Wildcard,
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
})
|
|
}
|
|
Some(Token::LParen) => {
|
|
self.next();
|
|
let mut patterns = Vec::new();
|
|
loop {
|
|
if matches!(self.peek(), Some(Token::RParen)) {
|
|
self.next();
|
|
break;
|
|
}
|
|
patterns.push(self.parse_pattern()?);
|
|
|
|
if matches!(self.peek(), Some(Token::Comma)) {
|
|
self.next();
|
|
}
|
|
}
|
|
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Pattern {
|
|
kind: PatternKind::Tuple(patterns),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
})
|
|
}
|
|
Some(Token::String(s)) => {
|
|
self.next();
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Pattern {
|
|
kind: PatternKind::Literal(s),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
})
|
|
}
|
|
Some(Token::Int(n)) => {
|
|
self.next();
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Pattern {
|
|
kind: PatternKind::Literal(n.to_string()),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
})
|
|
}
|
|
Some(Token::Float(f)) => {
|
|
self.next();
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Pattern {
|
|
kind: PatternKind::Literal(f.to_string()),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
})
|
|
}
|
|
Some(Token::Bool(b)) => {
|
|
self.next();
|
|
let end = self.peek_span().unwrap_or(start..start).end;
|
|
Ok(Pattern {
|
|
kind: PatternKind::Literal(b.to_string()),
|
|
span: Span::new(&(start..end), self.file.clone()),
|
|
})
|
|
}
|
|
Some(token) => {
|
|
let span = self.peek_span().unwrap_or(start..start);
|
|
self.error(format!("Unexpected token in pattern: {:?}", token), span)
|
|
}
|
|
None => self.error("Unexpected EOF".to_string(), start..start),
|
|
}
|
|
}
|
|
|
|
fn is_expr_end(&mut self) -> bool {
|
|
matches!(
|
|
self.peek(),
|
|
Some(Token::RParen)
|
|
| Some(Token::RBracket)
|
|
| Some(Token::RBrace)
|
|
| Some(Token::Comma)
|
|
| Some(Token::Semicolon)
|
|
| Some(Token::KeywordEnd)
|
|
| Some(Token::FatArrow)
|
|
)
|
|
}
|
|
}
|