diff options
| author | Laurenz <laurmaedje@gmail.com> | 2022-12-30 09:48:30 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2022-12-30 09:48:30 +0100 |
| commit | 68c6160a14be4e77b98cd704d9e641a03aefa332 (patch) | |
| tree | 3eb0d02bf16e62cf43b7e5426126934b4ce7fbf2 /src/model/func.rs | |
| parent | a12bc3a797c1a29ef223c9e8be6c52cc4e748a02 (diff) | |
Recursion with max depth
Diffstat (limited to 'src/model/func.rs')
| -rw-r--r-- | src/model/func.rs | 20 |
1 files changed, 16 insertions, 4 deletions
diff --git a/src/model/func.rs b/src/model/func.rs index f04b864e..3fb8f4d4 100644 --- a/src/model/func.rs +++ b/src/model/func.rs @@ -96,7 +96,7 @@ impl Func { pub fn call(&self, vm: &Vm, mut args: Args) -> SourceResult<Value> { let value = match self.0.as_ref() { Repr::Native(native) => (native.func)(vm, &mut args)?, - Repr::Closure(closure) => closure.call(vm, &mut args)?, + Repr::Closure(closure) => closure.call(vm, self, &mut args)?, Repr::With(wrapped, applied) => { args.items.splice(..0, applied.items.iter().cloned()); return wrapped.call(vm, args); @@ -115,7 +115,7 @@ impl Func { let route = Route::default(); let id = SourceId::detached(); let scopes = Scopes::new(None); - let vm = Vm::new(world, route.track(), id, scopes); + let vm = Vm::new(world, route.track(), id, scopes, 0); self.call(&vm, args) } @@ -274,12 +274,17 @@ pub(super) struct Closure { impl Closure { /// Call the function in the context with the arguments. - fn call(&self, vm: &Vm, args: &mut Args) -> SourceResult<Value> { + fn call(&self, vm: &Vm, this: &Func, args: &mut Args) -> SourceResult<Value> { // Don't leak the scopes from the call site. Instead, we use the scope // of captured variables we collected earlier. let mut scopes = Scopes::new(None); scopes.top = self.captured.clone(); + // Provide the closure itself for recursive calls. + if let Some(name) = &self.name { + scopes.top.define(name.clone(), Value::Func(this.clone())); + } + // Parse the arguments according to the parameter list. for (param, default) in &self.params { scopes.top.define( @@ -304,7 +309,7 @@ impl Closure { let route = if detached { fresh.track() } else { vm.route }; // Evaluate the body. - let mut sub = Vm::new(vm.world, route, self.location, scopes); + let mut sub = Vm::new(vm.world, route, self.location, scopes, vm.depth + 1); let result = self.body.eval(&mut sub); // Handle control flow. @@ -378,6 +383,10 @@ impl<'a> CapturesVisitor<'a> { } } + if let Some(name) = expr.name() { + self.bind(name); + } + for param in expr.params() { match param { ast::Param::Pos(ident) => self.bind(ident), @@ -456,6 +465,7 @@ mod tests { #[track_caller] fn test(text: &str, result: &[&str]) { let mut scopes = Scopes::new(None); + scopes.top.define("f", 0); scopes.top.define("x", 0); scopes.top.define("y", 0); scopes.top.define("z", 0); @@ -477,6 +487,8 @@ mod tests { test("#let x = x", &["x"]); test("#let x; {x + y}", &["y"]); test("#let f(x, y) = x + y", &[]); + test("#let f(x, y) = f", &[]); + test("#let f = (x, y) => f", &["f"]); // Closure with different kinds of params. test("{(x, y) => x + z}", &["z"]); |
