♻️ refactor
parent
b07de4810d
commit
8c70469276
213
src/main.rs
213
src/main.rs
|
@ -1,3 +1,5 @@
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
enum Instruction {
|
enum Instruction {
|
||||||
VarArgPrep (i32),
|
VarArgPrep (i32),
|
||||||
GetTabUp (u8, u8, u8),
|
GetTabUp (u8, u8, u8),
|
||||||
|
@ -21,7 +23,8 @@ enum Value {
|
||||||
// These are all bogus, I haven't figured out how to implement
|
// These are all bogus, I haven't figured out how to implement
|
||||||
// tables and function pointers yet
|
// tables and function pointers yet
|
||||||
|
|
||||||
BogusArg,
|
BogusArg (Vec <String>),
|
||||||
|
BogusEnv (BTreeMap <String, Value>),
|
||||||
BogusPrint,
|
BogusPrint,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +51,115 @@ struct Chunk {
|
||||||
constants: Vec <Value>,
|
constants: Vec <Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct VirtualMachine {
|
||||||
|
registers: Vec <Value>,
|
||||||
|
|
||||||
|
// i32 makes it a little easier to implement jumps
|
||||||
|
program_counter: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for VirtualMachine {
|
||||||
|
fn default () -> Self {
|
||||||
|
Self {
|
||||||
|
registers: vec! [Value::Nil; 256],
|
||||||
|
program_counter: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtualMachine {
|
||||||
|
fn execute_chunk (&mut self, chunk: &Chunk, upvalues: &Vec <Value>) {
|
||||||
|
let max_iters = 2000;
|
||||||
|
|
||||||
|
for _ in 0..max_iters {
|
||||||
|
let instruction = chunk.instructions.get (usize::try_from (self.program_counter).unwrap ()).unwrap ();
|
||||||
|
|
||||||
|
let r = &mut self.registers;
|
||||||
|
let k = &chunk.constants;
|
||||||
|
|
||||||
|
match instruction {
|
||||||
|
Instruction::Call (a, b, c) => {
|
||||||
|
// Take arguments from registers [a + 1, a + b)
|
||||||
|
// Call the function in register [a]
|
||||||
|
// Return values in registers [a, a + c - 1)
|
||||||
|
//
|
||||||
|
// That is, call a with b - 1 arguments and expect c returns
|
||||||
|
//
|
||||||
|
// e.g. CALL 0 2 1 mean "Call 0 with 1 argument, return 1 value", like for printing a constant
|
||||||
|
|
||||||
|
// TODO: Only implement printing constants for now
|
||||||
|
|
||||||
|
let a = usize::try_from (*a).unwrap ();
|
||||||
|
|
||||||
|
assert_eq! (*b, 2);
|
||||||
|
assert_eq! (*c, 1);
|
||||||
|
|
||||||
|
println! ("{:?}", r [a + 1]);
|
||||||
|
},
|
||||||
|
Instruction::EqK (a, b, c_k) => {
|
||||||
|
let a = usize::try_from (*a).unwrap ();
|
||||||
|
let b = usize::try_from (*b).unwrap ();
|
||||||
|
|
||||||
|
let equal = r [a] == k [b];
|
||||||
|
|
||||||
|
match (equal, c_k) {
|
||||||
|
(true, 0) => self.program_counter += 1,
|
||||||
|
(false, 1) => self.program_counter += 1,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Instruction::GetTabUp (a, b, c) => {
|
||||||
|
let a = usize::try_from (*a).unwrap ();
|
||||||
|
let b = usize::try_from (*b).unwrap ();
|
||||||
|
let c = usize::try_from (*c).unwrap ();
|
||||||
|
|
||||||
|
let env = match upvalues.get (b).unwrap () {
|
||||||
|
Value::BogusEnv (x) => x,
|
||||||
|
_ => panic! ("Only allowed upvalue is BogusEnv"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let key = match k.get (c).unwrap () {
|
||||||
|
Value::String (s) => s,
|
||||||
|
_ => panic! ("GetTabUp only supports string keys"),
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
let value = env.get (key).unwrap ();
|
||||||
|
|
||||||
|
r [a] = value.clone ();
|
||||||
|
},
|
||||||
|
Instruction::GetI (a, b, c) => {
|
||||||
|
let a = usize::try_from (*a).unwrap ();
|
||||||
|
let b = usize::try_from (*b).unwrap ();
|
||||||
|
let c = usize::try_from (*c).unwrap ();
|
||||||
|
|
||||||
|
let table = r.get (b).unwrap ();
|
||||||
|
let value = match table {
|
||||||
|
Value::BogusArg (arg) => arg.get (c).unwrap ().as_str().into (),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
r [a] = value;
|
||||||
|
},
|
||||||
|
Instruction::Jmp (sJ) => self.program_counter += sJ,
|
||||||
|
Instruction::LoadK (a, bx) => {
|
||||||
|
let a = usize::try_from (*a).unwrap ();
|
||||||
|
let bx = usize::try_from (*bx).unwrap ();
|
||||||
|
|
||||||
|
r [a] = k [bx].clone ();
|
||||||
|
},
|
||||||
|
Instruction::Return (_a, _b, _c) => {
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
Instruction::VarArgPrep (_) => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.program_counter += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let arg: Vec <_> = std::env::args ().collect ();
|
let arg: Vec <_> = std::env::args ().collect ();
|
||||||
|
|
||||||
|
@ -76,96 +188,15 @@ fn main() {
|
||||||
].into_iter ().map (|s| Value::from (s)).collect (),
|
].into_iter ().map (|s| Value::from (s)).collect (),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut registers = vec! [Value::default (); 256];
|
let env = BTreeMap::from_iter ([
|
||||||
|
("arg", Value::BogusArg (arg.clone ())),
|
||||||
|
("print", Value::BogusPrint),
|
||||||
|
].map (|(k, v)| (k.to_string (), v)).into_iter ());
|
||||||
|
|
||||||
|
let upvalues = vec! [
|
||||||
|
Value::BogusEnv (env),
|
||||||
|
];
|
||||||
|
|
||||||
let mut program_counter = 0i32;
|
let mut vm = VirtualMachine::default ();
|
||||||
let max_iters = 2000;
|
vm.execute_chunk (&chunk, &upvalues);
|
||||||
|
|
||||||
for _ in 0..max_iters {
|
|
||||||
let instruction = chunk.instructions.get (usize::try_from (program_counter).unwrap ()).unwrap ();
|
|
||||||
|
|
||||||
let r = &mut registers;
|
|
||||||
let k = &chunk.constants;
|
|
||||||
|
|
||||||
match instruction {
|
|
||||||
Instruction::Call (a, b, c) => {
|
|
||||||
// Take arguments from registers [a + 1, a + b)
|
|
||||||
// Call the function in register [a]
|
|
||||||
// Return values in registers [a, a + c - 1)
|
|
||||||
//
|
|
||||||
// That is, call a with b - 1 arguments and expect c returns
|
|
||||||
//
|
|
||||||
// e.g. CALL 0 2 1 mean "Call 0 with 1 argument, return 1 value", like for printing a constant
|
|
||||||
|
|
||||||
// TODO: Only implement printing constants for now
|
|
||||||
|
|
||||||
let a = usize::try_from (*a).unwrap ();
|
|
||||||
|
|
||||||
assert_eq! (*b, 2);
|
|
||||||
assert_eq! (*c, 1);
|
|
||||||
|
|
||||||
println! ("{:?}", r [a + 1]);
|
|
||||||
},
|
|
||||||
Instruction::EqK (a, b, c_k) => {
|
|
||||||
let a = usize::try_from (*a).unwrap ();
|
|
||||||
let b = usize::try_from (*b).unwrap ();
|
|
||||||
|
|
||||||
let equal = r [a] == k [b];
|
|
||||||
|
|
||||||
match (equal, c_k) {
|
|
||||||
(true, 0) => program_counter += 1,
|
|
||||||
(false, 1) => program_counter += 1,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Instruction::GetTabUp (a, b, c) => {
|
|
||||||
let a = usize::try_from (*a).unwrap ();
|
|
||||||
let b = usize::try_from (*b).unwrap ();
|
|
||||||
let c = usize::try_from (*c).unwrap ();
|
|
||||||
|
|
||||||
// Only supported upvalue is `_ENV`
|
|
||||||
assert_eq! (b, 0);
|
|
||||||
|
|
||||||
let key = k.get (c).unwrap ();
|
|
||||||
let value = match key {
|
|
||||||
Value::String (s) => match s.as_str() {
|
|
||||||
"arg" => Value::BogusArg,
|
|
||||||
"print" => Value::BogusPrint,
|
|
||||||
_ => panic! ("key not in _ENV upvalue"),
|
|
||||||
},
|
|
||||||
_ => unimplemented!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
r [a] = value;
|
|
||||||
},
|
|
||||||
Instruction::GetI (a, b, c) => {
|
|
||||||
let a = usize::try_from (*a).unwrap ();
|
|
||||||
let b = usize::try_from (*b).unwrap ();
|
|
||||||
let c = usize::try_from (*c).unwrap ();
|
|
||||||
|
|
||||||
let table = r.get (b).unwrap ();
|
|
||||||
let value = match table {
|
|
||||||
Value::BogusArg => arg.get (c).unwrap ().as_str().into (),
|
|
||||||
_ => unimplemented!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
r [a] = value;
|
|
||||||
},
|
|
||||||
Instruction::Jmp (sJ) => program_counter += sJ,
|
|
||||||
Instruction::LoadK (a, bx) => {
|
|
||||||
let a = usize::try_from (*a).unwrap ();
|
|
||||||
let bx = usize::try_from (*bx).unwrap ();
|
|
||||||
|
|
||||||
r [a] = k [bx].clone ();
|
|
||||||
},
|
|
||||||
Instruction::Return (_a, _b, _c) => {
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
Instruction::VarArgPrep (_) => (),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
program_counter += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue