diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-01-03 00:12:09 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-01-03 00:12:09 +0100 |
| commit | aae67bd572ad86f4c57e364daa51a9dc883b8913 (patch) | |
| tree | 0aba021e0748ebad2197ea390385ec5f93ccbc6e /src/eval/call.rs | |
| parent | 1c40dc42e7bc7b799b77f06d25414aca59a044ba (diff) | |
Move and rename many things 🚛
Diffstat (limited to 'src/eval/call.rs')
| -rw-r--r-- | src/eval/call.rs | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/src/eval/call.rs b/src/eval/call.rs new file mode 100644 index 00000000..186e7630 --- /dev/null +++ b/src/eval/call.rs @@ -0,0 +1,168 @@ +use super::*; +use crate::diag::Deco; + +impl Eval for Spanned<&ExprCall> { + type Output = Value; + + fn eval(self, ctx: &mut EvalContext) -> Self::Output { + let name = &self.v.name.v; + let span = self.v.name.span; + + if let Some(value) = ctx.state.scope.get(name) { + if let Value::Func(func) = value { + let func = func.clone(); + ctx.deco(Deco::Resolved.with_span(span)); + + let mut args = self.v.args.as_ref().eval(ctx); + let returned = func(ctx, &mut args); + args.finish(ctx); + + return returned; + } else { + let ty = value.type_name(); + ctx.diag(error!(span, "a value of type {} is not callable", ty)); + } + } else if !name.is_empty() { + ctx.diag(error!(span, "unknown function")); + } + + ctx.deco(Deco::Unresolved.with_span(span)); + Value::Error + } +} + +impl Eval for Spanned<&ExprArgs> { + type Output = Args; + + fn eval(self, ctx: &mut EvalContext) -> Self::Output { + let mut pos = vec![]; + let mut named = vec![]; + + for arg in self.v { + match arg { + Argument::Pos(expr) => { + pos.push(expr.as_ref().eval(ctx).with_span(expr.span)); + } + Argument::Named(Named { name, expr }) => { + named.push(( + name.as_ref().map(|id| id.0.clone()), + expr.as_ref().eval(ctx).with_span(expr.span), + )); + } + } + } + + Args { span: self.span, pos, named } + } +} + +/// Evaluated arguments to a function. +#[derive(Debug)] +pub struct Args { + span: Span, + pos: SpanVec<Value>, + named: Vec<(Spanned<String>, Spanned<Value>)>, +} + +impl Args { + /// Find the first convertible positional argument. + pub fn find<T>(&mut self, ctx: &mut EvalContext) -> Option<T> + where + T: Cast<Spanned<Value>>, + { + self.pos.iter_mut().find_map(move |slot| try_cast(ctx, slot)) + } + + /// Find the first convertible positional argument, producing an error if + /// no match was found. + pub fn require<T>(&mut self, ctx: &mut EvalContext, what: &str) -> Option<T> + where + T: Cast<Spanned<Value>>, + { + let found = self.find(ctx); + if found.is_none() { + ctx.diag(error!(self.span, "missing argument: {}", what)); + } + found + } + + /// Filter out all convertible positional arguments. + pub fn filter<'a, T>( + &'a mut self, + ctx: &'a mut EvalContext, + ) -> impl Iterator<Item = T> + 'a + where + T: Cast<Spanned<Value>>, + { + self.pos.iter_mut().filter_map(move |slot| try_cast(ctx, slot)) + } + + /// Convert the value for the given named argument, producing an error if + /// the conversion fails. + pub fn get<'a, T>(&mut self, ctx: &mut EvalContext, name: &str) -> Option<T> + where + T: Cast<Spanned<Value>>, + { + let index = self.named.iter().position(|(k, _)| k.v.as_str() == name)?; + let value = self.named.remove(index).1; + cast(ctx, value) + } + + /// Produce "unexpected argument" errors for all remaining arguments. + pub fn finish(self, ctx: &mut EvalContext) { + let a = self.pos.iter().map(|v| v.as_ref()); + let b = self.named.iter().map(|(k, v)| (&v.v).with_span(k.span.join(v.span))); + for value in a.chain(b) { + if value.v != &Value::Error { + ctx.diag(error!(value.span, "unexpected argument")); + } + } + } +} + +/// Cast the value into `T`, generating an error if the conversion fails. +fn cast<T>(ctx: &mut EvalContext, value: Spanned<Value>) -> Option<T> +where + T: Cast<Spanned<Value>>, +{ + let span = value.span; + match T::cast(value) { + CastResult::Ok(t) => Some(t), + CastResult::Warn(t, m) => { + ctx.diag(warning!(span, "{}", m)); + Some(t) + } + CastResult::Err(value) => { + ctx.diag(error!( + span, + "expected {}, found {}", + T::TYPE_NAME, + value.v.type_name() + )); + None + } + } +} + +/// Try to cast the value in the slot into `T`, putting it back if the +/// conversion fails. +fn try_cast<T>(ctx: &mut EvalContext, slot: &mut Spanned<Value>) -> Option<T> +where + T: Cast<Spanned<Value>>, +{ + // Replace with error placeholder when conversion works since error values + // are ignored when generating "unexpected argument" errors. + let value = std::mem::replace(slot, Spanned::zero(Value::Error)); + let span = value.span; + match T::cast(value) { + CastResult::Ok(t) => Some(t), + CastResult::Warn(t, m) => { + ctx.diag(warning!(span, "{}", m)); + Some(t) + } + CastResult::Err(value) => { + *slot = value; + None + } + } +} |
