use super::{functions::ResolvedArgs, Call, CallError, Name, Value};
use crate::css::CallArgs;
use crate::input::SourcePos;
use crate::{Error, ScopeError, ScopeRef};
use std::fmt;
type Result<T> = std::result::Result<T, ArgsError>;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd)]
pub struct FormalArgs(Vec<(Name, Option<Value>)>, Option<Name>);
impl FormalArgs {
pub fn new(args: Vec<(Name, Option<Value>)>) -> Self {
Self(args, None)
}
pub fn new_va(args: Vec<(Name, Option<Value>)>) -> Self {
let mut args = args;
let va = args.pop().map(|(name, _)| name);
Self(args, va)
}
pub fn none() -> Self {
Self(vec![], None)
}
pub fn is_varargs(&self) -> bool {
self.1.is_some()
}
pub fn eval_call(
&self,
decl: ScopeRef,
call: Call,
) -> Result<ResolvedArgs> {
Ok(ResolvedArgs::new(self.eval(decl, call.args)?, call.scope))
}
pub fn eval(&self, scope: ScopeRef, args: CallArgs) -> Result<ScopeRef> {
let mut args = args;
let argscope = ScopeRef::sub(scope);
if !self.is_varargs() {
let n = self.0.len();
let m = args.len();
if m > n {
let n_p = args.positional.len();
return Err(if n_p != m && n_p > n {
ArgsError::TooManyPos(n, n_p)
} else {
ArgsError::TooMany(n, n_p)
});
}
}
let positional = args.take_positional(self.0.len());
for ((name, _default), value) in self.0.iter().zip(&positional) {
argscope.define(name.clone(), value.clone())?;
}
if self.0.len() > positional.len() {
for (name, default) in &self.0[positional.len()..] {
if let Some(v) = args.named.remove(name) {
argscope.define(name.clone(), v)?;
} else if let Some(default) = default {
argscope.define(
name.clone(),
default.do_evaluate(argscope.clone(), true)?,
)?;
} else {
return Err(ArgsError::Missing(name.clone()));
}
}
}
if let Some(va_name) = &self.1 {
argscope.define(
va_name.clone(),
args.only_named(va_name).unwrap_or_else(|| args.into()),
)?;
} else {
args.check_no_named()?;
}
Ok(argscope)
}
}
impl fmt::Display for FormalArgs {
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
out.write_str("(")?;
if let Some((first, rest)) = self.0.split_first() {
write!(out, "${}", first.0)?;
if let Some(default) = &first.1 {
out.write_str(": ")?;
default.inspect(out)?;
}
for (name, default) in rest {
write!(out, ", ${name}")?;
if let Some(default) = default {
out.write_str(": ")?;
default.inspect(out)?;
}
}
}
if let Some(va) = &self.1 {
write!(out, ", ${va}...")?;
}
out.write_str(")")
}
}
#[derive(Debug)]
pub enum ArgsError {
TooMany(usize, usize),
TooManyPos(usize, usize),
Missing(Name),
Unexpected(Name),
Eval(Box<Error>),
}
impl ArgsError {
pub fn declared_at(self, pos: &SourcePos) -> CallError {
match self {
Self::Eval(e) => CallError::Wrap(e),
ae => CallError::Args(ae, pos.clone()),
}
}
}
impl fmt::Display for ArgsError {
fn fmt(&self, out: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::TooManyPos(n, m) => write!(
out,
"Only {} positional argument{} allowed, but {} {} passed.",
n,
if *n != 1 { "s" } else { "" },
m,
if *m != 1 { "were" } else { "was" },
),
Self::TooMany(n, m) => write!(
out,
"Only {} argument{} allowed, but {} {} passed.",
n,
if *n != 1 { "s" } else { "" },
m,
if *m != 1 { "were" } else { "was" },
),
Self::Missing(name) => {
write!(out, "Missing argument ${name}.")
}
Self::Unexpected(name) => {
write!(out, "No parameter named ${name}.")
}
Self::Eval(e) => e.fmt(out),
}
}
}
impl From<Error> for ArgsError {
fn from(e: Error) -> Self {
Self::Eval(Box::new(e))
}
}
impl From<ScopeError> for ArgsError {
fn from(e: ScopeError) -> Self {
Error::from(e).into()
}
}