Refactor binder to use scope depth
Introduced `scope_depth` to the `Binder` to track block nesting. This allows for correctly determining whether a `def` binding should be `Address::Global` (at scope depth 0) or `Address::Local`. The `type_checker` was also updated to handle `Program` and `Block` nodes by creating a new `TypeContext` for them. Removed the `wrap_as_lambda` method from `Environment` as the compiler now always produces a `Program` node, effectively handling the wrapping at the AST level. Correspondingly, `instantiate` and `run_script_compiled` were simplified to directly run the `Program` node.
This commit is contained in:
@@ -139,6 +139,10 @@ pub type BindingResult = (
|
|||||||
pub struct Binder {
|
pub struct Binder {
|
||||||
functions: Vec<FunctionCompiler>,
|
functions: Vec<FunctionCompiler>,
|
||||||
capture_map: HashMap<Identity, std::collections::HashSet<Identity>>,
|
capture_map: HashMap<Identity, std::collections::HashSet<Identity>>,
|
||||||
|
/// Block nesting depth. At 0 (inside a `Program` node), `def` bindings
|
||||||
|
/// receive `Address::Global` so their values persist in the GlobalStore.
|
||||||
|
/// Incremented by `Block`, not by `Program`.
|
||||||
|
scope_depth: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Binder {
|
impl Binder {
|
||||||
@@ -146,6 +150,7 @@ impl Binder {
|
|||||||
let mut binder = Self {
|
let mut binder = Self {
|
||||||
functions: Vec::new(),
|
functions: Vec::new(),
|
||||||
capture_map: HashMap::new(),
|
capture_map: HashMap::new(),
|
||||||
|
scope_depth: 0,
|
||||||
};
|
};
|
||||||
binder.functions.push(FunctionCompiler::new(
|
binder.functions.push(FunctionCompiler::new(
|
||||||
NodeIdentity::new(SourceLocation { line: 0, col: 0 }),
|
NodeIdentity::new(SourceLocation { line: 0, col: 0 }),
|
||||||
@@ -186,7 +191,7 @@ impl Binder {
|
|||||||
_kind: DeclarationKind,
|
_kind: DeclarationKind,
|
||||||
diag: &mut Diagnostics,
|
diag: &mut Diagnostics,
|
||||||
) -> Option<Address<VirtualId>> {
|
) -> Option<Address<VirtualId>> {
|
||||||
let as_global = self.functions.len() == 1;
|
let as_global = self.scope_depth == 0;
|
||||||
let current_fn = self.functions.last_mut().unwrap();
|
let current_fn = self.functions.last_mut().unwrap();
|
||||||
match current_fn.define_variable(name, identity, as_global) {
|
match current_fn.define_variable(name, identity, as_global) {
|
||||||
Ok(addr) => Some(addr),
|
Ok(addr) => Some(addr),
|
||||||
@@ -354,10 +359,12 @@ impl Binder {
|
|||||||
let identity = node.identity.clone();
|
let identity = node.identity.clone();
|
||||||
self.functions
|
self.functions
|
||||||
.push(FunctionCompiler::new(identity.clone(), vec![], 0));
|
.push(FunctionCompiler::new(identity.clone(), vec![], 0));
|
||||||
|
self.scope_depth += 1;
|
||||||
|
|
||||||
let params_bound =
|
let params_bound =
|
||||||
self.bind_pattern(params.as_ref(), DeclarationKind::Parameter, diag);
|
self.bind_pattern(params.as_ref(), DeclarationKind::Parameter, diag);
|
||||||
let body_bound = self.bind(body.as_ref(), ExprContext::Expression, diag);
|
let body_bound = self.bind(body.as_ref(), ExprContext::Expression, diag);
|
||||||
|
self.scope_depth -= 1;
|
||||||
let compiled_fn = self.functions.pop().unwrap();
|
let compiled_fn = self.functions.pop().unwrap();
|
||||||
|
|
||||||
fn count_params(node: &Node<BoundPhase>) -> Option<u32> {
|
fn count_params(node: &Node<BoundPhase>) -> Option<u32> {
|
||||||
@@ -429,6 +436,7 @@ impl Binder {
|
|||||||
|
|
||||||
SyntaxKind::Block { exprs } => {
|
SyntaxKind::Block { exprs } => {
|
||||||
self.functions.last_mut().unwrap().push_scope();
|
self.functions.last_mut().unwrap().push_scope();
|
||||||
|
self.scope_depth += 1;
|
||||||
let mut bound_exprs = Vec::new();
|
let mut bound_exprs = Vec::new();
|
||||||
for (i, expr) in exprs.iter().enumerate() {
|
for (i, expr) in exprs.iter().enumerate() {
|
||||||
let expr_ctx = if i == exprs.len() - 1 {
|
let expr_ctx = if i == exprs.len() - 1 {
|
||||||
@@ -438,6 +446,7 @@ impl Binder {
|
|||||||
};
|
};
|
||||||
bound_exprs.push(Rc::new(self.bind(expr, expr_ctx, diag)));
|
bound_exprs.push(Rc::new(self.bind(expr, expr_ctx, diag)));
|
||||||
}
|
}
|
||||||
|
self.scope_depth -= 1;
|
||||||
self.functions.last_mut().unwrap().pop_scope();
|
self.functions.last_mut().unwrap().pop_scope();
|
||||||
self.make_node(
|
self.make_node(
|
||||||
node.identity.clone(),
|
node.identity.clone(),
|
||||||
|
|||||||
@@ -63,9 +63,11 @@ impl<'a> TypeContext<'a> {
|
|||||||
}
|
}
|
||||||
Address::Global(idx) => {
|
Address::Global(idx) => {
|
||||||
let mut rt = self.root_types.borrow_mut();
|
let mut rt = self.root_types.borrow_mut();
|
||||||
if (idx.0 as usize) < rt.len() {
|
let i = idx.0 as usize;
|
||||||
rt[idx.0 as usize] = ty;
|
if i >= rt.len() {
|
||||||
|
rt.resize(i + 1, StaticType::Any);
|
||||||
}
|
}
|
||||||
|
rt[i] = ty;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,14 @@
|
|||||||
mod context;
|
|
||||||
mod inference;
|
|
||||||
mod finalize;
|
|
||||||
mod check;
|
mod check;
|
||||||
|
mod context;
|
||||||
|
mod finalize;
|
||||||
|
mod inference;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use crate::ast::compiler::call_hooks::RtlCompilerHook;
|
use crate::ast::compiler::call_hooks::RtlCompilerHook;
|
||||||
use crate::ast::diagnostics::Diagnostics;
|
use crate::ast::diagnostics::Diagnostics;
|
||||||
use crate::ast::nodes::{
|
use crate::ast::nodes::{BoundLike, BoundPhase, LambdaBinding, Node, NodeKind, TypedNode};
|
||||||
BoundLike, BoundPhase, LambdaBinding,
|
|
||||||
Node, NodeKind, TypedNode,
|
|
||||||
};
|
|
||||||
use crate::ast::types::{Signature, StaticType};
|
use crate::ast::types::{Signature, StaticType};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -72,11 +69,7 @@ impl TypeChecker {
|
|||||||
diag: &mut Diagnostics,
|
diag: &mut Diagnostics,
|
||||||
) -> TypedNode {
|
) -> TypedNode {
|
||||||
match &node.kind {
|
match &node.kind {
|
||||||
NodeKind::Lambda {
|
NodeKind::Lambda { params, body, info } => {
|
||||||
params,
|
|
||||||
body,
|
|
||||||
info,
|
|
||||||
} => {
|
|
||||||
let upvalues = &info.upvalues;
|
let upvalues = &info.upvalues;
|
||||||
let positional_count = info.positional_count;
|
let positional_count = info.positional_count;
|
||||||
|
|
||||||
@@ -95,12 +88,8 @@ impl TypeChecker {
|
|||||||
StaticType::Tuple(arg_types.to_vec())
|
StaticType::Tuple(arg_types.to_vec())
|
||||||
};
|
};
|
||||||
|
|
||||||
let params_typed = self.check_params(
|
let params_typed =
|
||||||
params.as_ref(),
|
self.check_params(params.as_ref(), &arg_tuple_ty, &mut lambda_ctx, diag);
|
||||||
&arg_tuple_ty,
|
|
||||||
&mut lambda_ctx,
|
|
||||||
diag,
|
|
||||||
);
|
|
||||||
|
|
||||||
let body_typed = self.check_node(body, &mut lambda_ctx, diag);
|
let body_typed = self.check_node(body, &mut lambda_ctx, diag);
|
||||||
let ret_ty = body_typed.ty.clone();
|
let ret_ty = body_typed.ty.clone();
|
||||||
@@ -125,6 +114,10 @@ impl TypeChecker {
|
|||||||
comments: node.comments.clone(),
|
comments: node.comments.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
NodeKind::Block { .. } | NodeKind::Program { .. } => {
|
||||||
|
let mut ctx = TypeContext::new(64, vec![], &self.root_types, None);
|
||||||
|
self.check_node(node, &mut ctx, diag)
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let mut root_ctx = TypeContext::new(0, vec![], &self.root_types, None);
|
let mut root_ctx = TypeContext::new(0, vec![], &self.root_types, None);
|
||||||
self.check_node(node, &mut root_ctx, diag)
|
self.check_node(node, &mut root_ctx, diag)
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ fn test_inference_constants() {
|
|||||||
fn test_inference_variable_propagation() {
|
fn test_inference_variable_propagation() {
|
||||||
// (do (def x 10) x) -> The last 'x' must be Int
|
// (do (def x 10) x) -> The last 'x' must be Int
|
||||||
let typed = check_source("(do (def x 10) x)");
|
let typed = check_source("(do (def x 10) x)");
|
||||||
// Outer is Lambda, Body is Block
|
// compile() wraps in a Program node; the (do ...) block is the single child.
|
||||||
if let NodeKind::Lambda { body, .. } = &typed.kind {
|
if let NodeKind::Program { exprs: prog } = &typed.kind {
|
||||||
if let NodeKind::Block { exprs } = &body.kind {
|
if let NodeKind::Block { exprs } = &prog[0].kind {
|
||||||
let last_expr = exprs.last().unwrap();
|
let last_expr = exprs.last().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
last_expr.ty,
|
last_expr.ty,
|
||||||
@@ -37,10 +37,10 @@ fn test_inference_variable_propagation() {
|
|||||||
"Variable 'x' should be inferred as Int"
|
"Variable 'x' should be inferred as Int"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected block in lambda body");
|
panic!("Expected Block inside Program");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected Lambda wrapper");
|
panic!("Expected Program, got {:?}", typed.kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,8 +78,8 @@ fn test_inference_lambda_return() {
|
|||||||
fn test_inference_assignment_updates_type() {
|
fn test_inference_assignment_updates_type() {
|
||||||
// (do (def x 10) (assign x 20.5) x) -> x becomes Float after assignment
|
// (do (def x 10) (assign x 20.5) x) -> x becomes Float after assignment
|
||||||
let typed = check_source("(do (def x 10) (assign x 20.5) x)");
|
let typed = check_source("(do (def x 10) (assign x 20.5) x)");
|
||||||
if let NodeKind::Lambda { body, .. } = &typed.kind {
|
if let NodeKind::Program { exprs: prog } = &typed.kind {
|
||||||
if let NodeKind::Block { exprs } = &body.kind {
|
if let NodeKind::Block { exprs } = &prog[0].kind {
|
||||||
let last_expr = exprs.last().unwrap();
|
let last_expr = exprs.last().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
last_expr.ty,
|
last_expr.ty,
|
||||||
@@ -87,10 +87,10 @@ fn test_inference_assignment_updates_type() {
|
|||||||
"Variable 'x' should be specialized to Float after assignment"
|
"Variable 'x' should be specialized to Float after assignment"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected block");
|
panic!("Expected Block inside Program");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!("Expected Lambda");
|
panic!("Expected Program, got {:?}", typed.kind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-89
@@ -4,7 +4,6 @@ use crate::ast::compiler::call_hooks::RtlCompilerHook;
|
|||||||
use crate::ast::compiler::{CapturePass, TypeChecker, TypedNode};
|
use crate::ast::compiler::{CapturePass, TypeChecker, TypedNode};
|
||||||
use crate::ast::nodes::{AnalyzedPhase, BoundPhase, Symbol, SyntaxKind, SyntaxNode};
|
use crate::ast::nodes::{AnalyzedPhase, BoundPhase, Symbol, SyntaxKind, SyntaxNode};
|
||||||
use crate::ast::parser::Parser;
|
use crate::ast::parser::Parser;
|
||||||
use crate::ast::closure::Closure;
|
|
||||||
use crate::ast::vm::{GlobalStore, TracingObserver, VM};
|
use crate::ast::vm::{GlobalStore, TracingObserver, VM};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
@@ -13,7 +12,7 @@ use std::rc::Rc;
|
|||||||
|
|
||||||
use crate::ast::nodes::{
|
use crate::ast::nodes::{
|
||||||
Address, AnalyzedNode, ExecNode, GlobalAnalyzedRegistry, GlobalFunctionRegistry, GlobalIdx,
|
Address, AnalyzedNode, ExecNode, GlobalAnalyzedRegistry, GlobalFunctionRegistry, GlobalIdx,
|
||||||
LambdaBinding, Node, NodeKind, VirtualId,
|
Node, NodeKind, VirtualId,
|
||||||
};
|
};
|
||||||
use crate::ast::compiler::dumper::Dumper;
|
use crate::ast::compiler::dumper::Dumper;
|
||||||
use crate::ast::compiler::lambda_collector::LambdaCollector;
|
use crate::ast::compiler::lambda_collector::LambdaCollector;
|
||||||
@@ -440,32 +439,6 @@ impl Environment {
|
|||||||
Some(bound_ast)
|
Some(bound_ast)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps a bound AST in a parameterless lambda (unless it already is one).
|
|
||||||
fn wrap_as_lambda(&self, bound_ast: Node<BoundPhase>) -> Node<BoundPhase> {
|
|
||||||
if let NodeKind::Lambda { .. } = bound_ast.kind {
|
|
||||||
bound_ast
|
|
||||||
} else {
|
|
||||||
Node {
|
|
||||||
identity: bound_ast.identity.clone(),
|
|
||||||
kind: NodeKind::Lambda {
|
|
||||||
params: Rc::new(Node {
|
|
||||||
identity: bound_ast.identity.clone(),
|
|
||||||
kind: NodeKind::Tuple { elements: vec![] },
|
|
||||||
ty: (),
|
|
||||||
comments: Rc::from([]),
|
|
||||||
}),
|
|
||||||
body: Rc::new(bound_ast),
|
|
||||||
info: LambdaBinding {
|
|
||||||
upvalues: vec![],
|
|
||||||
positional_count: Some(0),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ty: (),
|
|
||||||
comments: Rc::from([]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type-checks a bound AST and collects doc comments.
|
/// Type-checks a bound AST and collects doc comments.
|
||||||
fn type_check(&self, bound_ast: &Node<BoundPhase>, diagnostics: &mut Diagnostics) -> TypedNode {
|
fn type_check(&self, bound_ast: &Node<BoundPhase>, diagnostics: &mut Diagnostics) -> TypedNode {
|
||||||
let checker = TypeChecker::new(self.root_types.clone(), Rc::clone(&self.rtl.compiler_hooks));
|
let checker = TypeChecker::new(self.root_types.clone(), Rc::clone(&self.rtl.compiler_hooks));
|
||||||
@@ -474,12 +447,11 @@ impl Environment {
|
|||||||
typed
|
typed
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Full compilation pipeline: expand → bind → wrap → type-check.
|
/// Full compilation pipeline: expand → bind → type-check.
|
||||||
fn compile_pipeline(&self, syntax_ast: SyntaxNode, diagnostics: &mut Diagnostics) -> Option<TypedNode> {
|
fn compile_pipeline(&self, syntax_ast: SyntaxNode, diagnostics: &mut Diagnostics) -> Option<TypedNode> {
|
||||||
let expanded = self.expand(syntax_ast, diagnostics)?;
|
let expanded = self.expand(syntax_ast, diagnostics)?;
|
||||||
let bound = self.bind_and_update(&expanded, diagnostics)?;
|
let bound = self.bind_and_update(&expanded, diagnostics)?;
|
||||||
let wrapped = self.wrap_as_lambda(bound);
|
let typed = self.type_check(&bound, diagnostics);
|
||||||
let typed = self.type_check(&wrapped, diagnostics);
|
|
||||||
Some(typed)
|
Some(typed)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -738,17 +710,7 @@ impl Environment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut parser = Parser::new(source);
|
let mut parser = Parser::new(source);
|
||||||
let syntax_ast = parser.parse_expression();
|
let syntax_ast = parser.parse_program();
|
||||||
|
|
||||||
if !parser.at_eof() {
|
|
||||||
parser
|
|
||||||
.diagnostics
|
|
||||||
.push_error("Unexpected trailing expressions in script.", None);
|
|
||||||
return CompilationResult {
|
|
||||||
ast: None,
|
|
||||||
diagnostics: parser.diagnostics,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
let mut diagnostics = parser.diagnostics;
|
let mut diagnostics = parser.diagnostics;
|
||||||
|
|
||||||
let typed_ast = self.compile_pipeline(syntax_ast, &mut diagnostics);
|
let typed_ast = self.compile_pipeline(syntax_ast, &mut diagnostics);
|
||||||
@@ -780,43 +742,6 @@ impl Environment {
|
|||||||
Lowering::lower(optimized)
|
Lowering::lower(optimized)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a linked `ExecNode` into a callable `Closure`.
|
|
||||||
///
|
|
||||||
/// Fast path: if the node is already a top-level lambda without upvalues,
|
|
||||||
/// the `Closure` is constructed directly without running the VM.
|
|
||||||
///
|
|
||||||
/// Slow path: the node is executed once to obtain its resulting `Closure`
|
|
||||||
/// value (e.g. a `do`-block that returns a lambda).
|
|
||||||
///
|
|
||||||
/// The caller is responsible for creating a `VM` and invoking the closure
|
|
||||||
/// via `vm.run_with_args`. This keeps VM lifecycle and error handling at
|
|
||||||
/// the call site, where the required strategy (single run vs. repeated
|
|
||||||
/// benchmark iterations) is known.
|
|
||||||
pub fn instantiate(&self, node: ExecNode) -> Result<Rc<Closure>, String> {
|
|
||||||
// Fast path: top-level lambda without upvalues — build Closure directly.
|
|
||||||
if let NodeKind::Lambda { params, body, info } = &node.kind
|
|
||||||
&& info.upvalues.is_empty()
|
|
||||||
{
|
|
||||||
return Ok(Rc::new(Closure::new(
|
|
||||||
params.clone(),
|
|
||||||
body.ty.original.clone(),
|
|
||||||
body.clone(),
|
|
||||||
Vec::new(),
|
|
||||||
info.positional_count,
|
|
||||||
node.ty.stack_size,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slow path: run the node once to extract the resulting Closure.
|
|
||||||
let mut vm = VM::new(self.global_store());
|
|
||||||
let res = vm.run(&node)?;
|
|
||||||
if let Value::Closure(obj) = res {
|
|
||||||
Ok(obj)
|
|
||||||
} else {
|
|
||||||
Err("Script did not produce a callable closure".to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `VM` connected to this environment's global scope.
|
/// Creates a new `VM` connected to this environment's global scope.
|
||||||
pub fn create_vm(&self) -> VM {
|
pub fn create_vm(&self) -> VM {
|
||||||
VM::new(self.global_store())
|
VM::new(self.global_store())
|
||||||
@@ -920,9 +845,8 @@ impl Environment {
|
|||||||
|
|
||||||
pub fn run_script_compiled(&self, compiled: TypedNode) -> Result<Value, String> {
|
pub fn run_script_compiled(&self, compiled: TypedNode) -> Result<Value, String> {
|
||||||
let linked = self.link(compiled);
|
let linked = self.link(compiled);
|
||||||
let closure = self.instantiate(linked)?;
|
|
||||||
let mut vm = self.create_vm();
|
let mut vm = self.create_vm();
|
||||||
let res = vm.run_with_args(closure, &[])?;
|
let res = vm.run(&linked)?;
|
||||||
self.run_pipeline();
|
self.run_pipeline();
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
@@ -935,17 +859,10 @@ impl Environment {
|
|||||||
let mut vm = VM::new(self.global_store());
|
let mut vm = VM::new(self.global_store());
|
||||||
let mut observer = TracingObserver::new();
|
let mut observer = TracingObserver::new();
|
||||||
|
|
||||||
// 1. Run the script wrapper (returns a closure representing the script)
|
|
||||||
let result = vm.run_with_observer(&mut observer, &linked);
|
let result = vm.run_with_observer(&mut observer, &linked);
|
||||||
// 2. Execute the root closure immediately to get the actual script result.
|
|
||||||
// All Myc scripts are wrapped in a parameterless lambda for consistency.
|
|
||||||
let mut final_result = result;
|
|
||||||
if let Ok(Value::Closure(obj)) = &final_result {
|
|
||||||
final_result = vm.run_with_args_observed(&mut observer, obj.clone(), &[]);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.run_pipeline();
|
self.run_pipeline();
|
||||||
|
|
||||||
Ok((final_result, observer.logs))
|
Ok((result, observer.logs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -105,18 +105,17 @@ pub fn run_benchmarks(update: bool, filter: Option<&str>) -> Vec<BenchmarkResult
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Helper to measure sum of VM execution times over N executions in one environment.
|
// Helper to measure sum of VM execution times over N executions in one environment.
|
||||||
// A single VM is reused across iterations: run_with_args resets stack/frames
|
// A single VM is reused across iterations: run resets stack/frames
|
||||||
// on each call, so no re-allocation is needed between runs.
|
// on each call, so no re-allocation is needed between runs.
|
||||||
let measure_sum =
|
let measure_sum =
|
||||||
|n: u32, node: &TypedNode| -> Result<Duration, String> {
|
|n: u32, node: &TypedNode| -> Result<Duration, String> {
|
||||||
let env = Environment::new();
|
let env = Environment::new();
|
||||||
let linked = env.link(node.clone());
|
let linked = env.link(node.clone());
|
||||||
let closure = env.instantiate(linked)?;
|
|
||||||
let mut vm = env.create_vm();
|
let mut vm = env.create_vm();
|
||||||
let mut total = Duration::ZERO;
|
let mut total = Duration::ZERO;
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let _ = vm.run_with_args(closure.clone(), &[])?;
|
let _ = vm.run(&linked)?;
|
||||||
total += start.elapsed();
|
total += start.elapsed();
|
||||||
}
|
}
|
||||||
Ok(total)
|
Ok(total)
|
||||||
|
|||||||
+1
-2
@@ -18,8 +18,7 @@ fn test_closure_modification_from_source() {
|
|||||||
.into_result()
|
.into_result()
|
||||||
.expect("Failed to compile");
|
.expect("Failed to compile");
|
||||||
let linked = env.link(compiled);
|
let linked = env.link(compiled);
|
||||||
let closure = env.instantiate(linked).expect("Failed to instantiate");
|
let result = env.create_vm().run(&linked);
|
||||||
let result = env.create_vm().run_with_args(closure, &[]);
|
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(Value::Int(20)) => (),
|
Ok(Value::Int(20)) => (),
|
||||||
|
|||||||
Reference in New Issue
Block a user