🚧 wip: add string interning, but a few things broke

main
_ 2023-10-02 14:52:38 -05:00
parent 08bbb53900
commit eb32a53d18
7 changed files with 190 additions and 147 deletions

View File

@ -22,6 +22,8 @@ fn lunar_wave (args: Vec <String>) -> Result <Vec <lwvm::Value>, lwvm::StepError
let mut arg_iter = args.iter (); let mut arg_iter = args.iter ();
let _exe_name = arg_iter.next ().unwrap (); let _exe_name = arg_iter.next ().unwrap ();
let mut si = lwvm::Interner::default ();
while let Some (arg) = arg_iter.next () { while let Some (arg) = arg_iter.next () {
match arg.as_str () { match arg.as_str () {
"--break" => { "--break" => {
@ -40,7 +42,7 @@ fn lunar_wave (args: Vec <String>) -> Result <Vec <lwvm::Value>, lwvm::StepError
let mut buf = vec! []; let mut buf = vec! [];
std::io::stdin ().read_to_end (&mut buf).unwrap (); std::io::stdin ().read_to_end (&mut buf).unwrap ();
let bc = lwvm::ensure_bytecode (buf).unwrap (); let bc = lwvm::ensure_bytecode (buf).unwrap ();
chunk = Some (lwvm::parse_chunk (&bc).unwrap ()); chunk = Some (lwvm::parse_chunk (&bc, &mut si).unwrap ());
lua_args = vec! ["-".to_string ()]; lua_args = vec! ["-".to_string ()];
}, },
@ -51,7 +53,7 @@ fn lunar_wave (args: Vec <String>) -> Result <Vec <lwvm::Value>, lwvm::StepError
} }
else if chunk.is_none () { else if chunk.is_none () {
let bc = lwvm::compile_bytecode_from_file (x); let bc = lwvm::compile_bytecode_from_file (x);
chunk = Some (lwvm::parse_chunk (&bc).unwrap ()); chunk = Some (lwvm::parse_chunk (&bc, &mut si).unwrap ());
lua_args = vec! [x.to_string ()]; lua_args = vec! [x.to_string ()];
} }
@ -68,10 +70,12 @@ fn lunar_wave (args: Vec <String>) -> Result <Vec <lwvm::Value>, lwvm::StepError
chunk, chunk,
list_bytecode, list_bytecode,
lua_args, lua_args,
si,
}), }),
None => repl (ReplParams { None => repl (ReplParams {
list_bytecode, list_bytecode,
lua_args, lua_args,
si,
}), }),
} }
} }
@ -81,11 +85,13 @@ struct DebuggerParams {
chunk: lwvm::Chunk, chunk: lwvm::Chunk,
list_bytecode: bool, list_bytecode: bool,
lua_args: Vec <String>, lua_args: Vec <String>,
si: lwvm::Interner,
} }
struct ReplParams { struct ReplParams {
list_bytecode: bool, list_bytecode: bool,
lua_args: Vec <String>, lua_args: Vec <String>,
si: lwvm::Interner,
} }
/// The interpreter mode, which has optional debugging abilities /// The interpreter mode, which has optional debugging abilities
@ -97,9 +103,7 @@ fn debugger (params: DebuggerParams) -> Result <Vec <lwvm::Value>, lwvm::StepErr
dbg! (&params.chunk); dbg! (&params.chunk);
} }
let upvalues = lwvm::State::upvalues_from_args (params.lua_args.into_iter ()); let mut vm = lwvm::State::new_with_args (params.chunk, params.si, params.lua_args.into_iter ());
let mut vm = lwvm::State::new (params.chunk, upvalues);
if std::env::var("LWVM_DEBUG").is_ok() { if std::env::var("LWVM_DEBUG").is_ok() {
vm.debug_print = true; vm.debug_print = true;
} }
@ -160,9 +164,7 @@ fn debugger (params: DebuggerParams) -> Result <Vec <lwvm::Value>, lwvm::StepErr
fn repl (params: ReplParams) -> Result <Vec <lwvm::Value>, lwvm::StepError> fn repl (params: ReplParams) -> Result <Vec <lwvm::Value>, lwvm::StepError>
{ {
let upvalues = lwvm::State::upvalues_from_args (params.lua_args.into_iter ()); let mut vm = lwvm::State::new_with_args (lwvm::Chunk::default (), params.si, params.lua_args.into_iter ());
let mut vm = lwvm::State::new (lwvm::Chunk::default (), upvalues);
println! ("Lunar Wave 0.1.0-modified Copyright (C) 2023 ReactorScram (implements Lua 5.4 Copyright (C) 1994-2022 Lua.org, PUC-Rio"); println! ("Lunar Wave 0.1.0-modified Copyright (C) 2023 ReactorScram (implements Lua 5.4 Copyright (C) 1994-2022 Lua.org, PUC-Rio");
@ -188,7 +190,7 @@ fn repl (params: ReplParams) -> Result <Vec <lwvm::Value>, lwvm::StepError>
continue; continue;
}, },
}; };
let chunk = lwvm::parse_chunk (&bytecode).unwrap (); let chunk = lwvm::parse_chunk (&bytecode, &mut vm.si).unwrap ();
if params.list_bytecode { if params.list_bytecode {
dbg! (&chunk); dbg! (&chunk);

View File

@ -16,6 +16,7 @@ pub use state::Chunk as Chunk;
pub use state::State as State; pub use state::State as State;
pub use state::StepError as StepError; pub use state::StepError as StepError;
pub use state::StepOutput as StepOutput; pub use state::StepOutput as StepOutput;
pub use string_interner::Interner as Interner;
pub use value::Value as Value; pub use value::Value as Value;
#[cfg (test)] #[cfg (test)]

View File

@ -5,7 +5,8 @@ use crate::{
state::{ state::{
Block, Block,
Chunk, Chunk,
} },
string_interner::Interner,
}; };
pub fn compile_bytecode_from_file (path: &str) -> Vec <u8> { pub fn compile_bytecode_from_file (path: &str) -> Vec <u8> {
@ -242,7 +243,7 @@ fn parse_i64 <R: Read> (rdr: &mut R) -> Option <i64> {
// code, but I don't like recursion in general, and I don't know // code, but I don't like recursion in general, and I don't know
// why PUC wrote it that way. // why PUC wrote it that way.
pub fn parse_block <R: Read> (rdr: &mut R, blocks: &mut Vec <Block>) pub fn parse_block <R: Read> (rdr: &mut R, si: &mut Interner, blocks: &mut Vec <Block>)
-> Option <()> -> Option <()>
{ {
// Ignore things I haven't implemented yet // Ignore things I haven't implemented yet
@ -273,12 +274,12 @@ pub fn parse_block <R: Read> (rdr: &mut R, blocks: &mut Vec <Block>)
let val = match const_type { let val = match const_type {
3 => parse_i64 (rdr).unwrap ().into (), 3 => parse_i64 (rdr).unwrap ().into (),
4 => parse_string (rdr).unwrap ().into (), 4 => si.to_value (parse_string (rdr).unwrap ().as_str ()),
// For LUA_TNUMBER, PUC Lua uses a macro that adds 16 to signify a float // For LUA_TNUMBER, PUC Lua uses a macro that adds 16 to signify a float
19 => parse_float (rdr).unwrap ().into (), 19 => parse_float (rdr).unwrap ().into (),
// 0x10 + 4 = long string // 0x10 + 4 = long string
20 => parse_string (rdr).unwrap ().into (), 20 => si.to_value (parse_string (rdr).unwrap ().as_str ()),
x => panic! ("Constant {} has type {}", i, x), x => panic! ("Constant {} has type {}", i, x),
}; };
@ -312,7 +313,7 @@ pub fn parse_block <R: Read> (rdr: &mut R, blocks: &mut Vec <Block>)
// Subfunctions. PUC calls them protos. // Subfunctions. PUC calls them protos.
let protos_count = parse_int (rdr).unwrap (); let protos_count = parse_int (rdr).unwrap ();
for _ in 0..protos_count { for _ in 0..protos_count {
parse_block (rdr, blocks).unwrap (); parse_block (rdr, si, blocks).unwrap ();
} }
// Skip over debug stuff // Skip over debug stuff
@ -348,12 +349,12 @@ pub fn parse_block <R: Read> (rdr: &mut R, blocks: &mut Vec <Block>)
Some (()) Some (())
} }
pub fn parse_chunk (buf: &[u8]) -> Option <Chunk> { pub fn parse_chunk (buf: &[u8], si: &mut Interner) -> Option <Chunk> {
let mut rdr = std::io::Cursor::new (buf); let mut rdr = std::io::Cursor::new (buf);
parse_chunk_from_reader (&mut rdr) parse_chunk_from_reader (&mut rdr, si)
} }
pub fn parse_chunk_from_reader <R: Read> (rdr: &mut R) -> Option <Chunk> { pub fn parse_chunk_from_reader <R: Read> (rdr: &mut R, si: &mut Interner) -> Option <Chunk> {
// Discard 32 bytes from the start of the file. // Discard 32 bytes from the start of the file.
// This is magic number, version number, etc. // This is magic number, version number, etc.
@ -364,7 +365,7 @@ pub fn parse_chunk_from_reader <R: Read> (rdr: &mut R) -> Option <Chunk> {
let mut blocks = vec![]; let mut blocks = vec![];
parse_block (rdr, &mut blocks).unwrap (); parse_block (rdr, si, &mut blocks).unwrap ();
Some (Chunk { Some (Chunk {
blocks, blocks,
@ -373,6 +374,8 @@ pub fn parse_chunk_from_reader <R: Read> (rdr: &mut R) -> Option <Chunk> {
#[cfg (test)] #[cfg (test)]
mod tests { mod tests {
use super::*;
#[test] #[test]
fn load_size () { fn load_size () {
let f = |input: &[u8]| { let f = |input: &[u8]| {
@ -441,6 +444,8 @@ mod tests {
fn parse_nested_functions () { fn parse_nested_functions () {
use std::io::Read; use std::io::Read;
let mut si = Interner::default ();
let bytecode = include_bytes! ("../test_vectors/functions.luac"); let bytecode = include_bytes! ("../test_vectors/functions.luac");
{ {
@ -451,7 +456,7 @@ mod tests {
let mut blocks = vec! []; let mut blocks = vec! [];
super::parse_block (&mut rdr, &mut blocks).unwrap (); super::parse_block (&mut rdr, &mut si, &mut blocks).unwrap ();
assert_eq! (blocks [0].instructions.len (), 15); assert_eq! (blocks [0].instructions.len (), 15);
assert_eq! (blocks [1].instructions.len (), 6); assert_eq! (blocks [1].instructions.len (), 6);
@ -461,7 +466,7 @@ mod tests {
} }
if false { if false {
let file = crate::loader::parse_chunk (bytecode).unwrap (); let file = crate::loader::parse_chunk (bytecode, &mut si).unwrap ();
assert_eq! (file.blocks.len (), 5); assert_eq! (file.blocks.len (), 5);
} }

View File

@ -1,5 +1,6 @@
use crate::{ use crate::{
instruction::Instruction, instruction::Instruction,
string_interner::Interner,
value::{ value::{
BogusClosure, BogusClosure,
Value, Value,
@ -55,6 +56,7 @@ pub struct State {
step_count: u32, step_count: u32,
chunk: Chunk, chunk: Chunk,
upvalues: Vec <Value>, upvalues: Vec <Value>,
pub si: Interner,
} }
fn lw_io_write (l: &mut State, num_args: usize) -> usize { fn lw_io_write (l: &mut State, num_args: usize) -> usize {
@ -62,7 +64,7 @@ fn lw_io_write (l: &mut State, num_args: usize) -> usize {
match l.reg (i) { match l.reg (i) {
Value::Float (x) => print! ("{}", x), Value::Float (x) => print! ("{}", x),
Value::Integer (x) => print! ("{}", x), Value::Integer (x) => print! ("{}", x),
Value::String (x) => print! ("{}", x), Value::String (x) => print! ("{}", l.si.get (*x)),
_ => panic! ("Can't io.write this value"), _ => panic! ("Can't io.write this value"),
} }
} }
@ -97,12 +99,12 @@ fn lw_string_format (l: &mut State, num_args: usize) -> usize {
assert! (num_args >= 1, "string.format needs at least 1 argument"); assert! (num_args >= 1, "string.format needs at least 1 argument");
assert_eq! (l.get_top (), 2, "string.format not fully implemented"); assert_eq! (l.get_top (), 2, "string.format not fully implemented");
let f_string = l.reg (0).as_str ().unwrap (); let f_string = l.reg (0).as_str ().unwrap ();
assert_eq! (f_string, "%0.9f"); assert_eq! (Value::String (f_string), l.to_string ("%0.9f"));
let num = l.reg (1).as_float ().unwrap (); let num = l.reg (1).as_float ().unwrap ();
let output = format! ("{:0.9}", num); let output = format! ("{:0.9}", num);
*l.reg_mut (0) = Value::from (output); *l.reg_mut (0) = l.to_string (&output);
1 1
} }
@ -117,14 +119,14 @@ fn lw_table_concat (l: &mut State, num_args: usize) -> usize {
for i in 0..t.length () { for i in 0..t.length () {
if i > 0 { if i > 0 {
s.push_str (joiner); s.push_str (l.si.get (joiner));
} }
let x = t.get_int (i + 1); let x = t.get_int (i + 1);
s.push_str (&format! ("{}", x)); s.push_str (&format! ("{}", x));
} }
s s
}; };
*l.reg_mut (0) = Value::from (s); *l.reg_mut (0) = l.to_string (&s);
1 1
} }
@ -137,15 +139,15 @@ fn lw_table_pack (l: &mut State, num_args: usize) -> usize {
1 1
} }
fn tonumber (value: &Value) -> Value { fn tonumber (l: &State, value: &Value) -> Value {
match value { match value {
Value::Float (x) => Value::Float (*x), Value::Float (x) => Value::Float (*x),
Value::Integer (x) => Value::Integer (*x), Value::Integer (x) => Value::Integer (*x),
Value::String (x) => { Value::String (x) => {
if let Ok (x) = str::parse::<i64> (x) { if let Ok (x) = str::parse::<i64> (l.si.get (*x)) {
Value::from (x) Value::from (x)
} }
else if let Ok (x) = str::parse::<f64> (x) { else if let Ok (x) = str::parse::<f64> (l.si.get (*x)) {
Value::from (x) Value::from (x)
} }
else { else {
@ -158,7 +160,7 @@ fn tonumber (value: &Value) -> Value {
fn lw_tonumber (l: &mut State, num_args: usize) -> usize { fn lw_tonumber (l: &mut State, num_args: usize) -> usize {
assert_eq! (num_args, 1, "tonumber only implemented for 1 argument"); assert_eq! (num_args, 1, "tonumber only implemented for 1 argument");
let output = tonumber (l.reg (0)); let output = tonumber (&l, l.reg (0));
*l.reg_mut (0) = output; *l.reg_mut (0) = output;
1 1
} }
@ -191,6 +193,26 @@ impl State {
step_count: 0, step_count: 0,
chunk, chunk,
upvalues, upvalues,
si: Default::default (),
}
}
pub fn new_with_args <I: Iterator <Item = String>> (chunk: Chunk, mut si: Interner, args: I) -> Self {
let upvalues = Self::upvalues_from_args (&mut si, args);
Self {
// TODO: Stack is actually supposed to grow to a limit of
// idk 10,000. I thought it was fixed at 256.
registers: vec! [Value::Nil; 256],
top: 0,
stack: vec! [
StackFrame::default (),
],
debug_print: false,
step_count: 0,
chunk,
upvalues,
si,
} }
} }
@ -199,27 +221,27 @@ impl State {
frame.block_idx == bp.block_idx && frame.program_counter == bp.program_counter frame.block_idx == bp.block_idx && frame.program_counter == bp.program_counter
} }
pub fn upvalues_from_args <I: Iterator <Item = String>> (args: I) -> Vec <Value> fn upvalues_from_args <I: Iterator <Item = String>> (si: &mut Interner, args: I) -> Vec <Value>
{ {
let arg = args.map (|s| Value::from (s)).enumerate (); let arg = args.map (|s| si.intern (&s)).enumerate ();
let arg = Value::from_iter (arg.map (|(i, v)| (Value::from (i), v))); let arg = Value::from_iter (arg.map (|(i, v)| (Value::from (i), Value::String (v))));
let io = [ let io: Vec <_> = [
("write", Value::RsFunc (lw_io_write)), ("write", Value::RsFunc (lw_io_write)),
].into_iter ().map (|(k, v)| (k.to_string (), v)); ].into_iter ().map (|(k, v)| (si.intern (k), v)).collect ();
let math = [ let math: Vec <_> = [
("sqrt", Value::RsFunc (lw_sqrt)), ("sqrt", Value::RsFunc (lw_sqrt)),
].into_iter ().map (|(k, v)| (k.to_string (), v)); ].into_iter ().map (|(k, v)| (si.intern (k), v)).collect ();
let string = [ let string: Vec <_> = [
("format", Value::RsFunc (lw_string_format)), ("format", Value::RsFunc (lw_string_format)),
].into_iter ().map (|(k, v)| (k.to_string (), v)); ].into_iter ().map (|(k, v)| (si.intern (k), v)).collect ();
let table = [ let table: Vec <_> = [
("concat", Value::RsFunc (lw_table_concat)), ("concat", Value::RsFunc (lw_table_concat)),
("pack", Value::RsFunc (lw_table_pack)), ("pack", Value::RsFunc (lw_table_pack)),
].into_iter ().map (|(k, v)| (k.to_string (), v)); ].into_iter ().map (|(k, v)| (si.intern (k), v)).collect ();
let env = [ let env = [
("arg", arg), ("arg", arg),
@ -229,7 +251,7 @@ impl State {
("string", Value::from_iter (string.into_iter ())), ("string", Value::from_iter (string.into_iter ())),
("table", Value::from_iter (table.into_iter ())), ("table", Value::from_iter (table.into_iter ())),
("tonumber", Value::RsFunc (lw_tonumber)), ("tonumber", Value::RsFunc (lw_tonumber)),
].into_iter ().map (|(k, v)| (k.to_string (), v)); ].into_iter ().map (|(k, v)| (si.intern (k), v));
vec! [ vec! [
Value::from_iter (env.into_iter ()), Value::from_iter (env.into_iter ()),
@ -486,11 +508,11 @@ impl State {
}; };
let key = match &k [usize::from (c)] { let key = match &k [usize::from (c)] {
Value::String (s) => s, Value::String (s) => *s,
_ => panic! ("K[C] must be a string"), _ => panic! ("K[C] must be a string"),
}; };
let val = t.borrow ().get_str (key.as_str ()).clone (); let val = t.borrow ().get_str (key).clone ();
*self.reg_mut (a) = val; *self.reg_mut (a) = val;
}, },
@ -530,11 +552,11 @@ impl State {
let table = value.as_table ().expect ("GetTabUp only works on tables").borrow (); let table = value.as_table ().expect ("GetTabUp only works on tables").borrow ();
let key = match k.get (c).unwrap () { let key = match k.get (c).unwrap () {
Value::String (s) => String::from (s.as_ref()), Value::String (s) => *s,
_ => panic! ("GetTabUp only supports string keys"), _ => panic! ("GetTabUp only supports string keys"),
}; };
*self.reg_mut (a) = table.get (key); *self.reg_mut (a) = table.get_str (key).clone ();
}, },
Instruction::GetI (a, b, c) => { Instruction::GetI (a, b, c) => {
let key = i64::try_from (c).unwrap (); let key = i64::try_from (c).unwrap ();
@ -573,7 +595,7 @@ impl State {
Value::Integer (_) => Err (make_step_error ("attempt to get length of a number value"))?, Value::Integer (_) => Err (make_step_error ("attempt to get length of a number value"))?,
Value::Nil => Err (make_step_error ("attempt to get length of a nil value"))?, Value::Nil => Err (make_step_error ("attempt to get length of a nil value"))?,
Value::RsFunc (_) => Err (make_step_error ("attempt to get length of a function value"))?, Value::RsFunc (_) => Err (make_step_error ("attempt to get length of a function value"))?,
Value::String (s) => s.len ().into (), Value::String (s) => self.si.get (*s).len ().into (),
Value::Table (t) => t.borrow ().length ().into (), Value::Table (t) => t.borrow ().length ().into (),
}; };
@ -765,14 +787,14 @@ impl State {
let b = usize::try_from (b).unwrap (); let b = usize::try_from (b).unwrap ();
let key = match k.get (b).unwrap () { let key = match k.get (b).unwrap () {
Value::String (s) => s.as_ref (), Value::String (s) => *s,
_ => panic! ("SetField only supports string keys"), _ => panic! ("SetField only supports string keys"),
}; };
let mut dst = self.reg (a).as_table () let mut dst = self.reg (a).as_table ()
.expect ("SetField only works on tables").borrow_mut (); .expect ("SetField only works on tables").borrow_mut ();
dst.insert_str (key.as_str (), value); dst.insert_str (key, value);
}, },
Instruction::SetI (a, b, c, k_flag) => { Instruction::SetI (a, b, c, k_flag) => {
let value = if k_flag { let value = if k_flag {
@ -946,7 +968,7 @@ impl State {
pub fn eval (&mut self, src: &str) -> Result <Vec <Value>, crate::Error> pub fn eval (&mut self, src: &str) -> Result <Vec <Value>, crate::Error>
{ {
let bytecode = crate::compile_bytecode (src.as_bytes ().to_vec ())?; let bytecode = crate::compile_bytecode (src.as_bytes ().to_vec ())?;
let chunk = crate::parse_chunk (&bytecode).unwrap (); let chunk = crate::parse_chunk (&bytecode, &mut self.si).unwrap ();
self.set_chunk (chunk); self.set_chunk (chunk);
Ok (self.execute ()?) Ok (self.execute ()?)
@ -971,4 +993,8 @@ impl State {
self.stack = vec! [Default::default ()]; self.stack = vec! [Default::default ()];
self.chunk = chunk; self.chunk = chunk;
} }
pub fn to_string (&mut self, s: &str) -> Value {
Value::String (self.si.intern (s))
}
} }

View File

@ -1,25 +1,40 @@
use std::collections::BTreeMap;
use crate::value::Value;
#[derive (Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct InternedString (i64); pub struct InternedString (i64);
#[derive (Default)] #[derive (Debug, Default)]
pub struct Interner { pub struct Interner {
table: std::collections::BTreeMap <String, i64>, table_fwd: BTreeMap <String, i64>,
table_rev: BTreeMap <i64, String>,
counter: i64, counter: i64,
} }
impl Interner { impl Interner {
pub fn get (&self, s: InternedString) -> &str {
self.table_rev.get (&s.0).unwrap ()
}
pub fn intern (&mut self, s: &str) -> InternedString { pub fn intern (&mut self, s: &str) -> InternedString {
match self.table.get (s) { match self.table_fwd.get (s) {
Some (x) => InternedString (*x), Some (x) => InternedString (*x),
None => { None => {
self.counter += 1; self.counter += 1;
if self.counter == i64::MAX { if self.counter == i64::MAX {
panic! ("Out of IDs"); panic! ("Out of IDs");
} }
self.table.insert (s.to_string (), self.counter); self.table_fwd.insert (s.to_string (), self.counter);
self.table_rev.insert (self.counter, s.to_string ());
InternedString (self.counter) InternedString (self.counter)
} }
} }
} }
pub fn to_value (&mut self, s: &str) -> Value {
Value::from (self.intern (s))
}
} }
#[cfg (test)] #[cfg (test)]

View File

@ -8,6 +8,7 @@ use crate::{
Chunk, Chunk,
State, State,
}, },
string_interner::Interner,
value::Value, value::Value,
}; };
@ -23,16 +24,15 @@ fn calculate_hash<T: Hash>(t: &T) -> u64 {
/// and returns the output /// and returns the output
fn run_chunk (args: &[&str], chunk: Chunk) -> Vec <Value> { fn run_chunk (args: &[&str], chunk: Chunk) -> Vec <Value> {
let upvalues = State::upvalues_from_args (args.into_iter ().map (|s| s.to_string ())); let mut vm = State::new_with_args (chunk, args.iter ());
let mut vm = State::new (chunk, upvalues);
vm.execute ().unwrap () vm.execute ().unwrap ()
} }
/// Takes arguments and Lua bytecode, loads it, runs it, /// Takes arguments and Lua bytecode, loads it, runs it,
/// and return the output /// and return the output
fn run_bytecode (args: &[&str], bc: &[u8]) -> Vec <Value> { fn run_bytecode (args: &[&str], bc: &[u8], si: &mut Interner) -> Vec <Value> {
let chunk = loader::parse_chunk (&bc).unwrap (); let chunk = loader::parse_chunk (&bc, si).unwrap ();
run_chunk (args, chunk) run_chunk (args, chunk)
} }
@ -41,9 +41,9 @@ fn run_bytecode (args: &[&str], bc: &[u8]) -> Vec <Value> {
/// runs it, /// runs it,
/// and returns the output /// and returns the output
fn run_source (args: &[&str], s: &str) -> Vec <Value> { fn run_source (args: &[&str], s: &str, si: &mut Interner) -> Vec <Value> {
let bc = loader::compile_bytecode (s.as_bytes ().to_vec ()).unwrap (); let bc = loader::compile_bytecode (s.as_bytes ().to_vec ()).unwrap ();
run_bytecode (args, &bc) run_bytecode (args, &bc, si)
} }
#[test] #[test]
@ -62,6 +62,8 @@ fn bools () {
return x return x
*/ */
let mut si = Interner::default ();
let chunk = Chunk { let chunk = Chunk {
blocks: vec! [ blocks: vec! [
Block { Block {
@ -90,8 +92,8 @@ fn bools () {
Inst::Return (2, 1, 1, false), Inst::Return (2, 1, 1, false),
], ],
constants: vec! [ constants: vec! [
"arg".into (), si.to_value ("arg"),
"print".into (), si.to_value ("print"),
], ],
upvalues: vec! [], upvalues: vec! [],
}, },
@ -129,7 +131,7 @@ fn bools () {
fn closure () { fn closure () {
let source = include_bytes! ("../test_vectors/closure.lua"); let source = include_bytes! ("../test_vectors/closure.lua");
let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap (); let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap ();
let chunk = crate::loader::parse_chunk (bytecode).unwrap (); let chunk = crate::loader::parse_chunk (bytecode, &mut Interner::default ()).unwrap ();
assert_eq! (run_chunk (&["_exe_name"], chunk), vec! [Value::from (23i64)]); assert_eq! (run_chunk (&["_exe_name"], chunk), vec! [Value::from (23i64)]);
} }
@ -146,6 +148,8 @@ fn floats () {
return x return x
*/ */
let mut si = Interner::default ();
let block = Block { let block = Block {
instructions: vec! [ instructions: vec! [
Inst::VarArgPrep (0), Inst::VarArgPrep (0),
@ -161,7 +165,7 @@ fn floats () {
], ],
constants: vec! [ constants: vec! [
0.5.into (), 0.5.into (),
"print".into (), si.to_value ("print"),
], ],
upvalues: vec! [], upvalues: vec! [],
}; };
@ -186,7 +190,7 @@ fn floats () {
fn fma () { fn fma () {
let source = include_bytes! ("../test_vectors/fma.lua"); let source = include_bytes! ("../test_vectors/fma.lua");
let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap (); let bytecode = &crate::loader::compile_bytecode (source.to_vec ()).unwrap ();
let chunk = crate::loader::parse_chunk (bytecode).unwrap (); let chunk = crate::loader::parse_chunk (bytecode, &mut Interner::default ()).unwrap ();
assert_eq! (chunk.blocks.len (), 5); assert_eq! (chunk.blocks.len (), 5);
assert_eq! (chunk.blocks [3].upvalues.len (), 2); assert_eq! (chunk.blocks [3].upvalues.len (), 2);
@ -241,6 +245,8 @@ fn heap () {
}; };
use crate::value::Table; use crate::value::Table;
let mut si = Interner::default ();
{ {
let mut allocations = HashMap::new (); let mut allocations = HashMap::new ();
let mut ctr = 0; let mut ctr = 0;
@ -255,7 +261,7 @@ fn heap () {
allocations.get_mut (&a).unwrap ().insert (1, c); allocations.get_mut (&a).unwrap ().insert (1, c);
allocations.get_mut (&c).unwrap ().insert (2, "eee"); allocations.get_mut (&c).unwrap ().insert (2, si.to_value ("eee"));
} }
if true { if true {
@ -292,10 +298,12 @@ fn heap () {
#[test] #[test]
fn is_93 () { fn is_93 () {
assert_eq! (Value::from ("93"), Value::from ("93")); let mut si = Interner::default ();
assert_ne! (Value::from ("94"), Value::from ("93"));
assert_ne! (calculate_hash (&Value::from ("94")), calculate_hash (&Value::from ("93"))); assert_eq! (si.to_value ("93"), si.to_value ("93"));
assert_ne! (Value::Nil, Value::from ("93")); assert_ne! (si.to_value ("94"), si.to_value ("93"));
assert_ne! (calculate_hash (&si.to_value ("94")), calculate_hash (&si.to_value ("93")));
assert_ne! (Value::Nil, si.to_value ("93"));
let src = br#" let src = br#"
if arg [1] == "93" then if arg [1] == "93" then
@ -308,7 +316,7 @@ fn is_93 () {
"#; "#;
let bc = loader::compile_bytecode (src.to_vec ()).unwrap (); let bc = loader::compile_bytecode (src.to_vec ()).unwrap ();
let chunk = loader::parse_chunk (&bc).unwrap (); let chunk = loader::parse_chunk (&bc, &mut Interner::default ()).unwrap ();
assert_eq! (chunk.blocks [0].instructions [3], Inst::EqK (0, 1, false)); assert_eq! (chunk.blocks [0].instructions [3], Inst::EqK (0, 1, false));
@ -369,7 +377,7 @@ fn tables_1 () {
print (t ["t"][1]) print (t ["t"][1])
"#; "#;
run_source (&[], src); run_source (&[], src, &mut Interner::default ());
} }
#[test] #[test]
@ -384,7 +392,7 @@ fn tables_2 () {
print (a [2]) print (a [2])
"#; "#;
run_source (&[], src); run_source (&[], src, &mut Interner::default ());
} }
#[test] #[test]
@ -396,7 +404,7 @@ fn tailcall () {
"#; "#;
let bc = loader::compile_bytecode (src.to_vec ()).unwrap (); let bc = loader::compile_bytecode (src.to_vec ()).unwrap ();
let chunk = loader::parse_chunk (&bc).unwrap (); let chunk = loader::parse_chunk (&bc, &mut Interner::default ()).unwrap ();
assert_eq! (chunk.blocks [0].instructions [3], Instruction::TailCall (0, 2, 1, false)); assert_eq! (chunk.blocks [0].instructions [3], Instruction::TailCall (0, 2, 1, false));

View File

@ -4,11 +4,16 @@ use std::{
Eq, Eq,
PartialEq, PartialEq,
}, },
collections::HashMap, collections::{BTreeMap, HashMap},
fmt, fmt,
rc::Rc, rc::Rc,
}; };
use crate::string_interner::{
Interner,
InternedString,
};
#[derive (Debug, Eq, PartialEq)] #[derive (Debug, Eq, PartialEq)]
pub struct BogusClosure { pub struct BogusClosure {
// I'm pretty sure this should be absolute? // I'm pretty sure this should be absolute?
@ -27,7 +32,7 @@ pub enum Value {
Integer (i64), Integer (i64),
RsFunc (fn (&mut crate::state::State, usize) -> usize), RsFunc (fn (&mut crate::state::State, usize) -> usize),
String (Rc <String>), String (InternedString),
Table (Rc <RefCell <Table>>), Table (Rc <RefCell <Table>>),
// These are all bogus, I haven't figured out how to implement // These are all bogus, I haven't figured out how to implement
@ -47,7 +52,7 @@ impl fmt::Debug for Value {
Value::Float (x) => write! (f, "{:?}", x), Value::Float (x) => write! (f, "{:?}", x),
Value::Integer (x) => write! (f, "{}", x), Value::Integer (x) => write! (f, "{}", x),
Value::RsFunc (x) => write! (f, "function: {:?}", x), Value::RsFunc (x) => write! (f, "function: {:?}", x),
Value::String (s) => write! (f, "\"{}\"", s), Value::String (s) => write! (f, "unimplemented Debug",),
Value::Table (t) => write! (f, "{:?}", t.borrow ()), Value::Table (t) => write! (f, "{:?}", t.borrow ()),
Value::BogusClosure (x) => write! (f, "{:?}", x.borrow ()), Value::BogusClosure (x) => write! (f, "{:?}", x.borrow ()),
@ -70,7 +75,7 @@ impl fmt::Display for Value {
Value::Float (x) => write! (f, "{:?}", x), Value::Float (x) => write! (f, "{:?}", x),
Value::Integer (x) => write! (f, "{}", x), Value::Integer (x) => write! (f, "{}", x),
Value::RsFunc (x) => write! (f, "function: {:?}", x), Value::RsFunc (x) => write! (f, "function: {:?}", x),
Value::String (s) => write! (f, "{}", s), Value::String (s) => write! (f, "unimplemented Display"),
Value::Table (t) => write! (f, "table: {:?}", std::rc::Rc::as_ptr (t)), Value::Table (t) => write! (f, "table: {:?}", std::rc::Rc::as_ptr (t)),
Value::BogusClosure (x) => write! (f, "BogusClosure: {:?}", std::rc::Rc::as_ptr (x)), Value::BogusClosure (x) => write! (f, "BogusClosure: {:?}", std::rc::Rc::as_ptr (x)),
@ -90,18 +95,6 @@ impl From <bool> for Value {
} }
} }
impl From <String> for Value {
fn from (x: String) -> Self {
Self::String (x.into ())
}
}
impl From <&str> for Value {
fn from (x: &str) -> Self {
Self::from (String::from (x))
}
}
impl From <i32> for Value { impl From <i32> for Value {
fn from (x: i32) -> Self { fn from (x: i32) -> Self {
Self::Integer (i64::from (x)) Self::Integer (i64::from (x))
@ -126,6 +119,12 @@ impl From <usize> for Value {
} }
} }
impl From <InternedString> for Value {
fn from (x: InternedString) -> Self {
Self::String (x)
}
}
impl From <Table> for Value { impl From <Table> for Value {
fn from (x: Table) -> Self { fn from (x: Table) -> Self {
Self::Table (Rc::new (RefCell::new (x))) Self::Table (Rc::new (RefCell::new (x)))
@ -139,9 +138,9 @@ impl FromIterator <(Value, Value)> for Value {
} }
} }
impl FromIterator <(String, Value)> for Value { impl FromIterator <(InternedString, Value)> for Value {
fn from_iter <I: IntoIterator <Item=(String, Value)>> (i: I) -> Self { fn from_iter <I: IntoIterator <Item=(InternedString, Value)>> (i: I) -> Self {
Self::from_iter (i.into_iter ().map (|(s, v)| (Value::from (s), v))) Self::from_iter (i.into_iter ().map (|(s, v)| (Value::String (s), v)))
} }
} }
@ -209,9 +208,9 @@ impl Value {
} }
} }
pub fn as_str (&self) -> Option <&str> { pub fn as_str (&self) -> Option <InternedString> {
match self { match self {
Self::String (x) => Some (x.as_str ()), Self::String (x) => Some (*x),
_ => None, _ => None,
} }
} }
@ -223,6 +222,10 @@ impl Value {
} }
} }
pub fn from_str (si: &mut Interner, s: &str) -> Value {
Value::String (si.intern (s))
}
pub fn is_truthy (&self) -> bool { pub fn is_truthy (&self) -> bool {
// And this is something Lua does better than JS and Python. // And this is something Lua does better than JS and Python.
@ -244,11 +247,12 @@ impl Value {
pub struct Table { pub struct Table {
array: Vec <Value>, array: Vec <Value>,
hash: HashMap <Value, Value>, hash: HashMap <Value, Value>,
map: BTreeMap <InternedString, Value>,
} }
impl fmt::Debug for Table { impl fmt::Debug for Table {
fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result {
write! (f, "Table {:#?}", self.hash)?; write! (f, "Table {:#?}", self.map)?;
Ok (()) Ok (())
} }
@ -256,14 +260,11 @@ impl fmt::Debug for Table {
impl Table { impl Table {
fn get_inner (&self, key: &Value) -> Value { fn get_inner (&self, key: &Value) -> Value {
// self.hash.get (key).cloned ().unwrap_or_default () match key {
for (hay, value) in &self.hash { Value::Nil => Value::Nil,
if key == hay { Value::String (x) => self.map.get (x).cloned ().unwrap_or_default (),
return value.clone (); x => self.hash.get (x).cloned ().unwrap_or_default (),
}
} }
Value::Nil
} }
pub fn get <A: Into <Value>> (&self, key: A) -> Value { pub fn get <A: Into <Value>> (&self, key: A) -> Value {
@ -274,25 +275,11 @@ impl Table {
self.get_inner (&(key.into ())) self.get_inner (&(key.into ()))
} }
pub fn get_str (&self, key: &str) -> &Value { pub fn get_str (&self, key: InternedString) -> &Value {
for (hay, value) in &self.hash { match self.map.get (&key) {
if Some (key) == hay.as_str () { None => &NIL,
return value; Some (x) => x,
}
} }
&NIL
}
fn insert_inner (&mut self, a: Value, b: Value) {
for (hay, value) in &mut self.hash {
if &a == hay {
*value = b;
return;
}
}
self.hash.insert (a, b);
} }
/// Insert value at arbitrary key /// Insert value at arbitrary key
@ -302,25 +289,26 @@ impl Table {
a: A, a: A,
b: B, b: B,
) { ) {
self.insert_inner (a.into (), b.into ()) match a.into () {
Value::Nil => (),
Value::String (x) => {
self.map.insert (x, b.into ());
},
x => {
self.hash.insert (x, b.into ());
},
}
} }
/// Insert value at integer key /// Insert value at integer key
pub fn insert_int <A: Into <Value>> (&mut self, k: i64, v: A) pub fn insert_int <A: Into <Value>> (&mut self, k: i64, v: A)
{ {
self.insert_inner (k.into (), v.into ()) self.insert (Value::from (k), v.into ())
} }
pub fn insert_str (&mut self, key: &str, v: Value) { pub fn insert_str (&mut self, key: InternedString, v: Value) {
for (hay, value) in &mut self.hash { self.map.insert (key, v);
if Some (key) == hay.as_str () {
*value = v;
return;
}
}
self.hash.insert (key.into (), v);
} }
pub fn length (&self) -> i64 { pub fn length (&self) -> i64 {
@ -336,21 +324,18 @@ impl Table {
impl FromIterator <(Value, Value)> for Table { impl FromIterator <(Value, Value)> for Table {
fn from_iter<I: IntoIterator<Item = (Value, Value)>> (i: I) -> Self fn from_iter<I: IntoIterator<Item = (Value, Value)>> (i: I) -> Self
{ {
let hash = i.into_iter ().collect (); let mut that = Self::default ();
Self { for (k, v) in i.into_iter () {
array: Default::default (), that.insert (k, v);
hash,
} }
that
} }
} }
#[cfg (test)] #[cfg (test)]
mod tests { mod tests {
use std::collections::HashMap; use std::collections::HashMap;
use super::{ use super::*;
Table,
Value,
};
#[test] #[test]
fn smoke () { fn smoke () {
@ -364,23 +349,24 @@ mod tests {
#[test] #[test]
fn tables () { fn tables () {
let nil = Value::Nil; let nil = Value::Nil;
let mut si = Interner::default ();
assert_ne! (Value::from (18.0), Value::from (19.0)); assert_ne! (Value::from (18.0), Value::from (19.0));
let mut t = HashMap::new (); let mut t = HashMap::new ();
t.insert (Value::from ("x"), Value::from (19.0)); t.insert (Value::from_str (&mut si, "x"), Value::from (19.0));
assert_eq! (t.get (&Value::from ("x")), Some (&Value::from (19.0))); assert_eq! (t.get (&Value::from_str (&mut si, "x")), Some (&Value::from (19.0)));
let mut t = Table::default (); let mut t = Table::default ();
assert_eq! (t.get ("a"), nil); assert_eq! (t.get (Value::from_str (&mut si, "a")), nil);
assert_eq! (t.get ("b"), nil); assert_eq! (t.get (Value::from_str (&mut si, "b")), nil);
t.insert ("a", 1993); t.insert (si.to_value ("a"), 1993);
t.insert ("b", 2007); t.insert (si.to_value ("b"), 2007);
assert_eq! (t.get ("a"), 1993); assert_eq! (t.get (Value::from_str (&mut si, "a")), 1993);
assert_eq! (t.get ("b"), 2007); assert_eq! (t.get (Value::from_str (&mut si, "b")), 2007);
t.insert (19, 93); t.insert (19, 93);
assert_eq! (t.get (19), 93); assert_eq! (t.get (19), 93);