add src/vm,src/parser
This commit is contained in:
181
src/vm.rs
Normal file
181
src/vm.rs
Normal file
@@ -0,0 +1,181 @@
|
||||
use super::err::*;
|
||||
use super::ins::*;
|
||||
use super::context::*;
|
||||
|
||||
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.put_mem(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.get_mem(addr) {
|
||||
Some(val) => { context.put_mem(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) => match context.find_label(*i) {
|
||||
Some(ptr) => context.goto(ptr),
|
||||
None => return Err(RuntimeError::ErrorVmState(format!("Label not found: {}", i))),
|
||||
},
|
||||
Instruction::GotoLabelE0(i) => match context.pop() {
|
||||
Some(v) => if v == 0 {
|
||||
match context.find_label(*i) {
|
||||
Some(ptr) => context.goto(ptr),
|
||||
None => return Err(RuntimeError::ErrorVmState(format!("Label not found: {}", i))),
|
||||
}
|
||||
},
|
||||
None => return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call gotolabele0!"))),
|
||||
},
|
||||
Instruction::GotoLabelL0(i) => match context.pop() {
|
||||
Some(v) => if v < 0 {
|
||||
match context.find_label(*i) {
|
||||
Some(ptr) => context.goto(ptr),
|
||||
None => return Err(RuntimeError::ErrorVmState(format!("Label not found: {}", i))),
|
||||
}
|
||||
},
|
||||
None => return Err(RuntimeError::ErrorVmState(format!("Stack is not enough when call gotolabell0!"))),
|
||||
},
|
||||
Instruction::ReturnCallAt => return Err(RuntimeError::NotImplemented("ReturnCallAt".into())),
|
||||
Instruction::End => return Err(RuntimeError::EndVm),
|
||||
Instruction::StdOutChar => match context.pop() {
|
||||
Some(i) => print!("{}", 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) => print!("{}", 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<Instruction>,
|
||||
}
|
||||
|
||||
impl Vm {
|
||||
|
||||
pub fn new(instructions: Vec<Instruction>) -> 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 {
|
||||
if context.is_debug() {
|
||||
println!("[DEBUG] Define label: {} -> {}", *i, ptr as isize);
|
||||
}
|
||||
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) => {
|
||||
if context.is_debug() {
|
||||
let is_define_label = if let Instruction::DefineLabel(_) = ins { true } else { false };
|
||||
if !is_define_label {
|
||||
println!("[DEBUG] Execute instruction: {:?}", 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))),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user