use crate::{
Result,
ast::{
BinaryOp, Block, Const, Expr, ExprKind, Function, Item, MatchArm, Module, Param, Pattern,
PatternBinding, Statement, Test, TypeDecl, TypeRef, UnaryOp, Validator, Variant,
VariantField,
},
diagnostic::{AeriError, Span},
lexer::{Token, TokenKind, lex},
};
pub fn parse_module(file: &str, source: &str) -> Result<Module> {
let tokens = lex(file, source)?;
Parser::new(file, tokens).parse_module()
}
struct Parser<'a> {
file: &'a str,
tokens: Vec<Token>,
current: usize,
}
impl<'a> Parser<'a> {
fn new(file: &'a str, tokens: Vec<Token>) -> Self {
Self {
file,
tokens,
current: 0,
}
}
fn parse_module(&mut self) -> Result<Module> {
let name = if self.matches(&TokenKind::Module) {
let name = self.consume_identifier("expected module name after 'module'")?;
self.consume(
&TokenKind::Semicolon,
"expected ';' after module declaration",
)?;
name
} else {
"main".to_string()
};
let mut items = Vec::new();
while !self.check(&TokenKind::Eof) {
items.push(self.item()?);
}
Ok(Module::new(name, items))
}
fn item(&mut self) -> Result<Item> {
if self.matches(&TokenKind::Type) {
self.type_decl().map(Item::Type)
} else if self.matches(&TokenKind::Const) {
self.const_decl().map(Item::Const)
} else if self.matches(&TokenKind::Fn) {
self.function().map(Item::Function)
} else if self.matches(&TokenKind::Validator) {
self.validator().map(Item::Validator)
} else if self.matches(&TokenKind::Test) {
self.test().map(Item::Test)
} else {
Err(self.error_current("expected 'type', 'const', 'fn', 'validator', or 'test'"))
}
}
fn test(&mut self) -> Result<Test> {
let start = self.previous().span;
let name = self.consume_identifier("expected test name")?;
let should_fail = self.matches_identifier("fails");
let body = self.block()?;
let span = start.join(body.span);
Ok(Test {
name,
should_fail,
body,
span,
})
}
fn const_decl(&mut self) -> Result<Const> {
let start = self.previous().span;
let name = self.consume_identifier("expected constant name")?;
self.consume(&TokenKind::Colon, "expected ':' after constant name")?;
let ty = self.type_ref()?;
self.consume(&TokenKind::Equal, "expected '=' in constant declaration")?;
let value = self.expression()?;
let end = self
.consume(
&TokenKind::Semicolon,
"expected ';' after constant declaration",
)?
.span;
Ok(Const {
name,
ty,
value,
span: start.join(end),
})
}
fn type_decl(&mut self) -> Result<TypeDecl> {
let start = self.previous().span;
let name = self.consume_identifier("expected type name")?;
self.consume(&TokenKind::LBrace, "expected '{' after type name")?;
let mut variants = Vec::new();
while !self.check(&TokenKind::RBrace) && !self.check(&TokenKind::Eof) {
variants.push(self.variant()?);
if self.check(&TokenKind::RBrace) {
break;
}
if !self.matches_any(&[TokenKind::Comma, TokenKind::Semicolon]) {
return Err(self.error_current("expected ',' or ';' after type variant"));
}
}
let end = self
.consume(&TokenKind::RBrace, "expected '}' after type declaration")?
.span;
Ok(TypeDecl {
name,
variants,
span: start.join(end),
})
}
fn variant(&mut self) -> Result<Variant> {
let name = self.consume_identifier("expected variant name")?;
let start = self.previous().span;
let fields = if self.matches(&TokenKind::LParen) {
self.variant_fields()?
} else {
Vec::new()
};
let span = fields.last().map_or(start, |field| start.join(field.span));
Ok(Variant { name, fields, span })
}
fn variant_fields(&mut self) -> Result<Vec<VariantField>> {
let mut fields = Vec::new();
if !self.check(&TokenKind::RParen) {
loop {
let first = self.consume_identifier("expected field type or field name")?;
let first_span = self.previous().span;
let (name, ty) = if self.matches(&TokenKind::Colon) {
let ty = self.type_ref()?;
(Some(first), ty)
} else {
(None, self.type_ref_after_name(first, first_span)?)
};
let span = first_span.join(ty.span);
fields.push(VariantField { name, ty, span });
if !self.matches(&TokenKind::Comma) {
break;
}
}
}
self.consume(&TokenKind::RParen, "expected ')' after variant fields")?;
Ok(fields)
}
fn function(&mut self) -> Result<Function> {
let start = self.previous().span;
let name = self.consume_identifier("expected function name")?;
let params = self.params()?;
self.consume(
&TokenKind::Arrow,
"expected '->' before function return type",
)?;
let return_type = self.type_ref()?;
let body = self.block()?;
let span = start.join(body.span);
Ok(Function {
name,
params,
return_type,
body,
span,
})
}
fn validator(&mut self) -> Result<Validator> {
let start = self.previous().span;
let name = self.consume_identifier("expected validator name")?;
let params = self.params()?;
let body = self.block()?;
let span = start.join(body.span);
Ok(Validator {
name,
params,
body,
span,
})
}
fn params(&mut self) -> Result<Vec<Param>> {
self.consume(&TokenKind::LParen, "expected '('")?;
let mut params = Vec::new();
if !self.check(&TokenKind::RParen) {
loop {
let name = self.consume_identifier("expected parameter name")?;
let name_span = self.previous().span;
self.consume(&TokenKind::Colon, "expected ':' after parameter name")?;
let ty = self.type_ref()?;
let span = name_span.join(ty.span);
params.push(Param { name, ty, span });
if !self.matches(&TokenKind::Comma) {
break;
}
}
}
self.consume(&TokenKind::RParen, "expected ')' after parameters")?;
Ok(params)
}
fn type_ref(&mut self) -> Result<TypeRef> {
let name = self.consume_identifier("expected type name")?;
let start = self.previous().span;
self.type_ref_after_name(name, start)
}
fn type_ref_after_name(&mut self, name: String, start: Span) -> Result<TypeRef> {
let mut args = Vec::new();
let mut span = start;
if self.matches(&TokenKind::Less) {
loop {
let arg = self.type_ref()?;
span = span.join(arg.span);
args.push(arg);
if !self.matches(&TokenKind::Comma) {
break;
}
}
span = span.join(
self.consume(&TokenKind::Greater, "expected '>' after type arguments")?
.span,
);
}
Ok(TypeRef { name, args, span })
}
fn block(&mut self) -> Result<Block> {
let start = self.consume(&TokenKind::LBrace, "expected '{'")?.span;
let mut statements = Vec::new();
while !self.check(&TokenKind::RBrace) && !self.check(&TokenKind::Eof) {
statements.push(self.statement()?);
}
let end = self
.consume(&TokenKind::RBrace, "expected '}' after block")?
.span;
Ok(Block {
statements,
span: start.join(end),
})
}
fn statement(&mut self) -> Result<Statement> {
if self.matches(&TokenKind::Let) {
let start = self.previous().span;
let name = self.consume_identifier("expected binding name after 'let'")?;
let ty = if self.matches(&TokenKind::Colon) {
Some(self.type_ref()?)
} else {
None
};
self.consume(&TokenKind::Equal, "expected '=' in let binding")?;
let value = self.expression()?;
let end = self
.consume(&TokenKind::Semicolon, "expected ';' after let binding")?
.span;
Ok(Statement::Let {
name,
ty,
value,
span: start.join(end),
})
} else if self.matches(&TokenKind::Require) {
let start = self.previous().span;
let condition = self.expression()?;
let end = self
.consume(&TokenKind::Semicolon, "expected ';' after require")?
.span;
Ok(Statement::Require {
condition,
span: start.join(end),
})
} else if self.matches(&TokenKind::Trace) {
let start = self.previous().span;
let message = self.expression()?;
let end = self
.consume(&TokenKind::Semicolon, "expected ';' after trace")?
.span;
Ok(Statement::Trace {
message,
span: start.join(end),
})
} else if self.matches(&TokenKind::Return) {
let start = self.previous().span;
let value = self.expression()?;
let end = self
.consume(&TokenKind::Semicolon, "expected ';' after return")?
.span;
Ok(Statement::Return {
value,
span: start.join(end),
})
} else {
let value = self.expression()?;
let mut span = value.span;
let trailing_semicolon = if self.matches(&TokenKind::Semicolon) {
span = span.join(self.previous().span);
true
} else {
false
};
Ok(Statement::Expr {
value,
trailing_semicolon,
span,
})
}
}
fn expression(&mut self) -> Result<Expr> {
self.or()
}
fn or(&mut self) -> Result<Expr> {
let mut expr = self.and()?;
while self.matches(&TokenKind::PipePipe) {
let op_span = self.previous().span;
let right = self.and()?;
let span = expr.span.join(right.span);
expr = Expr {
kind: ExprKind::Binary {
left: Box::new(expr),
op: BinaryOp::Or,
right: Box::new(right),
},
span: span.join(op_span),
};
}
Ok(expr)
}
fn and(&mut self) -> Result<Expr> {
let mut expr = self.equality()?;
while self.matches(&TokenKind::AmpAmp) {
let op_span = self.previous().span;
let right = self.equality()?;
let span = expr.span.join(right.span);
expr = Expr {
kind: ExprKind::Binary {
left: Box::new(expr),
op: BinaryOp::And,
right: Box::new(right),
},
span: span.join(op_span),
};
}
Ok(expr)
}
fn equality(&mut self) -> Result<Expr> {
let mut expr = self.comparison()?;
while self.matches_any(&[TokenKind::EqualEqual, TokenKind::BangEqual]) {
let op = match &self.previous().kind {
TokenKind::EqualEqual => BinaryOp::Equal,
TokenKind::BangEqual => BinaryOp::NotEqual,
_ => unreachable!(),
};
let right = self.comparison()?;
let span = expr.span.join(right.span);
expr = Expr {
kind: ExprKind::Binary {
left: Box::new(expr),
op,
right: Box::new(right),
},
span,
};
}
Ok(expr)
}
fn comparison(&mut self) -> Result<Expr> {
let mut expr = self.term()?;
while self.matches_any(&[
TokenKind::Less,
TokenKind::LessEqual,
TokenKind::Greater,
TokenKind::GreaterEqual,
]) {
let op = match &self.previous().kind {
TokenKind::Less => BinaryOp::Less,
TokenKind::LessEqual => BinaryOp::LessEqual,
TokenKind::Greater => BinaryOp::Greater,
TokenKind::GreaterEqual => BinaryOp::GreaterEqual,
_ => unreachable!(),
};
let right = self.term()?;
let span = expr.span.join(right.span);
expr = Expr {
kind: ExprKind::Binary {
left: Box::new(expr),
op,
right: Box::new(right),
},
span,
};
}
Ok(expr)
}
fn term(&mut self) -> Result<Expr> {
let mut expr = self.factor()?;
while self.matches_any(&[TokenKind::Plus, TokenKind::Minus]) {
let op = match &self.previous().kind {
TokenKind::Plus => BinaryOp::Add,
TokenKind::Minus => BinaryOp::Subtract,
_ => unreachable!(),
};
let right = self.factor()?;
let span = expr.span.join(right.span);
expr = Expr {
kind: ExprKind::Binary {
left: Box::new(expr),
op,
right: Box::new(right),
},
span,
};
}
Ok(expr)
}
fn factor(&mut self) -> Result<Expr> {
let mut expr = self.unary()?;
while self.matches_any(&[TokenKind::Star, TokenKind::Slash, TokenKind::Percent]) {
let op = match &self.previous().kind {
TokenKind::Star => BinaryOp::Multiply,
TokenKind::Slash => BinaryOp::Divide,
TokenKind::Percent => BinaryOp::Remainder,
_ => unreachable!(),
};
let right = self.unary()?;
let span = expr.span.join(right.span);
expr = Expr {
kind: ExprKind::Binary {
left: Box::new(expr),
op,
right: Box::new(right),
},
span,
};
}
Ok(expr)
}
fn unary(&mut self) -> Result<Expr> {
if self.matches_any(&[TokenKind::Bang, TokenKind::Minus]) {
let op_token = self.previous().clone();
let op = match op_token.kind {
TokenKind::Bang => UnaryOp::Not,
TokenKind::Minus => UnaryOp::Negate,
_ => unreachable!(),
};
let expr = self.unary()?;
let span = op_token.span.join(expr.span);
return Ok(Expr {
kind: ExprKind::Unary {
op,
expr: Box::new(expr),
},
span,
});
}
self.call()
}
fn call(&mut self) -> Result<Expr> {
let mut expr = self.primary()?;
while self.matches(&TokenKind::LParen) {
let callee = match expr.kind {
ExprKind::Variable(ref name) => name.clone(),
_ => {
return Err(AeriError::at_span(
self.file,
expr.span,
"expected function name",
));
}
};
let mut args = Vec::new();
if !self.check(&TokenKind::RParen) {
loop {
args.push(self.expression()?);
if !self.matches(&TokenKind::Comma) {
break;
}
}
}
let end = self
.consume(&TokenKind::RParen, "expected ')' after arguments")?
.span;
let span = expr.span.join(end);
expr = Expr {
kind: ExprKind::Call { callee, args },
span,
};
}
Ok(expr)
}
fn primary(&mut self) -> Result<Expr> {
let token = self.advance().clone();
let span = token.span;
match token.kind {
TokenKind::True => Ok(Expr {
kind: ExprKind::Bool(true),
span,
}),
TokenKind::False => Ok(Expr {
kind: ExprKind::Bool(false),
span,
}),
TokenKind::Integer(value) => Ok(Expr {
kind: ExprKind::Int(value),
span,
}),
TokenKind::String(value) => Ok(Expr {
kind: ExprKind::String(value),
span,
}),
TokenKind::ByteArray(hex) => Ok(Expr {
kind: ExprKind::ByteArray(hex),
span,
}),
TokenKind::LBracket => self.list_expr(span),
TokenKind::Fail => Ok(Expr {
kind: ExprKind::Fail,
span,
}),
TokenKind::Identifier(name) => Ok(Expr {
kind: ExprKind::Variable(name),
span,
}),
TokenKind::LParen => {
if self.matches(&TokenKind::RParen) {
return Ok(Expr {
kind: ExprKind::Unit,
span: span.join(self.previous().span),
});
}
let expr = self.expression()?;
self.consume(&TokenKind::RParen, "expected ')' after expression")?;
Ok(expr)
}
TokenKind::If => self.if_expr(span),
TokenKind::Match => self.match_expr(span),
_ => Err(AeriError::at_span(self.file, span, "expected expression")),
}
}
fn list_expr(&mut self, start: Span) -> Result<Expr> {
let mut items = Vec::new();
if !self.check(&TokenKind::RBracket) {
loop {
items.push(self.expression()?);
if !self.matches(&TokenKind::Comma) {
break;
}
if self.check(&TokenKind::RBracket) {
break;
}
}
}
let end = self
.consume(&TokenKind::RBracket, "expected ']' after list literal")?
.span;
Ok(Expr {
kind: ExprKind::List(items),
span: start.join(end),
})
}
fn if_expr(&mut self, start: Span) -> Result<Expr> {
let condition = self.expression()?;
let then_branch = self.block()?;
self.consume(&TokenKind::Else, "expected 'else' after if block")?;
let else_branch = self.block()?;
let span = start.join(else_branch.span);
Ok(Expr {
kind: ExprKind::If {
condition: Box::new(condition),
then_branch,
else_branch,
},
span,
})
}
fn match_expr(&mut self, start: Span) -> Result<Expr> {
let subject = self.expression()?;
self.consume(&TokenKind::LBrace, "expected '{' before match arms")?;
let mut arms = Vec::new();
while !self.check(&TokenKind::RBrace) && !self.check(&TokenKind::Eof) {
let pattern = self.pattern()?;
self.consume(&TokenKind::FatArrow, "expected '=>' after match pattern")?;
let body = if self.check(&TokenKind::LBrace) {
self.block()?
} else {
let value = self.expression()?;
let value_span = value.span;
Block {
statements: vec![Statement::Expr {
span: value_span,
value,
trailing_semicolon: false,
}],
span: value_span,
}
};
let span = pattern.span().join(body.span);
arms.push(MatchArm {
pattern,
body,
span,
});
if self.check(&TokenKind::RBrace) {
break;
}
if !self.matches_any(&[TokenKind::Comma, TokenKind::Semicolon]) {
return Err(self.error_current("expected ',' or ';' after match arm"));
}
}
let end = self
.consume(&TokenKind::RBrace, "expected '}' after match arms")?
.span;
Ok(Expr {
kind: ExprKind::Match {
subject: Box::new(subject),
arms,
},
span: start.join(end),
})
}
fn pattern(&mut self) -> Result<Pattern> {
let token = self.advance().clone();
let span = token.span;
match token.kind {
TokenKind::Identifier(name) if name == "_" => Ok(Pattern::Wildcard { span }),
TokenKind::Identifier(name) => {
if self.check(&TokenKind::LParen) || starts_with_uppercase(&name) {
self.constructor_pattern(name, span)
} else {
Ok(Pattern::Variable { name, span })
}
}
TokenKind::True => Ok(Pattern::Bool { value: true, span }),
TokenKind::False => Ok(Pattern::Bool { value: false, span }),
TokenKind::Integer(value) => Ok(Pattern::Int { value, span }),
TokenKind::ByteArray(hex) => Ok(Pattern::ByteArray { hex, span }),
TokenKind::String(value) => Ok(Pattern::String { value, span }),
TokenKind::LParen => {
let end = self
.consume(&TokenKind::RParen, "expected ')' after unit pattern")?
.span;
Ok(Pattern::Unit {
span: span.join(end),
})
}
_ => Err(AeriError::at_span(
self.file,
span,
"expected match pattern",
)),
}
}
fn constructor_pattern(&mut self, name: String, start: Span) -> Result<Pattern> {
let mut bindings = Vec::new();
let mut span = start;
if self.matches(&TokenKind::LParen) {
if !self.check(&TokenKind::RParen) {
loop {
let binding = self.consume_identifier("expected pattern binding")?;
let binding_span = self.previous().span;
span = span.join(binding_span);
bindings.push(PatternBinding {
name: (binding != "_").then_some(binding),
span: binding_span,
});
if !self.matches(&TokenKind::Comma) {
break;
}
}
}
span = span.join(
self.consume(&TokenKind::RParen, "expected ')' after pattern bindings")?
.span,
);
}
Ok(Pattern::Constructor {
name,
bindings,
span,
})
}
fn matches_any(&mut self, kinds: &[TokenKind]) -> bool {
for kind in kinds {
if self.check(kind) {
self.advance();
return true;
}
}
false
}
fn matches(&mut self, kind: &TokenKind) -> bool {
if self.check(kind) {
self.advance();
true
} else {
false
}
}
fn matches_identifier(&mut self, expected: &str) -> bool {
if matches!(&self.peek().kind, TokenKind::Identifier(name) if name == expected) {
self.advance();
true
} else {
false
}
}
fn consume(&mut self, kind: &TokenKind, message: &str) -> Result<Token> {
if self.check(kind) {
Ok(self.advance().clone())
} else {
Err(self.error_current(message))
}
}
fn consume_identifier(&mut self, message: &str) -> Result<String> {
match self.advance().kind.clone() {
TokenKind::Identifier(name) => Ok(name),
_ => Err(AeriError::at_span(self.file, self.previous().span, message)),
}
}
fn check(&self, kind: &TokenKind) -> bool {
if self.is_at_end() && !matches!(kind, TokenKind::Eof) {
return false;
}
same_token_kind(&self.peek().kind, kind)
}
fn advance(&mut self) -> &Token {
if !self.is_at_end() {
self.current += 1;
}
self.previous()
}
fn is_at_end(&self) -> bool {
matches!(self.peek().kind, TokenKind::Eof)
}
fn peek(&self) -> &Token {
&self.tokens[self.current]
}
fn previous(&self) -> &Token {
&self.tokens[self.current - 1]
}
fn error_current(&self, message: impl Into<String>) -> AeriError {
AeriError::at_span(self.file, self.peek().span, message)
}
}
fn same_token_kind(left: &TokenKind, right: &TokenKind) -> bool {
std::mem::discriminant(left) == std::mem::discriminant(right)
}
fn starts_with_uppercase(name: &str) -> bool {
name.chars().next().is_some_and(char::is_uppercase)
}