JavaScript/TypeScript host for the feather TCL interpreter, compiled to WebAssembly.
Works in both Node.js and browsers using ES modules.
Build feather.wasm using Zig (via mise):
cd js
mise run buildimport { createFeather } from './feather.js';
const feather = await createFeather('./feather.wasm');
const interp = feather.create();
// Register host commands
feather.register(interp, 'puts', (args) => {
console.log(args.join(' '));
});
// Evaluate TCL scripts
feather.eval(interp, 'set x 10');
feather.eval(interp, 'puts "x = $x"');
// Clean up
feather.destroy(interp);<script type="module">
import { createFeather } from './feather.js';
const feather = await createFeather('./feather.wasm');
const interp = feather.create();
feather.register(interp, 'alert', (args) => {
window.alert(args.join(' '));
});
feather.eval(interp, 'alert "Hello from TCL!"');
</script>Run scripts from the command line:
# Start REPL
node cli.js
# Execute a script file
node cli.js script.tcl
# Execute inline script
node cli.js -e 'puts [expr {2 + 2}]'Create a feather host instance.
wasmSource: Path to.wasmfile (Node.js), URL string,ArrayBuffer, orResponse- Returns:
Promise<Feather>
Create a new interpreter instance.
- Returns:
number(interpreter ID)
Register a host command callable from TCL.
interpId: Interpreter IDname: Command namefn:(args: string[]) => string | number | void
Evaluate a TCL script.
interpId: Interpreter IDscript: TCL source code- Returns: Result string
- Throws:
TclErroron failure
Destroy an interpreter instance.
Expose JavaScript objects to TCL:
// Define a type
feather.registerType(interp, 'Counter', {
methods: {
incr: (counter) => ++counter.value,
get: (counter) => counter.value,
set: (counter, val) => counter.value = parseInt(val),
},
destroy: (counter) => console.log('Counter destroyed'),
});
// Create instances
const handle = feather.createForeign(interp, 'Counter', { value: 0 }, 'counter1');
// Use from TCL (requires host command to create)
feather.register(interp, 'Counter', (args) => {
if (args[0] === 'new') {
return feather.createForeign(interp, 'Counter', { value: 0 });
}
});Open index.html in a browser to try the interactive demo. Make sure feather.wasm is in the same directory.
Type definitions are included in feather.d.ts.
import { createFeather, Feather, TCL_OK } from './feather.js';
const feather: Feather = await createFeather('./feather.wasm');The host provides implementations for all feather_host_* functions as direct WASM imports:
- JavaScript implements all 97 host callbacks (frame, var, proc, string, list, etc.)
- These functions are provided in the
envimports object during WASM instantiation - The C code calls these via
externdeclarations that resolve to WASM imports - All API calls pass
0(NULL) for theopsparameter
Objects live in JavaScript (managed by the host), while the C code only handles opaque integer handles.
See WASM.md for detailed architecture documentation.
MIT