Skip to content

Commit

Permalink
Implement yield* instruction
Browse files Browse the repository at this point in the history
  • Loading branch information
voltrevo committed Jun 27, 2023
1 parent 074774d commit e49b1ff
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 110 deletions.
2 changes: 1 addition & 1 deletion valuescript_compiler/src/assembly_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ impl<'a> AssemblyParser<'a> {
),
Cat => Instruction::Cat(self.assemble_value(), self.assemble_register()),
Yield => Instruction::Yield(self.assemble_value(), self.assemble_register()),
YieldStar => Instruction::Yield(self.assemble_value(), self.assemble_register()),
YieldStar => Instruction::YieldStar(self.assemble_value(), self.assemble_register()),
};

self.parse_line();
Expand Down
102 changes: 4 additions & 98 deletions valuescript_compiler/src/expression_compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::mem::take;
use queues::*;
use swc_common::Spanned;

use crate::asm::{Array, Builtin, Instruction, Label, Number, Object, Register, Value};
use crate::asm::{Array, Instruction, Label, Number, Object, Register, Value};
use crate::diagnostic::{Diagnostic, DiagnosticLevel};
use crate::function_compiler::{FunctionCompiler, Functionish, QueuedFunction};
use crate::scope::{NameId, OwnerId};
Expand Down Expand Up @@ -1191,13 +1191,6 @@ impl<'a> ExpressionCompiler<'a> {
yield_expr: &swc_ecma_ast::YieldExpr,
target_register: Option<Register>,
) -> 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::<Register>::new();

let arg_compiled = match &yield_expr.arg {
Expand All @@ -1214,100 +1207,13 @@ impl<'a> ExpressionCompiler<'a> {
}
};

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<Register>,
) -> CompiledExpression {
let mut nested_registers = Vec::<Register>::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 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 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);
}

Expand Down
169 changes: 158 additions & 11 deletions valuescript_vm/src/generator.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
use std::{fmt, mem::take, rc::Rc};
use std::{
fmt,
mem::{swap, take},
rc::Rc,
};

use num_bigint::BigInt;

