Compare commits
4 Commits
bccd5fc3a7
...
c50c16b9db
Author | SHA1 | Date |
---|---|---|
_ | c50c16b9db | |
_ | fb46ede3ca | |
_ | a9820677e9 | |
_ | 06638574f7 |
|
@ -29,6 +29,7 @@
|
||||||
- [x] Hash tables
|
- [x] Hash tables
|
||||||
- [x] Fizzbuzz
|
- [x] Fizzbuzz
|
||||||
- [ ] Closures
|
- [ ] Closures
|
||||||
|
- [ ] Error handling
|
||||||
- [ ] Garbage collection
|
- [ ] Garbage collection
|
||||||
- [ ] Long strings
|
- [ ] Long strings
|
||||||
- [ ] Using arrays internally for tables
|
- [ ] Using arrays internally for tables
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#[derive (Debug, PartialEq)]
|
#[derive (Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum Instruction {
|
pub enum Instruction {
|
||||||
Add (u8, u8, u8),
|
Add (u8, u8, u8),
|
||||||
AddI (u8, u8, i8),
|
AddI (u8, u8, i8),
|
||||||
|
|
|
@ -221,8 +221,6 @@ pub fn parse_block <R: Read> (rdr: &mut R, blocks: &mut Vec <Block>)
|
||||||
{
|
{
|
||||||
// Ignore things I haven't implemented yet
|
// Ignore things I haven't implemented yet
|
||||||
|
|
||||||
use crate::value::Value;
|
|
||||||
|
|
||||||
parse_string (rdr)?; // function name
|
parse_string (rdr)?; // function name
|
||||||
parse_int (rdr).unwrap (); // start line in source code
|
parse_int (rdr).unwrap (); // start line in source code
|
||||||
parse_int (rdr).unwrap (); // last line in source code
|
parse_int (rdr).unwrap (); // last line in source code
|
||||||
|
|
15
src/main.rs
15
src/main.rs
|
@ -8,7 +8,7 @@ mod value;
|
||||||
#[cfg (test)]
|
#[cfg (test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
fn main () {
|
fn main () -> Result <(), state::StepError> {
|
||||||
use state::State;
|
use state::State;
|
||||||
|
|
||||||
let mut list_bytecode = false;
|
let mut list_bytecode = false;
|
||||||
|
@ -64,11 +64,10 @@ fn main () {
|
||||||
vm.debug_print = true;
|
vm.debug_print = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let max_iters = 2000;
|
|
||||||
let mut in_break = false;
|
let mut in_break = false;
|
||||||
let mut last_input = String::new ();
|
let mut last_input = String::new ();
|
||||||
|
|
||||||
for _ in 0..max_iters {
|
loop {
|
||||||
if in_break || breakpoints.iter ().any (|bp| vm.at_breakpoint (bp)) {
|
if in_break || breakpoints.iter ().any (|bp| vm.at_breakpoint (bp)) {
|
||||||
in_break = true;
|
in_break = true;
|
||||||
dbg! (&vm.stack);
|
dbg! (&vm.stack);
|
||||||
|
@ -86,17 +85,17 @@ fn main () {
|
||||||
|
|
||||||
match input.as_str ().trim_end () {
|
match input.as_str ().trim_end () {
|
||||||
"c" => in_break = false,
|
"c" => in_break = false,
|
||||||
"q" => return,
|
"q" => return Ok (()),
|
||||||
"registers" => {
|
"registers" => {
|
||||||
dbg! (&vm.registers);
|
dbg! (&vm.registers);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
"s" => {
|
"s" => {
|
||||||
match vm.step () {
|
match vm.step ()? {
|
||||||
None => (),
|
None => (),
|
||||||
Some (state::StepOutput::ChunkReturned (x)) => {
|
Some (state::StepOutput::ChunkReturned (x)) => {
|
||||||
dbg! (x);
|
dbg! (x);
|
||||||
return;
|
return Ok (());
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -105,11 +104,11 @@ fn main () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match vm.step () {
|
match vm.step ()? {
|
||||||
None => (),
|
None => (),
|
||||||
Some (state::StepOutput::ChunkReturned (x)) => {
|
Some (state::StepOutput::ChunkReturned (x)) => {
|
||||||
dbg! (x);
|
dbg! (x);
|
||||||
return;
|
return Ok (());
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
169
src/state.rs
169
src/state.rs
|
@ -59,8 +59,21 @@ pub struct State <'a> {
|
||||||
upvalues: &'a [Value],
|
upvalues: &'a [Value],
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lw_print (l: &mut State) -> i32 {
|
fn lw_io_write (l: &mut State, num_args: usize) -> usize {
|
||||||
for i in 0..u8::try_from (l.get_top ()).unwrap () {
|
for i in 0..u8::try_from (num_args).unwrap () {
|
||||||
|
match l.reg (i) {
|
||||||
|
Value::Float (x) => print! ("{}", x),
|
||||||
|
Value::Integer (x) => print! ("{}", x),
|
||||||
|
Value::String (x) => print! ("{}", x),
|
||||||
|
_ => panic! ("Can't io.write this value"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: PUC Lua actually returns the file handle here.
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lw_print (l: &mut State, num_args: usize) -> usize {
|
||||||
|
for i in 0..u8::try_from (num_args).unwrap () {
|
||||||
let input = l.reg (i);
|
let input = l.reg (i);
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
|
@ -75,14 +88,66 @@ fn lw_print (l: &mut State) -> i32 {
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lw_sqrt (l: &mut State, num_args: usize) -> usize {
|
||||||
|
assert! (num_args >= 1, "math.sqrt needs 1 argument");
|
||||||
|
let input = l.reg (0).as_float ().unwrap ();
|
||||||
|
let output = input.sqrt ();
|
||||||
|
*l.reg_mut (0) = Value::from (output);
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
let num = l.reg (1).as_float ().unwrap ();
|
||||||
|
|
||||||
|
let output = format! ("{:0.9}", num);
|
||||||
|
|
||||||
|
*l.reg_mut (0) = Value::from (output);
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lw_tonumber (l: &mut State, num_args: usize) -> usize {
|
||||||
|
assert_eq! (num_args, 1, "tonumber only implemented for 1 argument");
|
||||||
|
let output = match l.reg (0) {
|
||||||
|
Value::Float (x) => Value::Float (*x),
|
||||||
|
Value::Integer (x) => Value::Integer (*x),
|
||||||
|
Value::String (x) => {
|
||||||
|
if let Ok (x) = str::parse::<i64> (x) {
|
||||||
|
Value::from (x)
|
||||||
|
}
|
||||||
|
else if let Ok (x) = str::parse::<f64> (x) {
|
||||||
|
Value::from (x)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Value::Nil
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => Value::Nil,
|
||||||
|
};
|
||||||
|
*l.reg_mut (0) = output;
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
pub enum StepOutput {
|
pub enum StepOutput {
|
||||||
ChunkReturned (Vec <Value>),
|
ChunkReturned (Vec <Value>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive (Debug)]
|
||||||
|
pub struct StepError {
|
||||||
|
frame: StackFrame,
|
||||||
|
inst: Instruction,
|
||||||
|
msg: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
impl <'a> State <'a> {
|
impl <'a> State <'a> {
|
||||||
pub fn new (chunk: &'a Chunk, upvalues: &'a [Value]) -> Self {
|
pub fn new (chunk: &'a Chunk, upvalues: &'a [Value]) -> Self {
|
||||||
Self {
|
Self {
|
||||||
registers: vec! [Value::Nil; 16],
|
// 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,
|
top: 0,
|
||||||
stack: vec! [
|
stack: vec! [
|
||||||
StackFrame {
|
StackFrame {
|
||||||
|
@ -108,9 +173,25 @@ impl <'a> State <'a> {
|
||||||
let arg = args.map (|s| Value::from (s)).enumerate ();
|
let arg = args.map (|s| Value::from (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), v)));
|
||||||
|
|
||||||
|
let io = [
|
||||||
|
("write", Value::RsFunc (lw_io_write)),
|
||||||
|
].into_iter ().map (|(k, v)| (k.to_string (), v));
|
||||||
|
|
||||||
|
let math = [
|
||||||
|
("sqrt", Value::RsFunc (lw_sqrt)),
|
||||||
|
].into_iter ().map (|(k, v)| (k.to_string (), v));
|
||||||
|
|
||||||
|
let string = [
|
||||||
|
("format", Value::RsFunc (lw_string_format)),
|
||||||
|
].into_iter ().map (|(k, v)| (k.to_string (), v));
|
||||||
|
|
||||||
let env = [
|
let env = [
|
||||||
("arg", arg),
|
("arg", arg),
|
||||||
|
("io", Value::from_iter (io.into_iter ())),
|
||||||
|
("math", Value::from_iter (math.into_iter ())),
|
||||||
("print", Value::RsFunc (lw_print)),
|
("print", Value::RsFunc (lw_print)),
|
||||||
|
("string", Value::from_iter (string.into_iter ())),
|
||||||
|
("tonumber", Value::RsFunc (lw_tonumber)),
|
||||||
].into_iter ().map (|(k, v)| (k.to_string (), v));
|
].into_iter ().map (|(k, v)| (k.to_string (), v));
|
||||||
|
|
||||||
vec! [
|
vec! [
|
||||||
|
@ -140,7 +221,17 @@ impl <'a> State <'a> {
|
||||||
self.top - self.stack.last ().unwrap ().register_offset
|
self.top - self.stack.last ().unwrap ().register_offset
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step (&mut self) -> Option <StepOutput> {
|
fn make_step_error (&self, msg: &'static str, inst: &Instruction) -> StepError
|
||||||
|
{
|
||||||
|
StepError {
|
||||||
|
frame: self.stack.last ().unwrap ().clone (),
|
||||||
|
inst: inst.clone (),
|
||||||
|
msg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn step (&mut self) -> Result <Option <StepOutput>, StepError>
|
||||||
|
{
|
||||||
let chunk = self.chunk;
|
let chunk = self.chunk;
|
||||||
self.step_count += 1;
|
self.step_count += 1;
|
||||||
|
|
||||||
|
@ -161,6 +252,10 @@ impl <'a> State <'a> {
|
||||||
// let r = &mut self.registers [frame.register_offset..];
|
// let r = &mut self.registers [frame.register_offset..];
|
||||||
let k = &block.constants;
|
let k = &block.constants;
|
||||||
|
|
||||||
|
let make_step_error = |msg| {
|
||||||
|
self.make_step_error (msg, instruction)
|
||||||
|
};
|
||||||
|
|
||||||
match instruction {
|
match instruction {
|
||||||
Instruction::Add (a, b, c) => {
|
Instruction::Add (a, b, c) => {
|
||||||
let v_b = self.reg (*b);
|
let v_b = self.reg (*b);
|
||||||
|
@ -192,7 +287,7 @@ impl <'a> State <'a> {
|
||||||
|
|
||||||
*self.reg_mut (*a) = x;
|
*self.reg_mut (*a) = x;
|
||||||
},
|
},
|
||||||
Instruction::Call (a, b, _c) => {
|
Instruction::Call (a, b, c) => {
|
||||||
let b = usize::from (*b);
|
let b = usize::from (*b);
|
||||||
|
|
||||||
// Take arguments from registers [a + 1, a + b)
|
// Take arguments from registers [a + 1, a + b)
|
||||||
|
@ -230,7 +325,7 @@ impl <'a> State <'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip the PC increment at the bottom of the loop
|
// Skip the PC increment at the bottom of the loop
|
||||||
return None;
|
return Ok (None);
|
||||||
},
|
},
|
||||||
Value::RsFunc (x) => {
|
Value::RsFunc (x) => {
|
||||||
let current_frame = self.stack.last ().unwrap ();
|
let current_frame = self.stack.last ().unwrap ();
|
||||||
|
@ -242,21 +337,30 @@ impl <'a> State <'a> {
|
||||||
});
|
});
|
||||||
|
|
||||||
// No clue what the '1' is doing here
|
// No clue what the '1' is doing here
|
||||||
self.top = new_offset + b - 1;
|
let b = if b == 0 {
|
||||||
|
self.top - *a as usize
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
b
|
||||||
|
};
|
||||||
|
|
||||||
// Call
|
// Call
|
||||||
let num_results = x (self);
|
let num_results = x (self, b - 1);
|
||||||
|
|
||||||
let popped_frame = self.stack.pop ().unwrap ();
|
let popped_frame = self.stack.pop ().unwrap ();
|
||||||
let offset = popped_frame.register_offset - 1;
|
let offset = popped_frame.register_offset - 1;
|
||||||
|
|
||||||
for i in (offset)..(offset + usize::try_from (num_results).unwrap ()) {
|
for i in (offset)..(offset + usize::try_from (num_results).unwrap ()) {
|
||||||
self.registers [i] = self.registers [i + 1 + usize::from (*a)].take ();
|
self.registers [i] = self.registers [i + 1].take ();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up top for the next call
|
||||||
|
if *c == 0 {
|
||||||
|
self.top = popped_frame.register_offset - 1 + num_results;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let stack = &self.stack;
|
Err (make_step_error ("Cannot call value"))?;
|
||||||
panic! ("Cannot call value {a:?}. backtrace: {stack:?}");
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -293,7 +397,9 @@ impl <'a> State <'a> {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let v_b = v_b.as_float ().unwrap_or_else (|| panic! ("{v_b}"));
|
let v_b = v_b.as_float ().unwrap_or_else (|| panic! ("{v_b}"));
|
||||||
let v_c = v_c.as_float ().unwrap_or_else (|| panic! ("{v_c}"));
|
|
||||||
|
let v_c = v_c.as_float ().ok_or_else (|| make_step_error ("C must be a number"))?;
|
||||||
|
|
||||||
Value::from (v_b / v_c)
|
Value::from (v_b / v_c)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -341,9 +447,9 @@ impl <'a> State <'a> {
|
||||||
},
|
},
|
||||||
Instruction::GetField (a, b, c) => {
|
Instruction::GetField (a, b, c) => {
|
||||||
let t = match self.reg (*b) {
|
let t = match self.reg (*b) {
|
||||||
Value::Nil => panic! ("R[B] must not be nil {}:{}", frame.block_idx, frame.program_counter),
|
Value::Nil => Err (make_step_error ("R[B] must not be nil"))?,
|
||||||
Value::Table (t) => t,
|
Value::Table (t) => t,
|
||||||
_ => panic! ("R[B] must be a table"),
|
_ => Err (make_step_error ("R[B] must be a table"))?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let key = match &k [usize::from (*c)] {
|
let key = match &k [usize::from (*c)] {
|
||||||
|
@ -428,11 +534,17 @@ impl <'a> State <'a> {
|
||||||
Instruction::Jmp (s_j) => next_pc += s_j,
|
Instruction::Jmp (s_j) => next_pc += s_j,
|
||||||
Instruction::Len (a, b) => {
|
Instruction::Len (a, b) => {
|
||||||
let len = match self.reg (*b) {
|
let len = match self.reg (*b) {
|
||||||
Value::String (s) => s.len (),
|
Value::BogusClosure (_) => Err (make_step_error ("attempt to get length of a function value"))?,
|
||||||
_ => unimplemented!(),
|
Value::Boolean (_) => Err (make_step_error ("attempt to get length of a boolean value"))?,
|
||||||
|
Value::Float (_) => 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::RsFunc (_) => Err (make_step_error ("attempt to get length of a function value"))?,
|
||||||
|
Value::String (s) => s.len ().into (),
|
||||||
|
Value::Table (t) => t.borrow ().length ().into (),
|
||||||
};
|
};
|
||||||
|
|
||||||
*self.reg_mut (*a) = len.into ();
|
*self.reg_mut (*a) = len;
|
||||||
}
|
}
|
||||||
Instruction::LoadF (a, sbx) => {
|
Instruction::LoadF (a, sbx) => {
|
||||||
*self.reg_mut (*a) = Value::Float (*sbx as f64);
|
*self.reg_mut (*a) = Value::Float (*sbx as f64);
|
||||||
|
@ -570,15 +682,18 @@ impl <'a> State <'a> {
|
||||||
for i in (offset)..(offset - 1 + b) {
|
for i in (offset)..(offset - 1 + b) {
|
||||||
self.registers [i] = self.registers [i + 1 + a].take ();
|
self.registers [i] = self.registers [i + 1 + a].take ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.top = popped_frame.register_offset - 1 + b - 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Return from the entire program
|
// Return from the entire program
|
||||||
return Some (StepOutput::ChunkReturned (self.registers [a..(a + b - 1)].to_vec()));
|
return Ok (Some (StepOutput::ChunkReturned (self.registers [a..(a + b - 1)].to_vec())));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Instruction::Return0 => {
|
Instruction::Return0 => {
|
||||||
self.stack.pop ();
|
let popped_frame = self.stack.pop ().unwrap ();
|
||||||
next_pc = self.stack.last ().unwrap ().program_counter;
|
next_pc = self.stack.last ().unwrap ().program_counter;
|
||||||
|
self.top = popped_frame.register_offset - 1 + 0;
|
||||||
},
|
},
|
||||||
Instruction::Return1 (a) => {
|
Instruction::Return1 (a) => {
|
||||||
let a = usize::try_from (*a).unwrap ();
|
let a = usize::try_from (*a).unwrap ();
|
||||||
|
@ -602,6 +717,8 @@ impl <'a> State <'a> {
|
||||||
// Shift output register down
|
// Shift output register down
|
||||||
let offset = popped_frame.register_offset;
|
let offset = popped_frame.register_offset;
|
||||||
self.registers [offset - 1] = self.registers [offset + a].take ();
|
self.registers [offset - 1] = self.registers [offset + a].take ();
|
||||||
|
|
||||||
|
self.top = popped_frame.register_offset - 1 + 1;
|
||||||
},
|
},
|
||||||
Instruction::SetField (a, b, c, k_flag) => {
|
Instruction::SetField (a, b, c, k_flag) => {
|
||||||
let value = if *k_flag {
|
let value = if *k_flag {
|
||||||
|
@ -697,10 +814,10 @@ impl <'a> State <'a> {
|
||||||
frame.block_idx = closure.idx;
|
frame.block_idx = closure.idx;
|
||||||
|
|
||||||
// Skip the PC increment
|
// Skip the PC increment
|
||||||
return None;
|
return Ok (None);
|
||||||
},
|
},
|
||||||
Instruction::Test (a, _k) => {
|
Instruction::Test (a, k) => {
|
||||||
if self.reg (*a).is_truthy() {
|
if self.reg (*a).is_truthy() != *k {
|
||||||
next_pc += 1;
|
next_pc += 1;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -727,11 +844,11 @@ impl <'a> State <'a> {
|
||||||
frame.program_counter = next_pc;
|
frame.program_counter = next_pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
Ok (None)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_chunk (&mut self, breakpoints: &[Breakpoint])
|
pub fn execute_chunk (&mut self, breakpoints: &[Breakpoint])
|
||||||
-> Vec <Value> {
|
-> Result <Vec <Value>, StepError> {
|
||||||
let max_iters = 2000;
|
let max_iters = 2000;
|
||||||
|
|
||||||
for _ in 0..max_iters {
|
for _ in 0..max_iters {
|
||||||
|
@ -739,9 +856,9 @@ impl <'a> State <'a> {
|
||||||
dbg! (&self);
|
dbg! (&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.step () {
|
match self.step ()? {
|
||||||
None => (),
|
None => (),
|
||||||
Some (StepOutput::ChunkReturned (x)) => return x,
|
Some (StepOutput::ChunkReturned (x)) => return Ok (x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
46
src/tests.rs
46
src/tests.rs
|
@ -25,7 +25,7 @@ fn calculate_hash<T: Hash>(t: &T) -> u64 {
|
||||||
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 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 (chunk, &upvalues);
|
||||||
vm.execute_chunk (&[])
|
vm.execute_chunk (&[]).unwrap ()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes arguments and Lua bytecode, loads it, runs it,
|
/// Takes arguments and Lua bytecode, loads it, runs it,
|
||||||
|
@ -120,7 +120,7 @@ fn bools () {
|
||||||
|
|
||||||
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
||||||
let mut vm = State::new (&chunk, &upvalues);
|
let mut vm = State::new (&chunk, &upvalues);
|
||||||
let actual = vm.execute_chunk (&[]);
|
let actual = vm.execute_chunk (&[]).unwrap ();
|
||||||
assert_eq! (actual, expected);
|
assert_eq! (actual, expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,7 +176,7 @@ fn floats () {
|
||||||
let expected: Vec <Value> = expected;
|
let expected: Vec <Value> = expected;
|
||||||
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
||||||
let mut vm = State::new (&chunk, &upvalues);
|
let mut vm = State::new (&chunk, &upvalues);
|
||||||
let actual = vm.execute_chunk (&[]);
|
let actual = vm.execute_chunk (&[]).unwrap ();
|
||||||
|
|
||||||
assert_eq! (actual, expected);
|
assert_eq! (actual, expected);
|
||||||
}
|
}
|
||||||
|
@ -198,7 +198,7 @@ fn fma () {
|
||||||
let expected: Vec <Value> = expected;
|
let expected: Vec <Value> = expected;
|
||||||
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
let upvalues = State::upvalues_from_args (arg.into_iter ().map (|s| s.to_string ()));
|
||||||
let mut vm = State::new (&chunk, &upvalues);
|
let mut vm = State::new (&chunk, &upvalues);
|
||||||
let actual = vm.execute_chunk (&[]);
|
let actual = vm.execute_chunk (&[]).unwrap ();
|
||||||
|
|
||||||
assert_eq! (actual, expected);
|
assert_eq! (actual, expected);
|
||||||
}
|
}
|
||||||
|
@ -368,14 +368,50 @@ fn value_size () {
|
||||||
// with 64-bit floats
|
// with 64-bit floats
|
||||||
//
|
//
|
||||||
// It would be nice if LunarWaveVM is the same or better.
|
// It would be nice if LunarWaveVM is the same or better.
|
||||||
// There are some exploratory things in this test, too
|
// I'm also just checking a bunch of my assumptions about how
|
||||||
|
// Rust organizes and sizes different types
|
||||||
|
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Make sure that Rx / Box are both pointers that hide the size
|
||||||
|
// of big types
|
||||||
|
|
||||||
assert! (size_of::<Box <()>> () <= 8);
|
assert! (size_of::<Box <()>> () <= 8);
|
||||||
assert! (size_of::<std::rc::Rc <()>> () <= 8);
|
assert! (size_of::<std::rc::Rc <()>> () <= 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Make sure LWVM's Values are 16 bytes or smaller.
|
||||||
|
// Because types are usually aligned to their size, f64s
|
||||||
|
// are supposed to be aligned to 8 bytes. So even an `Option <f64>`
|
||||||
|
// uses 8 bytes to say "Some" or "None".
|
||||||
|
// I could _maybe_ fudge this somehow but it's fine to start with.
|
||||||
|
|
||||||
let sz = size_of::<crate::value::Value> ();
|
let sz = size_of::<crate::value::Value> ();
|
||||||
let expected = 16;
|
let expected = 16;
|
||||||
assert! (sz <= expected, "{sz} > {expected}");
|
assert! (sz <= expected, "{sz} > {expected}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// All these are 8 bytes for the same reason Value is 16 bytes.
|
||||||
|
// Luckily Rust doesn't seem to stack the 4-byte overhead
|
||||||
|
// of Result and Option.
|
||||||
|
|
||||||
|
let sz = size_of::<(i32, i32)> ();
|
||||||
|
let expected = 8;
|
||||||
|
assert! (sz == expected, "{sz} != {expected}");
|
||||||
|
|
||||||
|
let sz = size_of::<Option <i32>> ();
|
||||||
|
let expected = 8;
|
||||||
|
assert! (sz == expected, "{sz} != {expected}");
|
||||||
|
|
||||||
|
let sz = size_of::<Result <i32, i32>> ();
|
||||||
|
let expected = 8;
|
||||||
|
assert! (sz == expected, "{sz} != {expected}");
|
||||||
|
|
||||||
|
let sz = size_of::<Result <Option <i32>, i32>> ();
|
||||||
|
let expected = 8;
|
||||||
|
assert! (sz == expected, "{sz} != {expected}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
18
src/value.rs
18
src/value.rs
|
@ -26,7 +26,7 @@ pub enum Value {
|
||||||
Float (f64),
|
Float (f64),
|
||||||
|
|
||||||
Integer (i64),
|
Integer (i64),
|
||||||
RsFunc (fn (&mut crate::state::State) -> i32),
|
RsFunc (fn (&mut crate::state::State, usize) -> usize),
|
||||||
String (Rc <String>),
|
String (Rc <String>),
|
||||||
Table (Rc <RefCell <Table>>),
|
Table (Rc <RefCell <Table>>),
|
||||||
|
|
||||||
|
@ -207,6 +207,13 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_str (&self) -> Option <&str> {
|
||||||
|
match self {
|
||||||
|
Self::String (x) => Some (x.as_str ()),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_table (&self) -> Option <&Rc <RefCell <Table>>> {
|
pub fn as_table (&self) -> Option <&Rc <RefCell <Table>>> {
|
||||||
match self {
|
match self {
|
||||||
Self::Table (t) => Some (t),
|
Self::Table (t) => Some (t),
|
||||||
|
@ -265,6 +272,15 @@ impl Table {
|
||||||
) {
|
) {
|
||||||
self.insert_inner (a.into (), b.into ())
|
self.insert_inner (a.into (), b.into ())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn length (&self) -> i64 {
|
||||||
|
for i in 1..i64::MAX {
|
||||||
|
if self.get (i) == Value::Nil {
|
||||||
|
return i - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromIterator <(Value, Value)> for Table {
|
impl FromIterator <(Value, Value)> for Table {
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
print ()
|
||||||
|
print ("asdf")
|
||||||
|
print (math.sqrt (16))
|
||||||
|
print (math.sqrt (16.0))
|
||||||
|
|
||||||
|
local N = tonumber (arg and arg [1]) or 1000
|
||||||
|
print (N)
|
Loading…
Reference in New Issue