From 7889b2b2865134d5e067740b42de22a1693f2f0e Mon Sep 17 00:00:00 2001 From: MarvelousAnything <marvelousanything@gmail.com> Date: Tue, 1 Nov 2022 15:23:05 -0400 Subject: [PATCH] Did some work. I am committing to push to github. --- src/callable.rs | 44 +++++++- src/frame.rs | 82 ++++++++++++++- src/function.rs | 19 ++++ src/instruction.rs | 251 ++++++++++++++++++++++++++++++++++++++++++++- src/main.rs | 15 ++- src/native.rs | 97 +++++++++++++++++- src/stack.rs | 2 + src/state.rs | 66 +++++++++--- src/tvm.rs | 43 +++++++- 9 files changed, 586 insertions(+), 33 deletions(-) diff --git a/src/callable.rs b/src/callable.rs index 083d7eb..f66b0cf 100644 --- a/src/callable.rs +++ b/src/callable.rs @@ -1,26 +1,60 @@ use std::fmt::Debug; use crate::function::Function; use crate::native::NativeFunction; +use crate::stack::StackHolder; use crate::tvm::Tvm; #[derive(Debug, Clone, PartialEq, Eq)] pub enum Callable { Function(Function), - NativeFunction(NativeFunction), + Native(NativeFunction), +} + +impl Callable { + pub fn get_callable(id: i32) -> Self { + match id { + n if n < -111 => panic!("Invalid callable id: {}", n), + n @ -111..=-101 => Callable::Native(NativeFunction::get_native(n)), + n if n >= 0 => Callable::Function(Function::get_function(n as usize)), + _ => unreachable!(), + } + } + + pub fn get_id(&self) -> i32 { + match self { + Callable::Function(function) => function.id as i32, + Callable::Native(native) => native.get_id(), + } + } } pub trait Caller: Debug + Clone { - fn call(callable: Callable); + fn do_call(&mut self, callable: Callable); } impl Caller for Tvm { - fn call(callable: Callable) { + fn do_call(&mut self, callable: Callable) { match callable { Callable::Function(function) => { println!("Calling function: {:?}", function); + self.push(0); }, - Callable::NativeFunction(native_function) => { - println!("Calling native function: {:?}", native_function); + Callable::Native(native_function) => { + + match native_function { + NativeFunction::IPrint { .. } => { + let value = self.pop(); + println!("stdout: {}", value); + self.push(0); + }, + NativeFunction::Alloc { .. } => { + let size = self.pop(); + self.push(self.heap_size as i32); + self.heap_size += size as usize; + println!("Allocating {} bytes", size); + }, + _ => println!("Calling native function: {:?}", native_function), + } }, } } diff --git a/src/frame.rs b/src/frame.rs index e35e0ac..7dc701b 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -1,5 +1,7 @@ use crate::callable::Callable; use crate::instruction::Instruction; +use crate::state::{Stateful, StateResult}; +use crate::state::StateResult::Continue; use crate::tvm::Tvm; #[derive(Debug, Clone, PartialEq, Eq)] @@ -8,6 +10,14 @@ pub struct Frame { pub name: String, pub data: Vec<FrameData>, pub pc: usize, + pub previous_frame: Option<Box<Frame>>, + pub result: Option<StateResult>, +} + +impl Frame { + pub fn builder() -> FrameBuilder { + FrameBuilder::new() + } } #[derive(Debug, Clone, PartialEq, Eq)] @@ -18,12 +28,80 @@ pub enum FrameData { Primitive(i32), } +impl FrameData { + pub fn get_id(&self) -> i32 { + match self { + FrameData::Frame(frame) => frame.id as i32, + FrameData::Callable(callable, _) => callable.get_id(), + FrameData::Instruction(instruction, _) => instruction.get_op() as i32, + FrameData::Primitive(value) => *value, + } + } +} + pub trait FrameEvaluator { - fn eval_frame(&mut self, frame: Frame); + fn do_frame_eval(&mut self, frame: &Frame); } impl FrameEvaluator for Tvm { - fn eval_frame(&mut self, frame: Frame) { + fn do_frame_eval(&mut self, frame: &Frame) { println!("Evaluating frame: {:?}", frame); + if self.should_continue() { + self.eval(frame.clone()); + } + } +} + +#[derive(Default)] +pub struct FrameBuilder { + id: usize, + name: String, + data: Vec<FrameData>, +} + +impl FrameBuilder { + pub fn new() -> Self { + Self::default() + } + + pub fn id(mut self, id: usize) -> Self { + self.id = id; + self + } + + pub fn name(mut self, name: String) -> Self { + self.name = name; + self + } + + pub fn frame(mut self, frame: Frame) -> Self { + self.data.push(FrameData::Frame(frame)); + self + } + + pub fn callable(mut self, callable: i32, args: Vec<i32>) -> Self { + self.data.push(FrameData::Callable(Callable::get_callable(callable), args)); + self + } + + pub fn instruction(mut self, instruction: Instruction, args: Vec<i32>) -> Self { + self.data.push(FrameData::Instruction(instruction, args)); + self + } + + pub fn primitive(mut self, primitive: i32) -> Self { + self.data.push(FrameData::Primitive(primitive)); + self + } + + pub fn build(self) -> Frame { + Frame { + id: self.id, + name: self.name, + data: self.data, + pc: 0, + previous_frame: None, + result: None + } } } \ No newline at end of file diff --git a/src/function.rs b/src/function.rs index 1b31fba..b5eca56 100644 --- a/src/function.rs +++ b/src/function.rs @@ -7,4 +7,23 @@ pub struct Function { pub args: usize, pub locals: usize, pub frame: Frame, +} + +impl Function { + pub(crate) fn get_function(id: usize) -> Function { + Function { + id, + name: "test-function".to_string(), + args: 0, + locals: 0, + frame: Frame { + id: 0, + name: "test-frame".to_string(), + data: vec![], + pc: 0, + previous_frame: None, + result: None, + } + } + } } \ No newline at end of file diff --git a/src/instruction.rs b/src/instruction.rs index f343b05..305e98b 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -1,4 +1,11 @@ +use std::cell::RefCell; use std::fmt::Debug; +use std::rc::Rc; +use crate::callable::Callable; +use crate::frame::{Frame, FrameData}; +use crate::stack::StackHolder; +use crate::state::{Stateful, StateResult}; +use crate::state::StateResult::Exit; use crate::tvm::Tvm; #[derive(Debug, Clone, PartialEq, Eq)] @@ -33,12 +40,250 @@ pub enum Instruction { Unknown(u32), } +impl Instruction { + pub fn get_instruction(op_code: u32) -> Instruction { + match op_code { + 1 => Instruction::Push { + op: 1, + name: "push".to_string(), + num_operands: 0 + }, + 2 => Instruction::Fetch { + op: 2, + name: "fetch".to_string(), + num_operands: 0 + }, + 3 => Instruction::Store { + op: 3, + name: "store".to_string(), + num_operands: 0 + }, + 4 => Instruction::IF { + op: 4, + name: "if".to_string(), + num_operands: 0 + }, + 5 => Instruction::Loop { + op: 5, + name: "loop".to_string(), + num_operands: 0 + }, + 6 => Instruction::Break { + op: 6, + name: "break".to_string(), + num_operands: 0 + }, + 7 => Instruction::Return { + op: 7, + name: "return".to_string(), + num_operands: 0 + }, + 8 => Instruction::Call { + op: 8, + name: "call".to_string(), + num_operands: 0 + }, + 9 => Instruction::FPPlus { + op: 9, + name: "fp+".to_string(), + num_operands: 0 + }, + 10 => Instruction::Add { + op: 10, + name: "+".to_string(), + num_operands: 0 + }, + 11 => Instruction::Sub { + op: 11, + name: "-".to_string(), + num_operands: 0 + }, + 12 => Instruction::Mul { + op: 12, + name: "*".to_string(), + num_operands: 0 + }, + 13 => Instruction::Div { + op: 13, + name: "/".to_string(), + num_operands: 0 + }, + 14 => Instruction::Mod { + op: 14, + name: "%".to_string(), + num_operands: 0 + }, + 15 => Instruction::Not { + op: 15, + name: "!".to_string(), + num_operands: 0 + }, + 16 => Instruction::And { + op: 16, + name: "&".to_string(), + num_operands: 0 + }, + 17 => Instruction::OR { + op: 17, + name: "|".to_string(), + num_operands: 0 + }, + 18 => Instruction::Xor { + op: 18, + name: "^".to_string(), + num_operands: 0 + }, + 19 => Instruction::EQ { + op: 19, + name: "==".to_string(), + num_operands: 0 + }, + 20 => Instruction::Neq { + op: 20, + name: "!=".to_string(), + num_operands: 0 + }, + 21 => Instruction::LT { + op: 21, + name: "<".to_string(), + num_operands: 0 + }, + 22 => Instruction::Leq { + op: 22, + name: "<=".to_string(), + num_operands: 0 + }, + 23 => Instruction::GT { + op: 23, + name: ">".to_string(), + num_operands: 0 + }, + 24 => Instruction::Geq { + op: 24, + name: ">=".to_string(), + num_operands: 0 + }, + 25 => Instruction::Pop { + op: 25, + name: "pop".to_string(), + num_operands: 0 + }, + 26 => Instruction::LShift { + op: 26, + name: "<<".to_string(), + num_operands: 0 + }, + 27 => Instruction::RShift { + op: 27, + name: ">>".to_string(), + num_operands: 0 + }, + _ => Instruction::Unknown(op_code), + } + } + + pub fn get_op(&self) -> u32 { + match self { + Instruction::Push { op, .. } => *op, + Instruction::Fetch { op, .. } => *op, + Instruction::Store { op, .. } => *op, + Instruction::IF { op, .. } => *op, + Instruction::Loop { op, .. } => *op, + Instruction::Break { op, .. } => *op, + Instruction::Return { op, .. } => *op, + Instruction::Call { op, .. } => *op, + Instruction::FPPlus { op, .. } => *op, + Instruction::Add { op, .. } => *op, + Instruction::Sub { op, .. } => *op, + Instruction::Mul { op, .. } => *op, + Instruction::Div { op, .. } => *op, + Instruction::Mod { op, .. } => *op, + Instruction::Not { op, .. } => *op, + Instruction::And { op, .. } => *op, + Instruction::OR { op, .. } => *op, + Instruction::Xor { op, .. } => *op, + Instruction::EQ { op, .. } => *op, + Instruction::Neq { op, .. } => *op, + Instruction::LT { op, .. } => *op, + Instruction::Leq { op, .. } => *op, + Instruction::GT { op, .. } => *op, + Instruction::Geq { op, .. } => *op, + Instruction::Pop { op, .. } => *op, + Instruction::LShift { op, .. } => *op, + Instruction::RShift { op, .. } => *op, + Instruction::Unknown(op) => *op, + } + } +} + pub trait Evaluator: Debug + Clone { - fn eval(&mut self, instruction: Instruction); + fn do_eval(&mut self, frame: &mut Frame); } impl Evaluator for Tvm { - fn eval(&mut self, instruction: Instruction) { - println!("Evaluating instruction: {:?}", instruction); + fn do_eval(&mut self, frame: &mut Frame) { + if frame.pc >= frame.data.len() { + self.last_result = Some(Exit); + return; + } + let data = &frame.data.get(frame.pc).unwrap(); + frame.pc += 1; + match data { + FrameData::Frame(frame) => { + self.frame_eval(frame.clone()) + }, + FrameData::Instruction(instruction, ..) => { + println!("Evaluating instruction: {:?}", instruction); + match instruction { + Instruction::Push { .. } => { + let x = &frame.data[frame.pc].get_id(); + self.push(*x); + frame.pc += 1; + } + Instruction::Fetch { .. } => { + let index = self.pop(); + self.push(self.memory[index as usize]); + } + Instruction::Store { .. } => {} + Instruction::IF { .. } => {} + Instruction::Loop { .. } => {} + Instruction::Break { .. } => {} + Instruction::Return { .. } => {} + Instruction::Call { .. } => { + let id = &frame.data[frame.pc].get_id(); + let callable = Callable::get_callable(*id); + self.call(callable); + frame.pc += 1; + } + Instruction::FPPlus { .. } => {} + Instruction::Add { .. } => {} + Instruction::Sub { .. } => {} + Instruction::Mul { .. } => {} + Instruction::Div { .. } => {} + Instruction::Mod { .. } => {} + Instruction::Not { .. } => {} + Instruction::And { .. } => {} + Instruction::OR { .. } => {} + Instruction::Xor { .. } => {} + Instruction::EQ { .. } => {} + Instruction::Neq { .. } => {} + Instruction::LT { .. } => {} + Instruction::Leq { .. } => {} + Instruction::GT { .. } => {} + Instruction::Geq { .. } => {} + Instruction::Pop { .. } => {} + Instruction::LShift { .. } => {} + Instruction::RShift { .. } => {} + Instruction::Unknown(_) => {} + } + }, + FrameData::Callable(callable, ..) => { + println!("Evaluating callable: {:?}", callable); + self.call(callable.clone()) + }, + FrameData::Primitive(primitive) => { + println!("Primitive: {:?}", primitive); + } + } } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 8c18c67..04c7410 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use crate::state::Stateful; use crate::tvm::Tvm; mod tvm; @@ -12,6 +13,16 @@ mod function; mod instruction; fn main() { - let tvm = Tvm::default(); - println!("{:?}", tvm); + use std::io::{ stdin ,stdout, Write}; + let mut s = String::new(); + let _ = stdout().flush(); + let mut tvm = Tvm::default(); + tvm.start(); + loop { + stdin().read_line(&mut s).expect("Did not enter a correct string"); + if !s.is_empty() { + print!("Tick {}:\t\t", tvm.ticks); + tvm.tick(); + } + } } diff --git a/src/native.rs b/src/native.rs index 2a3aea5..2b0cdb5 100644 --- a/src/native.rs +++ b/src/native.rs @@ -1,6 +1,95 @@ #[derive(Debug, Clone, PartialEq, Eq)] -pub struct NativeFunction { - pub id: i32, - pub name: String, - pub args: usize, +pub enum NativeFunction { + IPrint { id: i32, name: String, args: u32 }, + SPrint { id: i32, name: String, args: u32 }, + IRead { id: i32, name: String, args: u32 }, + SRead { id: i32, name: String, args: u32 }, + NL { id: i32, name: String, args: u32 }, + Random { id: i32, name: String, args: u32 }, + Timer { id: i32, name: String, args: u32 }, + StopTimer { id: i32, name: String, args: u32 }, + Alloc { id: i32, name: String, args: u32 }, + Free { id: i32, name: String, args: u32 }, + I2S { id: i32, name: String, args: u32 }, + Unknown(i32), +} + +impl NativeFunction { + pub fn get_native(id: i32) -> Self { + match id { + -101 => NativeFunction::IPrint { + id, + name: "iprint".to_string(), + args: 1, + }, + -102 => NativeFunction::SPrint { + id, + name: "sprint".to_string(), + args: 1, + }, + -103 => NativeFunction::IRead { + id, + name: "iread".to_string(), + args: 1, + }, + -104 => NativeFunction::SRead { + id, + name: "sread".to_string(), + args: 2, + }, + -105 => NativeFunction::NL { + id, + name: "nl".to_string(), + args: 0, + }, + -106 => NativeFunction::Random { + id, + name: "random".to_string(), + args: 1, + }, + -107 => NativeFunction::Timer { + id, + name: "timer".to_string(), + args: 2, + }, + -108 => NativeFunction::StopTimer { + id, + name: "stoptimer".to_string(), + args: 1, + }, + -109 => NativeFunction::Alloc { + id, + name: "alloc".to_string(), + args: 1, + }, + -110 => NativeFunction::Free { + id, + name: "free".to_string(), + args: 1, + }, + -111 => NativeFunction::I2S { + id, + name: "i2s".to_string(), + args: 1, + }, + n => NativeFunction::Unknown(n), + } + } + + pub fn get_id(&self) -> i32 { + match self { + NativeFunction::IPrint { id, .. } => *id, + NativeFunction::SPrint { id, .. } => *id, + NativeFunction::IRead { id, .. } => *id, + NativeFunction::SRead { id, .. } => *id, + NativeFunction::NL { id, .. } => *id, + NativeFunction::Random { id, .. } => *id, + NativeFunction::Timer { id, .. } => *id, + NativeFunction::StopTimer { id, .. } => *id, + NativeFunction::Alloc { id, .. } => *id, + NativeFunction::Free { id, .. } => *id, + NativeFunction::I2S { id, .. } => *id, + NativeFunction::Unknown(n) => *n, + } + } } \ No newline at end of file diff --git a/src/stack.rs b/src/stack.rs index e3217f0..684c256 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -23,11 +23,13 @@ impl StackHolder for Tvm { } fn pop(&mut self) -> i32 { + println!("Popping from stack"); self.stack_pointer += 1; self.memory[self.stack_pointer] } fn push(&mut self, value: i32) { + println!("Pushing {} to stack", value); self.memory[self.stack_pointer] = value; self.stack_pointer -= 1; } diff --git a/src/state.rs b/src/state.rs index 5bd6d54..5e07364 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,4 +1,5 @@ use std::fmt::Debug; +use std::rc::Rc; use crate::callable::Callable; use crate::frame::Frame; use crate::tvm::Tvm; @@ -12,6 +13,7 @@ pub enum StateResult { Return(i32), Break, Continue, + Exit, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -19,7 +21,7 @@ pub enum TvmState { Waiting, Paused, Call(Callable), - Eval(Frame, usize), + Eval(Frame), FrameEval(Frame), Halted, } @@ -30,7 +32,7 @@ impl TvmState { TvmState::Waiting => Box::new(states::Waiting), TvmState::Paused => Box::new(states::Paused), TvmState::Call(callable) => Box::new(states::Call { callable: callable.clone() }), - TvmState::Eval(frame, pc) => Box::new(states::Eval { frame: frame.clone(), pc: *pc }), + TvmState::Eval(frame) => Box::new(states::Eval { frame: frame.clone(), pc: 0 }), TvmState::FrameEval(frame) => Box::new(states::FrameEval { frame: frame.clone() }), TvmState::Halted => Box::new(states::Halted), } @@ -49,7 +51,7 @@ impl TvmState { } pub fn is_eval(&self) -> bool { - matches!(self, TvmState::Eval(_, _)) + matches!(self, TvmState::Eval(_)) } pub fn is_frame_eval(&self) -> bool { @@ -73,6 +75,25 @@ pub trait Stateful : Debug { fn get_last_result(&self) -> Option<StateResult>; fn is_paused(&self) -> bool; fn handle_result(&mut self, result: StateResult); + + fn call(&mut self, callable: Callable) { + self.set_state(TvmState::Call(callable)); + } + + fn eval(&mut self, frame: Frame) { + self.set_state(TvmState::Eval(frame)); + } + + fn frame_eval(&mut self, frame: Frame) { + self.set_state(TvmState::FrameEval(frame)); + } + + fn should_continue(&self) -> bool { + !self.get_state().is_halted() + || matches!(self.get_last_result(), Some(StateResult::Exit)) + || matches!(self.get_last_result(), Some(StateResult::Break)) + || matches!(self.get_last_result(), Some(StateResult::Return(_))) + } } impl Stateful for Tvm { @@ -81,6 +102,7 @@ impl Stateful for Tvm { } fn set_state(&mut self, state: TvmState) { + println!("Setting state to: {:?}", state); self.previous_state = Some(self.state.clone()); self.state = state; } @@ -110,15 +132,23 @@ impl Stateful for Tvm { } fn tick(&mut self) { + // Do nothing if the tvm is paused + if !self.is_paused() { + self.increment_ticks(); + self.state.to_state().tick(self); + let result = self.get_last_result(); + self.handle_result(result.unwrap()); + } + println!("Tvm state: {:?}", self.state); } fn get_last_result(&self) -> Option<StateResult> { - todo!() + self.last_result.clone() } fn is_paused(&self) -> bool { - self.state == TvmState::Paused + matches!(self.state, TvmState::Paused) } fn handle_result(&mut self, result: StateResult) { @@ -127,6 +157,10 @@ impl Stateful for Tvm { } pub mod states { + use crate::callable::Caller; + use crate::frame::FrameEvaluator; + use crate::instruction::Evaluator; + use crate::state::StateResult::{Continue, Exit}; use super::*; #[derive(Debug, Clone)] @@ -152,40 +186,46 @@ pub mod states { impl State for Waiting { // Tick should do nothing. fn tick(&mut self, tvm: &mut Tvm) -> StateResult { - StateResult::Continue + Continue } } impl State for Paused { // Tick should do nothing. fn tick(&mut self, tvm: &mut Tvm) -> StateResult { - StateResult::Continue + Continue } } impl State for Call { fn tick(&mut self, tvm: &mut Tvm) -> StateResult { - unimplemented!() + tvm.do_call(self.callable.clone()); + Continue } } impl State for Eval { fn tick(&mut self, tvm: &mut Tvm) -> StateResult { - unimplemented!() + if tvm.should_continue() { + tvm.do_eval(&mut self.frame); + self.pc = self.frame.pc; + } + Exit } } impl State for FrameEval { - fn tick(&mut self, tvm: &mut Tvm) -> StateResult { - unimplemented!() + if tvm.should_continue() { + tvm.do_frame_eval(&self.frame); + } + Exit } } impl State for Halted { - fn tick(&mut self, tvm: &mut Tvm) -> StateResult { - unimplemented!() + Continue } } } diff --git a/src/tvm.rs b/src/tvm.rs index e670e38..e025a81 100644 --- a/src/tvm.rs +++ b/src/tvm.rs @@ -1,5 +1,7 @@ use crate::frame::Frame; -use crate::state::{StateResult, TvmState}; +use crate::instruction::Instruction; +use crate::state::{Stateful, StateResult, TvmState}; +use crate::state::StateResult::Continue; #[derive(Debug, Clone)] pub struct Tvm { @@ -23,19 +25,33 @@ impl Default for Tvm { state: TvmState::Waiting, ticks: 0, previous_state: None, - last_result: None, + last_result: Some(Continue), } } } impl Tvm { - pub fn frame_eval(&mut self, frame: Frame) { - self.state = TvmState::FrameEval(frame); + + pub fn start(&mut self) { + let builder = Frame::builder(); + let frame = builder + .id(0) + .name("main".to_string()) + .instruction(Instruction::get_instruction(1), vec![]) + .primitive(0) + .callable(-101, vec![]) + .instruction(Instruction::get_instruction(8), vec![]) + .primitive(-109) + .primitive(10) + .build(); + + self.frame_eval(frame); } } #[cfg(test)] mod test { + use crate::state::Stateful; use super::*; #[test] @@ -49,4 +65,23 @@ mod test { assert_eq!(tvm.ticks, 0); assert_eq!(tvm.previous_state, None); } + + #[test] + fn test_tick_count() { + let mut tvm = Tvm::default(); + tvm.tick(); + tvm.tick(); + tvm.tick(); + tvm.tick(); + tvm.tick(); + tvm.tick(); + tvm.tick(); + tvm.tick(); + tvm.tick(); + tvm.tick(); + tvm.tick(); + tvm.tick(); + + assert_eq!(tvm.ticks, 12); + } } \ No newline at end of file -- GitLab