diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-05-18 21:32:40 +0200 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-05-18 21:32:40 +0200 |
| commit | 118fc1014bcfc5585fd3ce32348fbfe14bdc05a9 (patch) | |
| tree | 51ef7937188776d0343196fda207c77e732859d1 /src/eval | |
| parent | 7025590405623b99ec93aa6eddc9fc909e8f83a8 (diff) | |
Leaner argument parsing
Diffstat (limited to 'src/eval')
| -rw-r--r-- | src/eval/mod.rs | 2 | ||||
| -rw-r--r-- | src/eval/value.rs | 118 |
2 files changed, 49 insertions, 71 deletions
diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 88110d88..da7fca53 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -401,7 +401,7 @@ impl Eval for ClosureExpr { for param in params.iter() { // Set the parameter to `none` if the argument is missing. let value = - args.require::<Value>(ctx, param.as_str()).unwrap_or_default(); + args.eat_expect::<Value>(ctx, param.as_str()).unwrap_or_default(); ctx.scopes.def_mut(param.as_str(), value); } diff --git a/src/eval/value.rs b/src/eval/value.rs index 33592e5d..a69398f0 100644 --- a/src/eval/value.rs +++ b/src/eval/value.rs @@ -7,7 +7,6 @@ use std::rc::Rc; use super::{EvalContext, NodeMap}; use crate::color::Color; -use crate::diag::DiagSet; use crate::exec::ExecContext; use crate::geom::{Angle, Length, Linear, Relative}; use crate::syntax::{Span, Spanned, Tree}; @@ -261,51 +260,67 @@ pub struct FuncArgs { } impl FuncArgs { - /// Find and remove the first convertible positional argument. - pub fn find<T>(&mut self, ctx: &mut EvalContext) -> Option<T> + /// Find and consume the first castable positional argument. + pub fn eat<T>(&mut self, ctx: &mut EvalContext) -> Option<T> where T: Cast<Spanned<Value>>, { - (0 .. self.items.len()).find_map(move |i| self.try_take(&mut ctx.diags, i)) + (0 .. self.items.len()).find_map(|index| { + let slot = &mut self.items[index]; + if slot.name.is_some() { + return None; + } + + let value = std::mem::replace(&mut slot.value, Spanned::zero(Value::None)); + let span = value.span; + + match T::cast(value) { + CastResult::Ok(t) => { + self.items.remove(index); + Some(t) + } + CastResult::Warn(t, m) => { + self.items.remove(index); + ctx.diag(warning!(span, "{}", m)); + Some(t) + } + CastResult::Err(value) => { + slot.value = value; + None + } + } + }) } - /// Find and remove 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> + /// Find and consume the first castable positional argument, producing a + /// `missing argument: {what}` error if no match was found. + pub fn eat_expect<T>(&mut self, ctx: &mut EvalContext, what: &str) -> Option<T> where T: Cast<Spanned<Value>>, { - let found = self.find(ctx); + let found = self.eat(ctx); if found.is_none() { ctx.diag(error!(self.span, "missing argument: {}", what)); } found } - /// Filter out and remove all convertible positional arguments. - pub fn filter<'a, T>( - &'a mut self, - ctx: &'a mut EvalContext, - ) -> impl Iterator<Item = T> + 'a + /// Find, consume and collect all castable positional arguments. + /// + /// This function returns a vector instead of an iterator because the + /// iterator would require unique access to the context, rendering it rather + /// unusable. If you need to process arguments one-by-one, you probably want + /// to use a while-let loop together with [`eat()`](Self::eat). + pub fn eat_all<T>(&mut self, ctx: &mut EvalContext) -> Vec<T> where T: Cast<Spanned<Value>>, { - let diags = &mut ctx.diags; - let mut i = 0; - std::iter::from_fn(move || { - while i < self.items.len() { - if let Some(val) = self.try_take(diags, i) { - return Some(val); - } - i += 1; - } - None - }) + std::iter::from_fn(|| self.eat(ctx)).collect() } - /// Convert and remove the value for the given named argument, producing an + /// Cast and remove the value for the given named argument, producing an /// error if the conversion fails. - pub fn get<T>(&mut self, ctx: &mut EvalContext, name: &str) -> Option<T> + pub fn eat_named<T>(&mut self, ctx: &mut EvalContext, name: &str) -> Option<T> where T: Cast<Spanned<Value>>, { @@ -315,24 +330,8 @@ impl FuncArgs { .position(|arg| arg.name.as_ref().map(|s| s.v.as_str()) == Some(name))?; let value = self.items.remove(index).value; - self.cast(ctx, value) - } - - /// Produce "unexpected argument" errors for all remaining arguments. - pub fn finish(self, ctx: &mut EvalContext) { - for arg in &self.items { - if arg.value.v != Value::Error { - ctx.diag(error!(arg.span(), "unexpected argument")); - } - } - } - - /// Cast the value into `T`, generating an error if the conversion fails. - fn cast<T>(&self, 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) => { @@ -344,39 +343,18 @@ impl FuncArgs { span, "expected {}, found {}", T::TYPE_NAME, - value.v.type_name() + value.v.type_name(), )); None } } } - /// Try to take and cast a positional argument in the i'th slot into `T`, - /// putting it back if the conversion fails. - fn try_take<T>(&mut self, diags: &mut DiagSet, i: usize) -> Option<T> - where - T: Cast<Spanned<Value>>, - { - let slot = &mut self.items[i]; - if slot.name.is_some() { - return None; - } - - let value = std::mem::replace(&mut slot.value, Spanned::zero(Value::None)); - let span = value.span; - match T::cast(value) { - CastResult::Ok(t) => { - self.items.remove(i); - Some(t) - } - CastResult::Warn(t, m) => { - self.items.remove(i); - diags.insert(warning!(span, "{}", m)); - Some(t) - } - CastResult::Err(value) => { - slot.value = value; - None + /// Produce "unexpected argument" errors for all remaining arguments. + pub fn finish(self, ctx: &mut EvalContext) { + for arg in &self.items { + if arg.value.v != Value::Error { + ctx.diag(error!(arg.span(), "unexpected argument")); } } } |
