From 382b604f39a99d389e6f09d2a334824f7618d0f3 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Fri, 8 May 2020 00:57:57 +0800 Subject: [PATCH] add execute --- src/context.rs | 194 +++++++++++++++++++++++++++++++++++++++++++++++-- src/err.rs | 12 +++ src/main.rs | 17 ++++- 3 files changed, 214 insertions(+), 9 deletions(-) diff --git a/src/context.rs b/src/context.rs index 9145e37..7b09dd2 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,4 +1,6 @@ use std::collections::HashMap; +use super::ins::Instruction; +use super::err::RuntimeError; #[derive(Debug)] pub struct Context { @@ -19,15 +21,193 @@ impl Context { } } + // label and ptr + pub fn define_label(&mut self, label: isize, ptr: isize) { + self.label_map.insert(label, ptr); + } + + // pub fn find_label(&mut self, label: isize) -> Option { + // self.label_map.get(&label).map(|i| *i) + // } + // pub fn goto(&mut self, p: isize) { // self.ptr = p; // } - // pub fn push(&mut self, n: isize) { - // self.stack.push(n); - // } + pub fn get_ptr(&self) -> isize { + self.ptr + } - // pub fn pop(&mut self) -> Option { - // self.stack.pop() - // } -} \ No newline at end of file + pub fn move_ptr_next(&mut self) { + self.ptr += 1; + } + + // stack + pub fn get_at(&self, i: usize) -> Option { + self.stack.get(i).map(|i| *i) + } + + pub fn push(&mut self, n: isize) { + self.stack.push(n); + } + + pub fn pop(&mut self) -> Option { + self.stack.pop() + } + + // memory + pub fn mem_put(&mut self, addr: isize, val: isize) { + self.mem_map.insert(addr, val); + } + + pub fn mem_get(&self, addr: isize) -> Option { + self.mem_map.get(&addr).map(|i| *i) + } +} + +impl Instruction { + + pub fn execute(&self, context: &mut Context) -> Result<(), RuntimeError> { + match self { + Instruction::Push(i) => context.push(*i), + Instruction::Dup => match context.pop() { + Some(n) => { context.push(n); context.push(n); } + None => return Err(RuntimeError::ErrorVmState(format!("Stack is empty when call dup!"))), + }, + Instruction::CopyN(i) => match context.get_at(*i as usize) { + Some(n) => context.push(n), + None => return Err(RuntimeError::ErrorVmState(format!("Stack is empty when call dup!"))), + }, + Instruction::Swap => { + let a = context.pop(); + let b = context.pop(); + if let (Some(a), Some(b)) = (a, b) { + context.push(b); + context.push(a); + } else { + return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call swap!"))); + } + }, + Instruction::Pop => { context.pop(); }, + Instruction::PopN(i) => { for _ in 0..*i { context.pop(); } }, + Instruction::Add => { + let a = context.pop(); + let b = context.pop(); + if let (Some(a), Some(b)) = (a, b) { + context.push(a + b); + } else { + return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call add!"))); + } + }, + Instruction::Sub => { + let a = context.pop(); + let b = context.pop(); + if let (Some(a), Some(b)) = (a, b) { + context.push(b - a); + } else { + return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call sub!"))); + } + }, + Instruction::Mul => { + let a = context.pop(); + let b = context.pop(); + if let (Some(a), Some(b)) = (a, b) { + context.push(a * b); + } else { + return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call mul!"))); + } + }, + Instruction::Div => { + let a = context.pop(); + let b = context.pop(); + if let (Some(a), Some(b)) = (a, b) { + context.push(b / a); + } else { + return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call div!"))); + } + }, + Instruction::Mod => { + let a = context.pop(); + let b = context.pop(); + if let (Some(a), Some(b)) = (a, b) { + context.push(b % a); + } else { + return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call mod!"))); + } + }, + Instruction::Store => { + let val = context.pop(); + let addr = context.pop(); + if let (Some(val), Some(addr)) = (val, addr) { + context.mem_put(addr, val); + } else { + return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call store!"))); + } + }, + Instruction::Retrieve => { + let addr = context.pop(); + if let Some(addr) = addr { + match context.mem_get(addr) { + Some(val) => { context.mem_put(addr, val); }, + None => return Err(RuntimeError::ErrorVmState(format!("Memory addr not found: {}!", addr))), + } + } else { + return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call retrieve!"))); + } + } + Instruction::DefineLabel(_) => (), // JUST SKIP! + Instruction::CallAtLabel(_i) => return Err(RuntimeError::NotImplemented("CallAtLabel".into())), + Instruction::GotoLabel(_i) => return Err(RuntimeError::NotImplemented("GotoLabel".into())), + Instruction::GotoLabelE0(_i) => return Err(RuntimeError::NotImplemented("GotoLabelE0".into())), + Instruction::GotoLabelL0(_i) => return Err(RuntimeError::NotImplemented("GotoLabelL0".into())), + Instruction::ReturnCallAt => return Err(RuntimeError::NotImplemented("ReturnCallAt".into())), + Instruction::End => return Err(RuntimeError::EndVm), + Instruction::StdOutChar => match context.pop() { + Some(i) => println!("{}", i as u8 as char), + None => return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call stdoutchar!"))), + }, + Instruction::StdOutNum => match context.pop() { + Some(i) => println!("{}", i), + None => return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call stdoutnum!"))), + }, + Instruction::StdInChar => return Err(RuntimeError::NotImplemented("StdInChar".into())), + Instruction::StdInNum => return Err(RuntimeError::NotImplemented("StdInNum".into())), + } + + Ok(()) + } +} + +#[derive(Debug)] +pub struct Vm { + instructions: Vec, +} + +impl Vm { + + pub fn new(instructions: Vec) -> Vm { + Vm { instructions, } + } + + pub fn defint_labels(&self, context: &mut Context) { + for (ptr, ins) in self.instructions.iter().enumerate() { + if let Instruction::DefineLabel(i) = ins { + context.define_label(*i, ptr as isize); + } + } + } + + pub fn execute(&self, context: &mut Context) -> Result<(), RuntimeError> { + loop { + let ptr = context.get_ptr(); + let ins = self.instructions.get(ptr as usize); + match ins { + Some(ins) => match ins.execute(context) { + Err(err) => return Err(err), + Ok(_) => context.move_ptr_next(), + }, + None => return Err(RuntimeError::ErrorVmState(format!("Instruction at {} not found", ptr))), + } + } + } +} diff --git a/src/err.rs b/src/err.rs index 687d503..9b7816d 100644 --- a/src/err.rs +++ b/src/err.rs @@ -5,3 +5,15 @@ pub enum ParseError { #[error(display = "error syntax: {:?}", _0)] ErrorSyntax(String), } + +#[derive(Debug, Error)] +pub enum RuntimeError { + #[error(display = "error vm state: {:?}", _0)] + ErrorVmState(String), + + #[error(display = "end vm")] + EndVm, + + #[error(display = "not implemented: {:?}", _0)] + NotImplemented(String), +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index c1c927f..51d3649 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,8 @@ use err::*; use ins::*; use context::*; +// type XResult = Result>; + const VALID_INSTRUCTION_CHARS: &str = "草泥马河蟹"; // https://playsecurity.org/rawfile/grass_mud_horse_language_specification.md @@ -13,14 +15,25 @@ const VALID_INSTRUCTION_CHARS: &str = "草泥马河蟹"; fn main() { let input = "草草草泥马 马草草草泥草草草草泥泥马 草马草 泥马草泥 草草草泥草泥草马 泥马草草 草草草泥马 泥草草草 草马草 草草草泥草泥泥马 泥草草泥 马泥草草泥草草草泥草泥马 马草马草泥草草草草泥泥马 马草草草泥草草草泥草泥马 草马马 马马马"; - let _instructions = match parse_lang(input) { + let instructions = match parse_lang(input) { Ok(i) => i, Err(err) => { println!("Parse error: {}", err); return; }, }; - let _context = Context::new(); + let mut context = Context::new(); + + let vm = Vm::new(instructions); + vm.defint_labels(&mut context); + + match vm.execute(&mut context) { + Err(RuntimeError::EndVm) => (), + Err(err) => println!("Vm error: {}", err), + Ok(_) => (), + } + println!("{:?}", vm); + println!("{:?}", context); } fn parse_lang(lang: &str) -> Result, ParseError> {