summaryrefslogtreecommitdiff
path: root/src/runtime.rs
diff options
context:
space:
mode:
authorJesse Morgan <jesse@jesterpm.net>2023-12-28 17:12:54 -0800
committerJesse Morgan <jesse@jesterpm.net>2023-12-28 17:12:54 -0800
commitd04b4dfa993681060d363b5ef994c1a635f3c1f9 (patch)
tree6d7ae65ed7c14e32df50858c0bc28bf4996cd730 /src/runtime.rs
Checkpoint
Diffstat (limited to 'src/runtime.rs')
-rw-r--r--src/runtime.rs671
1 files changed, 671 insertions, 0 deletions
diff --git a/src/runtime.rs b/src/runtime.rs
new file mode 100644
index 0000000..3954a80
--- /dev/null
+++ b/src/runtime.rs
@@ -0,0 +1,671 @@
+use std::{collections::LinkedList, cmp::Ordering, sync::Arc};
+
+use ion_rs::SymbolTable;
+
+use crate::ion::{IonReader, IonValue};
+
+
+
+
+#[repr(u8)]
+pub enum OpCode {
+
+ Jump = 0x01,
+
+ Dup2 = 0x03,
+
+ /// Push a typed Ion value onto the stack.
+ /// operands: T and L
+ /// length
+ /// repr
+ /// stack: [] -> [representation, length, T and L]
+ /// Note: lengths are reversed on the stack.
+ /// representations are not reversed.
+ TypePush = 0x10,
+
+ /// Pop a typed Ion value from the stack.
+ /// operands:
+ /// stack: [repr, length, T and L] -> []
+ TypePop = 0x11,
+
+ /// Load a typed Ion value from a variable onto the stack.
+ /// operands: variableId (VarUInt)
+ /// stack: [] -> [repr, length, T and L]
+ TypeLoad = 0x12,
+
+ /// Save a typed Ion value from the stack into a variable.
+ /// operands: variableId (VarUInt)
+ /// stack: [repr, length, T and L] -> []
+ TypeStore = 0x13,
+
+ /// Push the length of the type.
+ /// operands: variableId (VarUInt)
+ /// stack: [repr, length, T+L] -> [Int]
+ TypeLength = 0x14,
+
+ /// Append bytes onto the end of a typed sequence of bytes.
+ /// This operation is valid with string, clob, and blob types.
+ /// operands: variableId (VarUInt)
+ /// number of octets to push
+ /// stack: [repr, length, T+L, bytes] -> [repr, bytes, length', T+L']
+ BytesAppend = 0x15,
+
+ /// Append a typed value onto the end of a list or sexp.
+ /// operands: variableId (VarUInt)
+ /// stack: [repr, length, T+L] -> []
+ ListAppend = 0x16,
+
+ /// Push the element from a list or sexp onto the stack.
+ /// operands: variableId (VarUInt)
+ /// stack: [offset (Int)] -> [{next offset}, {value}]
+ ListLoad = 0x17,
+
+ /// Append a typed value onto the end of a struct.
+ /// operands: variableId (VarUInt)
+ /// stack: [{value}, {symbol}] -> []
+ FieldAppend = 0x18,
+
+ /// Prepare an iterator an iterator over the variable.
+ /// operands: variableId (VarUInt)
+ /// stack: [{offset (UInt)}] -> [{next offset}]
+ Iterator = 0x19,
+
+ /// Move an iterator to the next value.
+ /// operands: variableId (VarUInt)
+ /// stack: [{offset (UInt)}] -> [{next offset}]
+ Next = 0x1A,
+
+ /// Move an iterator to a nested value.
+ /// operands: variableId (VarUInt)
+ /// stack: [{offset (UInt)}] -> [{offset}, {next end}, {next offset}]
+ StepIn = 0x1B,
+
+ /// Move an iterator out of a nested value.
+ /// operands: variableId (VarUInt)
+ /// stack: [{offset (UInt)}] -> [{next offset}]
+ /// stack: [{{end}, {offset}] -> []
+ StepOut = 0x1C,
+
+ /// Copy a value onto the stack
+ /// operands: variableId (VarUInt)
+ /// stack: [{offset (UInt)}] -> [{offset}, {value}]
+ LoadValue = 0x1D,
+
+ /// Copy a struct field symbol onto the stack
+ /// operands: variableId (VarUInt)
+ /// stack: [{offset (UInt)}] -> [{offset}, {value}]
+ StructField = 0x1E,
+
+
+ /// Add two ints.
+ /// operands:
+ /// stack -> [{value1}, {value2} -> {result}]
+ IntAdd = 0x21,
+
+ /// Subtract value2 from value1.
+ /// operands:
+ /// stack -> [{value1}, {value2} -> {result}]
+ IntSub = 0x22,
+
+ /// Multiply value1 and value2.
+ /// operands:
+ /// stack -> [{value1}, {value2} -> {result}]
+ IntMul = 0x23,
+
+ /// Divide value2 by value1.
+ /// operands:
+ /// stack -> [{value1}, {value2} -> {result}]
+ IntDiv = 0x24,
+
+
+ /// Add two floats.
+ /// operands:
+ /// stack -> [{value1}, {value2} -> {result}]
+ FloatAdd = 0x41,
+
+ /// Subtract value2 from value1.
+ /// operands:
+ /// stack -> [{value1}, {value2} -> {result}]
+ FloatSub = 0x42,
+
+ /// Multiply value1 and value2.
+ /// operands:
+ /// stack -> [{value1}, {value2} -> {result}]
+ FloatMul = 0x43,
+
+ /// Divide value2 by value1.
+ /// operands:
+ /// stack -> [{value1}, {value2} -> {result}]
+ FloatDiv = 0x44,
+
+
+ /// Add two decimals.
+ /// operands:
+ /// stack -> [{value1}, {value2} -> {result}]
+ DecimalAdd = 0x51,
+
+ /// Subtract value2 from value1.
+ /// operands:
+ /// stack -> [{value1}, {value2} -> {result}]
+ DecimalSub = 0x52,
+
+ /// Multiply value1 and value2.
+ /// operands:
+ /// stack -> [{value1}, {value2} -> {result}]
+ DecimalMul = 0x53,
+
+ /// Divide value2 by value1.
+ /// operands:
+ /// stack -> [{value1}, {value2} -> {result}]
+ DecimalDiv = 0x54,
+
+
+ /// Jump to the given instruction if value == 0.
+ /// operands: instruction (VarUInt)
+ /// stack: [{value}] -> []
+ IfEq = 0x30,
+
+ /// Jump to the given instruction if values != 0.
+ /// operands: instruction (VarUInt)
+ /// stack: [{value}] -> []
+ IfNe = 0x31,
+
+ /// Jump to the given instruction if value >= 0
+ /// operands: instruction (VarUInt)
+ /// stack: [{value}] -> []
+ IfGe = 0x32,
+
+ /// Jump to the given instruction if value > 0.
+ /// operands: instruction (VarUInt)
+ /// stack: [{value}] -> []
+ IfGt = 0x33,
+
+ /// Jump to the given instruction if value <= 0.
+ /// operands: instruction (VarUInt)
+ /// stack: [{value}] -> []
+ IfLe = 0x34,
+
+ /// Jump to the given instruction if value < 0.
+ /// operands: instruction (VarUInt)
+ /// stack: [{value}] -> []
+ IfLt = 0x35,
+
+
+ /// Jump to the given instruction if two values are equal.
+ /// operands: instruction (VarUInt)
+ /// stack: [{value1}, {value2}] -> []
+ IfEq2 = 0x36,
+
+ /// Jump to the given instruction if two values are not equal.
+ /// operands: instruction (VarUInt)
+ /// stack: [{value1}, {value2}] -> []
+ IfNe2 = 0x37,
+
+ /// Jump to the given instruction if value2 >= value1.
+ /// operands: instruction (VarUInt)
+ /// stack: [{value1}, {value2}] -> []
+ IfGe2 = 0x38,
+
+ /// Jump to the given instruction if value2 > value1.
+ /// operands: instruction (VarUInt)
+ /// stack: [{value1}, {value2}] -> []
+ IfGt2 = 0x39,
+
+ /// Jump to the given instruction if value2 <= value1.
+ /// operands: instruction (VarUInt)
+ /// stack: [{value1}, {value2}] -> []
+ IfLe2 = 0x3a,
+
+ /// Jump to the given instruction if value2 < value1.
+ /// operands: instruction (VarUInt)
+ /// stack: [{value1}, {value2}] -> []
+ IfLt2 = 0x3b,
+
+}
+
+impl OpCode {
+ pub fn as_u8(&self) -> u8 {
+ unsafe { *(self as *const Self as *const u8) }
+ }
+}
+
+pub struct Runtime {
+ symbols: SymbolTable,
+ stacks: LinkedList<StackFrame>,
+ functions: Vec<Arc<Function>>,
+}
+
+pub enum ExecutionState {
+ Continue,
+ Halt,
+}
+
+pub struct Function {
+ bytecode: ByteCode,
+ arguments: u16,
+ variables: u16,
+ expected_stack_depth: usize,
+}
+
+pub type FnRef = usize;
+pub type ByteCode = Vec<u8>;
+
+struct Stack(Vec<u8>);
+
+pub struct StackFrame {
+ func: Arc<Function>,
+ pc: usize,
+ variables: Vec<IonValue>,
+ stack: Stack,
+}
+
+impl Runtime {
+ pub fn new() -> Runtime {
+ Runtime {
+ symbols: SymbolTable::new(),
+ stacks: LinkedList::new(),
+ functions: Vec::new(),
+ }
+ }
+
+ pub fn load_bytecode(&mut self, data: Vec<u8>) -> FnRef {
+ let arguments = (data[0] as u16) << 8 | (data[1] as u16);
+ let variables = (data[2] as u16) << 8 | (data[3] as u16);
+ let func = Function {
+ bytecode: data.into_iter().skip(4).collect(),
+ arguments,
+ variables,
+ expected_stack_depth: 32,
+ };
+ self.functions.push(func.into());
+ self.functions.len() - 1
+ }
+
+ pub fn invoke(&mut self, fnref: FnRef, arguments: Vec<IonValue>) -> Vec<IonValue> {
+ let func = self.functions.get(fnref).expect("Undefined function").clone();
+ let mut frame = StackFrame::new(func.clone(), arguments);
+ loop {
+ match self.execute(&mut frame) {
+ ExecutionState::Continue => (),
+ ExecutionState::Halt => {
+ frame.variables.truncate(func.arguments.into());
+ return frame.variables;
+ },
+ }
+ }
+ }
+
+ fn execute(&mut self, frame: &mut StackFrame) -> ExecutionState {
+ if frame.eof() {
+ return ExecutionState::Halt;
+ }
+
+ let op = frame.next_byte();
+ println!("PC: {:02X}, OP: {:02X}, Stack: {:02X}", frame.pc, op, frame.stack.0.len());
+ for x in &frame.stack.0 {
+ print!(" {x:02X}");
+ }
+ println!();
+
+ match op {
+ 0x01 => { // OpType::Jump
+ let pc = frame.next_varuint();
+ frame.jump(pc);
+ },
+ 0x03 => { // OpType::Dup2
+ let value1 = frame.stack.pop_value();
+ let value2 = frame.stack.pop_value();
+ frame.stack.push_value(&value2);
+ frame.stack.push_value(&value1);
+ frame.stack.push_value(&value2);
+ frame.stack.push_value(&value1);
+ },
+ 0x10 => { // OpType::Push
+ let buf = &frame.func.bytecode[frame.pc..];
+ let mut reader = IonReader::new(buf.iter().copied(), 0);
+ let value = reader.next_value();
+ frame.pc += reader.offset();
+ frame.stack.push_value(&value);
+ },
+ 0x11 => { // OpCode::TypePop
+ frame.stack.drop_value();
+ },
+ 0x12 => { // OpCode::TypeLoad
+ let var = frame.next_varuint();
+ frame.stack.push_value(&frame.variables[var]);
+ },
+ 0x13 => { // OpCode::TypeStore
+ let var = frame.next_varuint();
+ frame.variables[var] = frame.stack.pop_value();
+ },
+ 0x14 => { // OpCode::TypeLength
+ let var = frame.next_varuint();
+ let buf = frame.variables.get(var).expect("Undefined variable");
+ let len = buf.len();
+ frame.stack.push_usize(len);
+ },
+ 0x15 => todo!(), // OpCode::BytesAppend
+ 0x16 => todo!(), // OpCode::ListAppend
+ 0x17 => todo!(), // OpCode::ListLoad
+ 0x18 => todo!(), // OpCode::FieldAppend
+ 0x19 => { // OpCode::Iterate
+ let varid = frame.next_varuint();
+ let value = frame.variables
+ .get(varid)
+ .expect("Undefined variable");
+
+ let mut reader = value.reader();
+ let (ion_type, end) = reader.step_in();
+ let pos = reader.offset();
+
+ frame.stack.push_usize(ion_type as usize);
+ frame.stack.push_usize(end);
+ frame.stack.push_usize(pos);
+
+ },
+ 0x1A => { // OpCode::Next
+ let pos = frame.stack.pop_value().to_usize();
+ let len = frame.stack.pop_value().to_usize();
+ let ion_type = frame.stack.pop_value().to_usize();
+
+ let varid = frame.next_varuint();
+ let mut value = if ion_type == 0x0D {
+ frame.variables
+ .get(varid)
+ .expect("Undefined variable")
+ .struct_reader_at(pos)
+ } else {
+ frame.variables
+ .get(varid)
+ .expect("Undefined variable")
+ .reader_at(pos)
+ };
+ value.skip_value();
+ let next = value.offset();
+ frame.stack.push_usize(ion_type); // TODO peek
+ frame.stack.push_usize(len); // TODO peek
+ frame.stack.push_usize(next);
+ },
+ 0x1B => { // OpCode::StepIn
+ let pos = frame.stack.pop_value().to_usize();
+ let len = frame.stack.pop_value().to_usize();
+ let ion_type = frame.stack.pop_value().to_usize();
+
+ frame.stack.push_usize(ion_type); // TODO peek
+ frame.stack.push_usize(len); // TODO peek
+ frame.stack.push_usize(pos); // TODO peek
+
+ let varid = frame.next_varuint();
+ let mut value = if ion_type == 0x0D {
+ frame.variables
+ .get(varid)
+ .expect("Undefined variable")
+ .struct_reader_at(pos)
+ } else {
+ frame.variables
+ .get(varid)
+ .expect("Undefined variable")
+ .reader_at(pos)
+ };
+
+ let (next_type, next_end) = value.step_in();
+ let next_pos = value.offset();
+
+ frame.stack.push_usize(next_type as usize);
+ frame.stack.push_usize(next_end);
+ frame.stack.push_usize(next_pos);
+ },
+ 0x1C => { // OpCode::StepOut
+ frame.stack.drop_value();
+ frame.stack.drop_value();
+ frame.stack.drop_value();
+ },
+ 0x1D => { // OpCode::LoadValue
+ let pos = frame.stack.pop_value().to_usize();
+ let len = frame.stack.pop_value().to_usize();
+ let ion_type = frame.stack.pop_value().to_usize();
+
+ frame.stack.push_usize(ion_type); // TODO peek
+ frame.stack.push_usize(len); // TODO peek
+ frame.stack.push_usize(pos); // TODO peek
+
+ let varid = frame.next_varuint();
+ let mut value = if ion_type == 0x0D {
+ frame.variables
+ .get(varid)
+ .expect("Undefined variable")
+ .struct_reader_at(pos)
+ } else {
+ frame.variables
+ .get(varid)
+ .expect("Undefined variable")
+ .reader_at(pos)
+ };
+
+ let next_value = value.next_value();
+ frame.stack.push_value(&next_value);
+ },
+ 0x1E => { // OpCode::StructField
+ let pos = frame.stack.pop_value().to_usize();
+ let len = frame.stack.pop_value().to_usize();
+ let ion_type = frame.stack.pop_value().to_usize();
+
+ frame.stack.push_usize(ion_type); // TODO peek
+ frame.stack.push_usize(len); // TODO peek
+ frame.stack.push_usize(pos); // TODO peek
+
+ let varid = frame.next_varuint();
+ let mut value = if ion_type == 0x0D {
+ frame.variables
+ .get(varid)
+ .expect("Undefined variable")
+ .struct_reader_at(pos)
+ } else {
+ frame.variables
+ .get(varid)
+ .expect("Undefined variable")
+ .reader_at(pos)
+ };
+
+ value.skip_value();
+ if let Some(field_id) = value.field_id() {
+ frame.stack.push_symbol(field_id);
+ } else {
+ panic!("Not a struct");
+ }
+ },
+ 0x21 => self.int_add(frame), // OpCode::IntAdd
+ 0x22 => todo!(), // OpCode::IntSub
+ 0x23 => todo!(), // OpCode::IntMul
+ 0x24 => todo!(), // OpCode::IntDiv
+ 0x41 => todo!(), // OpCode::FloatAdd
+ 0x42 => todo!(), // OpCode::FloatSub
+ 0x43 => todo!(), // OpCode::FloatMul
+ 0x44 => todo!(), // OpCode::FloatDiv
+ 0x51 => todo!(), // OpCode::DecimalAdd
+ 0x52 => todo!(), // OpCode::DecimalSub
+ 0x53 => todo!(), // OpCode::DecimalMul
+ 0x54 => todo!(), // OpCode::DecimalDiv
+ 0x30 => todo!(), // OpCode::IfEq
+ 0x31 => todo!(), // OpCode::IfNe
+ 0x32 => todo!(), // OpCode::IfGe
+ 0x33 => todo!(), // OpCode::IfGt
+ 0x34 => todo!(), // OpCode::IfLe
+ 0x35 => todo!(), // OpCode::IfLt
+ 0x36 => { // OpCode::IfEq2
+ let next_pc = frame.next_varuint();
+ let value1 = frame.stack.pop_value();
+ let value2 = frame.stack.pop_value();
+ if value1 == value2 {
+ frame.pc = next_pc;
+ }
+ },
+ 0x37 => { // OpCode::IfNe2
+ let next_pc = frame.next_varuint();
+ let value1 = frame.stack.pop_value();
+ let value2 = frame.stack.pop_value();
+ if value1 != value2 {
+ frame.pc = next_pc;
+ }
+ },
+ 0x38 => { // OpCode::IfGe2
+ let next_pc = frame.next_varuint();
+ let value1 = frame.stack.pop_value();
+ let value2 = frame.stack.pop_value();
+ if value1 >= value2 {
+ frame.pc = next_pc;
+ }
+ },
+ 0x39 => { // OpCode::IfGt2
+ let next_pc = frame.next_varuint();
+ let value1 = frame.stack.pop_value();
+ let value2 = frame.stack.pop_value();
+ if value1 > value2 {
+ frame.pc = next_pc;
+ }
+ },
+ 0x3A => { // OpCode::IfLe2
+ let next_pc = frame.next_varuint();
+ let value1 = frame.stack.pop_value();
+ let value2 = frame.stack.pop_value();
+ if value1 <= value2 {
+ frame.pc = next_pc;
+ }
+ },
+ 0x3B => { // OpCode::IfLt2
+ let next_pc = frame.next_varuint();
+ let value1 = frame.stack.pop_value();
+ let value2 = frame.stack.pop_value();
+ if value1 < value2 {
+ frame.pc = next_pc;
+ }
+ },
+ _ => panic!("Invalid opcode {:02x} at {}", op, frame.pc),
+ }
+
+ ExecutionState::Continue
+ }
+
+ fn int_add(&self, frame: &mut StackFrame) {
+ todo!()
+ }
+}
+
+impl StackFrame {
+ pub fn new(func: Arc<Function>, arguments: Vec<IonValue>) -> StackFrame {
+ let stack = Stack::new(func.expected_stack_depth);
+ let mut frame = StackFrame {
+ func,
+ pc: 0,
+ variables: arguments,
+ stack,
+ };
+
+ for _ in frame.func.arguments..frame.func.variables {
+ frame.variables.push(IonValue::new_null());
+ }
+
+ frame
+ }
+
+ fn jump(&mut self, pc: usize) {
+ self.pc = pc;
+ }
+
+ fn eof(&self) -> bool {
+ self.pc >= self.func.bytecode.len()
+ }
+
+ fn next_byte(&mut self) -> u8 {
+ let b = self.func.bytecode[self.pc];
+ self.pc += 1;
+ b
+ }
+
+ fn next_bytes(&mut self, len: usize) -> &[u8] {
+ let end = self.pc + len;
+ let bytes = &self.func.bytecode[self.pc..end];
+ self.pc = end;
+ bytes
+ }
+
+ fn next_varuint(&mut self) -> usize {
+ let mut b: usize = self.next_byte().into();
+ let mut v = b & 0x7f;
+ while (b & 0x80) == 0 {
+ b = self.next_byte().into();
+ v <<= 7;
+ v |= b & 0x7f;
+ }
+ v
+ }
+}
+
+impl Stack {
+ pub fn new(expected_depth: usize) -> Stack {
+ Stack(Vec::with_capacity(expected_depth))
+ }
+
+ pub fn peak_reader(&self) -> IonReader<impl DoubleEndedIterator<Item = u8> + '_> {
+ IonReader::new(self.0.iter().copied().rev(), 0)
+ }
+
+ pub fn drop_value(&mut self) {
+ let mut it = IonReader::new(self.0.iter().copied().rev(), 0);
+ it.skip_value();
+ let offset = it.offset();
+ self.0.truncate(self.0.len() - offset);
+ }
+
+ pub fn pop_value(&mut self) -> IonValue {
+ let mut it = IonReader::new(self.0.iter().copied().rev(), 0);
+ let value = it.next_value();
+ let offset = it.offset();
+ self.0.truncate(self.0.len() - offset);
+ value
+ }
+
+ pub fn push_value(&mut self, value: &IonValue) {
+ for byte in value.bytes().rev() {
+ self.0.push(byte);
+ }
+ }
+
+ pub fn push_symbol(&mut self, value: usize) {
+ match value.cmp(&0) {
+ Ordering::Equal => self.0.push(0x70),
+ Ordering::Greater => {
+ let mut v = value;
+ let mut octets = 0;
+ while v != 0 {
+ self.0.push((v & 0xFF) as u8);
+ octets += 1;
+ v >>= 8;
+ }
+ let tl = 0x70 | octets;
+ self.0.push(tl);
+ },
+ Ordering::Less => (),
+ }
+ }
+
+ pub fn push_usize(&mut self, value: usize) {
+ match value.cmp(&0) {
+ Ordering::Equal => self.0.push(0x20),
+ Ordering::Greater => {
+ let mut v = value;
+ let mut octets = 0;
+ while v != 0 {
+ self.0.push((v & 0xFF) as u8);
+ octets += 1;
+ v >>= 8;
+ }
+ let tl = 0x20 | octets;
+ self.0.push(tl);
+ },
+ Ordering::Less => (),
+ }
+ }
+
+}
+