diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..78013b0 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "forte" +version = "0.1.0" diff --git a/demo.asm b/demo.asm new file mode 100644 index 0000000..a987c15 --- /dev/null +++ b/demo.asm @@ -0,0 +1,11 @@ +fun 0 +push 2 15 30 +dif 5 +ret 0 + +fun 1 +push 2 16 4 +dif 1 +call 0 +add 1 +exe 0 diff --git a/src/asm.rs b/src/asm.rs index ccc64e6..de71c48 100644 --- a/src/asm.rs +++ b/src/asm.rs @@ -1,5 +1,4 @@ use crate::{OpKind, Word}; -use log::*; /// Assembler @@ -9,8 +8,25 @@ pub enum AsmError { InvalidOp, } +#[derive(Debug, Clone)] +pub struct TaggedAsmError { + kind: AsmError, + word: String, +} + +impl std::fmt::Display for TaggedAsmError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "[Assembler] Error parsing token: ")?; + match self.kind { + AsmError::ExpectedNum => { + write!(f, "Expected number, found {}", self.word) + }, + AsmError::InvalidOp => write!(f, "Expected opcode, found {}", self.word), + } + } +} + fn parse_num(input: &str) -> Result { - debug!("Parsing {} as number", input); if let Ok(n) = input.parse::() { return Ok(n); } @@ -21,7 +37,6 @@ fn parse_num(input: &str) -> Result { } fn parse_op(input: &str) -> Result { - debug!("Parsing {} as opcode", input); let op = match input { "push" => OpKind::Push, "pop" => OpKind::Pop, @@ -54,7 +69,7 @@ fn parse_op(input: &str) -> Result { Ok(op as Word) } -pub fn assemble(input: &str) -> Result, AsmError> { +pub fn assemble(input: &str) -> Result, TaggedAsmError> { let mut last: Option = None; let split = input.split_whitespace(); @@ -62,7 +77,10 @@ pub fn assemble(input: &str) -> Result, AsmError> { for part in split { // If just parsed op, next should be num if last.is_some() { - let num = parse_num(part)?; + let num = parse_num(part).map_err(|kind| TaggedAsmError { + kind, + word: part.to_string(), + })?; let op = last.take().unwrap(); let word = (op << 64) | num; output.push(word); @@ -72,12 +90,18 @@ pub fn assemble(input: &str) -> Result, AsmError> { if let Ok(op) = parse_op(part) { last = Some(op); } else { - output.push(parse_num(part)?); + output.push(parse_num(part).map_err(|kind| TaggedAsmError { + kind, + word: part.to_string(), + })?); } } } - if last.is_some() { - Err(AsmError::ExpectedNum) + if let Some(l) = last { + Err(TaggedAsmError { + kind: AsmError::ExpectedNum, + word: l.to_string(), + }) } else { Ok(output) } diff --git a/src/main.rs b/src/lib.rs similarity index 87% rename from src/main.rs rename to src/lib.rs index 5feced3..344dd7e 100644 --- a/src/main.rs +++ b/src/lib.rs @@ -5,7 +5,6 @@ mod util; use op::*; use util::*; -use log::*; pub type Byte = u32; pub type Word = u128; @@ -19,6 +18,7 @@ pub struct Machine { ip: Word, ebp: Word, phase: Phase, + pub log: String, pub cstack: Vec, pub pstack: Vec, pub fstack: Vec, @@ -32,6 +32,7 @@ impl Machine { ip: 0, ebp: 0, phase: Phase::Warmup, + log: "".to_string(), cstack: vec![], pstack: vec![], fstack: vec![], @@ -108,18 +109,15 @@ impl Machine { let word = self .instructions .get(self.ip as usize) - .expect("Ran out of instructions"); + .ok_or(Error::NoExec)?; let instr: Op = match word.try_into() { Ok(i) => i, - Err(_) => { - error!("Invalid opcode: {:#x}", word); - panic!() - }, + Err(_) => return Err(Error::IllegalOp), }; - debug!( - "[{:?}]{}: {:?} {}", - self.phase, self.ip, instr.kind, instr.var - ); + self.log.push_str(&format!( + "[{:?}] {}: {:?} {}\n", + self.phase, self.ip, instr.kind, instr.var, + )); self.ip += 1; match self.phase { Phase::Warmup => self.step_warmup(instr), @@ -135,7 +133,10 @@ impl Machine { Some(n) => n, None => return Err(Error::StackUnderflow), }; - debug!("{}: _ {}", self.ip, num); + self.log.push_str(&format!( + "[Recital] {}: {} added to stack\n", + self.ip, num + )); self.pstack.push(*num); self.ip += 1; } @@ -329,9 +330,9 @@ impl Machine { } }, OpKind::Exe => { - // Ignore in execution + // Successfully reached end of main! self.phase = Phase::Recital; - // TODO: Maybe break program here? + return Err(Error::EndReached); }, OpKind::Sto => { @@ -348,7 +349,10 @@ impl Machine { }, _ => {}, }; - debug!("{:?}", self.pstack); + + self + .log + .push_str(&format!("[STACK]: {:?}\n", self.pstack)); Ok(()) } @@ -362,6 +366,11 @@ impl Machine { None => return Err(Error::StackUnderflow), } }, + OpKind::Push => { + for _ in 0..instr.var { + self.ip += 1; + } + }, _ => {}, } Ok(()) @@ -373,35 +382,39 @@ impl Machine { } Ok(()) } + + pub fn run(&mut self) -> String { + loop { + match self.step() { + Ok(_) => {}, + Err(e) => { + if e == Error::EndReached { + self + .log + .push_str(&format!("[BRAVO] Final stack: {:?}\n", self.pstack)) + } else { + self.log.push_str(&format!("[CLAM] {}\n", e)) + } + return self.log.clone(); + }, + } + } + } +} + +pub fn interpret(program: &str) -> String { + let mut m = Machine::new(); + let program = match asm::assemble(program) { + Ok(p) => p, + Err(e) => return format!("{}", e), + }; + m.read(program); + let s = m.run(); + s } fn main() { - flexi_logger::Logger::try_with_str("rh24") - .unwrap() - .start() - .unwrap(); - - let mut m = Machine::new(); - // let program = vec![ - // // - // Op::new(OpKind::Fun, 0), - // Op::new(OpKind::Push, 2), - // 15, - // 30, - // Op::new(OpKind::Dif, 1), - // Op::new(OpKind::Ret, 0), - // Op::new(OpKind::Fun, 0), - // Op::new(OpKind::Push, 2), - // 16, - // 4, - // Op::new(OpKind::Dif, 1), - // Op::new(OpKind::Call, 0), - // Op::new(OpKind::Add, 1), - // Op::new(OpKind::Exe, 0), - // ]; - let program = include_str!("./../demo.asm"); - let program = asm::assemble(program).unwrap(); - m.read(program); - m.steps(32).unwrap(); - println!("RESULT = {:?}", m.pstack); + let p = include_str!("../demo.asm"); + let s = interpret(&p); + println!("{s}"); } diff --git a/src/util.rs b/src/util.rs index fce496c..016267d 100644 --- a/src/util.rs +++ b/src/util.rs @@ -17,6 +17,7 @@ pub enum Error { ReadOob, BadCall, NoExec, + IllegalOp, EndReached, } @@ -28,6 +29,7 @@ impl Display for Error { Self::StackUnbalanced => write!(f, "Stack Unbalanced"), Self::WriteOob => write!(f, "Memory write out of bounds"), Self::ReadOob => write!(f, "Memory read out of bounds"), + Self::IllegalOp => write!(f, "Illegal instruction reached"), Self::BadCall => { write!(f, "Attempted to call function which does not exist") },