diff options
| author | Brian Cully <bjc@spork.org> | 2025-08-23 10:22:11 -0400 |
|---|---|---|
| committer | Brian Cully <bjc@spork.org> | 2025-08-23 11:36:31 -0400 |
| commit | 5b8962e35836cf7ccbfdbca312f6b0eb9269e2a6 (patch) | |
| tree | e537f04279c4b1ef27f7040c25beff2551c8bf84 /src | |
| parent | 12c06171b3f94696e852c3910c116f56cbfc5b64 (diff) | |
| download | automathon-5b8962e35836cf7ccbfdbca312f6b0eb9269e2a6.tar.gz automathon-5b8962e35836cf7ccbfdbca312f6b0eb9269e2a6.zip | |
show wordlist in html on compile
Diffstat (limited to 'src')
| -rw-r--r-- | src/forth/interp.rs | 17 | ||||
| -rw-r--r-- | src/forth/parser.rs | 3 | ||||
| -rwxr-xr-x | src/lib.rs | 106 |
3 files changed, 77 insertions, 49 deletions
diff --git a/src/forth/interp.rs b/src/forth/interp.rs index cacc5e3..e41f0d9 100644 --- a/src/forth/interp.rs +++ b/src/forth/interp.rs @@ -29,6 +29,12 @@ pub enum OpCode { #[derive(Clone, Debug)] pub struct ByteCode(pub Vec<OpCode>); +impl std::ops::Deref for ByteCode { + type Target = Vec<OpCode>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} impl ByteCode { pub fn len(&self) -> usize { @@ -67,15 +73,20 @@ pub struct CallStack(pub Vec<InstructionPointer>); #[derive(Clone, Debug)] pub struct WordList(pub Vec<ByteCode>); +impl std::ops::Deref for WordList { + type Target = Vec<ByteCode>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} #[derive(Debug)] pub struct Interp { // todo: don't be pub, probably pub stack: DataStack, - callstack: CallStack, - // todo: don't be pub + pub callstack: CallStack, pub wordlist: WordList, - ip: InstructionPointer, + pub ip: InstructionPointer, } #[derive(Debug)] diff --git a/src/forth/parser.rs b/src/forth/parser.rs index ac6715d..1eb93df 100644 --- a/src/forth/parser.rs +++ b/src/forth/parser.rs @@ -1,7 +1,5 @@ use super::interp::{ByteCode, OpCode, WordList}; -use log::debug; - use std::collections::HashMap; use std::iter::{Enumerate, Iterator}; use std::str::Chars; @@ -81,7 +79,6 @@ impl<'a> Parser<'a> { self.enumerator.by_ref() .find(|(_i, c)| c.is_whitespace())?; let word = &self.text[start..end]; - debug!("Parser::next_word → ‘{}’ ({} → {})", word, start, end); Some((word, start, end)) } @@ -1,4 +1,4 @@ -use log::{Level, debug, info}; +use log::{Level, debug, error, info}; use console_log; use wasm_bindgen::prelude::*; @@ -10,75 +10,95 @@ pub struct ExportedInstructionPointer { pub offset: usize, } +// wasm can't wrap Vec<Vec<String>>, so we need a custom type +// - 23-aug-2025 #[wasm_bindgen] pub struct ExportedByteCode(Vec<String>); +impl ExportedByteCode { + pub fn from_bc(bc: &forth::interp::ByteCode) -> Self { + fn tr(op: &forth::interp::OpCode) -> String { + use forth::interp::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), + }; + s.to_string() + } + + ExportedByteCode(bc.iter().map(tr).collect()) + } +} + #[wasm_bindgen] impl ExportedByteCode { pub fn len(&self) -> usize { self.0.len() } - pub fn at(&self, index: usize) -> String { - self.0[index].clone() + pub fn at(&self, offset: usize) -> String { + self.0[offset].clone() } } #[wasm_bindgen] -pub fn compile(text: &str) { - info!("compiling code: `{}`", text); - let mut p = forth::parser::Parser::new(&text); - p.parse().expect("couldn't parse text"); - debug!("wordlist: {:?}", &p.wordlist); - let interp = forth::interp::Interp::new(p.wordlist); - debug!("interp: {:?}", interp); -} - -#[wasm_bindgen] -pub fn tick() { - info!("executing single instruction"); +pub struct ExportedInterp { + i: Option<forth::interp::Interp>, } #[wasm_bindgen] -pub fn ip() -> ExportedInstructionPointer { - ExportedInstructionPointer { - word: 0, - offset: 0, +impl ExportedInterp { + fn new() -> Self { + Self { i: None } } -} -#[wasm_bindgen] -pub fn wordlist() -> Vec<ExportedByteCode> { - vec![ - ExportedByteCode(vec!["NOP".to_string(), "2".to_string(), "DUP".to_string()]), - ] -} + 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); + return false + } + debug!("wordlist: {:?}", &p.wordlist); + let interp = forth::interp::Interp::new(p.wordlist); + let _ = self.i.insert(interp); + true + } -#[wasm_bindgen] -pub struct ExportedInterp { - i: forth::interp::Interp, -} + pub fn tick(&mut self) { + info!("executing single instruction"); + } -#[wasm_bindgen] -impl ExportedInterp { - fn from_interp(i: forth::interp::Interp) -> Self { - Self { i } + pub fn run(&mut self) { + info!("running to completion"); } - pub fn foo(&self) { - info!("in interp::foo: {:?}", self.i.wordlist); + pub fn wordlist(&self) -> Vec<ExportedByteCode> { + let Some(interp) = self.i.as_ref() else { + return vec![] + }; + + info!("wordlist: ‘{:?}’", interp.wordlist); + interp.wordlist.iter().map(|bc| ExportedByteCode::from_bc(bc)).collect() } -} -#[wasm_bindgen] -pub fn interp() -> ExportedInterp { - let i = forth::interp::Interp::new(forth::interp::WordList(vec![])); - ExportedInterp::from_interp(i) + pub fn ip(&self) -> ExportedInstructionPointer { + let Some(interp) = self.i.as_ref() else { + return ExportedInstructionPointer { word: 0, offset: 0 } + }; + + ExportedInstructionPointer { + word: interp.ip.word, + offset: interp.ip.offset, + } + } } #[wasm_bindgen] -pub fn run() { - info!("running to completion"); +pub fn make_interp() -> ExportedInterp { + ExportedInterp::new() } #[wasm_bindgen(start)] |
