feat(tazjin/rlox): Intern all string constants

This is again a step closer to the book, but there are some notable
differences:

* Only constants encountered by the compiler are interned, all other
  string operations (well, concatenation) happen with heap objects.

* OpReturn will always ensure that a returned string value is newly
  heap allocated and does not reference the interner.

Change-Id: If4f04309446e01b8ff2db51094e9710d465dbc50
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2582
Reviewed-by: tazjin <mail@tazj.in>
Tested-by: BuildkiteCI
This commit is contained in:
Vincent Ambo 2021-03-02 13:11:21 +02:00 committed by tazjin
parent bcea8e0d16
commit 432e7a7ddd
4 changed files with 65 additions and 19 deletions

View file

@ -1,7 +1,8 @@
use super::chunk;
use super::errors::*;
use super::interner::Interner;
use super::opcode::OpCode;
use super::value::Value;
use super::value::{LoxString, Value};
pub struct VM {
chunk: chunk::Chunk,
@ -11,6 +12,7 @@ pub struct VM {
ip: usize,
stack: Vec<Value>,
strings: Interner,
}
impl VM {
@ -69,7 +71,10 @@ impl VM {
self.ip += 1;
match op {
OpCode::OpReturn => return Ok(self.pop()),
OpCode::OpReturn => {
let val = self.pop();
return Ok(self.return_value(val));
}
OpCode::OpConstant(idx) => {
let c = self.chunk.constant(*idx).clone();
@ -114,9 +119,9 @@ impl VM {
match (a, b) {
(Value::String(s_a), Value::String(s_b)) => {
let mut new_s = s_a.clone();
new_s.push_str(&s_b);
self.push(Value::String(new_s));
let mut new_s = self.resolve_str(&s_a).to_string();
new_s.push_str(self.resolve_str(&s_b));
self.push(Value::String(new_s.into()));
}
(Value::Number(n_a), Value::Number(n_b)) =>
@ -136,11 +141,30 @@ impl VM {
println!("=> {:?}", self.stack);
}
}
// For some types of values (e.g. interned strings), returns
// should no longer include any references into the interpreter.
fn return_value(&self, val: Value) -> Value {
match val {
Value::String(string @ LoxString::Interned(_)) => {
Value::String(self.resolve_str(&string).to_string().into())
}
_ => val,
}
}
fn resolve_str<'a>(&'a self, string: &'a LoxString) -> &'a str {
match string {
LoxString::Heap(s) => s.as_str(),
LoxString::Interned(id) => self.strings.lookup(*id),
}
}
}
pub fn interpret(chunk: chunk::Chunk) -> LoxResult<Value> {
pub fn interpret(strings: Interner, chunk: chunk::Chunk) -> LoxResult<Value> {
let mut vm = VM {
chunk,
strings,
ip: 0,
stack: vec![],
};