Generators make regular calls and regular returns

This commit is contained in:
Andrew Morris
2023-06-01 11:08:33 +10:00
parent 6710d98476
commit 95e702b85e
2 changed files with 107 additions and 29 deletions

View File

@@ -1,12 +1,12 @@
use std::{fmt, rc::Rc};
use std::{fmt, mem::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_function::{native_fn, NativeFunction},
stack_frame::StackFrame,
native_frame_function::NativeFrameFunction,
stack_frame::{CallResult, FrameStepOk, FrameStepResult, StackFrame, StackFrameTrait},
vs_array::VsArray,
vs_class::VsClass,
vs_symbol::VsSymbol,
@@ -14,15 +14,12 @@ use crate::{
LoadFunctionResult, ValTrait,
};
#[derive(Clone)]
#[derive(Clone, Default)]
pub struct Generator {
#[allow(dead_code)] // TODO
frame: StackFrame,
#[allow(dead_code)] // TODO
stack: Vec<StackFrame>,
done: bool,
}
impl Generator {
@@ -30,7 +27,6 @@ impl Generator {
return Generator {
frame,
stack: vec![],
done: false,
};
}
}
@@ -112,34 +108,78 @@ impl fmt::Display for Generator {
}
}
// We can't use a native function for this. It needs to make a new frame which implements step by
// stepping the contained stack.
//
// Note that next() requires copying the stack in the general case since exceptions must revert the
// iterator. One practical solution for this might be to detect when the generator is in a state
// where it doesn't use the result of the yield expression and reason that a subsequent next() call
// must have the same output, therefore it can store the generated exception in that case instead of
// needing to copy.
//
static NEXT: NativeFunction = native_fn(|mut this, _| {
let dynamic = match this.get_mut()? {
Val::Dynamic(dynamic) => dynamic,
_ => return Err("TODO: indirection".to_error()),
};
static NEXT: NativeFrameFunction = NativeFrameFunction {
make_frame: || Box::new(GeneratorFrame::default()),
};
let generator = dynamic_make_mut(dynamic)
.as_any_mut()
.downcast_mut::<Generator>()
.ok_or_else(|| "Generator.next called on different object".to_type_error())?;
#[derive(Clone, Default)]
struct GeneratorFrame {
generator: Generator,
}
let done = generator.done;
generator.done = true;
impl GeneratorFrame {}
Ok(
IterationResult {
value: "TODO".to_val(),
done,
impl StackFrameTrait for GeneratorFrame {
fn write_this(&mut self, const_: bool, this: Val) -> Result<(), Val> {
let mut dynamic = match this {
Val::Dynamic(dynamic) => dynamic,
_ => return Err("TODO: indirection".to_error()),
};
if const_ {
return Err("Cannot call Generator.next on a const generator".to_type_error());
}
.to_dynamic_val(),
)
});
let mut generator = dynamic_make_mut(&mut dynamic)
.as_any_mut()
.downcast_mut::<Generator>()
.ok_or_else(|| "Generator.next called on different object".to_type_error())?;
self.generator = take(&mut generator);
Ok(())
}
fn write_param(&mut self, _param: Val) {
panic!("TODO: results of yield expressions")
}
fn step(&mut self) -> FrameStepResult {
let fsr = self.generator.frame.step();
match fsr {
Err(_) => Err("TODO: exceptions inside generators".to_error()),
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,
}
.to_dynamic_val(),
this: take(&mut self.generator).to_dynamic_val(),
})),
}
}
fn apply_call_result(&mut self, call_result: CallResult) {
self.generator.frame.apply_call_result(call_result)
}
fn get_call_result(&mut self) -> CallResult {
panic!("Not appropriate for GeneratorFrame")
}
fn catch_exception(&mut self, exception: Val) -> bool {
self.generator.frame.catch_exception(exception)
}
fn clone_to_stack_frame(&self) -> StackFrame {
Box::new(self.clone())
}
}

View File

@@ -31,3 +31,41 @@ impl Clone for StackFrame {
self.clone_to_stack_frame()
}
}
impl Default for StackFrame {
fn default() -> Self {
Box::new(VoidStackFrame {})
}
}
#[derive(Clone)]
struct VoidStackFrame {}
impl StackFrameTrait for VoidStackFrame {
fn write_this(&mut self, _const: bool, _this: Val) -> Result<(), Val> {
Ok(())
}
fn write_param(&mut self, _param: Val) {}
fn step(&mut self) -> FrameStepResult {
Ok(FrameStepOk::Continue)
}
fn apply_call_result(&mut self, _call_result: CallResult) {}
fn get_call_result(&mut self) -> CallResult {
CallResult {
return_: Val::Void,
this: Val::Void,
}
}
fn catch_exception(&mut self, _exception: Val) -> bool {
false
}
fn clone_to_stack_frame(&self) -> StackFrame {
Box::new(self.clone())
}
}