diff --git a/src/vstc/capture_finder.rs b/src/vstc/capture_finder.rs index fdac163..6584891 100644 --- a/src/vstc/capture_finder.rs +++ b/src/vstc/capture_finder.rs @@ -22,20 +22,31 @@ impl CaptureFinder { return; } + let mut insert = |n: &String| { + let inserted = self.names.insert(n.clone()); + + if inserted { + self.ordered_names.push(n.clone()); + } + }; + match self.outside_scope.get(&name) { None => std::panic!("Unresolved name"), Some(MappedName::Definition(_)) => {}, // Not capture - just definition - Some(MappedName::Register(_)) => { - let inserted = self.names.insert(name.clone()); + Some(MappedName::Register(_)) => insert(&name), + Some(MappedName::QueuedFunction(qfn)) => { + for cap in &qfn.capture_params { + if scope.get(cap).is_some() { + std::panic!("Not implemented: Nested capture edge case"); + } - if inserted { - self.ordered_names.push(name); + insert(cap); } }, } } - fn fn_decl(&mut self, parent_scope: &Scope, decl: &swc_ecma_ast::FnDecl) { + pub fn fn_decl(&mut self, parent_scope: &Scope, decl: &swc_ecma_ast::FnDecl) { let scope = parent_scope.nest(); scope.set( diff --git a/src/vstc/expression_compiler.rs b/src/vstc/expression_compiler.rs index 611d4fb..11b1318 100644 --- a/src/vstc/expression_compiler.rs +++ b/src/vstc/expression_compiler.rs @@ -204,6 +204,7 @@ impl<'a> ExpressionCompiler<'a> { swc_ecma_ast::Expr::Ident(ident) => match ec.scope.get(&ident.sym.to_string()) { None => std::panic!("Unresolved identifier"), Some(MappedName::Definition(_)) => std::panic!("Invalid: definition mutation"), + Some(MappedName::QueuedFunction(_)) => std::panic!("Invalid: assign to declaration"), Some(MappedName::Register(reg)) => AssignTarget::Register(reg), }, swc_ecma_ast::Expr::This(_) => AssignTarget::Register("this".to_string()), @@ -220,6 +221,7 @@ impl<'a> ExpressionCompiler<'a> { return match ec.scope.get(&ident.sym.to_string()) { None => std::panic!("Unresolved identifier"), Some(MappedName::Definition(_)) => std::panic!("Invalid: definition mutation"), + Some(MappedName::QueuedFunction(_)) => std::panic!("Invalid: assign to declaration"), Some(MappedName::Register(reg)) => AssignTarget::Register(reg), }; } @@ -299,6 +301,7 @@ impl<'a> ExpressionCompiler<'a> { match self.scope.get(&ident.id.sym.to_string()) { None => std::panic!("Unresolved identifier"), Some(MappedName::Definition(_)) => std::panic!("Invalid: definition mutation"), + Some(MappedName::QueuedFunction(_)) => std::panic!("Invalid: assign to declaration"), Some(MappedName::Register(reg)) => reg, } ), @@ -742,7 +745,7 @@ impl<'a> ExpressionCompiler<'a> { self.fnc.queue.add(QueuedFunction { definition_name: definition_name.clone(), fn_name: fn_name.clone(), - extra_params: cf.ordered_names.clone(), + capture_params: cf.ordered_names.clone(), function: fn_.function.clone(), }).expect("Failed to queue function"); @@ -753,7 +756,23 @@ impl<'a> ExpressionCompiler<'a> { ); } + return self.capturing_fn_ref( + fn_name, + &definition_name, + &cf.ordered_names, + target_register, + ); + } + + pub fn capturing_fn_ref( + &mut self, + fn_name: Option, + definition_name: &String, + captures: &Vec, + target_register: Option, + ) -> CompiledExpression { let mut nested_registers = Vec::::new(); + let mut sub_nested_registers = Vec::::new(); let reg = match target_register { None => { @@ -771,23 +790,39 @@ impl<'a> ExpressionCompiler<'a> { let mut bind_instr = format!(" bind @{} [", definition_name); - for i in 0..cf.ordered_names.len() { - let captured_name = &cf.ordered_names[i]; + for i in 0..captures.len() { + let captured_name = &captures[i]; if i > 0 { bind_instr += ", "; } bind_instr += &match self.scope.get(captured_name) { - None => std::panic!(""), - Some(MappedName::Definition(_)) => std::panic!(""), + None => std::panic!("Captured names should always be in scope"), + Some(MappedName::Definition(_)) => std::panic!("Definitions should never be recorded as captures"), Some(MappedName::Register(cap_reg)) => format!("%{}", cap_reg), + Some(MappedName::QueuedFunction(qfn)) => { + let mut compiled_ref = self.capturing_fn_ref( + qfn.fn_name.clone(), + &qfn.definition_name, + &qfn.capture_params, + None, + ); + + sub_nested_registers.append(&mut compiled_ref.nested_registers); + + compiled_ref.value_assembly + }, }; } bind_instr += &format!("] %{}", reg); self.fnc.definition.push(bind_instr); + for reg in sub_nested_registers { + self.fnc.reg_allocator.release(®); + } + return CompiledExpression { value_assembly: format!("%{}", reg), nested_registers: nested_registers, @@ -840,12 +875,16 @@ impl<'a> ExpressionCompiler<'a> { let mapped = self.scope.get(&ident_string).expect("Identifier not found in scope"); - let value_assembly = match mapped { - MappedName::Register(reg) => "%".to_string() + ®, - MappedName::Definition(def) => "@".to_string() + &def, + return match mapped { + MappedName::Register(reg) => self.inline("%".to_string() + ®, target_register), + MappedName::Definition(def) => self.inline("@".to_string() + &def, target_register), + MappedName::QueuedFunction(qfn) => self.capturing_fn_ref( + qfn.fn_name.clone(), + &qfn.definition_name, + &qfn.capture_params, + target_register, + ), }; - - return self.inline(value_assembly, target_register); } } @@ -951,6 +990,7 @@ impl TargetAccessor { Ident(ident) => match ec.scope.get(&ident.sym.to_string()) { None => std::panic!("Unresolved identifier"), Some(MappedName::Definition(_)) => std::panic!("Invalid: definition mutation"), + Some(MappedName::QueuedFunction(_)) => std::panic!("Invalid: assign to declaration"), Some(MappedName::Register(reg)) => TargetAccessor::Register(reg), }, This(_) => TargetAccessor::Register("this".to_string()), diff --git a/src/vstc/function_compiler.rs b/src/vstc/function_compiler.rs index a13ae6f..c5f330e 100644 --- a/src/vstc/function_compiler.rs +++ b/src/vstc/function_compiler.rs @@ -1,16 +1,18 @@ use std::rc::Rc; use std::cell::RefCell; +use std::collections::{HashMap, HashSet}; use queues::*; use super::name_allocator::NameAllocator; use super::expression_compiler::ExpressionCompiler; -use super::scope::{Scope, MappedName, ScopeTrait}; +use super::scope::{Scope, MappedName, ScopeTrait, init_scope}; +use super::capture_finder::CaptureFinder; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct QueuedFunction { pub definition_name: String, pub fn_name: Option, - pub extra_params: Vec, + pub capture_params: Vec, pub function: swc_ecma_ast::Function, } @@ -56,7 +58,7 @@ impl FunctionCompiler { self_.queue.add(QueuedFunction { definition_name: definition_name.clone(), fn_name: fn_name, - extra_params: Vec::new(), + capture_params: Vec::new(), function: fn_.clone(), }).expect("Failed to queue function"); @@ -65,7 +67,7 @@ impl FunctionCompiler { Ok(qfn) => self_.compile_fn( qfn.definition_name, qfn.fn_name, - qfn.extra_params, + qfn.capture_params, &qfn.function, parent_scope, ), @@ -80,7 +82,7 @@ impl FunctionCompiler { &mut self, definition_name: String, fn_name: Option, - mut extra_params: Vec, + mut capture_params: Vec, fn_: &swc_ecma_ast::Function, parent_scope: &Scope, ) { @@ -103,7 +105,7 @@ impl FunctionCompiler { heading += " = function("; let mut params = Vec::::new(); - params.append(&mut extra_params); + params.append(&mut capture_params); for p in &fn_.params { match &p.pat { @@ -254,6 +256,8 @@ impl FunctionCompiler { block: &swc_ecma_ast::BlockStmt, scope: &Scope, ) { + let mut function_decls = Vec::::new(); + for statement in &block.stmts { use swc_ecma_ast::Stmt::*; @@ -280,27 +284,7 @@ impl FunctionCompiler { match decl { Class(_) => std::panic!("Not implemented: Class declaration"), - Fn(fn_) => { - let fn_name = fn_.ident.sym.to_string(); - - let definition_name = self - .definition_allocator - .borrow_mut() - .allocate(&fn_name) - ; - - scope.set( - fn_name.clone(), - MappedName::Definition(definition_name.clone()), - ); - - self.queue.add(QueuedFunction { - definition_name: definition_name, - fn_name: Some(fn_name), - extra_params: Vec::new(), - function: fn_.function.clone(), - }).expect("Failed to queue function"); - }, + Fn(fn_) => function_decls.push(fn_.clone()), Var(var_decl) => self.populate_block_scope_var_decl(var_decl, scope), TsInterface(_) => {}, TsTypeAlias(_) => {}, @@ -311,6 +295,89 @@ impl FunctionCompiler { Expr(_) => {}, }; } + + // Create a synth scope where the function decls that can co-mingle are + // present but don't signal any nested captures. This allows us to first + // construct all the direct captures and use that to find the complete + // captures. + let synth_scope = scope.nest(); + + for fn_ in &function_decls { + synth_scope.set( + fn_.ident.sym.to_string(), + MappedName::Register("".to_string()), + ); + } + + let mut direct_captures_map = HashMap::>::new(); + + for fn_ in &function_decls { + let mut cf = CaptureFinder::new(synth_scope.clone()); + cf.fn_decl(&init_scope(), fn_); + + direct_captures_map.insert( + fn_.ident.sym.to_string(), + cf.ordered_names, + ); + } + + for fn_ in &function_decls { + let mut full_captures = Vec::::new(); + let mut full_captures_set = HashSet::::new(); + + let mut cap_queue = Queue::::new(); + + for dc in direct_captures_map.get(&fn_.ident.sym.to_string()) + .expect("direct captures not found") + { + cap_queue.add(dc.clone()) + .expect("Failed to add to queue"); + } + + loop { + let cap = match cap_queue.remove() { + Ok(c) => c, + Err(_) => { break; }, + }; + + let is_new = full_captures_set.insert(cap.clone()); + + if !is_new { + continue; + } + + full_captures.push(cap.clone()); + + for nested_caps in direct_captures_map.get(&cap) { + for nested_cap in nested_caps { + cap_queue.add(nested_cap.clone()) + .expect("Failed to add to queue"); + } + } + } + + let fn_name = fn_.ident.sym.to_string(); + + let definition_name = self + .definition_allocator + .borrow_mut() + .allocate(&fn_name) + ; + + let qf = QueuedFunction { + definition_name: definition_name, + fn_name: Some(fn_name.clone()), + capture_params: full_captures, + function: fn_.function.clone(), + }; + + scope.set( + fn_name.clone(), + MappedName::QueuedFunction(qf.clone()), + ); + + self.queue.add(qf).expect("Failed to queue function"); + } } fn populate_block_scope_var_decl( @@ -360,6 +427,7 @@ impl FunctionCompiler { self.reg_allocator.release(reg); }, MappedName::Definition(_) => {}, + MappedName::QueuedFunction(_) => {}, } } }, diff --git a/src/vstc/scope.rs b/src/vstc/scope.rs index 78bfd70..7fcfa57 100644 --- a/src/vstc/scope.rs +++ b/src/vstc/scope.rs @@ -2,10 +2,13 @@ use std::collections::HashMap; use std::rc::Rc; use std::cell::RefCell; +use super::function_compiler::QueuedFunction; + #[derive(Clone, Debug)] pub enum MappedName { Register(String), Definition(String), + QueuedFunction(QueuedFunction), } pub struct ScopeData {