use std::fmt::{self, Debug, Formatter, Write}; use std::rc::Rc; use super::{Cast, EvalContext, Value}; use crate::diag::{At, TypResult}; use crate::syntax::{Span, Spanned}; use crate::util::EcoString; /// An evaluatable function. #[derive(Clone)] pub struct Function(Rc>); /// The unsized structure behind the [`Rc`]. struct Inner { name: Option, func: T, } type Func = dyn Fn(&mut EvalContext, &mut Args) -> TypResult; impl Function { /// Create a new function from a rust closure. pub fn new(name: Option, func: F) -> Self where F: Fn(&mut EvalContext, &mut Args) -> TypResult + 'static, { Self(Rc::new(Inner { name, func })) } /// The name of the function. pub fn name(&self) -> Option<&EcoString> { self.0.name.as_ref() } /// Call the function in the context with the arguments. pub fn call(&self, ctx: &mut EvalContext, args: &mut Args) -> TypResult { (&self.0.func)(ctx, args) } } impl Debug for Function { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.write_str("') } } impl PartialEq for Function { fn eq(&self, other: &Self) -> bool { // We cast to thin pointers for comparison. std::ptr::eq( Rc::as_ptr(&self.0) as *const (), Rc::as_ptr(&other.0) as *const (), ) } } /// Evaluated arguments to a function. #[derive(Clone, PartialEq)] pub struct Args { /// The span of the whole argument list. pub span: Span, /// The positional and named arguments. pub items: Vec, } /// An argument to a function call: `12` or `draw: false`. #[derive(Clone, PartialEq)] pub struct Arg { /// The span of the whole argument. pub span: Span, /// The name of the argument (`None` for positional arguments). pub name: Option, /// The value of the argument. pub value: Spanned, } impl Args { /// Consume and cast the first positional argument. /// /// Returns a `missing argument: {what}` error if no positional argument is /// left. pub fn expect(&mut self, what: &str) -> TypResult where T: Cast>, { match self.eat()? { Some(v) => Ok(v), None => bail!(self.span, "missing argument: {}", what), } } /// Consume and cast the first positional argument if there is one. pub fn eat(&mut self) -> TypResult> where T: Cast>, { for (i, slot) in self.items.iter().enumerate() { if slot.name.is_none() { let value = self.items.remove(i).value; let span = value.span; return T::cast(value).at(span).map(Some); } } Ok(None) } /// Find and consume the first castable positional argument. pub fn find(&mut self) -> Option where T: Cast>, { for (i, slot) in self.items.iter().enumerate() { if slot.name.is_none() && T::is(&slot.value) { let value = self.items.remove(i).value; return T::cast(value).ok(); } } None } /// Find and consume all castable positional arguments. pub fn all(&mut self) -> impl Iterator + '_ where T: Cast>, { std::iter::from_fn(move || self.find()) } /// Cast and remove the value for the given named argument, returning an /// error if the conversion fails. pub fn named(&mut self, name: &str) -> TypResult> where T: Cast>, { // We don't quit once we have a match because when multiple matches // exist, we want to remove all of them and use the last one. let mut i = 0; let mut found = None; while i < self.items.len() { if self.items[i].name.as_deref() == Some(name) { let value = self.items.remove(i).value; let span = value.span; found = Some(T::cast(value).at(span)?); } else { i += 1; } } Ok(found) } /// Take out all arguments into a new instance. pub fn take(&mut self) -> Self { Self { span: self.span, items: std::mem::take(&mut self.items), } } /// Return an "unexpected argument" error if there is any remaining /// argument. pub fn finish(self) -> TypResult<()> { if let Some(arg) = self.items.first() { bail!(arg.span, "unexpected argument"); } Ok(()) } /// Reinterpret these arguments as actually being an array index. pub fn into_index(self) -> TypResult { self.into_castable("index") } /// Reinterpret these arguments as actually being a dictionary key. pub fn into_key(self) -> TypResult { self.into_castable("key") } /// Reinterpret these arguments as actually being a single castable thing. fn into_castable(self, what: &str) -> TypResult where T: Cast, { let mut iter = self.items.into_iter(); let value = match iter.next() { Some(Arg { name: None, value, .. }) => value.v.cast().at(value.span)?, None => { bail!(self.span, "missing {}", what); } Some(Arg { name: Some(_), span, .. }) => { bail!(span, "named pair is not allowed here"); } }; if let Some(arg) = iter.next() { bail!(arg.span, "only one {} is allowed", what); } Ok(value) } } impl Debug for Args { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.write_char('(')?; for (i, arg) in self.items.iter().enumerate() { arg.fmt(f)?; if i + 1 < self.items.len() { f.write_str(", ")?; } } f.write_char(')') } } impl Debug for Arg { fn fmt(&self, f: &mut Formatter) -> fmt::Result { if let Some(name) = &self.name { f.write_str(name)?; f.write_str(": ")?; } Debug::fmt(&self.value.v, f) } }