diff --git a/inputs/passing/iteration/yieldStar.ts b/inputs/passing/iteration/yieldStar.ts new file mode 100644 index 0000000..164e12d --- /dev/null +++ b/inputs/passing/iteration/yieldStar.ts @@ -0,0 +1,16 @@ +// test_output! [1,2,3,1,2,"H","i"] + +export default function main() { + return [...gen()]; +} + +function* gen() { + yield* [1, 2, 3]; + yield* gen12(); + yield* "Hi"; +} + +function* gen12() { + yield 1; + yield 2; +} diff --git a/valuescript_compiler/src/expression_compiler.rs b/valuescript_compiler/src/expression_compiler.rs index f0dbe7a..22b63d9 100644 --- a/valuescript_compiler/src/expression_compiler.rs +++ b/valuescript_compiler/src/expression_compiler.rs @@ -5,7 +5,7 @@ use queues::*; use swc_common::Spanned; -use crate::asm::{Array, Instruction, Label, Object, Register, Value}; +use crate::asm::{Array, Builtin, Instruction, Label, Object, Register, Value}; use crate::diagnostic::{Diagnostic, DiagnosticLevel}; use crate::function_compiler::{FunctionCompiler, Functionish, QueuedFunction}; use crate::scope::{NameId, OwnerId}; @@ -35,7 +35,7 @@ impl ReleaseChecker { impl CompiledExpression { pub fn empty() -> CompiledExpression { CompiledExpression { - value: Value::Void, // TODO: Allocate register instead + value: Value::Void, // TODO: Allocate register instead (why?) nested_registers: vec![], release_checker: ReleaseChecker::new(false), } @@ -1209,6 +1209,13 @@ impl<'a> ExpressionCompiler<'a> { yield_expr: &swc_ecma_ast::YieldExpr, target_register: Option, ) -> CompiledExpression { + // TODO: Implement and use the yield* instruction instead + // It will use the stack within the generator which avoids reloading the generator's stack each + // time. + if yield_expr.delegate { + return self.yield_star_expr(yield_expr, target_register); + } + let mut nested_registers = Vec::::new(); let arg_compiled = match &yield_expr.arg { @@ -1225,16 +1232,103 @@ impl<'a> ExpressionCompiler<'a> { } }; - let instr = match yield_expr.delegate { - false => Instruction::Yield(self.fnc.use_(arg_compiled), dst.clone()), - true => Instruction::YieldStar(self.fnc.use_(arg_compiled), dst.clone()), - }; + let instr = Instruction::Yield(self.fnc.use_(arg_compiled), dst.clone()); self.fnc.push(instr); return CompiledExpression::new(Value::Register(dst), nested_registers); } + pub fn yield_star_expr( + &mut self, + yield_expr: &swc_ecma_ast::YieldExpr, + target_register: Option, + ) -> CompiledExpression { + let mut nested_registers = Vec::::new(); + + let arg_compiled = match &yield_expr.arg { + Some(arg) => self.compile(arg, None), + None => CompiledExpression::empty(), + }; + + let dst = match target_register { + Some(t) => t, + None => { + let tmp = self.fnc.allocate_tmp(); + nested_registers.push(tmp.clone()); + tmp + } + }; + + let iter_reg = self.fnc.allocate_numbered_reg(&"_iter".to_string()); + let iter_res_reg = self.fnc.allocate_numbered_reg(&"_iter_res".to_string()); + let done_reg = self.fnc.allocate_numbered_reg(&"_done".to_string()); + + let test_label = Label { + name: self + .fnc + .label_allocator + .allocate_numbered(&"yield_star_test".to_string()), + }; + + let next_label = Label { + name: self + .fnc + .label_allocator + .allocate_numbered(&"yield_star_next".to_string()), + }; + + let end_label = Label { + name: self + .fnc + .label_allocator + .allocate_numbered(&"yield_star_end".to_string()), + }; + + let instr = Instruction::ConstSubCall( + self.fnc.use_(arg_compiled), + Value::Builtin(Builtin { + name: "SymbolIterator".to_string(), + }), + Value::Array(Box::new(Array::default())), + iter_reg.clone(), + ); + + self.fnc.push(instr); + + self.fnc.push(Instruction::Jmp(next_label.ref_())); + + self.fnc.label(test_label.clone()); + + self.fnc.push(Instruction::JmpIf( + Value::Register(done_reg.clone()), + end_label.ref_(), + )); + + self.fnc.push(Instruction::Yield( + Value::Register(dst.clone()), + Register::Ignore, + )); + + self.fnc.label(next_label); + + self + .fnc + .push(Instruction::Next(iter_reg, iter_res_reg.clone())); + + self.fnc.push(Instruction::UnpackIterRes( + iter_res_reg, + dst.clone(), + done_reg, + )); + + self.fnc.push(Instruction::Jmp(test_label.ref_())); + + self.fnc.label(end_label); + + return CompiledExpression::new(Value::Register(dst), nested_registers); + } + pub fn literal( &mut self, lit: &swc_ecma_ast::Lit,