summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBrian Cully <bjc@spork.org>2025-08-25 08:20:22 -0400
committerBrian Cully <bjc@spork.org>2025-08-25 08:22:13 -0400
commit8879df0983737b42eee1df26004b861599dd88d6 (patch)
treed95dcfedf2aaa1ff8381bf13990ada289d33c047 /src
parent4b8188f69272658945e3c20425308a9951c0eba6 (diff)
downloadautomathon-8879df0983737b42eee1df26004b861599dd88d6.tar.gz
automathon-8879df0983737b42eee1df26004b861599dd88d6.zip
clippy
Diffstat (limited to 'src')
-rw-r--r--src/forth/compiler.rs98
-rw-r--r--src/forth/vm.rs41
-rwxr-xr-xsrc/lib.rs31
3 files changed, 78 insertions, 92 deletions
diff --git a/src/forth/compiler.rs b/src/forth/compiler.rs
index 9120506..17f0113 100644
--- a/src/forth/compiler.rs
+++ b/src/forth/compiler.rs
@@ -19,7 +19,7 @@ impl std::fmt::Display for CompileError {
Self::DefStackEmpty => write!(f, "def stack empty"),
Self::MissingIf => write!(f, "missing if"),
Self::MissingQuote => write!(f, "missing ending quote"),
- Self::UnknownWord(word) => write!(f, "unknown word: {}", word),
+ Self::UnknownWord(word) => write!(f, "unknown word: {word}"),
}
}
}
@@ -66,13 +66,11 @@ pub struct Compiler<'a> {
impl<'a> Compiler<'a> {
pub fn new(text: &'a str) -> Self {
- let mut wl = vec![];
- // main routine is always the first entry.
- wl.push(ByteCode(vec![]));
Self {
text,
enumerator: text.chars().enumerate(),
- wordlist: WordList(wl),
+ // we always need something at index 0
+ wordlist: WordList(vec![ByteCode(vec![])]),
annotations: vec![vec![]],
wordalog: WordCatalog(HashMap::new()),
defstack: vec![],
@@ -127,7 +125,7 @@ impl<'a> Compiler<'a> {
r#"s""# => {
let (s_end, _) =
self.enumerator
- .find(|(_i, c)| return *c == '"')
+ .find(|(_i, c)| *c == '"')
.ok_or(CompileError::MissingQuote)?;
self.bc_push(OpCode::Str(end+1, s_end), anno);
},
@@ -235,32 +233,28 @@ mod tests {
fn literal_num() {
let comp = compiler_for("1\n");
let main = &comp.wordlist.0[0];
- assert_eq!(main.len(), 1);
- assert_eq!(main[0], OpCode::Num(1));
+ assert_eq!(main, vec![OpCode::Num(1)]);
}
#[test]
fn literal_string() {
let comp = compiler_for(r#"s" hello there""#);
let main = &comp.wordlist.0[0];
- assert_eq!(main.len(), 1);
- assert_eq!(main[0], OpCode::Str(3, 14));
+ assert_eq!(main, vec![OpCode::Str(3, 14)]);
}
#[test]
fn add_opcode() {
let comp = compiler_for("+\n");
let main = &comp.wordlist.0[0];
- assert_eq!(main.len(), 1);
- assert_eq!(main[0], OpCode::Add);
+ assert_eq!(main, vec![OpCode::Add]);
}
#[test]
fn sub_opcode() {
let comp = compiler_for("-\n");
let main = &comp.wordlist.0[0];
- assert_eq!(main.len(), 1);
- assert_eq!(main[0], OpCode::Sub);
+ assert_eq!(main, vec![OpCode::Sub]);
}
#[test]
@@ -268,14 +262,10 @@ mod tests {
let comp = compiler_for(": add2 2 + ; 3 add2\n");
let main = &comp.wordlist.0[0];
let add2_index = comp.wordalog.0.get("add2").expect("add2 has entry in wordlist");
+ assert_eq!(main, vec![OpCode::Num(3), OpCode::Call(*add2_index)]);
+
let add2 = &comp.wordlist.0[*add2_index];
- assert_eq!(main.len(), 2);
- assert_eq!(main[0], OpCode::Num(3));
- assert_eq!(main[1], OpCode::Call(*add2_index));
- assert_eq!(add2.len(), 3);
- assert_eq!(add2[0], OpCode::Num(2));
- assert_eq!(add2[1], OpCode::Add);
- assert_eq!(add2[2], OpCode::Ret);
+ assert_eq!(add2, vec![OpCode::Num(2), OpCode::Add, OpCode::Ret]);
}
#[test]
@@ -284,14 +274,9 @@ mod tests {
eprintwordlist(&comp.wordlist);
assert_eq!(comp.wordlist.0.len(), 2);
let main = &comp.wordlist.0[0];
- assert_eq!(main.len(), 1);
- assert_eq!(main[0], OpCode::If(1, None));
+ assert_eq!(main, vec![OpCode::If(1, None)]);
let tr = &comp.wordlist.0[1];
- assert_eq!(tr.len(), 4);
- assert_eq!(tr[0], OpCode::Num(1));
- assert_eq!(tr[1], OpCode::Num(2));
- assert_eq!(tr[2], OpCode::Add);
- assert_eq!(tr[3], OpCode::Ret);
+ assert_eq!(tr, vec![OpCode::Num(1), OpCode::Num(2), OpCode::Add, OpCode::Ret]);
}
#[test]
@@ -302,18 +287,11 @@ mod tests {
let main = &comp.wordlist.0[0];
assert_eq!(main.len(), 1);
assert_eq!(main[0], OpCode::If(1, Some(2)));
+ assert_eq!(main, vec![OpCode::If(1, Some(2))]);
let tr = &comp.wordlist.0[1];
- assert_eq!(tr.len(), 4);
- assert_eq!(tr[0], OpCode::Num(1));
- assert_eq!(tr[1], OpCode::Num(2));
- assert_eq!(tr[2], OpCode::Add);
- assert_eq!(tr[3], OpCode::Ret);
+ assert_eq!(tr, vec![OpCode::Num(1), OpCode::Num(2), OpCode::Add, OpCode::Ret]);
let fl = &comp.wordlist.0[2];
- assert_eq!(fl.len(), 4);
- assert_eq!(fl[0], OpCode::Num(1));
- assert_eq!(fl[1], OpCode::Num(2));
- assert_eq!(fl[2], OpCode::Sub);
- assert_eq!(fl[3], OpCode::Ret);
+ assert_eq!(fl, vec![OpCode::Num(1), OpCode::Num(2), OpCode::Sub, OpCode::Ret]);
}
#[test]
@@ -321,6 +299,12 @@ mod tests {
let comp = compiler_for(": first + ; : second 3 first ; 1 second\n");
assert_eq!(comp.wordlist.0.len(), 3, "wordlist length");
eprintwordlist(&comp.wordlist);
+ let main = &comp.wordlist.0[0];
+ assert_eq!(main, vec![OpCode::Num(1), OpCode::Call(2)]);
+ let first = &comp.wordlist.0[1];
+ assert_eq!(first, vec![OpCode::Add, OpCode::Ret]);
+ let second = &comp.wordlist.0[2];
+ assert_eq!(second, vec![OpCode::Num(3), OpCode::TCall(1)]);
}
#[test]
@@ -329,18 +313,11 @@ mod tests {
eprintwordlist(&comp.wordlist);
assert_eq!(comp.wordlist.0.len(), 3);
let main = &comp.wordlist.0[0];
- assert_eq!(main.len(), 1);
- assert_eq!(main[0], OpCode::Call(1));
+ assert_eq!(main, vec![OpCode::Call(1)]);
let t = &comp.wordlist.0[1];
- assert_eq!(t.len(), 2);
- assert_eq!(t[0], OpCode::TIf(2, None));
- assert_eq!(t[1], OpCode::Ret);
+ assert_eq!(t, vec![OpCode::TIf(2, None), OpCode::Ret]);
let tr = &comp.wordlist.0[2];
- assert_eq!(tr.len(), 4);
- assert_eq!(tr[0], OpCode::Num(1));
- assert_eq!(tr[1], OpCode::Num(2));
- assert_eq!(tr[2], OpCode::Add);
- assert_eq!(tr[3], OpCode::Ret);
+ assert_eq!(tr, vec![OpCode::Num(1), OpCode::Num(2), OpCode::Add, OpCode::Ret]);
}
#[test]
@@ -349,24 +326,13 @@ mod tests {
eprintwordlist(&comp.wordlist);
assert_eq!(comp.wordlist.0.len(), 4, "wordlist length");
let main = &comp.wordlist.0[0];
- assert_eq!(main.len(), 1);
- assert_eq!(main[0], OpCode::Call(1));
+ assert_eq!(main, vec![OpCode::Call(1)]);
let t = &comp.wordlist.0[1];
- assert_eq!(t.len(), 2);
- assert_eq!(t[0], OpCode::TIf(2, Some(3)));
- assert_eq!(t[1], OpCode::Ret);
+ assert_eq!(t, vec![OpCode::TIf(2, Some(3)), OpCode::Ret]);
let tr = &comp.wordlist.0[2];
- assert_eq!(tr.len(), 4);
- assert_eq!(tr[0], OpCode::Num(1));
- assert_eq!(tr[1], OpCode::Num(2));
- assert_eq!(tr[2], OpCode::Add);
- assert_eq!(tr[3], OpCode::Ret);
+ assert_eq!(tr, vec![OpCode::Num(1), OpCode::Num(2), OpCode::Add, OpCode::Ret]);
let fl = &comp.wordlist.0[3];
- assert_eq!(fl.len(), 4);
- assert_eq!(fl[0], OpCode::Num(1));
- assert_eq!(fl[1], OpCode::Num(2));
- assert_eq!(fl[2], OpCode::Sub);
- assert_eq!(fl[3], OpCode::Ret);
+ assert_eq!(fl, vec![OpCode::Num(1), OpCode::Num(2), OpCode::Sub, OpCode::Ret]);
}
#[test]
@@ -375,10 +341,8 @@ mod tests {
eprintwordlist(&comp.wordlist);
assert_eq!(comp.wordlist.0.len(), 2);
let main = &comp.wordlist.0[0];
- assert_eq!(main.len(), 1);
- assert_eq!(main[0], OpCode::Call(1));
+ assert_eq!(main, vec![OpCode::Call(1)]);
let foo = &comp.wordlist.0[1];
- assert_eq!(foo.len(), 1);
- assert_eq!(foo[0], OpCode::TCall(1));
+ assert_eq!(foo, vec![OpCode::TCall(1)]);
}
}
diff --git a/src/forth/vm.rs b/src/forth/vm.rs
index b4ce674..fe034a6 100644
--- a/src/forth/vm.rs
+++ b/src/forth/vm.rs
@@ -2,7 +2,7 @@ use log::debug;
use std::ops::Index;
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, Eq, PartialEq)]
pub enum OpCode {
Num(i32),
Str(usize, usize),
@@ -27,7 +27,7 @@ pub enum OpCode {
Ret,
}
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ByteCode(pub Vec<OpCode>);
impl std::ops::Deref for ByteCode {
type Target = Vec<OpCode>;
@@ -40,6 +40,19 @@ impl ByteCode {
pub fn len(&self) -> usize {
self.0.len()
}
+
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+}
+
+// really just to make unit testing look nicer. should probably move
+// it there.
+impl PartialEq<Vec<OpCode>> for &ByteCode {
+ fn eq(&self, other: &Vec<OpCode>) -> bool {
+ self.0.len() == other.len() &&
+ std::iter::zip(&self.0, other).all(|(a, b)| a == b)
+ }
}
impl Index<usize> for ByteCode {
@@ -65,6 +78,12 @@ impl InstructionPointer {
}
}
+impl Default for InstructionPointer {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
#[derive(Debug)]
pub struct DataStack(pub Vec<i32>);
@@ -119,7 +138,7 @@ impl VM {
let bc = &self.wordlist.0[self.ip.word];
match bc[self.ip.offset] {
OpCode::Num(n) => self.stack.0.push(n),
- OpCode::Str(start, end) => debug!("got str: {} to {}", start, end),
+ OpCode::Str(start, end) => debug!("got str: {start} to {end}"),
OpCode::Add => {
let n1 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?;
let n2 = self.stack.0.pop().ok_or(RuntimeError::StackUnderflow)?;
@@ -511,21 +530,17 @@ mod tests {
let mut vm = VM::new(wordlist);
vm.tick()?;
- assert_eq!(vm.stack.0.len(), 0, "call(1) stack len");
+ assert_eq!(vm.stack.0, vec![], "call(1) stack");
vm.tick()?;
- assert_eq!(vm.stack.0.len(), 1, "push 0 stack len");
- assert_eq!(vm.stack.0[0], 0, "push 0 val");
+ assert_eq!(vm.stack.0, vec![0], "push 0 stack");
vm.tick()?;
- assert_eq!(vm.stack.0.len(), 0, "tif stack len");
+ assert_eq!(vm.stack.0, vec![], "tif stack");
vm.tick()?;
- assert_eq!(vm.stack.0.len(), 1, "push 2 stack len");
- assert_eq!(vm.stack.0[0], 2, "push 2 val");
+ assert_eq!(vm.stack.0, vec![2], "push 2 stack");
vm.tick()?;
- assert_eq!(vm.stack.0.len(), 1, "ret stack len");
- assert_eq!(vm.stack.0[0], 2, "ret val");
+ assert_eq!(vm.stack.0, vec![2], "ret stack");
vm.tick()?;
- assert_eq!(vm.stack.0.len(), 2, "push -2 stack len");
- assert_eq!(vm.stack.0[1], -2, "push -2 val");
+ assert_eq!(vm.stack.0, vec![2, -2], "push -2 stack");
Ok(())
}
diff --git a/src/lib.rs b/src/lib.rs
index 60a406f..487a5ce 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,4 @@
use log::{Level, error, info};
-use console_log;
use wasm_bindgen::prelude::*;
pub mod forth;
@@ -25,11 +24,11 @@ impl ExportedByteCode {
fn tr_op(op: &forth::vm::OpCode) -> String {
use forth::vm::OpCode::*;
let s = match op {
- If(t, None) => format!("If({}, none)", t),
- If(t, Some(f)) => format!("If({}, {})", t, f),
- TIf(t, None) => format!("TIf({}, none)", t),
- TIf(t, Some(f)) => format!("TIf({}, {})", t, f),
- other => format!("{:?}", other),
+ If(t, None) => format!("If({t}, none)"),
+ If(t, Some(f)) => format!("If({t}, {f})"),
+ TIf(t, None) => format!("TIf({t}, none)"),
+ TIf(t, Some(f)) => format!("TIf({t}, {f})"),
+ other => format!("{other:?}"),
};
s.to_string()
}
@@ -47,6 +46,10 @@ impl ExportedByteCode {
self.0.len()
}
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+
pub fn at(&self, offset: usize) -> String {
self.0[offset].clone()
}
@@ -86,6 +89,10 @@ impl ExportedWordAnnotations {
self.0.len()
}
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+
pub fn at(&self, offset: usize) -> ExportedAnnotation {
self.0[offset].clone()
}
@@ -104,15 +111,15 @@ impl ExportedVM {
}
pub fn compile(&mut self, text: &str) -> bool {
- let mut comp = forth::compiler::Compiler::new(&text);
+ let mut comp = forth::compiler::Compiler::new(text);
if let Err(e) = comp.compile() {
- error!("couldn't compile program text: {:?}", e);
+ error!("couldn't compile program text: {e:?}");
return false
}
self.annos =
comp.annotations.iter()
.map(|word_anno| -> ExportedWordAnnotations {
- word_anno.into_iter().map(|a| a.into()).collect()
+ word_anno.iter().map(|a| a.into()).collect()
})
.collect();
let vm = forth::vm::VM::new(comp.wordlist);
@@ -124,7 +131,7 @@ impl ExportedVM {
let Some(vm) = &mut self.vm else {
return Err("no vm".to_string())
};
- vm.tick().or_else(|err| Err(format!("runtime error: {:?}", err)))
+ vm.tick().map_err(|err| format!("runtime error: {err:?}"))
}
pub fn run(&mut self) -> Result<usize, String> {
@@ -134,7 +141,7 @@ impl ExportedVM {
vm.ip.word = 0;
vm.ip.offset = 0;
- vm.run().or_else(|err| Err(format!("runtime error: {:?}", err)))
+ vm.run().map_err(|err| format!("runtime error: {err:?}"))
}
pub fn stack(&self) -> Vec<i32> {
@@ -142,7 +149,7 @@ impl ExportedVM {
return vec![]
};
- return vm.stack.0.clone()
+ vm.stack.0.clone()
}
pub fn wordlist(&self) -> Vec<ExportedByteCode> {