use super::*;
impl Parser {
fn is_upper_camel_segment(name: &str) -> bool {
name.chars().next().is_some_and(|c| c.is_uppercase())
}
fn is_constructor_path(path: &str) -> bool {
path.rsplit('.')
.next()
.is_some_and(Self::is_upper_camel_segment)
}
fn reject_zero_arg_constructor_call(&self, path: &str) -> Result<(), ParseError> {
if Self::is_constructor_path(path) && self.peek(1).kind == TokenKind::RParen {
return Err(self.error(format!(
"Zero-argument constructor call '{}()' is not allowed. Use '{}' (no parentheses).",
path, path
)));
}
Ok(())
}
fn spanned(&self, expr: Expr, line: usize) -> Spanned<Expr> {
Spanned::new(expr, line)
}
pub fn parse_expr(&mut self) -> Result<Spanned<Expr>, ParseError> {
self.parse_comparison()
}
pub(super) fn parse_comparison(&mut self) -> Result<Spanned<Expr>, ParseError> {
let mut left = self.parse_additive()?;
loop {
let line = self.current().line;
let op = match &self.current().kind {
TokenKind::Eq => BinOp::Eq,
TokenKind::Neq => BinOp::Neq,
TokenKind::Lt => BinOp::Lt,
TokenKind::Gt => BinOp::Gt,
TokenKind::Lte => BinOp::Lte,
TokenKind::Gte => BinOp::Gte,
_ => break,
};
self.advance();
let right = self.parse_additive()?;
left = self.spanned(Expr::BinOp(op, Box::new(left), Box::new(right)), line);
}
Ok(left)
}
pub(super) fn parse_additive(&mut self) -> Result<Spanned<Expr>, ParseError> {
let mut left = self.parse_multiplicative()?;
loop {
let line = self.current().line;
let op = match &self.current().kind {
TokenKind::Plus => BinOp::Add,
TokenKind::Minus => BinOp::Sub,
_ => break,
};
self.advance();
let right = self.parse_multiplicative()?;
left = self.spanned(Expr::BinOp(op, Box::new(left), Box::new(right)), line);
}
Ok(left)
}
pub(super) fn parse_multiplicative(&mut self) -> Result<Spanned<Expr>, ParseError> {
let mut left = self.parse_unary()?;
loop {
let line = self.current().line;
let op = match &self.current().kind {
TokenKind::Star => BinOp::Mul,
TokenKind::Slash => BinOp::Div,
_ => break,
};
self.advance();
let right = self.parse_unary()?;
left = self.spanned(Expr::BinOp(op, Box::new(left), Box::new(right)), line);
}
Ok(left)
}
pub(super) fn parse_unary(&mut self) -> Result<Spanned<Expr>, ParseError> {
if self.check_exact(&TokenKind::Minus) {
let line = self.current().line;
self.advance();
let operand = self.parse_postfix()?;
return Ok(self.spanned(
Expr::BinOp(
BinOp::Sub,
Box::new(Spanned::bare(Expr::Literal(Literal::Int(0)))),
Box::new(operand),
),
line,
));
}
self.parse_postfix()
}
pub(super) fn parse_postfix(&mut self) -> Result<Spanned<Expr>, ParseError> {
let mut expr = self.parse_call_or_atom()?;
loop {
if self.check_exact(&TokenKind::Question) && self.peek(1).kind == TokenKind::Bang {
let line = self.current().line;
if let Expr::Tuple(elements) = expr.node {
self.advance(); self.advance(); expr = self.spanned(Expr::IndependentProduct(elements, true), line);
} else {
return Err(self.error(
"Operator '?!' can only be applied to a tuple expression, e.g. (a, b)?!"
.to_string(),
));
}
} else if self.check_exact(&TokenKind::Bang)
&& !matches!(self.peek(1).kind, TokenKind::LBracket)
{
let line = self.current().line;
if let Expr::Tuple(elements) = expr.node {
self.advance(); expr = self.spanned(Expr::IndependentProduct(elements, false), line);
} else {
break; }
} else if self.check_exact(&TokenKind::Question) {
let line = self.current().line;
self.advance();
expr = self.spanned(Expr::ErrorProp(Box::new(expr)), line);
} else if self.check_exact(&TokenKind::Dot) {
let dot_line = self.current().line;
self.advance();
let field_tok = self.expect_kind(
&TokenKind::Ident(String::new()),
"Expected field name after '.'",
)?;
let field = match field_tok.kind {
TokenKind::Ident(s) => s,
_ => unreachable!(),
};
expr = self.spanned(Expr::Attr(Box::new(expr), field), dot_line);
if self.check_exact(&TokenKind::LParen) {
if let Some(path) = Self::dotted_name(&expr)
&& path.ends_with(".update")
{
let prefix = &path[..path.len() - ".update".len()];
if !prefix.is_empty()
&& prefix.chars().next().is_some_and(|c| c.is_uppercase())
{
let update_line = self.current().line;
self.advance(); let base = self.parse_expr()?;
let updates = if self.check_exact(&TokenKind::Comma) {
self.advance();
self.skip_formatting();
self.parse_record_create_fields()?
} else {
Vec::new()
};
self.expect_exact(&TokenKind::RParen)?;
expr = self.spanned(
Expr::RecordUpdate {
type_name: prefix.to_string(),
base: Box::new(base),
updates,
},
update_line,
);
continue;
}
}
if let Some(path) = Self::dotted_name(&expr) {
self.reject_zero_arg_constructor_call(&path)?;
}
let named_arg_start = matches!(&self.peek(1).kind, TokenKind::Ident(_))
&& self.peek(2).kind == TokenKind::Assign;
if named_arg_start && let Some(path) = Self::dotted_name(&expr) {
let ctor_line = self.current().line;
self.advance();
let fields = self.parse_record_create_fields()?;
self.expect_exact(&TokenKind::RParen)?;
expr = self.spanned(
Expr::RecordCreate {
type_name: path,
fields,
},
ctor_line,
);
} else {
let call_line = self.current().line;
self.advance();
let args = self.parse_args()?;
self.expect_exact(&TokenKind::RParen)?;
expr = self.spanned(Expr::FnCall(Box::new(expr), args), call_line);
}
}
} else {
break;
}
}
Ok(expr)
}
pub(super) fn dotted_name(expr: &Spanned<Expr>) -> Option<String> {
match &expr.node {
Expr::Ident(name) => Some(name.clone()),
Expr::Attr(inner, field) => {
let mut base = Self::dotted_name(inner)?;
base.push('.');
base.push_str(field);
Some(base)
}
_ => None,
}
}
pub(super) fn parse_call_or_atom(&mut self) -> Result<Spanned<Expr>, ParseError> {
let atom = self.parse_atom()?;
if self.check_exact(&TokenKind::LParen) {
if let Some(path) = Self::dotted_name(&atom) {
self.reject_zero_arg_constructor_call(&path)?;
}
let is_record_create = if let Expr::Ident(ref name) = atom.node {
name.chars().next().is_some_and(|c| c.is_uppercase())
&& matches!(&self.peek_skip_formatting(1).kind, TokenKind::Ident(_))
&& self.peek_skip_formatting(2).kind == TokenKind::Assign
} else {
false
};
let named_arg_start = matches!(&self.peek_skip_formatting(1).kind, TokenKind::Ident(_))
&& self.peek_skip_formatting(2).kind == TokenKind::Assign;
if is_record_create && let Expr::Ident(type_name) = atom.node {
let line = atom.line;
self.advance(); let fields = self.parse_record_create_fields()?;
self.expect_exact(&TokenKind::RParen)?;
return Ok(self.spanned(Expr::RecordCreate { type_name, fields }, line));
}
if named_arg_start && let Some(path) = Self::dotted_name(&atom) {
let line = atom.line;
self.advance();
let fields = self.parse_record_create_fields()?;
self.expect_exact(&TokenKind::RParen)?;
return Ok(self.spanned(
Expr::RecordCreate {
type_name: path,
fields,
},
line,
));
}
let call_line = self.current().line;
self.advance();
let args = self.parse_args()?;
self.expect_exact(&TokenKind::RParen)?;
return Ok(self.spanned(Expr::FnCall(Box::new(atom), args), call_line));
}
Ok(atom)
}
pub(super) fn parse_record_create_fields(
&mut self,
) -> Result<Vec<(String, Spanned<Expr>)>, ParseError> {
let mut fields = Vec::new();
self.skip_formatting();
while !self.check_exact(&TokenKind::RParen) && !self.is_eof() {
if self.check_exact(&TokenKind::Comma) {
self.advance();
self.skip_formatting();
continue;
}
let name_tok =
self.expect_kind(&TokenKind::Ident(String::new()), "Expected field name")?;
let field_name = match name_tok.kind {
TokenKind::Ident(s) => s,
_ => unreachable!(),
};
self.expect_exact(&TokenKind::Assign)?;
let value = self.parse_expr()?;
fields.push((field_name, value));
self.skip_formatting();
}
Ok(fields)
}
pub(super) fn parse_args(&mut self) -> Result<Vec<Spanned<Expr>>, ParseError> {
let mut args = Vec::new();
self.skip_formatting();
while !self.check_exact(&TokenKind::RParen) && !self.is_eof() {
if self.check_exact(&TokenKind::Comma) {
self.advance();
self.skip_formatting();
continue;
}
args.push(self.parse_expr()?);
self.skip_formatting();
}
Ok(args)
}
pub(super) fn parse_map_literal(&mut self) -> Result<Expr, ParseError> {
self.expect_exact(&TokenKind::LBrace)?;
let mut entries = Vec::new();
self.skip_formatting();
while !self.check_exact(&TokenKind::RBrace) && !self.is_eof() {
if self.check_exact(&TokenKind::Comma) {
self.advance();
self.skip_formatting();
continue;
}
let key = self.parse_expr()?;
self.skip_formatting();
if !self.check_exact(&TokenKind::FatArrow) {
return Err(
self.error("Expected '=>' between key and value in map literal".to_string())
);
}
self.advance(); self.skip_formatting();
let value = self.parse_expr()?;
entries.push((key, value));
self.skip_formatting();
if self.check_exact(&TokenKind::Comma) {
self.advance();
self.skip_formatting();
}
}
self.expect_exact(&TokenKind::RBrace)?;
Ok(Expr::MapLiteral(entries))
}
pub(super) fn parse_atom(&mut self) -> Result<Spanned<Expr>, ParseError> {
let line = self.current().line;
match self.current().kind.clone() {
TokenKind::Int(i) => {
self.advance();
Ok(self.spanned(Expr::Literal(Literal::Int(i)), line))
}
TokenKind::Float(f) => {
self.advance();
Ok(self.spanned(Expr::Literal(Literal::Float(f)), line))
}
TokenKind::Str(s) => {
self.advance();
Ok(self.spanned(Expr::Literal(Literal::Str(s)), line))
}
TokenKind::InterpStr(parts) => {
self.advance();
let mut str_parts = Vec::new();
for (is_expr, s) in parts {
if is_expr {
if s.trim().is_empty() {
str_parts.push(StrPart::Literal(String::new()));
} else {
let mut lexer = crate::lexer::Lexer::new(&s);
let tokens = lexer.tokenize().map_err(|e| ParseError::Error {
msg: format!("Error in interpolation: {}", e),
line: self.current().line,
col: self.current().col,
})?;
let mut sub_parser = Parser::new(tokens);
let expr = sub_parser.parse_expr().map_err(|e| ParseError::Error {
msg: format!("Error in interpolation: {}", e),
line: self.current().line,
col: self.current().col,
})?;
str_parts.push(StrPart::Parsed(Box::new(expr)));
}
} else {
str_parts.push(StrPart::Literal(s));
}
}
Ok(self.spanned(Expr::InterpolatedStr(str_parts), line))
}
TokenKind::Bool(b) => {
self.advance();
Ok(self.spanned(Expr::Literal(Literal::Bool(b)), line))
}
TokenKind::Match => {
let m = self.parse_match()?;
Ok(self.spanned(m, line))
}
TokenKind::LParen => {
self.advance();
let first = self.parse_expr()?;
if self.check_exact(&TokenKind::Comma) {
let mut items = vec![first];
while self.check_exact(&TokenKind::Comma) {
self.advance();
items.push(self.parse_expr()?);
}
self.expect_exact(&TokenKind::RParen)?;
Ok(self.spanned(Expr::Tuple(items), line))
} else {
self.expect_exact(&TokenKind::RParen)?;
Ok(first)
}
}
TokenKind::Ident(s) => {
self.advance();
if s == "Unit" {
Ok(self.spanned(Expr::Literal(Literal::Unit), line))
} else {
Ok(self.spanned(Expr::Ident(s), line))
}
}
TokenKind::LBracket => {
self.advance(); let mut elements = Vec::new();
self.skip_formatting();
while !self.check_exact(&TokenKind::RBracket) && !self.is_eof() {
if self.check_exact(&TokenKind::Comma) {
self.advance();
self.skip_formatting();
continue;
}
elements.push(self.parse_expr()?);
self.skip_formatting();
}
self.expect_exact(&TokenKind::RBracket)?;
Ok(self.spanned(Expr::List(elements), line))
}
TokenKind::LBrace => {
let map = self.parse_map_literal()?;
Ok(self.spanned(map, line))
}
TokenKind::Fn => Err(self.error(
"Anonymous functions are not supported in Aver. Define a top-level function and pass its name."
.to_string(),
)),
_ => Err(self.error(format!(
"Expected expression (identifier, literal, '[', or '{{'), found {}",
self.current().kind
))),
}
}
}