use crate::{
builtins::{error_builtin::ToError, type_error_builtin::ToTypeError},
iteration::{iteration_result::IterationResult, return_this::RETURN_THIS},
native_frame_function::NativeFrameFunction,
native_function::ThisWrapper,
stack_frame::{CallResult, FrameStepOk, FrameStepResult, StackFrame, StackFrameTrait},
vs_array::VsArray,
vs_class::VsClass,
Expand All @@ -17,8 +22,6 @@ use crate::{
#[derive(Clone, Default)]
pub struct Generator {
frame: StackFrame,

#[allow(dead_code)] // TODO
stack: Vec<StackFrame>,
}

Expand Down Expand Up @@ -156,14 +159,22 @@ impl StackFrameTrait for GeneratorFrame {
match fsr {
Err(_) => fsr, // TODO: Stack unwind internal stack first
Ok(FrameStepOk::Continue) | Ok(FrameStepOk::Push(_)) => fsr,
Ok(FrameStepOk::Pop(call_result)) => Ok(FrameStepOk::Pop(CallResult {
return_: IterationResult {
value: call_result.return_, // TODO: Assert call_result.this is undefined?
done: true,
Ok(FrameStepOk::Pop(call_result)) => match self.generator.stack.pop() {
Some(mut frame) => {
frame.apply_call_result(call_result);
swap(&mut frame, &mut self.generator.frame);

Ok(FrameStepOk::Continue)
}
.to_dynamic_val(),
this: take(&mut self.generator).to_dynamic_val(),
})),
None => Ok(FrameStepOk::Pop(CallResult {
return_: IterationResult {
value: call_result.return_, // TODO: Assert call_result.this is undefined?
done: true,
}
.to_dynamic_val(),
this: take(&mut self.generator).to_dynamic_val(),
})),
},
Ok(FrameStepOk::Yield(val)) => Ok(FrameStepOk::Pop(CallResult {
return_: IterationResult {
value: val,
Expand All @@ -172,7 +183,30 @@ impl StackFrameTrait for GeneratorFrame {
.to_dynamic_val(),
this: take(&mut self.generator).to_dynamic_val(),
})),
Ok(FrameStepOk::YieldStar(_)) => panic!("TODO: yield* (as instruction)"),
Ok(FrameStepOk::YieldStar(iterable)) => {
let make_iter = iterable.sub(&VsSymbol::ITERATOR.to_val())?;

let mut frame = 'f: {
if let Val::Function(make_iter) = &make_iter {
if make_iter.is_generator {
let mut frame: StackFrame = Box::new(make_iter.make_bytecode_frame());
frame.write_this(true, iterable)?;

break 'f frame;
}
}

Box::new(YieldStarFrame {
iter: YieldStarIter::MakeIterator(iterable, make_iter),
iter_result: None,
})
};

swap(&mut frame, &mut self.generator.frame);
self.generator.stack.push(frame);

return Ok(FrameStepOk::Continue);
}
}
}

Expand All @@ -192,3 +226,116 @@ impl StackFrameTrait for GeneratorFrame {
Box::new(self.clone())
}
}

#[derive(Clone, Default)]
struct YieldStarFrame {
iter: YieldStarIter,
iter_result: Option<Val>,
}

#[derive(Clone)]
enum YieldStarIter {
MakeIterator(Val, Val),
Iterator(Val),
}

impl Default for YieldStarIter {
fn default() -> Self {
YieldStarIter::MakeIterator(Val::default(), Val::default())
}
}

impl StackFrameTrait for YieldStarFrame {
fn write_this(&mut self, _const: bool, _this: Val) -> Result<(), Val> {
panic!("Not appropriate for YieldStarFrame")
}

fn write_param(&mut self, _param: Val) {
panic!("Not appropriate for YieldStarFrame")
}

fn step(&mut self) -> FrameStepResult {
let iter_result = take(&mut self.iter_result);

if let Some(iter_result) = iter_result {
let value = iter_result.sub(&"value".to_val())?; // TODO: mutable subscript to avoid cloning

return match iter_result.sub(&"done".to_val())?.is_truthy() {
false => Ok(FrameStepOk::Yield(value)),
true => Ok(FrameStepOk::Pop(CallResult {
return_: value,
this: Val::Undefined,
})),
};
}

match &mut self.iter {
YieldStarIter::MakeIterator(this, make_iter) => match make_iter.load_function() {
LoadFunctionResult::NotAFunction => Err("yield* argument is not iterable".to_type_error()),
LoadFunctionResult::NativeFunction(native_fn) => {
let iterator = native_fn(ThisWrapper::new(true, this), vec![])?;

self.iter = YieldStarIter::Iterator(iterator);

Ok(FrameStepOk::Continue)
}
LoadFunctionResult::StackFrame(frame) => Ok(FrameStepOk::Push(frame)),
},
YieldStarIter::Iterator(iterator) => {
let next_fn = iterator.sub(&"next".to_val())?;

match next_fn.load_function() {
LoadFunctionResult::NotAFunction => {
Err("iterator.next is not a function".to_type_error())
}
LoadFunctionResult::NativeFunction(native_fn) => {
let iter_result = native_fn(ThisWrapper::new(false, iterator), vec![])?;
let value = iter_result.sub(&"value".to_val())?;

match iter_result.sub(&"done".to_val())?.is_truthy() {
false => Ok(FrameStepOk::Yield(value)),
true => Ok(FrameStepOk::Pop(CallResult {
return_: value,
this: Val::Undefined,
})),
}
}
LoadFunctionResult::StackFrame(mut frame) => {
frame.write_this(false, take(iterator))?;

Ok(FrameStepOk::Push(frame))
}
}
}
}
}

fn apply_call_result(&mut self, call_result: CallResult) {
let iter = &mut self.iter;
let mut iter_result = None;

match iter {
YieldStarIter::MakeIterator(..) => {
*iter = YieldStarIter::Iterator(call_result.return_);
}
YieldStarIter::Iterator(iterator) => {
iter_result = Some(call_result.return_);
*iterator = call_result.this;
}
}

self.iter_result = iter_result;
}

fn get_call_result(&mut self) -> CallResult {
panic!("Not appropriate for YieldStarFrame")
}

fn catch_exception(&mut self, _exception: Val) -> bool {
false
}

fn clone_to_stack_frame(&self) -> StackFrame {
Box::new(self.clone())
}
}

0 comments on commit e49b1ff

Please sign in to comment.