library now
This commit is contained in:
parent
cf59d6f88c
commit
dd9a0c6f4e
7
Cargo.lock
generated
Normal file
7
Cargo.lock
generated
Normal file
|
@ -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"
|
11
demo.asm
Normal file
11
demo.asm
Normal file
|
@ -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
|
40
src/asm.rs
40
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<Word, AsmError> {
|
||||
debug!("Parsing {} as number", input);
|
||||
if let Ok(n) = input.parse::<Word>() {
|
||||
return Ok(n);
|
||||
}
|
||||
|
@ -21,7 +37,6 @@ fn parse_num(input: &str) -> Result<Word, AsmError> {
|
|||
}
|
||||
|
||||
fn parse_op(input: &str) -> Result<Word, AsmError> {
|
||||
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<Word, AsmError> {
|
|||
Ok(op as Word)
|
||||
}
|
||||
|
||||
pub fn assemble(input: &str) -> Result<Vec<Word>, AsmError> {
|
||||
pub fn assemble(input: &str) -> Result<Vec<Word>, TaggedAsmError> {
|
||||
let mut last: Option<Word> = None;
|
||||
|
||||
let split = input.split_whitespace();
|
||||
|
@ -62,7 +77,10 @@ pub fn assemble(input: &str) -> Result<Vec<Word>, 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<Vec<Word>, 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)
|
||||
}
|
||||
|
|
|
@ -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<Word>,
|
||||
pub pstack: Vec<Word>,
|
||||
pub fstack: Vec<Word>,
|
||||
|
@ -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}");
|
||||
}
|
|
@ -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")
|
||||
},
|
||||
|
|
Loading…
Reference in a new issue