Runtime abstraction + browser REPL
Decouple ForthVM from wasmtime via a Runtime trait so the same outer interpreter, compiler, and 200+ word definitions work on both native (wasmtime) and browser (js-sys WebAssembly API) backends. Runtime trait (runtime.rs): - HostAccess trait for memory/global ops inside host function closures - HostFn type: Box<dyn Fn(&mut dyn HostAccess) -> Result<()>> - Runtime trait: memory, globals, table, instantiate, call, register NativeRuntime (runtime_native.rs): - Wraps wasmtime Engine/Store/Memory/Table/Global/Func - CallerHostAccess bridges HostAccess to wasmtime Caller API - Feature-gated behind "native" (default) outer.rs refactor: - ForthVM<R: Runtime> — generic over execution backend - All 87 host functions converted from Func::new closures to HostFn - All memory access via rt.mem_read/write_*, global access via rt.get/set_* - Zero logic changes — pure API conversion wafer-core feature gates: - default = ["native"] includes wasmtime + all native modules - Without "native": pure Rust only (outer, codegen, optimizer, dictionary) Browser REPL (crates/web): - WebRuntime: js-sys WebAssembly.Memory/Table/Global/Module/Instance - WaferRepl: wasm-bindgen entry point (evaluate, data_stack, reset) - WebAssembly.Function with Safari fallback (wrapper module) - Frontend: dark terminal UI, word panel, init code editor, history - Build: wasm-pack build --target web All 452 tests pass (431 unit + 1 benchmark + 9 comparison + 11 compliance).
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
//! WAFER Web REPL — browser-based Forth REPL using WebAssembly.
|
||||
|
||||
mod runtime_web;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use wafer_core::outer::ForthVM;
|
||||
|
||||
use crate::runtime_web::WebRuntime;
|
||||
|
||||
/// Browser REPL for WAFER Forth.
|
||||
#[wasm_bindgen]
|
||||
pub struct WaferRepl {
|
||||
vm: ForthVM<WebRuntime>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl WaferRepl {
|
||||
/// Create a new WAFER REPL instance with all built-in words.
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new() -> Result<WaferRepl, JsError> {
|
||||
let vm = ForthVM::<WebRuntime>::new().map_err(|e| JsError::new(&e.to_string()))?;
|
||||
Ok(WaferRepl { vm })
|
||||
}
|
||||
|
||||
/// Evaluate a line of Forth input. Returns output text.
|
||||
pub fn evaluate(&mut self, input: &str) -> Result<String, JsError> {
|
||||
self.vm
|
||||
.evaluate(input)
|
||||
.map_err(|e| JsError::new(&e.to_string()))?;
|
||||
Ok(self.vm.take_output())
|
||||
}
|
||||
|
||||
/// Get the current data stack as an array (top-first).
|
||||
pub fn data_stack(&mut self) -> Vec<i32> {
|
||||
self.vm.data_stack()
|
||||
}
|
||||
|
||||
/// Check if the VM is currently in compile mode.
|
||||
pub fn is_compiling(&self) -> bool {
|
||||
self.vm.is_compiling()
|
||||
}
|
||||
|
||||
/// Get the current number base (10 = decimal, 16 = hex).
|
||||
pub fn base(&mut self) -> u32 {
|
||||
// BASE is stored at SYSVAR_BASE_VAR in WASM memory
|
||||
self.vm.take_output(); // no-op side effect; just return base
|
||||
10 // TODO: read from memory once we have a getter
|
||||
}
|
||||
|
||||
/// Reset the VM to initial state.
|
||||
pub fn reset(&mut self) -> Result<(), JsError> {
|
||||
self.vm = ForthVM::<WebRuntime>::new().map_err(|e| JsError::new(&e.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user