feat(tazjin/rlox): Implement expression statements

These aren't particularly useful without side effects, but one step at
a time.

This diverges slightly from the book, in that OpPop retains the last
value it "forgot" from the stack in a special field on the
interpreter.

This makes it possible to return values from expression statements,
which helps in cases where Lox is embedded as a scripting
language (please don't do this ever) or in tests.

Change-Id: Ided0bc04c6e80ddb23ba4693d61ac9e08b002d58
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2584
Reviewed-by: tazjin <mail@tazj.in>
Tested-by: BuildkiteCI
This commit is contained in:
Vincent Ambo 2021-03-02 22:26:02 +02:00 committed by tazjin
parent 2cd77ea26d
commit ed3fce2b19
4 changed files with 76 additions and 51 deletions

View file

@ -13,6 +13,12 @@ pub struct VM {
stack: Vec<Value>,
strings: Interner,
// Operations that consume values from the stack without pushing
// anything leave their last value in this slot, which makes it
// possible to return values from interpreters that ran code which
// ended with a statement.
last_drop: Option<Value>,
}
impl VM {
@ -72,12 +78,15 @@ impl VM {
match op {
OpCode::OpReturn => {
if self.stack.is_empty() {
if !self.stack.is_empty() {
let val = self.pop();
return Ok(self.return_value(val));
} else if self.last_drop.is_some() {
let val = self.last_drop.take().unwrap();
return Ok(self.return_value(val));
} else {
return Ok(Value::Nil);
}
let val = self.pop();
return Ok(self.return_value(val));
}
OpCode::OpConstant(idx) => {
@ -144,6 +153,10 @@ impl VM {
let val = self.pop();
println!("{}", self.print_value(val));
}
OpCode::OpPop => {
self.last_drop = Some(self.pop());
}
}
#[cfg(feature = "disassemble")]
@ -186,6 +199,7 @@ pub fn interpret(strings: Interner, chunk: chunk::Chunk) -> LoxResult<Value> {
strings,
ip: 0,
stack: vec![],
last_drop: None,
};
vm.run()