summaryrefslogtreecommitdiff
path: root/src/eval
diff options
context:
space:
mode:
authorMarmare314 <49279081+Marmare314@users.noreply.github.com>2023-04-25 11:22:12 +0200
committerGitHub <noreply@github.com>2023-04-25 11:22:12 +0200
commitd5d98b67a83944d72a5c0f8e8e2f43aeee667122 (patch)
tree11580441926d1c24c6243868232b44b844663c4b /src/eval
parentefad1e71fa699e0d2413d3a6a3ce5a4163e38112 (diff)
Destructuring assign (#703)
Diffstat (limited to 'src/eval')
-rw-r--r--src/eval/func.rs4
-rw-r--r--src/eval/mod.rs223
2 files changed, 142 insertions, 85 deletions
diff --git a/src/eval/func.rs b/src/eval/func.rs
index 489527ef..e3b6a99c 100644
--- a/src/eval/func.rs
+++ b/src/eval/func.rs
@@ -445,7 +445,9 @@ impl<'a> CapturesVisitor<'a> {
match param {
ast::Param::Pos(ident) => self.bind(ident),
ast::Param::Named(named) => self.bind(named.name()),
- ast::Param::Sink(Some(ident)) => self.bind(ident),
+ ast::Param::Sink(spread) => {
+ self.bind(spread.name().unwrap_or_default())
+ }
_ => {}
}
}
diff --git a/src/eval/mod.rs b/src/eval/mod.rs
index 850a3d32..d8f49d66 100644
--- a/src/eval/mod.rs
+++ b/src/eval/mod.rs
@@ -456,6 +456,7 @@ impl Eval for ast::Expr {
Self::Unary(v) => v.eval(vm),
Self::Binary(v) => v.eval(vm),
Self::Let(v) => v.eval(vm),
+ Self::DestructAssign(v) => v.eval(vm),
Self::Set(_) => bail!(forbidden("set")),
Self::Show(_) => bail!(forbidden("show")),
Self::Conditional(v) => v.eval(vm),
@@ -1212,8 +1213,8 @@ impl Eval for ast::Closure {
ast::Param::Named(named) => {
params.push(Param::Named(named.name(), named.expr().eval(vm)?));
}
- ast::Param::Sink(name) => params.push(Param::Sink(name)),
- ast::Param::Placeholder => params.push(Param::Placeholder),
+ ast::Param::Sink(spread) => params.push(Param::Sink(spread.name())),
+ ast::Param::Placeholder(_) => params.push(Param::Placeholder),
}
}
@@ -1231,98 +1232,142 @@ impl Eval for ast::Closure {
}
impl ast::Pattern {
- // Destruct the given value into the pattern.
- #[tracing::instrument(skip_all)]
- pub fn define(&self, vm: &mut Vm, value: Value) -> SourceResult<Value> {
- match self {
- ast::Pattern::Ident(ident) => {
- vm.define(ident.clone(), value);
- Ok(Value::None)
- }
- ast::Pattern::Placeholder => Ok(Value::None),
- ast::Pattern::Destructuring(destruct) => {
- match value {
- Value::Array(value) => {
- let mut i = 0;
- for p in destruct.bindings() {
- match p {
- ast::DestructuringKind::Ident(ident) => {
- let Ok(v) = value.at(i) else {
- bail!(ident.span(), "not enough elements to destructure");
- };
- vm.define(ident.clone(), v.clone());
- i += 1;
- }
- ast::DestructuringKind::Sink(ident) => {
- (1 + value.len() as usize).checked_sub(destruct.bindings().count()).and_then(|sink_size| {
- let Ok(sink) = value.slice(i, Some(i + sink_size as i64)) else {
- return None;
- };
- if let Some(ident) = ident {
- vm.define(ident, sink);
- }
- i += sink_size as i64;
- Some(())
- }).ok_or("not enough elements to destructure").at(self.span())?;
- }
- ast::DestructuringKind::Named(key, _) => {
- bail!(
- key.span(),
- "cannot destructure named elements from an array"
- )
- }
- ast::DestructuringKind::Placeholder => i += 1,
- }
- }
- if i < value.len() {
- bail!(self.span(), "too many elements to destructure");
+ fn destruct_array<T>(
+ &self,
+ vm: &mut Vm,
+ value: Array,
+ f: T,
+ destruct: &ast::Destructuring,
+ ) -> SourceResult<Value>
+ where
+ T: Fn(&mut Vm, ast::Expr, Value) -> SourceResult<Value>,
+ {
+ let mut i = 0;
+ for p in destruct.bindings() {
+ match p {
+ ast::DestructuringKind::Normal(expr) => {
+ let Ok(v) = value.at(i) else {
+ bail!(expr.span(), "not enough elements to destructure");
+ };
+ f(vm, expr, v.clone())?;
+ i += 1;
+ }
+ ast::DestructuringKind::Sink(spread) => {
+ let sink_size = (1 + value.len() as usize)
+ .checked_sub(destruct.bindings().count());
+ let sink =
+ sink_size.and_then(|s| value.slice(i, Some(i + s as i64)).ok());
+
+ if let (Some(sink_size), Some(sink)) = (sink_size, sink) {
+ if let Some(expr) = spread.expr() {
+ f(vm, expr, Value::Array(sink.clone()))?;
}
+ i += sink_size as i64;
+ } else {
+ bail!(self.span(), "not enough elements to destructure")
}
- Value::Dict(value) => {
- let mut sink = None;
- let mut used = HashSet::new();
- for p in destruct.bindings() {
- match p {
- ast::DestructuringKind::Ident(ident) => {
- let Ok(v) = value.at(&ident) else {
+ }
+ ast::DestructuringKind::Named(named) => {
+ bail!(named.span(), "cannot destructure named elements from an array")
+ }
+ ast::DestructuringKind::Placeholder(_) => i += 1,
+ }
+ }
+ if i < value.len() {
+ bail!(self.span(), "too many elements to destructure");
+ }
+
+ Ok(Value::None)
+ }
+
+ fn destruct_dict<T>(
+ &self,
+ vm: &mut Vm,
+ value: Dict,
+ f: T,
+ destruct: &ast::Destructuring,
+ ) -> SourceResult<Value>
+ where
+ T: Fn(&mut Vm, ast::Expr, Value) -> SourceResult<Value>,
+ {
+ let mut sink = None;
+ let mut used = HashSet::new();
+ for p in destruct.bindings() {
+ match p {
+ ast::DestructuringKind::Normal(ast::Expr::Ident(ident)) => {
+ let Ok(v) = value.at(&ident) else {
bail!(ident.span(), "destructuring key not found in dictionary");
};
- vm.define(ident.clone(), v.clone());
- used.insert(ident.clone().take());
- }
- ast::DestructuringKind::Sink(ident) => {
- sink = ident.clone()
- }
- ast::DestructuringKind::Named(key, ident) => {
- let Ok(v) = value.at(&key) else {
- bail!(ident.span(), "destructuring key not found in dictionary");
+ f(vm, ast::Expr::Ident(ident.clone()), v.clone())?;
+ used.insert(ident.take());
+ }
+ ast::DestructuringKind::Sink(spread) => sink = spread.expr(),
+ ast::DestructuringKind::Named(named) => {
+ let Ok(v) = value.at(named.name().as_str()) else {
+ bail!(named.name().span(), "destructuring key not found in dictionary");
};
- vm.define(ident.clone(), v.clone());
- used.insert(key.clone().take());
- }
- ast::DestructuringKind::Placeholder => {}
- }
- }
+ f(vm, named.expr(), v.clone())?;
+ used.insert(named.name().take());
+ }
+ ast::DestructuringKind::Placeholder(_) => {}
+ ast::DestructuringKind::Normal(expr) => {
+ bail!(expr.span(), "expected key, found expression");
+ }
+ }
+ }
- if let Some(ident) = sink {
- let mut sink = Dict::new();
- for (key, value) in value {
- if !used.contains(key.as_str()) {
- sink.insert(key, value);
- }
- }
- vm.define(ident, Value::Dict(sink));
- }
- }
- _ => {
- bail!(self.span(), "cannot destructure {}", value.type_name());
- }
+ if let Some(expr) = sink {
+ let mut sink = Dict::new();
+ for (key, value) in value {
+ if !used.contains(key.as_str()) {
+ sink.insert(key, value);
}
+ }
+ f(vm, expr, Value::Dict(sink))?;
+ }
+
+ Ok(Value::None)
+ }
+ /// Destruct the given value into the pattern and apply the function to each binding.
+ #[tracing::instrument(skip_all)]
+ fn apply<T>(&self, vm: &mut Vm, value: Value, f: T) -> SourceResult<Value>
+ where
+ T: Fn(&mut Vm, ast::Expr, Value) -> SourceResult<Value>,
+ {
+ match self {
+ ast::Pattern::Normal(expr) => {
+ f(vm, expr.clone(), value)?;
Ok(Value::None)
}
+ ast::Pattern::Placeholder(_) => Ok(Value::None),
+ ast::Pattern::Destructuring(destruct) => match value {
+ Value::Array(value) => self.destruct_array(vm, value, f, destruct),
+ Value::Dict(value) => self.destruct_dict(vm, value, f, destruct),
+ _ => bail!(self.span(), "cannot destructure {}", value.type_name()),
+ },
}
}
+
+ /// Destruct the value into the pattern by binding.
+ pub fn define(&self, vm: &mut Vm, value: Value) -> SourceResult<Value> {
+ self.apply(vm, value, |vm, expr, value| match expr {
+ ast::Expr::Ident(ident) => {
+ vm.define(ident, value);
+ Ok(Value::None)
+ }
+ _ => unreachable!(),
+ })
+ }
+
+ /// Destruct the value into the pattern by assignment.
+ pub fn assign(&self, vm: &mut Vm, value: Value) -> SourceResult<Value> {
+ self.apply(vm, value, |vm, expr, value| {
+ let location = expr.access(vm)?;
+ *location = value;
+ Ok(Value::None)
+ })
+ }
}
impl Eval for ast::LetBinding {
@@ -1345,6 +1390,16 @@ impl Eval for ast::LetBinding {
}
}
+impl Eval for ast::DestructAssignment {
+ type Output = Value;
+
+ fn eval(&self, vm: &mut Vm) -> SourceResult<Self::Output> {
+ let value = self.value().eval(vm)?;
+ self.pattern().assign(vm, value)?;
+ Ok(Value::None)
+ }
+}
+
impl Eval for ast::SetRule {
type Output = Styles;
@@ -1514,7 +1569,7 @@ impl Eval for ast::ForLoop {
let pattern = self.pattern();
match (&pattern, iter.clone()) {
- (ast::Pattern::Ident(_), Value::Str(string)) => {
+ (ast::Pattern::Normal(_), Value::Str(string)) => {
// Iterate over graphemes of string.
iter!(for pattern in string.as_str().graphemes(true));
}
@@ -1526,7 +1581,7 @@ impl Eval for ast::ForLoop {
// Iterate over values of array.
iter!(for pattern in array);
}
- (ast::Pattern::Ident(_), _) => {
+ (ast::Pattern::Normal(_), _) => {
bail!(self.iter().span(), "cannot loop over {}", iter.type_name());
}
(_, _) => {