diff options
| -rw-r--r-- | main.mjs | 52 | ||||
| -rw-r--r-- | src/forth/compiler.rs (renamed from src/forth/parser.rs) | 126 | ||||
| -rw-r--r--[-rwxr-xr-x] | src/forth/mod.rs | 24 | ||||
| -rw-r--r-- | src/forth/vm.rs (renamed from src/forth/interp.rs) | 356 | ||||
| -rwxr-xr-x | src/lib.rs | 78 |
5 files changed, 318 insertions, 318 deletions
@@ -1,4 +1,4 @@ -import init, { make_interp } from './pkg/automathon.js'; +import init, { make_vm } from './pkg/automathon.js'; function wordlistElts(wordlist) { return wordlist.map((bc, i) => { @@ -25,12 +25,12 @@ function initWordlist() { }); } -function renderStack(interp) { +function renderStack(vm) { document.querySelectorAll('#stack').forEach(e => { while (e.lastChild) { e.removeChild(e.lastChild); } - interp.stack().reverse() + vm.stack().reverse() .forEach(datum => { const elt = document.createElement('li'); elt.textContent = datum; @@ -40,12 +40,12 @@ function renderStack(interp) { }); } -function renderCallStack(interp) { +function renderCallStack(vm) { document.querySelectorAll('#callstack').forEach(e => { while (e.lastChild) { e.removeChild(e.lastChild); } - interp.callstack().reverse() + vm.callstack().reverse() .forEach(datum => { const elt = document.createElement('li'); elt.textContent = `${datum.word}@${datum.offset}`; @@ -59,9 +59,9 @@ const highRange = new Range(); const highlight = new Highlight(highRange); CSS.highlights.set('exec', highlight); -function renderTextHighlight(interp) { - const ip = interp.ip(); - const anno = interp.annotation_at(ip) +function renderTextHighlight(vm) { + const ip = vm.ip(); + const anno = vm.annotation_at(ip) const src = document.querySelector('#src'); // this assumes the text node is the first child, maybe it isn't? @@ -69,27 +69,27 @@ function renderTextHighlight(interp) { highRange.setEnd(src.childNodes[0], anno.end); } -function tick(interp) { - if (!interp.tick()) { - interp.reset_ip(); +function tick(vm) { + if (!vm.tick()) { + vm.reset_ip(); } - const { word, offset } = interp.ip(); + const { word, offset } = vm.ip(); document.querySelectorAll('#wordlist .ip').forEach(e => e.classList.remove('ip')); const sel = selectorForIP(word, offset); document.querySelectorAll(sel).forEach(e => { e.classList.add('ip'); }); - renderStack(interp); - renderCallStack(interp); - renderTextHighlight(interp); + renderStack(vm); + renderCallStack(vm); + renderTextHighlight(vm); } async function loaded() { console.debug('running init'); const mod = await init(); console.debug('init done', mod); - const interp = make_interp(); + const vm = make_vm(); document.querySelector('#compile').onclick = e => { console.debug('compile clicked', e); @@ -104,32 +104,32 @@ async function loaded() { const text = document.querySelector('#src').textContent + '\n'; console.debug('compiling', text); const start = performance.now(); - const res = interp.compile(text); + const res = vm.compile(text); const end = performance.now(); console.info(`compile took ${end-start} ms`); if (res) { - const wordlist = wordlistElts(interp.wordlist()); + const wordlist = wordlistElts(vm.wordlist()); wordlist.forEach(elt => wordlistContainer.appendChild(elt)); initWordlist(); - renderStack(interp); - renderCallStack(interp); - renderTextHighlight(interp); + renderStack(vm); + renderCallStack(vm); + renderTextHighlight(vm); } }; document.querySelector('#tick').onclick = e => { console.debug('tick clicked', e); - tick(interp); + tick(vm); }; document.querySelector('#bench').onclick = e => { console.debug('bench clicked', e); const start = performance.now(); let tickCount = 0; for (let i = 0; i < 1_000_000; i++) { - tickCount += interp.run(); + tickCount += vm.run(); } const end = performance.now(); console.info(`run took ${end-start} ms for ${tickCount} ticks (${(end-start)/tickCount * 1_000_000} ns/tick).`); - console.info('result', interp.stack()); + console.info('result', vm.stack()); }; let blinkenRun = false; @@ -144,13 +144,13 @@ async function loaded() { const frameRate = 6; const onTimeout = _ => { if (blinkenRun) { - tick(interp); + tick(vm); setTimeout(onTimeout, 1_000 / frameRate); } } setTimeout(onTimeout); } - window.interp = interp; + window.vm = vm; } document.addEventListener('DOMContentLoaded', loaded); diff --git a/src/forth/parser.rs b/src/forth/compiler.rs index c934c4a..9120506 100644 --- a/src/forth/parser.rs +++ b/src/forth/compiler.rs @@ -1,18 +1,18 @@ -use super::interp::{ByteCode, OpCode, WordList}; +use super::vm::{ByteCode, OpCode, WordList}; use std::collections::HashMap; use std::iter::{Enumerate, Iterator}; use std::str::Chars; #[derive(Debug)] -pub enum ParseError { +pub enum CompileError { EOF, DefStackEmpty, MissingIf, MissingQuote, UnknownWord(String), } -impl std::fmt::Display for ParseError { +impl std::fmt::Display for CompileError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::EOF => write!(f, "premature end-of-file"), @@ -23,9 +23,9 @@ impl std::fmt::Display for ParseError { } } } -impl std::error::Error for ParseError {} +impl std::error::Error for CompileError {} -type ParseResult<T> = Result<T, ParseError>; +type CompileResult<T> = Result<T, CompileError>; // todo: the annotations should be directly tied to the wordlist so // they can't get out of sync. @@ -45,7 +45,7 @@ pub enum IfClauses { } #[derive(Debug)] -pub struct Parser<'a> { +pub struct Compiler<'a> { text: &'a str, enumerator: Enumerate<Chars<'a>>, // list of all word definitions, in the order defined. the main @@ -64,7 +64,7 @@ pub struct Parser<'a> { if_stack: Vec<(IfClauses, Annotation)>, } -impl<'a> Parser<'a> { +impl<'a> Compiler<'a> { pub fn new(text: &'a str) -> Self { let mut wl = vec![]; // main routine is always the first entry. @@ -115,7 +115,7 @@ impl<'a> Parser<'a> { self.anno_mut().push(anno); } - pub fn parse(&mut self) -> ParseResult<()> { + pub fn compile(&mut self) -> CompileResult<()> { while let Some((word, start, end)) = self.next_word() { let anno = Annotation { loc: (start, end) }; if let Ok(i) = word.parse::<i32>() { @@ -128,11 +128,11 @@ impl<'a> Parser<'a> { let (s_end, _) = self.enumerator .find(|(_i, c)| return *c == '"') - .ok_or(ParseError::MissingQuote)?; + .ok_or(CompileError::MissingQuote)?; self.bc_push(OpCode::Str(end+1, s_end), anno); }, ":" => { - let (name, _, _) = self.next_word().ok_or(ParseError::EOF)?; + let (name, _, _) = self.next_word().ok_or(CompileError::EOF)?; self.wordalog.0.insert(name, self.wordlist.0.len()); self.defstack.push(self.wordlist.0.len()); self.wordlist.0.push(ByteCode(vec![])); @@ -155,7 +155,7 @@ impl<'a> Parser<'a> { }, _ => self.bc_push(OpCode::Ret, anno), } - self.defstack.pop().ok_or(ParseError::DefStackEmpty)?; + self.defstack.pop().ok_or(CompileError::DefStackEmpty)?; }, "if" => { let i = self.wordlist.0.len(); @@ -175,8 +175,8 @@ impl<'a> Parser<'a> { self.defstack.push(i); let (true_clause, anno) = match self.if_stack.pop() { - None => return Err(ParseError::MissingIf), - Some((IfClauses::TrueFalse(_, _), _)) => return Err(ParseError::MissingIf), + None => return Err(CompileError::MissingIf), + Some((IfClauses::TrueFalse(_, _), _)) => return Err(CompileError::MissingIf), Some((IfClauses::True(cl), anno)) => (cl, anno), }; self.if_stack.push((IfClauses::TrueFalse(true_clause, i), anno)); @@ -186,7 +186,7 @@ impl<'a> Parser<'a> { self.defstack.pop(); match self.if_stack.pop() { - None => return Err(ParseError::MissingIf), + None => return Err(CompileError::MissingIf), Some((IfClauses::True(true_clause), anno)) => { self.bc_push(OpCode::If(true_clause, None), anno); }, @@ -206,7 +206,7 @@ impl<'a> Parser<'a> { ">=" => self.bc_push(OpCode::GTE, anno), "<" => self.bc_push(OpCode::LT, anno), "<=" => self.bc_push(OpCode::LTE, anno), - other => return Err(ParseError::UnknownWord(String::from(other))), + other => return Err(CompileError::UnknownWord(String::from(other))), } } } @@ -217,11 +217,11 @@ impl<'a> Parser<'a> { #[cfg(test)] mod tests { use super::*; - use super::super::interp::OpCode; + use super::super::vm::OpCode; - fn parser_for(text: &str) -> Parser { - let mut p = Parser::new(text); - p.parse().expect("badparse"); + fn compiler_for(text: &str) -> Compiler { + let mut p = Compiler::new(text); + p.compile().expect("badparse"); p } @@ -233,42 +233,42 @@ mod tests { #[test] fn literal_num() { - let p = parser_for("1\n"); - let main = &p.wordlist.0[0]; + let comp = compiler_for("1\n"); + let main = &comp.wordlist.0[0]; assert_eq!(main.len(), 1); assert_eq!(main[0], OpCode::Num(1)); } #[test] fn literal_string() { - let p = parser_for(r#"s" hello there""#); - let main = &p.wordlist.0[0]; + 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)); } #[test] fn add_opcode() { - let p = parser_for("+\n"); - let main = &p.wordlist.0[0]; + let comp = compiler_for("+\n"); + let main = &comp.wordlist.0[0]; assert_eq!(main.len(), 1); assert_eq!(main[0], OpCode::Add); } #[test] fn sub_opcode() { - let p = parser_for("-\n"); - let main = &p.wordlist.0[0]; + let comp = compiler_for("-\n"); + let main = &comp.wordlist.0[0]; assert_eq!(main.len(), 1); assert_eq!(main[0], OpCode::Sub); } #[test] fn def_word() { - let p = parser_for(": add2 2 + ; 3 add2\n"); - let main = &p.wordlist.0[0]; - let add2_index = p.wordalog.0.get("add2").expect("add2 has entry in wordlist"); - let add2 = &p.wordlist.0[*add2_index]; + 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"); + 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)); @@ -280,13 +280,13 @@ mod tests { #[test] fn if_then() { - let p = parser_for("if 1 2 + then\n"); - eprintwordlist(&p.wordlist); - assert_eq!(p.wordlist.0.len(), 2); - let main = &p.wordlist.0[0]; + let comp = compiler_for("if 1 2 + then\n"); + 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)); - let tr = &p.wordlist.0[1]; + 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)); @@ -296,19 +296,19 @@ mod tests { #[test] fn if_else_then() { - let p = parser_for("if 1 2 + else 1 2 - then\n"); - eprintwordlist(&p.wordlist); - assert_eq!(p.wordlist.0.len(), 3, "wordlist length"); - let main = &p.wordlist.0[0]; + let comp = compiler_for("if 1 2 + else 1 2 - then\n"); + eprintwordlist(&comp.wordlist); + assert_eq!(comp.wordlist.0.len(), 3, "wordlist length"); + let main = &comp.wordlist.0[0]; assert_eq!(main.len(), 1); assert_eq!(main[0], OpCode::If(1, Some(2))); - let tr = &p.wordlist.0[1]; + 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); - let fl = &p.wordlist.0[2]; + 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)); @@ -318,24 +318,24 @@ mod tests { #[test] fn tail_call() { - let p = parser_for(": first + ; : second 3 first ; 1 second\n"); - assert_eq!(p.wordlist.0.len(), 3, "wordlist length"); - eprintwordlist(&p.wordlist); + let comp = compiler_for(": first + ; : second 3 first ; 1 second\n"); + assert_eq!(comp.wordlist.0.len(), 3, "wordlist length"); + eprintwordlist(&comp.wordlist); } #[test] fn tail_if() { - let p = parser_for(": t if 1 2 + then ; t\n"); - eprintwordlist(&p.wordlist); - assert_eq!(p.wordlist.0.len(), 3); - let main = &p.wordlist.0[0]; + let comp = compiler_for(": t if 1 2 + then ; t\n"); + 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)); - let t = &p.wordlist.0[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); - let tr = &p.wordlist.0[2]; + 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)); @@ -345,23 +345,23 @@ mod tests { #[test] fn tail_if_else() { - let p = parser_for(": t if 1 2 + else 1 2 - then ; t\n"); - eprintwordlist(&p.wordlist); - assert_eq!(p.wordlist.0.len(), 4, "wordlist length"); - let main = &p.wordlist.0[0]; + let comp = compiler_for(": t if 1 2 + else 1 2 - then ; t\n"); + 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)); - let t = &p.wordlist.0[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); - let tr = &p.wordlist.0[2]; + 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); - let fl = &p.wordlist.0[3]; + 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)); @@ -371,13 +371,13 @@ mod tests { #[test] fn recursion() { - let p = parser_for(": foo foo ; foo\n"); - eprintwordlist(&p.wordlist); - assert_eq!(p.wordlist.0.len(), 2); - let main = &p.wordlist.0[0]; + let comp = compiler_for(": foo foo ; foo\n"); + 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)); - let foo = &p.wordlist.0[1]; + let foo = &comp.wordlist.0[1]; assert_eq!(foo.len(), 1); assert_eq!(foo[0], OpCode::TCall(1)); } diff --git a/src/forth/mod.rs b/src/forth/mod.rs index 063b40e..f244ef4 100755..100644 --- a/src/forth/mod.rs +++ b/src/forth/mod.rs @@ -1,16 +1,16 @@ -pub mod interp; -pub mod parser; +pub mod compiler; +pub mod vm; #[cfg(test)] mod tests { use super::{ - interp::{Interp, WordList}, - parser::Parser + vm::{VM, WordList}, + compiler::Compiler, }; - fn parser_for(text: &str) -> Parser { - let mut p = Parser::new(text); - p.parse().expect("badparse"); + fn parser_for(text: &str) -> Compiler { + let mut p = Compiler::new(text); + p.compile().expect("badparse"); p } @@ -26,10 +26,10 @@ mod tests { let prog = ": fac dup 1 > if dup 1 - fac * then ; 5 fac\n"; let p = parser_for(prog); eprintwordlist(&p.wordlist); - let mut interp = Interp::new(p.wordlist); - interp.run().expect("should run to completion"); - eprintln!("stack: {:?}", interp.stack); - assert_eq!(interp.stack.0.len(), 1, "factorial result stack len"); - assert_eq!(interp.stack.0[0], 120, "factorial result value"); + let mut vm = VM::new(p.wordlist); + vm.run().expect("should run to completion"); + eprintln!("stack: {:?}", vm.stack); + assert_eq!(vm.stack.0.len(), 1, "factorial result stack len"); + assert_eq!(vm.stack.0[0], 120, "factorial result value"); } } diff --git a/src/forth/interp.rs b/src/forth/vm.rs index 512c022..b4ce674 100644 --- a/src/forth/interp.rs +++ b/src/forth/vm.rs @@ -81,7 +81,7 @@ impl std::ops::Deref for WordList { } #[derive(Debug)] -pub struct Interp { +pub struct VM { // todo: don't be pub, probably pub stack: DataStack, pub callstack: CallStack, @@ -94,7 +94,7 @@ pub enum RuntimeError { StackUnderflow, } -impl Interp { +impl VM { pub fn new(wordlist: WordList) -> Self { Self { stack: DataStack(Vec::new()), @@ -232,22 +232,22 @@ mod tests { fn simple_ticks() -> Result<(), RuntimeError> { let mut wordlist = WordList(vec![]); wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Num(3), OpCode::Add])); - let mut interp = Interp::new(wordlist); - assert_eq!(interp.tick()?, true, "first arg running"); - assert_eq!(interp.ip.word, 0); - assert_eq!(interp.ip.offset, 1); - assert_eq!(interp.stack.0.len(), 1); - assert_eq!(interp.stack.0[0], 2, "first argument"); - assert_eq!(interp.tick()?, true, "second arg running"); - assert_eq!(interp.ip.word, 0); - assert_eq!(interp.ip.offset, 2); - assert_eq!(interp.stack.0.len(), 2); - assert_eq!(interp.stack.0[1], 3, "second argument"); - assert_eq!(interp.tick()?, false, "stopped after addition"); - assert_eq!(interp.ip.word, 0); - assert_eq!(interp.ip.offset, 3); - assert_eq!(interp.stack.0.len(), 1); - assert_eq!(interp.stack.0[0], 5, "result of addition"); + let mut vm = VM::new(wordlist); + assert_eq!(vm.tick()?, true, "first arg running"); + assert_eq!(vm.ip.word, 0); + assert_eq!(vm.ip.offset, 1); + assert_eq!(vm.stack.0.len(), 1); + assert_eq!(vm.stack.0[0], 2, "first argument"); + assert_eq!(vm.tick()?, true, "second arg running"); + assert_eq!(vm.ip.word, 0); + assert_eq!(vm.ip.offset, 2); + assert_eq!(vm.stack.0.len(), 2); + assert_eq!(vm.stack.0[1], 3, "second argument"); + assert_eq!(vm.tick()?, false, "stopped after addition"); + assert_eq!(vm.ip.word, 0); + assert_eq!(vm.ip.offset, 3); + assert_eq!(vm.stack.0.len(), 1); + assert_eq!(vm.stack.0[0], 5, "result of addition"); Ok(()) } @@ -259,50 +259,50 @@ mod tests { OpCode::Num(2), OpCode::Num(3), OpCode::Call(1), OpCode::Num(-2), OpCode::Add, OpCode::Sub, OpCode::Ret, ])); - let mut interp = Interp::new(wordlist); + let mut vm = VM::new(wordlist); // "sub" definition - interp.wordlist.0.push(ByteCode(vec![ + vm.wordlist.0.push(ByteCode(vec![ OpCode::Sub, OpCode::Ret, ])); - interp.tick()?; - assert_eq!(interp.ip.word, 0); - assert_eq!(interp.ip.offset, 1); - assert_eq!(interp.stack.0.len(), 1); - assert_eq!(interp.stack.0[0], 2, "first argument"); - interp.tick()?; - assert_eq!(interp.ip.word, 0); - assert_eq!(interp.ip.offset, 2); - assert_eq!(interp.stack.0.len(), 2); - assert_eq!(interp.stack.0[1], 3, "second argument"); + vm.tick()?; + assert_eq!(vm.ip.word, 0); + assert_eq!(vm.ip.offset, 1); + assert_eq!(vm.stack.0.len(), 1); + assert_eq!(vm.stack.0[0], 2, "first argument"); + vm.tick()?; + assert_eq!(vm.ip.word, 0); + assert_eq!(vm.ip.offset, 2); + assert_eq!(vm.stack.0.len(), 2); + assert_eq!(vm.stack.0[1], 3, "second argument"); - interp.tick()?; // call sub - assert_eq!(interp.ip.word, 1); - assert_eq!(interp.ip.offset, 0); - assert_eq!(interp.stack.0.len(), 2); + vm.tick()?; // call sub + assert_eq!(vm.ip.word, 1); + assert_eq!(vm.ip.offset, 0); + assert_eq!(vm.stack.0.len(), 2); - interp.tick()?; // - - assert_eq!(interp.ip.word, 1); - assert_eq!(interp.ip.offset, 1); - assert_eq!(interp.stack.0.len(), 1); + vm.tick()?; // - + assert_eq!(vm.ip.word, 1); + assert_eq!(vm.ip.offset, 1); + assert_eq!(vm.stack.0.len(), 1); - interp.tick()?; // ret - assert_eq!(interp.ip.word, 0); - assert_eq!(interp.ip.offset, 3); - assert_eq!(interp.stack.0.len(), 1); - assert_eq!(interp.stack.0[0], -1, "result of sub word"); + vm.tick()?; // ret + assert_eq!(vm.ip.word, 0); + assert_eq!(vm.ip.offset, 3); + assert_eq!(vm.stack.0.len(), 1); + assert_eq!(vm.stack.0[0], -1, "result of sub word"); - interp.tick()?; // 2 - assert_eq!(interp.ip.word, 0); - assert_eq!(interp.ip.offset, 4); - assert_eq!(interp.stack.0.len(), 2); - assert_eq!(interp.stack.0[1], -2, "post sub arg"); + vm.tick()?; // 2 + assert_eq!(vm.ip.word, 0); + assert_eq!(vm.ip.offset, 4); + assert_eq!(vm.stack.0.len(), 2); + assert_eq!(vm.stack.0[1], -2, "post sub arg"); - interp.tick()?; // - - assert_eq!(interp.ip.word, 0); - assert_eq!(interp.ip.offset, 5); - assert_eq!(interp.stack.0.len(), 1); - assert_eq!(interp.stack.0[0], -3, "add opcode result"); + vm.tick()?; // - + assert_eq!(vm.ip.word, 0); + assert_eq!(vm.ip.offset, 5); + assert_eq!(vm.stack.0.len(), 1); + assert_eq!(vm.stack.0[0], -3, "add opcode result"); Ok(()) } @@ -313,14 +313,14 @@ mod tests { wordlist.0.push(ByteCode(vec![OpCode::Call(1)])); wordlist.0.push(ByteCode(vec![OpCode::TCall(2), OpCode::Ret])); wordlist.0.push(ByteCode(vec![OpCode::Ret])); - let mut interp = Interp::new(wordlist); + let mut vm = VM::new(wordlist); - interp.tick()?; // call - assert_eq!(interp.callstack.0.len(), 1, "callstack after call"); - interp.tick()?; // tcall - assert_eq!(interp.callstack.0.len(), 1, "callstack after tcall"); - interp.tick()?; // ret - assert_eq!(interp.callstack.0.len(), 0, "callstack after ret"); + vm.tick()?; // call + assert_eq!(vm.callstack.0.len(), 1, "callstack after call"); + vm.tick()?; // tcall + assert_eq!(vm.callstack.0.len(), 1, "callstack after tcall"); + vm.tick()?; // ret + assert_eq!(vm.callstack.0.len(), 0, "callstack after ret"); Ok(()) } @@ -329,22 +329,22 @@ mod tests { let mut wordlist = WordList(vec![]); wordlist.0.push(ByteCode(vec![OpCode::Num(-1), OpCode::If(1, None), OpCode::Num(0)])); wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); - let mut interp = Interp::new(wordlist); + let mut vm = VM::new(wordlist); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "push -1 stack len"); - assert_eq!(interp.stack.0[0], -1, "push -1 val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 0, "if stack "); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "push 1 stack len"); - assert_eq!(interp.stack.0[0], 1, "push 1 val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "ret stack len"); - assert_eq!(interp.stack.0[0], 1, "ret stack val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 2, "push 0 stack len"); - assert_eq!(interp.stack.0[1], 0, "push 0 stack val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "push -1 stack len"); + assert_eq!(vm.stack.0[0], -1, "push -1 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 0, "if stack "); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "push 1 stack len"); + assert_eq!(vm.stack.0[0], 1, "push 1 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "ret stack len"); + assert_eq!(vm.stack.0[0], 1, "ret stack val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 2, "push 0 stack len"); + assert_eq!(vm.stack.0[1], 0, "push 0 stack val"); Ok(()) } @@ -354,16 +354,16 @@ mod tests { let mut wordlist = WordList(vec![]); wordlist.0.push(ByteCode(vec![OpCode::Num(0), OpCode::If(1, None), OpCode::Num(-1)])); wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); - let mut interp = Interp::new(wordlist); + let mut vm = VM::new(wordlist); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "push 0 stack len"); - assert_eq!(interp.stack.0[0], 0, "push 0 val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 0, "if stack len"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "push -1 stack len"); - assert_eq!(interp.stack.0[0], -1, "push -1 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "push 0 stack len"); + assert_eq!(vm.stack.0[0], 0, "push 0 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 0, "if stack len"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "push -1 stack len"); + assert_eq!(vm.stack.0[0], -1, "push -1 val"); Ok(()) } @@ -374,22 +374,22 @@ mod tests { wordlist.0.push(ByteCode(vec![OpCode::Num(-1), OpCode::If(1, Some(2)), OpCode::Num(-2)])); wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret])); - let mut interp = Interp::new(wordlist); + let mut vm = VM::new(wordlist); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "push -1 stack len"); - assert_eq!(interp.stack.0[0], -1, "push -1 val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 0, "if stack len"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "push 1 stack len"); - assert_eq!(interp.stack.0[0], 1, "push 1 val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "ret stack len"); - assert_eq!(interp.stack.0[0], 1, "ret val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 2, "push -2 stack len"); - assert_eq!(interp.stack.0[1], -2, "push -2 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "push -1 stack len"); + assert_eq!(vm.stack.0[0], -1, "push -1 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 0, "if stack len"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "push 1 stack len"); + assert_eq!(vm.stack.0[0], 1, "push 1 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "ret stack len"); + assert_eq!(vm.stack.0[0], 1, "ret val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 2, "push -2 stack len"); + assert_eq!(vm.stack.0[1], -2, "push -2 val"); Ok(()) } @@ -400,22 +400,22 @@ mod tests { wordlist.0.push(ByteCode(vec![OpCode::Num(0), OpCode::If(1, Some(2)), OpCode::Num(-2)])); wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret])); - let mut interp = Interp::new(wordlist); + let mut vm = VM::new(wordlist); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "push 0 stack len"); - assert_eq!(interp.stack.0[0], 0, "push 0 val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 0, "if"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "push 2 stack len"); - assert_eq!(interp.stack.0[0], 2, "push 2 val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "ret"); - assert_eq!(interp.stack.0[0], 2, "ret"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 2, "push -2 stack len"); - assert_eq!(interp.stack.0[1], -2, "push -2 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "push 0 stack len"); + assert_eq!(vm.stack.0[0], 0, "push 0 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 0, "if"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "push 2 stack len"); + assert_eq!(vm.stack.0[0], 2, "push 2 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "ret"); + assert_eq!(vm.stack.0[0], 2, "ret"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 2, "push -2 stack len"); + assert_eq!(vm.stack.0[1], -2, "push -2 val"); Ok(()) } @@ -426,24 +426,24 @@ mod tests { wordlist.0.push(ByteCode(vec![OpCode::Call(1), OpCode::Num(0)])); wordlist.0.push(ByteCode(vec![OpCode::Num(-1), OpCode::TIf(2, None), OpCode::Ret])); wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); - let mut interp = Interp::new(wordlist); + let mut vm = VM::new(wordlist); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 0, "call(1) stack len"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "push -1 stack len"); - assert_eq!(interp.stack.0[0], -1, "push -1 val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 0, "tif stack "); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "push 1 stack len"); - assert_eq!(interp.stack.0[0], 1, "push 1 val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "ret stack len"); - assert_eq!(interp.stack.0[0], 1, "ret stack val"); - interp.tick()?; // no ret on wordlist[1] since it's ‘tif’ - assert_eq!(interp.stack.0.len(), 2, "push 0 stack len"); - assert_eq!(interp.stack.0[1], 0, "push 0 stack val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 0, "call(1) stack len"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "push -1 stack len"); + assert_eq!(vm.stack.0[0], -1, "push -1 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 0, "tif stack "); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "push 1 stack len"); + assert_eq!(vm.stack.0[0], 1, "push 1 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "ret stack len"); + assert_eq!(vm.stack.0[0], 1, "ret stack val"); + vm.tick()?; // no ret on wordlist[1] since it's ‘tif’ + assert_eq!(vm.stack.0.len(), 2, "push 0 stack len"); + assert_eq!(vm.stack.0[1], 0, "push 0 stack val"); Ok(()) } @@ -454,20 +454,20 @@ mod tests { wordlist.0.push(ByteCode(vec![OpCode::Call(1), OpCode::Num(0)])); wordlist.0.push(ByteCode(vec![OpCode::Num(0), OpCode::TIf(2, None), OpCode::Ret])); wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); - let mut interp = Interp::new(wordlist); + let mut vm = VM::new(wordlist); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 0, "call(1) stack len"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "push 0 stack len"); - assert_eq!(interp.stack.0[0], 0, "push 0 val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 0, "tif stack len"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 0, "ret stack len"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "push 0 stack len"); - assert_eq!(interp.stack.0[0], 0, "push 0 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 0, "call(1) stack len"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "push 0 stack len"); + assert_eq!(vm.stack.0[0], 0, "push 0 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 0, "tif stack len"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 0, "ret stack len"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "push 0 stack len"); + assert_eq!(vm.stack.0[0], 0, "push 0 val"); Ok(()) } @@ -479,24 +479,24 @@ mod tests { wordlist.0.push(ByteCode(vec![OpCode::Num(-1), OpCode::TIf(2, Some(3)), OpCode::Ret])); wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret])); - let mut interp = Interp::new(wordlist); + let mut vm = VM::new(wordlist); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 0, "call(1) stack len"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "push -1 stack len"); - assert_eq!(interp.stack.0[0], -1, "push -1 val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 0, "tif stack len"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "push 1 stack len"); - assert_eq!(interp.stack.0[0], 1, "push 1 val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "ret stack len"); - assert_eq!(interp.stack.0[0], 1, "ret val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 2, "push -2 stack len"); - assert_eq!(interp.stack.0[1], -2, "push -2 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 0, "call(1) stack len"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "push -1 stack len"); + assert_eq!(vm.stack.0[0], -1, "push -1 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 0, "tif stack len"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "push 1 stack len"); + assert_eq!(vm.stack.0[0], 1, "push 1 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "ret stack len"); + assert_eq!(vm.stack.0[0], 1, "ret val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 2, "push -2 stack len"); + assert_eq!(vm.stack.0[1], -2, "push -2 val"); Ok(()) } @@ -508,24 +508,24 @@ mod tests { wordlist.0.push(ByteCode(vec![OpCode::Num(0), OpCode::TIf(2, Some(3)), OpCode::Ret])); wordlist.0.push(ByteCode(vec![OpCode::Num(1), OpCode::Ret])); wordlist.0.push(ByteCode(vec![OpCode::Num(2), OpCode::Ret])); - let mut interp = Interp::new(wordlist); + let mut vm = VM::new(wordlist); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 0, "call(1) stack len"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "push 0 stack len"); - assert_eq!(interp.stack.0[0], 0, "push 0 val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 0, "tif stack len"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "push 2 stack len"); - assert_eq!(interp.stack.0[0], 2, "push 2 val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 1, "ret stack len"); - assert_eq!(interp.stack.0[0], 2, "ret val"); - interp.tick()?; - assert_eq!(interp.stack.0.len(), 2, "push -2 stack len"); - assert_eq!(interp.stack.0[1], -2, "push -2 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 0, "call(1) stack len"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "push 0 stack len"); + assert_eq!(vm.stack.0[0], 0, "push 0 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 0, "tif stack len"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "push 2 stack len"); + assert_eq!(vm.stack.0[0], 2, "push 2 val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 1, "ret stack len"); + assert_eq!(vm.stack.0[0], 2, "ret val"); + vm.tick()?; + assert_eq!(vm.stack.0.len(), 2, "push -2 stack len"); + assert_eq!(vm.stack.0[1], -2, "push -2 val"); Ok(()) } @@ -10,8 +10,8 @@ pub struct ExportedInstructionPointer { pub offset: usize, } -impl From<&forth::interp::InstructionPointer> for ExportedInstructionPointer { - fn from(ip: &forth::interp::InstructionPointer) -> Self { +impl From<&forth::vm::InstructionPointer> for ExportedInstructionPointer { + fn from(ip: &forth::vm::InstructionPointer) -> Self { Self { word: ip.word, offset: ip.offset } } } @@ -22,8 +22,8 @@ impl From<&forth::interp::InstructionPointer> for ExportedInstructionPointer { pub struct ExportedByteCode(Vec<String>); impl ExportedByteCode { - fn tr_op(op: &forth::interp::OpCode) -> String { - use forth::interp::OpCode::*; + 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), @@ -35,8 +35,8 @@ impl ExportedByteCode { } } -impl From<&forth::interp::ByteCode> for ExportedByteCode { - fn from(v: &forth::interp::ByteCode) -> Self { +impl From<&forth::vm::ByteCode> for ExportedByteCode { + fn from(v: &forth::vm::ByteCode) -> Self { Self(v.iter().map(Self::tr_op).collect()) } } @@ -60,8 +60,8 @@ pub struct ExportedAnnotation { pub end: usize, } -impl From<&forth::parser::Annotation> for ExportedAnnotation { - fn from(v: &forth::parser::Annotation) -> Self { +impl From<&forth::compiler::Annotation> for ExportedAnnotation { + fn from(v: &forth::compiler::Annotation) -> Self { Self { start: v.loc.0, end: v.loc.1, @@ -92,65 +92,65 @@ impl ExportedWordAnnotations { } #[wasm_bindgen] -pub struct ExportedInterp { +pub struct ExportedVM { annos: Vec<ExportedWordAnnotations>, - i: Option<forth::interp::Interp>, + vm: Option<forth::vm::VM>, } #[wasm_bindgen] -impl ExportedInterp { +impl ExportedVM { fn new() -> Self { - Self { annos: vec![], i: None } + Self { annos: vec![], vm: None } } pub fn compile(&mut self, text: &str) -> bool { - let mut p = forth::parser::Parser::new(&text); - if let Err(e) = p.parse() { - error!("couldn't parse program text: {:?}", e); + let mut comp = forth::compiler::Compiler::new(&text); + if let Err(e) = comp.compile() { + error!("couldn't compile program text: {:?}", e); return false } self.annos = - p.annotations.iter() + comp.annotations.iter() .map(|word_anno| -> ExportedWordAnnotations { word_anno.into_iter().map(|a| a.into()).collect() }) .collect(); - let interp = forth::interp::Interp::new(p.wordlist); - let _ = self.i.insert(interp); + let vm = forth::vm::VM::new(comp.wordlist); + let _ = self.vm.insert(vm); true } pub fn tick(&mut self) -> Result<bool, String> { - let Some(interp) = &mut self.i else { - return Err("no interpreter".to_string()) + let Some(vm) = &mut self.vm else { + return Err("no vm".to_string()) }; - interp.tick().or_else(|err| Err(format!("runtime error: {:?}", err))) + vm.tick().or_else(|err| Err(format!("runtime error: {:?}", err))) } pub fn run(&mut self) -> Result<usize, String> { - let Some(interp) = &mut self.i else { - return Err("no interpreter".to_string()) + let Some(vm) = &mut self.vm else { + return Err("no vm".to_string()) }; - interp.ip.word = 0; - interp.ip.offset = 0; - interp.run().or_else(|err| Err(format!("runtime error: {:?}", err))) + vm.ip.word = 0; + vm.ip.offset = 0; + vm.run().or_else(|err| Err(format!("runtime error: {:?}", err))) } pub fn stack(&self) -> Vec<i32> { - let Some(interp) = &self.i else { + let Some(vm) = &self.vm else { return vec![] }; - return interp.stack.0.clone() + return vm.stack.0.clone() } pub fn wordlist(&self) -> Vec<ExportedByteCode> { - let Some(interp) = &self.i else { + let Some(vm) = &self.vm else { return vec![] }; - interp.wordlist.iter().map(|bc| bc.into()).collect() + vm.wordlist.iter().map(|bc| bc.into()).collect() } pub fn annotations(&self) -> Vec<ExportedWordAnnotations> { @@ -162,33 +162,33 @@ impl ExportedInterp { } pub fn callstack(&self) -> Vec<ExportedInstructionPointer> { - let Some(interp) = &self.i else { + let Some(vm) = &self.vm else { return vec![] }; - interp.callstack.0.iter() + vm.callstack.0.iter() .map(|ip| ip.into()) .collect() } pub fn ip(&self) -> ExportedInstructionPointer { - let Some(interp) = &self.i else { + let Some(vm) = &self.vm else { return ExportedInstructionPointer { word: 0, offset: 0 } }; - (&interp.ip).into() + (&vm.ip).into() } pub fn reset_ip(&mut self) { - let Some(interp) = &mut self.i else { + let Some(vm) = &mut self.vm else { return; }; - interp.ip.word = 0; - interp.ip.offset = 0; + vm.ip.word = 0; + vm.ip.offset = 0; } } #[wasm_bindgen] -pub fn make_interp() -> ExportedInterp { - ExportedInterp::new() +pub fn make_vm() -> ExportedVM { + ExportedVM::new() } #[wasm_bindgen(start)] |
