use crate::error::{ParseError, Result};
use crate::types::{Expression, ParamAttr, ParamDefault, Parameter, Span, Term, Token};
use super::Parser;
fn find_formal<'a>(
attrs: &'a [ParamAttr],
mut pred: impl FnMut(&'a str) -> bool,
) -> Option<(Span, &'a str)> {
for attr in attrs {
if let ParamAttr::Attr {
name: name_leaf, ..
} = attr
&& let Token::Identifier(name) = &name_leaf.value
&& pred(name.as_str())
{
return Some((name_leaf.span, name.as_str()));
}
}
None
}
fn duplicate_formal_error(span: Span, name: &str) -> ParseError {
ParseError::invalid(
span,
format!("duplicate formal function argument '{name}'"),
None,
)
}
impl Parser {
pub(super) fn parse_context_second(&mut self, first: &Parameter) -> Result<Parameter> {
match first {
Parameter::Id(name) => {
let open = self.expect_token(Token::TBraceOpen, "'{'")?;
let attrs = self.parse_param_attrs()?;
Self::check_duplicate_formals(&attrs)?;
if let Token::Identifier(n) = &name.value {
Self::check_pattern_shadows_formal(n, &attrs)?;
}
let close = self.expect_token(Token::TBraceClose, "'}'")?;
Ok(Parameter::Set { open, attrs, close })
}
Parameter::Set { attrs, .. } => {
if !matches!(self.current.value, Token::Identifier(_)) {
return Err(ParseError::unexpected(
self.current.span,
vec!["identifier".to_string()],
format!("'{}'", self.current.value.text()),
));
}
let name = self.take_and_advance()?;
if let Token::Identifier(n) = &name.value {
Self::check_pattern_shadows_formal(n, attrs)?;
}
Ok(Parameter::Id(name))
}
Parameter::Context { .. } => unreachable!("callers pass Id or Set"),
}
}
pub(super) fn parse_param_attrs(&mut self) -> Result<Vec<ParamAttr>> {
match self.try_parse_param_attrs()? {
Some(attrs) => Ok(attrs),
None => Err(ParseError::invalid(
self.current.span,
"not a parameter - looks like binding",
Some("parameters cannot have '=' or '.'".to_string()),
)),
}
}
pub(super) fn try_parse_param_attrs(&mut self) -> Result<Option<Vec<ParamAttr>>> {
let mut attrs = Vec::new();
while !matches!(self.current.value, Token::TBraceClose | Token::Sof) {
if matches!(self.current.value, Token::TEllipsis) {
let dots = self.take_and_advance()?;
attrs.push(ParamAttr::Ellipsis(dots));
if matches!(self.current.value, Token::TComma) {
self.advance()?;
}
break; } else if matches!(self.current.value, Token::Identifier(_)) {
let name = self.take_and_advance()?;
if matches!(self.current.value, Token::TAssign | Token::TDot) {
return Ok(None);
}
let default = if matches!(self.current.value, Token::TQuestion) {
let q = self.take_and_advance()?;
let def_expr = self.parse_expression()?;
Some(ParamDefault {
question: q,
value: def_expr,
})
} else {
None
};
let comma = if matches!(self.current.value, Token::TComma) {
Some(self.take_and_advance()?)
} else {
None
};
attrs.push(ParamAttr::Attr {
name,
default,
comma,
});
} else {
break;
}
}
Ok(Some(attrs))
}
pub(super) fn check_duplicate_formals(attrs: &[ParamAttr]) -> Result<()> {
use std::collections::HashSet;
let mut seen: HashSet<&str> = HashSet::new();
match find_formal(attrs, |name| !seen.insert(name)) {
Some((span, name)) => Err(duplicate_formal_error(span, name)),
None => Ok(()),
}
}
fn check_pattern_shadows_formal(pattern_name: &str, attrs: &[ParamAttr]) -> Result<()> {
match find_formal(attrs, |name| name == pattern_name) {
Some((span, name)) => Err(duplicate_formal_error(span, name)),
None => Ok(()),
}
}
pub(super) fn reject_non_parameter_expr(expr: &Expression) -> ParseError {
if let Expression::Term(Term::Token(ann)) = expr {
return ParseError::unexpected(
ann.span,
vec!["identifier".to_string()],
format!("'{}'", ann.value.text()),
);
}
ParseError::invalid(
Span::point(0),
"expression before ':' / '@' is not a valid lambda parameter",
Some("use a simple identifier or '{ ... }' set pattern".to_string()),
)
}
}