From eb32a53d1842c583b30a5783f260abfc7f8745e8 Mon Sep 17 00:00:00 2001 From: _ <_@_> Date: Mon, 2 Oct 2023 14:52:38 -0500 Subject: [PATCH] :construction: wip: add string interning, but a few things broke --- lunar_wave_cli/src/main.rs | 20 ++-- lunar_wave_vm/src/lib.rs | 1 + lunar_wave_vm/src/loader.rs | 27 +++--- lunar_wave_vm/src/state.rs | 84 +++++++++++------ lunar_wave_vm/src/string_interner.rs | 23 ++++- lunar_wave_vm/src/tests.rs | 48 ++++++---- lunar_wave_vm/src/value.rs | 134 ++++++++++++--------------- 7 files changed, 190 insertions(+), 147 deletions(-) diff --git a/lunar_wave_cli/src/main.rs b/lunar_wave_cli/src/main.rs index 054f0c2..8687df5 100644 --- a/lunar_wave_cli/src/main.rs +++ b/lunar_wave_cli/src/main.rs @@ -22,6 +22,8 @@ fn lunar_wave (args: Vec ) -> Result , lwvm::StepError let mut arg_iter = args.iter (); let _exe_name = arg_iter.next ().unwrap (); + let mut si = lwvm::Interner::default (); + while let Some (arg) = arg_iter.next () { match arg.as_str () { "--break" => { @@ -40,7 +42,7 @@ fn lunar_wave (args: Vec ) -> Result , lwvm::StepError let mut buf = vec! []; std::io::stdin ().read_to_end (&mut 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 ()]; }, @@ -51,7 +53,7 @@ fn lunar_wave (args: Vec ) -> Result , lwvm::StepError } else if chunk.is_none () { 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 ()]; } @@ -68,10 +70,12 @@ fn lunar_wave (args: Vec ) -> Result , lwvm::StepError chunk, list_bytecode, lua_args, + si, }), None => repl (ReplParams { list_bytecode, lua_args, + si, }), } } @@ -81,11 +85,13 @@ struct DebuggerParams { chunk: lwvm::Chunk, list_bytecode: bool, lua_args: Vec , + si: lwvm::Interner, } struct ReplParams { list_bytecode: bool, lua_args: Vec , + si: lwvm::Interner, } /// The interpreter mode, which has optional debugging abilities @@ -97,9 +103,7 @@ fn debugger (params: DebuggerParams) -> Result , lwvm::StepErr dbg! (¶ms.chunk); } - let upvalues = lwvm::State::upvalues_from_args (params.lua_args.into_iter ()); - - let mut vm = lwvm::State::new (params.chunk, upvalues); + let mut vm = lwvm::State::new_with_args (params.chunk, params.si, params.lua_args.into_iter ()); if std::env::var("LWVM_DEBUG").is_ok() { vm.debug_print = true; } @@ -160,9 +164,7 @@ fn debugger (params: DebuggerParams) -> Result , lwvm::StepErr fn repl (params: ReplParams) -> Result , lwvm::StepError> { - let upvalues = lwvm::State::upvalues_from_args (params.lua_args.into_iter ()); - - let mut vm = lwvm::State::new (lwvm::Chunk::default (), upvalues); + let mut vm = lwvm::State::new_with_args (lwvm::Chunk::default (), params.si, params.lua_args.into_iter ()); 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 , lwvm::StepError> continue; }, }; - let chunk = lwvm::parse_chunk (&bytecode).unwrap (); + let chunk = lwvm::parse_chunk (&bytecode, &mut vm.si).unwrap (); if params.list_bytecode { dbg! (&chunk); diff --git a/lunar_wave_vm/src/lib.rs b/lunar_wave_vm/src/lib.rs index c154147..196da06 100644 --- a/lunar_wave_vm/src/lib.rs +++ b/lunar_wave_vm/src/lib.rs @@ -16,6 +16,7 @@ pub use state::Chunk as Chunk; pub use state::State as State; pub use state::StepError as StepError; pub use state::StepOutput as StepOutput; +pub use string_interner::Interner as Interner; pub use value::Value as Value; #[cfg (test)] diff --git a/lunar_wave_vm/src/loader.rs b/lunar_wave_vm/src/loader.rs index 4828512..0188e95 100644 --- a/lunar_wave_vm/src/loader.rs +++ b/lunar_wave_vm/src/loader.rs @@ -5,7 +5,8 @@ use crate::{ state::{ Block, Chunk, - } + }, + string_interner::Interner, }; pub fn compile_bytecode_from_file (path: &str) -> Vec { @@ -242,7 +243,7 @@ fn parse_i64 (rdr: &mut R) -> Option { // code, but I don't like recursion in general, and I don't know // why PUC wrote it that way. -pub fn parse_block (rdr: &mut R, blocks: &mut Vec ) +pub fn parse_block (rdr: &mut R, si: &mut Interner, blocks: &mut Vec ) -> Option <()> { // Ignore things I haven't implemented yet @@ -273,12 +274,12 @@ pub fn parse_block (rdr: &mut R, blocks: &mut Vec ) let val = match const_type { 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 19 => parse_float (rdr).unwrap ().into (), // 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), }; @@ -312,7 +313,7 @@ pub fn parse_block (rdr: &mut R, blocks: &mut Vec ) // Subfunctions. PUC calls them protos. let protos_count = parse_int (rdr).unwrap (); for _ in 0..protos_count { - parse_block (rdr, blocks).unwrap (); + parse_block (rdr, si, blocks).unwrap (); } // Skip over debug stuff @@ -348,12 +349,12 @@ pub fn parse_block (rdr: &mut R, blocks: &mut Vec ) Some (()) } -pub fn parse_chunk (buf: &[u8]) -> Option { +pub fn parse_chunk (buf: &[u8], si: &mut Interner) -> Option { 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 (rdr: &mut R) -> Option { +pub fn parse_chunk_from_reader (rdr: &mut R, si: &mut Interner) -> Option { // Discard 32 bytes from the start of the file. // This is magic number, version number, etc. @@ -364,7 +365,7 @@ pub fn parse_chunk_from_reader (rdr: &mut R) -> Option { let mut blocks = vec![]; - parse_block (rdr, &mut blocks).unwrap (); + parse_block (rdr, si, &mut blocks).unwrap (); Some (Chunk { blocks, @@ -373,6 +374,8 @@ pub fn parse_chunk_from_reader (rdr: &mut R) -> Option { #[cfg (test)] mod tests { + use super::*; + #[test] fn load_size () { let f = |input: &[u8]| { @@ -441,6 +444,8 @@ mod tests { fn parse_nested_functions () { use std::io::Read; + let mut si = Interner::default (); + let bytecode = include_bytes! ("../test_vectors/functions.luac"); { @@ -451,7 +456,7 @@ mod tests { 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 [1].instructions.len (), 6); @@ -461,7 +466,7 @@ mod tests { } 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); } diff --git a/lunar_wave_vm/src/state.rs b/lunar_wave_vm/src/state.rs index 9e09e97..6998ee7 100644 --- a/lunar_wave_vm/src/state.rs +++ b/lunar_wave_vm/src/state.rs @@ -1,5 +1,6 @@ use crate::{ instruction::Instruction, + string_interner::Interner, value::{ BogusClosure, Value, @@ -55,6 +56,7 @@ pub struct State { step_count: u32, chunk: Chunk, upvalues: Vec , + pub si: Interner, } 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) { Value::Float (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"), } } @@ -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_eq! (l.get_top (), 2, "string.format not fully implemented"); 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 output = format! ("{:0.9}", num); - *l.reg_mut (0) = Value::from (output); + *l.reg_mut (0) = l.to_string (&output); 1 } @@ -117,14 +119,14 @@ fn lw_table_concat (l: &mut State, num_args: usize) -> usize { for i in 0..t.length () { if i > 0 { - s.push_str (joiner); + s.push_str (l.si.get (joiner)); } let x = t.get_int (i + 1); s.push_str (&format! ("{}", x)); } s }; - *l.reg_mut (0) = Value::from (s); + *l.reg_mut (0) = l.to_string (&s); 1 } @@ -137,15 +139,15 @@ fn lw_table_pack (l: &mut State, num_args: usize) -> usize { 1 } -fn tonumber (value: &Value) -> Value { +fn tonumber (l: &State, value: &Value) -> Value { match value { Value::Float (x) => Value::Float (*x), Value::Integer (x) => Value::Integer (*x), Value::String (x) => { - if let Ok (x) = str::parse:: (x) { + if let Ok (x) = str::parse:: (l.si.get (*x)) { Value::from (x) } - else if let Ok (x) = str::parse:: (x) { + else if let Ok (x) = str::parse:: (l.si.get (*x)) { Value::from (x) } else { @@ -158,7 +160,7 @@ fn tonumber (value: &Value) -> Value { fn lw_tonumber (l: &mut State, num_args: usize) -> usize { 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; 1 } @@ -191,6 +193,26 @@ impl State { step_count: 0, chunk, upvalues, + si: Default::default (), + } + } + + pub fn new_with_args > (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 } - pub fn upvalues_from_args > (args: I) -> Vec + fn upvalues_from_args > (si: &mut Interner, args: I) -> Vec { - let arg = args.map (|s| Value::from (s)).enumerate (); - let arg = Value::from_iter (arg.map (|(i, v)| (Value::from (i), v))); + let arg = args.map (|s| si.intern (&s)).enumerate (); + 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)), - ].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)), - ].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)), - ].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)), ("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 = [ ("arg", arg), @@ -229,7 +251,7 @@ impl State { ("string", Value::from_iter (string.into_iter ())), ("table", Value::from_iter (table.into_iter ())), ("tonumber", Value::RsFunc (lw_tonumber)), - ].into_iter ().map (|(k, v)| (k.to_string (), v)); + ].into_iter ().map (|(k, v)| (si.intern (k), v)); vec! [ Value::from_iter (env.into_iter ()), @@ -486,11 +508,11 @@ impl State { }; let key = match &k [usize::from (c)] { - Value::String (s) => s, + Value::String (s) => *s, _ => 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; }, @@ -530,11 +552,11 @@ impl State { let table = value.as_table ().expect ("GetTabUp only works on tables").borrow (); 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"), }; - *self.reg_mut (a) = table.get (key); + *self.reg_mut (a) = table.get_str (key).clone (); }, Instruction::GetI (a, b, c) => { 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::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::String (s) => s.len ().into (), + Value::String (s) => self.si.get (*s).len ().into (), Value::Table (t) => t.borrow ().length ().into (), }; @@ -765,14 +787,14 @@ impl State { let b = usize::try_from (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"), }; let mut dst = self.reg (a).as_table () .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) => { let value = if k_flag { @@ -946,7 +968,7 @@ impl State { pub fn eval (&mut self, src: &str) -> Result , crate::Error> { 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); Ok (self.execute ()?) @@ -971,4 +993,8 @@ impl State { self.stack = vec! [Default::default ()]; self.chunk = chunk; } + + pub fn to_string (&mut self, s: &str) -> Value { + Value::String (self.si.intern (s)) + } } diff --git a/lunar_wave_vm/src/string_interner.rs b/lunar_wave_vm/src/string_interner.rs index 7b6a03f..c4e2f61 100644 --- a/lunar_wave_vm/src/string_interner.rs +++ b/lunar_wave_vm/src/string_interner.rs @@ -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); -#[derive (Default)] +#[derive (Debug, Default)] pub struct Interner { - table: std::collections::BTreeMap , + table_fwd: BTreeMap , + table_rev: BTreeMap , counter: i64, } impl Interner { + pub fn get (&self, s: InternedString) -> &str { + self.table_rev.get (&s.0).unwrap () + } + pub fn intern (&mut self, s: &str) -> InternedString { - match self.table.get (s) { + match self.table_fwd.get (s) { Some (x) => InternedString (*x), None => { self.counter += 1; if self.counter == i64::MAX { 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) } } } + + pub fn to_value (&mut self, s: &str) -> Value { + Value::from (self.intern (s)) + } } #[cfg (test)] diff --git a/lunar_wave_vm/src/tests.rs b/lunar_wave_vm/src/tests.rs index 137bbdc..0f161d0 100644 --- a/lunar_wave_vm/src/tests.rs +++ b/lunar_wave_vm/src/tests.rs @@ -8,6 +8,7 @@ use crate::{ Chunk, State, }, + string_interner::Interner, value::Value, }; @@ -23,16 +24,15 @@ fn calculate_hash(t: &T) -> u64 { /// and returns the output fn run_chunk (args: &[&str], chunk: Chunk) -> Vec { - let upvalues = State::upvalues_from_args (args.into_iter ().map (|s| s.to_string ())); - let mut vm = State::new (chunk, upvalues); + let mut vm = State::new_with_args (chunk, args.iter ()); vm.execute ().unwrap () } /// Takes arguments and Lua bytecode, loads it, runs it, /// and return the output -fn run_bytecode (args: &[&str], bc: &[u8]) -> Vec { - let chunk = loader::parse_chunk (&bc).unwrap (); +fn run_bytecode (args: &[&str], bc: &[u8], si: &mut Interner) -> Vec { + let chunk = loader::parse_chunk (&bc, si).unwrap (); run_chunk (args, chunk) } @@ -41,9 +41,9 @@ fn run_bytecode (args: &[&str], bc: &[u8]) -> Vec { /// runs it, /// and returns the output -fn run_source (args: &[&str], s: &str) -> Vec { +fn run_source (args: &[&str], s: &str, si: &mut Interner) -> Vec { let bc = loader::compile_bytecode (s.as_bytes ().to_vec ()).unwrap (); - run_bytecode (args, &bc) + run_bytecode (args, &bc, si) } #[test] @@ -62,6 +62,8 @@ fn bools () { return x */ + let mut si = Interner::default (); + let chunk = Chunk { blocks: vec! [ Block { @@ -90,8 +92,8 @@ fn bools () { Inst::Return (2, 1, 1, false), ], constants: vec! [ - "arg".into (), - "print".into (), + si.to_value ("arg"), + si.to_value ("print"), ], upvalues: vec! [], }, @@ -129,7 +131,7 @@ fn bools () { fn closure () { let source = include_bytes! ("../test_vectors/closure.lua"); 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)]); } @@ -146,6 +148,8 @@ fn floats () { return x */ + let mut si = Interner::default (); + let block = Block { instructions: vec! [ Inst::VarArgPrep (0), @@ -161,7 +165,7 @@ fn floats () { ], constants: vec! [ 0.5.into (), - "print".into (), + si.to_value ("print"), ], upvalues: vec! [], }; @@ -186,7 +190,7 @@ fn floats () { fn fma () { let source = include_bytes! ("../test_vectors/fma.lua"); 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 [3].upvalues.len (), 2); @@ -241,6 +245,8 @@ fn heap () { }; use crate::value::Table; + let mut si = Interner::default (); + { let mut allocations = HashMap::new (); let mut ctr = 0; @@ -255,7 +261,7 @@ fn heap () { 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 { @@ -292,10 +298,12 @@ fn heap () { #[test] fn is_93 () { - assert_eq! (Value::from ("93"), Value::from ("93")); - assert_ne! (Value::from ("94"), Value::from ("93")); - assert_ne! (calculate_hash (&Value::from ("94")), calculate_hash (&Value::from ("93"))); - assert_ne! (Value::Nil, Value::from ("93")); + let mut si = Interner::default (); + + assert_eq! (si.to_value ("93"), si.to_value ("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#" if arg [1] == "93" then @@ -308,7 +316,7 @@ fn is_93 () { "#; 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)); @@ -369,7 +377,7 @@ fn tables_1 () { print (t ["t"][1]) "#; - run_source (&[], src); + run_source (&[], src, &mut Interner::default ()); } #[test] @@ -384,7 +392,7 @@ fn tables_2 () { print (a [2]) "#; - run_source (&[], src); + run_source (&[], src, &mut Interner::default ()); } #[test] @@ -396,7 +404,7 @@ fn tailcall () { "#; 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)); diff --git a/lunar_wave_vm/src/value.rs b/lunar_wave_vm/src/value.rs index b1f514c..8920de2 100644 --- a/lunar_wave_vm/src/value.rs +++ b/lunar_wave_vm/src/value.rs @@ -4,11 +4,16 @@ use std::{ Eq, PartialEq, }, - collections::HashMap, + collections::{BTreeMap, HashMap}, fmt, rc::Rc, }; +use crate::string_interner::{ + Interner, + InternedString, +}; + #[derive (Debug, Eq, PartialEq)] pub struct BogusClosure { // I'm pretty sure this should be absolute? @@ -27,7 +32,7 @@ pub enum Value { Integer (i64), RsFunc (fn (&mut crate::state::State, usize) -> usize), - String (Rc ), + String (InternedString), Table (Rc >), // 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::Integer (x) => write! (f, "{}", 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::BogusClosure (x) => write! (f, "{:?}", x.borrow ()), @@ -70,7 +75,7 @@ impl fmt::Display for Value { Value::Float (x) => write! (f, "{:?}", x), Value::Integer (x) => write! (f, "{}", 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::BogusClosure (x) => write! (f, "BogusClosure: {:?}", std::rc::Rc::as_ptr (x)), @@ -90,18 +95,6 @@ impl From for Value { } } -impl From 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 for Value { fn from (x: i32) -> Self { Self::Integer (i64::from (x)) @@ -126,6 +119,12 @@ impl From for Value { } } +impl From for Value { + fn from (x: InternedString) -> Self { + Self::String (x) + } +} + impl From for Value { fn from (x: Table) -> Self { Self::Table (Rc::new (RefCell::new (x))) @@ -139,9 +138,9 @@ impl FromIterator <(Value, Value)> for Value { } } -impl FromIterator <(String, Value)> for Value { - fn from_iter > (i: I) -> Self { - Self::from_iter (i.into_iter ().map (|(s, v)| (Value::from (s), v))) +impl FromIterator <(InternedString, Value)> for Value { + fn from_iter > (i: I) -> Self { + 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 { match self { - Self::String (x) => Some (x.as_str ()), + Self::String (x) => Some (*x), _ => 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 { // And this is something Lua does better than JS and Python. @@ -244,11 +247,12 @@ impl Value { pub struct Table { array: Vec , hash: HashMap , + map: BTreeMap , } impl fmt::Debug for Table { fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result { - write! (f, "Table {:#?}", self.hash)?; + write! (f, "Table {:#?}", self.map)?; Ok (()) } @@ -256,14 +260,11 @@ impl fmt::Debug for Table { impl Table { fn get_inner (&self, key: &Value) -> Value { - // self.hash.get (key).cloned ().unwrap_or_default () - for (hay, value) in &self.hash { - if key == hay { - return value.clone (); - } + match key { + Value::Nil => Value::Nil, + Value::String (x) => self.map.get (x).cloned ().unwrap_or_default (), + x => self.hash.get (x).cloned ().unwrap_or_default (), } - - Value::Nil } pub fn get > (&self, key: A) -> Value { @@ -274,25 +275,11 @@ impl Table { self.get_inner (&(key.into ())) } - pub fn get_str (&self, key: &str) -> &Value { - for (hay, value) in &self.hash { - if Some (key) == hay.as_str () { - return value; - } + pub fn get_str (&self, key: InternedString) -> &Value { + match self.map.get (&key) { + None => &NIL, + 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 @@ -302,25 +289,26 @@ impl Table { a: A, 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 pub fn insert_int > (&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) { - for (hay, value) in &mut self.hash { - if Some (key) == hay.as_str () { - *value = v; - return; - } - } - - self.hash.insert (key.into (), v); + pub fn insert_str (&mut self, key: InternedString, v: Value) { + self.map.insert (key, v); } pub fn length (&self) -> i64 { @@ -336,21 +324,18 @@ impl Table { impl FromIterator <(Value, Value)> for Table { fn from_iter> (i: I) -> Self { - let hash = i.into_iter ().collect (); - Self { - array: Default::default (), - hash, + let mut that = Self::default (); + for (k, v) in i.into_iter () { + that.insert (k, v); } + that } } #[cfg (test)] mod tests { use std::collections::HashMap; - use super::{ - Table, - Value, - }; + use super::*; #[test] fn smoke () { @@ -364,23 +349,24 @@ mod tests { #[test] fn tables () { let nil = Value::Nil; + let mut si = Interner::default (); assert_ne! (Value::from (18.0), Value::from (19.0)); let mut t = HashMap::new (); - t.insert (Value::from ("x"), Value::from (19.0)); - assert_eq! (t.get (&Value::from ("x")), Some (&Value::from (19.0))); + t.insert (Value::from_str (&mut si, "x"), Value::from (19.0)); + assert_eq! (t.get (&Value::from_str (&mut si, "x")), Some (&Value::from (19.0))); let mut t = Table::default (); - assert_eq! (t.get ("a"), nil); - assert_eq! (t.get ("b"), nil); + assert_eq! (t.get (Value::from_str (&mut si, "a")), nil); + assert_eq! (t.get (Value::from_str (&mut si, "b")), nil); - t.insert ("a", 1993); - t.insert ("b", 2007); + t.insert (si.to_value ("a"), 1993); + t.insert (si.to_value ("b"), 2007); - assert_eq! (t.get ("a"), 1993); - assert_eq! (t.get ("b"), 2007); + assert_eq! (t.get (Value::from_str (&mut si, "a")), 1993); + assert_eq! (t.get (Value::from_str (&mut si, "b")), 2007); t.insert (19, 93); assert_eq! (t.get (19), 93);