Begin implementing human interface for Hyphae
Some checks failed
per-push tests / build (push) Failing after 39s
per-push tests / test-frontend (push) Has been skipped
per-push tests / timed-decomposer-parse (push) Has been skipped
per-push tests / test-utility (push) Has been skipped
per-push tests / test-backend (push) Has been skipped
Some checks failed
per-push tests / build (push) Failing after 39s
per-push tests / test-frontend (push) Has been skipped
per-push tests / timed-decomposer-parse (push) Has been skipped
per-push tests / test-utility (push) Has been skipped
per-push tests / test-backend (push) Has been skipped
This commit implements Display and FromStr for Datum, Operation, Operand, Instruction, and Program types. Additionally, tests are added for the new routines. This change was implemented in furtherance of a command line assembler and disassembler, as well as for the implementation of a debugger.
This commit is contained in:
parent
389bf6e9a0
commit
5582da5b41
7 changed files with 286 additions and 652 deletions
|
|
@ -105,13 +105,21 @@ fn main() {
|
|||
let mut isa_fromstr = "impl FromStr for Operation {\n".to_owned();
|
||||
isa_fromstr += " type Err = &'static str;\n";
|
||||
isa_fromstr += " fn from_str(v: &str) -> Result<Self, Self::Err> {\n";
|
||||
isa_fromstr += " let a = v.to_ascii_uppercase();\n";
|
||||
isa_fromstr += " let v = a.as_str();\n";
|
||||
isa_fromstr += " match v {\n";
|
||||
|
||||
let mut isa_from_str = "impl TryFrom<&str> for Operation {\n".to_owned();
|
||||
isa_from_str += " type Error = &'static str;\n";
|
||||
isa_from_str += " fn try_from(v: &str) -> Result<Self, Self::Error> {\n";
|
||||
isa_from_str += " let a = v.to_ascii_uppercase();\n";
|
||||
isa_from_str += " let v = a.as_str();\n";
|
||||
isa_from_str += " match v {\n";
|
||||
|
||||
let mut isa_into_str = "impl Display for Operation {\n".to_owned();
|
||||
isa_into_str += " fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> {\n";
|
||||
isa_into_str += " match self.0 {\n";
|
||||
|
||||
let mut isa_num_args = "impl Operation {\n".to_owned();
|
||||
isa_num_args += " pub fn num_args(&self) -> Result<u8, &'static str> {\n";
|
||||
isa_num_args += " match self.0 {\n";
|
||||
|
|
@ -133,6 +141,9 @@ fn main() {
|
|||
isa_fromstr += format!(" \"{}\" => Ok({}),\n",
|
||||
const_name, const_name).as_str();
|
||||
|
||||
isa_into_str += format!(" {} => write!(f, \"{}\"),\n",
|
||||
idx, const_name).as_str();
|
||||
|
||||
isa_num_args += format!(" {} => Ok({}),\n", idx, instr.args.len())
|
||||
.as_str();
|
||||
|
||||
|
|
@ -154,6 +165,11 @@ fn main() {
|
|||
isa_fromstr += " }\n";
|
||||
isa_fromstr += "}\n\n";
|
||||
|
||||
isa_into_str += " _ => panic!(\"illegal instruction\"),\n";
|
||||
isa_into_str += " }\n";
|
||||
isa_into_str += " }\n";
|
||||
isa_into_str += "}\n\n";
|
||||
|
||||
isa_num_args += " _ => Err(\"illegal instruction\"),\n";
|
||||
isa_num_args += " }\n";
|
||||
isa_num_args += " }\n";
|
||||
|
|
@ -163,9 +179,11 @@ fn main() {
|
|||
isa += isa_from_byte.as_str();
|
||||
isa += isa_from_str.as_str();
|
||||
isa += isa_fromstr.as_str();
|
||||
isa += isa_into_str.as_str();
|
||||
isa += isa_num_args.as_str();
|
||||
|
||||
write!(&mut output_file, "use core::str::FromStr;\n\n\n").unwrap();
|
||||
write!(&mut output_file, "use core::str::FromStr;\n").unwrap();
|
||||
write!(&mut output_file, "use alloc::fmt::{{Display, Formatter, Error as E}};\n\n\n").unwrap();
|
||||
write!(&mut output_file, "{}", isa).unwrap();
|
||||
write!(&mut output_file, "\n\npub const TOTAL_INSTRUCTIONS: usize = {};", peak)
|
||||
.unwrap();
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ use core::ptr::NonNull;
|
|||
use alloc::{vec, vec::Vec};
|
||||
use alloc::rc::Rc;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::fmt::Debug;
|
||||
use alloc::fmt::{Error as E, Debug, Formatter, Display};
|
||||
use alloc::string::String;
|
||||
|
||||
use organelle::{Number, Fraction, SymbolicNumber, Float, ScientificNotation};
|
||||
|
||||
|
|
@ -97,6 +98,12 @@ impl<T> DerefMut for Gc<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Display> Display for Gc<T> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> {
|
||||
write!(f, "{}", **self)
|
||||
}
|
||||
}
|
||||
|
||||
// takes a pointer to target Rc
|
||||
macro_rules! shallow_copy_rc {
|
||||
( $src:expr ) => {
|
||||
|
|
@ -264,6 +271,18 @@ impl Index<usize> for Cons {
|
|||
}
|
||||
}
|
||||
|
||||
impl Display for Cons {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> {
|
||||
write!(f, "(")?;
|
||||
self.0.clone()
|
||||
.and_then(|x| write!(f, "{}", x).into());
|
||||
write!(f, " ")?;
|
||||
self.1.clone()
|
||||
.and_then(|x| write!(f, "{}", x).into());
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum Datum {
|
||||
Number(Number),
|
||||
|
|
@ -276,6 +295,43 @@ pub enum Datum {
|
|||
None
|
||||
}
|
||||
|
||||
impl Display for Datum {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> {
|
||||
match self {
|
||||
Datum::Number(n) => write!(f, "{}", Into::<String>::into(*n)),
|
||||
Datum::Bool(b) => write!(f, "{}", b),
|
||||
Datum::Cons(c) => write!(f, "'{}", c),
|
||||
Datum::Char(c) => write!(f, "'{}'", c),
|
||||
Datum::String(s) => write!(f, "\"{}\"", str::from_utf8(&s).unwrap()),
|
||||
Datum::Vector(v) => {
|
||||
write!(f, "#(")?;
|
||||
if v.len() > 0 {
|
||||
write!(f, "{}", v[0])?;
|
||||
}
|
||||
if v.len() > 1 {
|
||||
for i in &v[1..] {
|
||||
write!(f, ", {}", i)?;
|
||||
}
|
||||
}
|
||||
write!(f, ")")
|
||||
},
|
||||
Datum::ByteVector(v) => {
|
||||
write!(f, "#u8(")?;
|
||||
if v.len() > 0 {
|
||||
write!(f, "{}", v[0])?;
|
||||
}
|
||||
if v.len() > 1 {
|
||||
for i in &v[1..] {
|
||||
write!(f, ", {}", i)?;
|
||||
}
|
||||
}
|
||||
write!(f, ")")
|
||||
},
|
||||
Datum::None => write!(f, "nil"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// implemented by hand to force deep copy on Cons datum
|
||||
impl Clone for Datum {
|
||||
fn clone(&self) -> Datum {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ use crate::heap::Datum;
|
|||
|
||||
use alloc::vec::Vec;
|
||||
use alloc::vec;
|
||||
use alloc::str::FromStr;
|
||||
use alloc::fmt::{Display, Formatter, Error as E};
|
||||
|
||||
use core::ops::Index;
|
||||
use core::mem::transmute;
|
||||
|
|
@ -64,12 +66,6 @@ pub enum Address {
|
|||
Char = 0xfa, // immutable access only
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Deserializer<'a> {
|
||||
pub input: &'a [u8],
|
||||
// TODO: Debug levels for errors
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Operand(pub Address, pub usize);
|
||||
|
||||
|
|
@ -146,6 +142,56 @@ impl Into<Vec<u8>> for Operand {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Operand {
|
||||
type Err = &'static str;
|
||||
fn from_str(v: &str) -> Result<Self, Self::Err> {
|
||||
match v {
|
||||
"$expr" => Ok(Operand(Address::Expr, 0)),
|
||||
"$oper1" => Ok(Operand(Address::Oper1, 0)),
|
||||
"$oper2" => Ok(Operand(Address::Oper2, 0)),
|
||||
"$oper3" => Ok(Operand(Address::Oper3, 0)),
|
||||
"$oper4" => Ok(Operand(Address::Oper4, 0)),
|
||||
"true" => Ok(Operand(Address::Bool, 1)),
|
||||
"false" => Ok(Operand(Address::Bool, 0)),
|
||||
a if a.chars().nth(0).unwrap() == '%' &&
|
||||
a.len() > 1 &&
|
||||
a[1..].parse::<usize>().is_ok() =>
|
||||
Ok(Operand(Address::Stack, a[1..].parse::<usize>().unwrap())),
|
||||
a if a.chars().nth(0).unwrap() == '@' &&
|
||||
a.len() > 1 &&
|
||||
a[1..].parse::<usize>().is_ok() =>
|
||||
Ok(Operand(Address::Instr, a[1..].parse::<usize>().unwrap())),
|
||||
a if a.chars().nth(0).unwrap() == '\'' &&
|
||||
a.len() == 3 &&
|
||||
a.chars().nth(2).unwrap() == '\'' =>
|
||||
Ok(Operand(Address::Char, a.chars().nth(1).unwrap() as usize)),
|
||||
a if a.parse::<usize>().is_ok() =>
|
||||
Ok(Operand(Address::Numer, a.parse::<usize>().unwrap())),
|
||||
_ => Err("invalid operand")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Operand {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> {
|
||||
match self.0 {
|
||||
Address::Expr => write!(f, "$expr"),
|
||||
Address::Oper1 => write!(f, "$oper1"),
|
||||
Address::Oper2 => write!(f, "$oper2"),
|
||||
Address::Oper3 => write!(f, "$oper3"),
|
||||
Address::Oper4 => write!(f, "$oper4"),
|
||||
Address::Bool =>
|
||||
write!(f, "{}", if self.1 > 0 { "true" } else { "false" }),
|
||||
Address::Stack => write!(f, "%")
|
||||
.and_then(|_| write!(f, "{}", self.1)),
|
||||
Address::Instr => write!(f, "@")
|
||||
.and_then(|_| write!(f, "{}", self.1)),
|
||||
Address::Numer => write!(f, "{}", self.1),
|
||||
Address::Char => write!(f, "'{}'", self.1 as u8 as char),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Operand {
|
||||
fn byte_length(&self) -> u8 {
|
||||
1 + self.0.operand_size()
|
||||
|
|
@ -183,6 +229,46 @@ impl Into<Vec<u8>> for Instruction {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Instruction {
|
||||
type Err = &'static str;
|
||||
fn from_str(v: &str) -> Result<Self, Self::Err> {
|
||||
let toks: Vec<&str> = v.trim().split(' ').collect();
|
||||
if toks.len() < 1 {
|
||||
return Err("empty string");
|
||||
}
|
||||
|
||||
let oper = Operation::from_str(toks[0])?;
|
||||
let mut args = vec![];
|
||||
if toks.len() == 1 && oper.num_args()? == 0 {
|
||||
return Ok(Instruction(oper, args));
|
||||
}
|
||||
for i in toks[1..].iter() {
|
||||
args.push(Operand::from_str(i.trim_matches(','))?);
|
||||
}
|
||||
|
||||
if oper.num_args()? as usize != args.len() {
|
||||
return Err("instruction has incorrect number of operands");
|
||||
}
|
||||
|
||||
Ok(Instruction(oper, args))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Instruction {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> {
|
||||
write!(f, "{}\t", self.0)?;
|
||||
if self.1.len() > 0 {
|
||||
write!(f, "{}", self.1[0])?;
|
||||
}
|
||||
if self.1.len() > 1 {
|
||||
for i in self.1[1..].iter() {
|
||||
write!(f, ", {}", i)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
fn byte_length(&self) -> u8 {
|
||||
self.1.iter()
|
||||
|
|
@ -247,6 +333,52 @@ impl<'a> Index<usize> for Program {
|
|||
}
|
||||
}
|
||||
|
||||
impl Display for Program {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), E> {
|
||||
write!(f, "DATA:\n")?;
|
||||
for i in self.0.iter() {
|
||||
write!(f, " {}", i)?;
|
||||
}
|
||||
|
||||
write!(f, "\nCODE:\n")?;
|
||||
for i in self.1.iter() {
|
||||
write!(f, " {}", i)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Program {
|
||||
type Err = &'static str;
|
||||
fn from_str(val: &str) -> Result<Self, Self::Err> {
|
||||
//let mut datum = vec![];
|
||||
let mut instrs = vec![];
|
||||
let lines: Vec<&str> = val.split('\n').collect();
|
||||
let mut cur = 0;
|
||||
let mut toggle = 0;
|
||||
|
||||
while cur < lines.len() {
|
||||
if toggle == 1 {
|
||||
instrs.push(lines[cur].parse::<Instruction>()?);
|
||||
|
||||
// TODO: toggle == 2 case for interpreting a DATA chunk
|
||||
|
||||
} else {
|
||||
match lines[cur] {
|
||||
"DATA:" => return Err("datum parser unimplemented"),
|
||||
"CODE:" => toggle = 1,
|
||||
a => return Err("unknown section in document: "),
|
||||
}
|
||||
}
|
||||
|
||||
cur += 1;
|
||||
}
|
||||
|
||||
Ok(Program(vec![], instrs))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for DeserializerControlCode {
|
||||
type Error = &'static str;
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
|
|
@ -280,6 +412,31 @@ mod tests {
|
|||
use crate::instr;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_operand_tofrom_str() {
|
||||
let cases = vec![
|
||||
("$expr", Operand(Address::Expr, 0)),
|
||||
("$oper1", Operand(Address::Oper1, 0)),
|
||||
("$oper2", Operand(Address::Oper2, 0)),
|
||||
("$oper3", Operand(Address::Oper3, 0)),
|
||||
("$oper4", Operand(Address::Oper4, 0)),
|
||||
("true", Operand(Address::Bool, 1)),
|
||||
("false", Operand(Address::Bool, 0)),
|
||||
("%12", Operand(Address::Stack, 12)),
|
||||
("%1", Operand(Address::Stack, 1)),
|
||||
("@1", Operand(Address::Instr, 1)),
|
||||
("@12", Operand(Address::Instr, 12)),
|
||||
("1234", Operand(Address::Numer, 1234)),
|
||||
("'c'", Operand(Address::Char, 'c' as usize)),
|
||||
];
|
||||
|
||||
for i in cases.iter() {
|
||||
let a = Operand::from_str(i.0).unwrap();
|
||||
assert_eq!(a, i.1);
|
||||
assert_eq!(i.0, a.to_string().as_str());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_operand_parse() {
|
||||
let bad_addressing =
|
||||
|
|
@ -357,7 +514,36 @@ mod tests {
|
|||
assert_eq!(two_bytes.unwrap(), vec![instr::LINK.0, 0xf3, 0xf4]);
|
||||
assert_eq!(two_oper.1[0], Operand(Address::Oper1, 0));
|
||||
assert_eq!(two_oper.1[1], Operand(Address::Oper2, 0));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_instruction_tofrom_string() {
|
||||
let happy_cases = vec![
|
||||
"NOP",
|
||||
" NOP",
|
||||
"NOP ",
|
||||
"nop",
|
||||
"PUSH $expr",
|
||||
"CONST $expr 4",
|
||||
"jmp @3",
|
||||
];
|
||||
|
||||
let sad_cases = vec![
|
||||
"NOP 1",
|
||||
"push",
|
||||
"const 4",
|
||||
];
|
||||
|
||||
for i in happy_cases.iter() {
|
||||
assert!(i.parse::<Instruction>().is_ok());
|
||||
}
|
||||
|
||||
for i in sad_cases.iter() {
|
||||
assert!(i.parse::<Instruction>().is_err());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: test program from and to string
|
||||
|
||||
#[test]
|
||||
fn test_program_parse() {
|
||||
|
|
|
|||
|
|
@ -145,6 +145,14 @@ impl VM {
|
|||
self.running = false;
|
||||
}
|
||||
|
||||
pub fn run_step(&mut self) {
|
||||
if self.ictr < self.prog.len() &&
|
||||
!self.err_state && self.running {
|
||||
self.execute_instruction();
|
||||
self.ictr += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn execute_instruction(&mut self) {
|
||||
macro_rules! e {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue