{"id":23722661,"url":"https://github.com/remko/waforth","last_synced_at":"2025-04-09T11:05:12.737Z","repository":{"id":40373178,"uuid":"133494313","full_name":"remko/waforth","owner":"remko","description":"Small but complete dynamic Forth Interpreter/Compiler for and in WebAssembly","archived":false,"fork":false,"pushed_at":"2025-02-19T20:44:00.000Z","size":8215,"stargazers_count":517,"open_issues_count":7,"forks_count":28,"subscribers_count":22,"default_branch":"master","last_synced_at":"2025-04-02T09:53:11.157Z","etag":null,"topics":["compiler","compilers","forth","interpreter","javascript","logo","turtle-graphics","typescript","wasm","webassembly"],"latest_commit_sha":null,"homepage":"https://mko.re/waforth","language":"WebAssembly","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/remko.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-05-15T09:39:31.000Z","updated_at":"2025-03-30T21:04:06.000Z","dependencies_parsed_at":"2024-02-23T20:22:53.786Z","dependency_job_id":"d1064e6f-4554-48ce-9e05-de1a5432bf48","html_url":"https://github.com/remko/waforth","commit_stats":{"total_commits":526,"total_committers":5,"mean_commits":105.2,"dds":0.03992395437262353,"last_synced_commit":"f9a07b2bcfe8bd10897e0c4e7c114980e9761d84"},"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remko%2Fwaforth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remko%2Fwaforth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remko%2Fwaforth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/remko%2Fwaforth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/remko","download_url":"https://codeload.github.com/remko/waforth/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248027406,"owners_count":21035594,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["compiler","compilers","forth","interpreter","javascript","logo","turtle-graphics","typescript","wasm","webassembly"],"created_at":"2024-12-30T23:55:19.105Z","updated_at":"2025-04-09T11:05:12.713Z","avatar_url":"https://github.com/remko.png","language":"WebAssembly","funding_links":[],"categories":["Languages","Compilers \u0026 Language Interpreters"],"sub_categories":["Forth"],"readme":"\u003cimg src=\"./doc/logo.svg\" height=\"64\"\u003e\n\n# [WAForth](https://mko.re/waforth): Forth Interpreter+Compiler for WebAssembly\n\n[![Build](https://github.com/remko/waforth/actions/workflows/build.yml/badge.svg)](https://github.com/remko/waforth/actions/workflows/build.yml)\n\n\nWAForth is a small but complete Forth interpreter and dynamic compiler for and in\n[WebAssembly](https://webassembly.org). You can see it in action\n[in an interactive Forth console](https://mko.re/waforth/), in [a Logo-like Turtle graphics language](https://mko.re/thurtle/), and in [an interactive notebook](https://mko.re/wafnb/drawing-with-forth).\n\nWAForth is [entirely written in (raw)\nWebAssembly](https://github.com/remko/waforth/blob/master/src/waforth.wat), and\nthe compiler generates WebAssembly code on the fly. The only parts for which it\nrelies on external code is to dynamically load modules (since\nWebAssembly [doesn't support JIT\nyet](https://webassembly.org/docs/future-features/#platform-independent-just-in-time-jit-compilation)),\nand the I/O primitives to read and write a character to a screen.\n\nThe WebAssembly module containing the interpreter, dynamic compiler, and \nall built-in words comes down to 14k (7k gzipped), with an extra 15k (7k gzipped) for the JavaScript wrapper, web UI, \nand encoding overhead.\n\nWAForth implements all [ANS Core\nWords](http://lars.nocrew.org/dpans/dpans6.htm#6.1) (and passes [Forth 200x\nTest Suite](https://forth-standard.org/standard/testsuite) core word tests),\nand many [ANS Core Extension\nWords](http://lars.nocrew.org/dpans/dpans6.htm#6.2). You can get the complete\nlist of supported words [from the interactive\nconsole](https://mko.re/waforth/?p=WORDS).\n\nYou can watch [a video of a talk at FOSDEM 2023](https://www.youtube.com/watch?v=QqW39jElFhA) introducing\nWAForth, and explaining the goals and some of the internals.\n\nYou can read more about the internals and the design of WAForth in the [Design\ndocument](doc/Design.md).\n\n\u003cdiv align=\"center\"\u003e\n\u003cdiv\u003e\n\u003ca href=\"https://mko.re/waforth/\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/remko/waforth/master/doc/console.gif\" alt=\"WAForth console\"\u003e\u003c/a\u003e\n\u003c/div\u003e\n\u003cfigcaption\u003e\u003cem\u003e\u003ca href=\"https://mko.re/waforth/\"\u003eWAForth console\u003c/a\u003e\u003c/em\u003e\u003c/figcaption\u003e\n\u003c/div\u003e\n\n\u003cdiv align=\"center\"\u003e\n\u003cdiv\u003e\n\u003ca href=\"https://mko.re/thurtle/\"\u003e\u003cimg style=\"width: 550px; margin-top: 1.5em;\" src=\"https://raw.githubusercontent.com/remko/waforth/master/doc/thurtle.png\" alt=\"Thurtle program\"\u003e\u003c/a\u003e\n\u003c/div\u003e\n\u003cfigcaption\u003e\u003cem\u003eWAForth integrated in \u003ca href=\"https://mko.re/thurtle/\"\u003eThurtle\u003c/a\u003e, a \u003ca href=\"https://en.wikipedia.org/wiki/Turtle_graphics\"\u003eturtle graphics\u003c/a\u003e programming environment using Forth\u003c/em\u003e\u003c/figcaption\u003e\n\u003c/div\u003e\n\n## Standalone shell\n\nAlthough WebAssembly (and therefore WAForth) is typically used in a web environment \n(web browsers, Node.js), WAForth also has a standalone native command-line shell. \nYou can download a pre-built binary of the standalone shell from \n[the Releases page](https://github.com/remko/waforth/releases).\n\nThe standalone shell uses the [Wasmtime](https://wasmtime.dev) engine,\nbut its build configuration can easily be adapted to build using any \nWebAssembly engine that supports the\n[WebAssembly C API](https://github.com/WebAssembly/wasm-c-api) (although some\nengines have [known issues](https://github.com/remko/waforth/issues/6#issue-326830993)).\n\n\u003cdiv align=\"center\"\u003e\n\u003cdiv\u003e\n\u003ca href=\"https://mko.re/thurtle/\"\u003e\u003cimg style=\"width: 550px;\" src=\"https://raw.githubusercontent.com/remko/waforth/master/doc/standalone.png\" alt=\"Thurtle program\"\u003e\u003c/a\u003e\n\u003c/div\u003e\n\u003cfigcaption\u003e\u003cem\u003eStandalone WAForth shell executable\u003c/em\u003e\u003c/figcaption\u003e\n\u003c/div\u003e\n\n## Native compiler\n\nBesides just-in-time compilation (in a browser or native), WAForth can also be used to compile Forth ahead-of-time.\n[`waforthc`](https://github.com/remko/waforth/tree/master/src/waforthc) is a tool that uses WAForth to compile a Forth program into a native executable.\nWebAssembly is used as the host runtime platform and intermediate representation during compilation, and then compiled into an executable\nthat no longer contains any WebAssembly infrastructure.\n\n## Using WAForth in a JavaScript application\n\nYou can embed WAForth in any JavaScript application. \n\nA [simple example](https://github.com/remko/waforth/blob/master/src/web/examples/prompt/prompt.ts) ([CodePen](https://codepen.io/mko-re/pen/gOzzmXZ)) to illustrate starting WAForth, and binding JavaScript functions:\n\n```typescript\nimport WAForth, { withLineBuffer } from \"waforth\";\n\n(async () =\u003e {\n  // Create the UI\n  document.body.innerHTML = `\u003cbutton\u003eGo!\u003c/button\u003e\u003cpre\u003e\u003c/pre\u003e`;\n  const btn = document.querySelector(\"button\");\n  const log = document.querySelector(\"pre\");\n\n  // Initialize WAForth\n  const forth = new WAForth();\n  forth.onEmit = withLineBuffer((c) =\u003e\n    log.appendChild(document.createTextNode(c)));\n  await forth.load();\n\n  // Bind \"prompt\" call to a function that pops up a JavaScript \n  // prompt, and pushes the entered number back on the stack\n  forth.bind(\"prompt\", (stack) =\u003e {\n    const message = stack.popString();\n    const result = window.prompt(message);\n    stack.push(parseInt(result));\n  });\n\n  // Load Forth code to bind the \"prompt\" call to a word, \n  // and call the word\n  forth.interpret(`\n( Call \"prompt\" with the given string )\n: PROMPT ( c-addr u -- n )\n  S\" prompt\" SCALL \n;\n\n( Prompt the user for a number, and write it to output )\n: ASK-NUMBER ( -- )\n  S\" Please enter a number\" PROMPT\n  .\" The number was\" SPACE .\n;\n`);\n\n  btn.addEventListener(\"click\", () =\u003e {\n    forth.interpret(\"ASK-NUMBER\");\n  });\n})();\n```\n\n### Asynchronous bindings\n\nFor asynchronous bindings, use `bindAsync` instead of `bind`.\n\n`bindAsync` expects an execution token on the stack, which is\nto be called with a success flag after the bound function is called. This is illustrated in [the fetch example](https://github.com/remko/waforth/blob/master/src/web/examples/fetch/fetch.ts):\n\n```typescript\nforth.bindAsync(\"ip?\", async () =\u003e {\n  const result = await (\n    await fetch(\"https://api.ipify.org?format=json\")\n  ).json();\n  forth.pushString(result.ip);\n});\n\nforth.interpret(`\n( IP? callback. Called after IP address was received )\n: IP?-CB ( true c-addr n | false -- )\n  IF \n    .\" Your IP address is \" TYPE CR\n  ELSE\n    .\" Unable to fetch IP address\" CR\n  THEN\n;\n\n( Fetch the IP address, and print it to console )\n: IP? ( -- )\n  ['] IP?-CB\n  S\" ip?\" SCALL \n;\n`);\n```\n\n\n## Writing WebAssembly in Forth\n\nWAForth supports directly writing WebAssembly in Forth using the [`CODE`](https://forth-standard.org/standard/tools/CODE) word.\nFor example, the following snippet defines a raw WebAssembly version of [`DUP`](https://forth-standard.org/standard/core/DUP):\n\n```forth\nCODE DUP' ( n -- n n )\n  \\ Put pointer to top of Forth stack (local_0) on the \n  \\ Wasm operand stack (for use later)\n  [ 0 ] $LOCAL.GET\n\n  \\ Load the number at the top of the Forth stack \n  \\ (local_0 - 4) on the Wasm operand stack\n  [ 0 ] $LOCAL.GET\n  [ 4 ] $I32.CONST\n  $I32.SUB\n  $I32.LOAD\n\n  \\ Store the number on the Wasm operand stack on \n  \\ top of the Forth stack. The first operand (Forth \n  \\ stack pointer) was put on the Wasm operand stack \n  \\ at the beginning of this snippet\n  $I32.STORE\n\n  \\ Increment the Forth top of stack pointer (local_0), \n  \\ and leave it on the Wasm operand stack as return value\n  [ 0 ] $LOCAL.GET\n  [ 4 ] $I32.CONST \n  $I32.ADD\n;CODE\n```\n\nThis creates a word with the specified WebAssembly:\n\n```wasm\n(func $DUP' (param $tos i32) (result i32)\n  local.get $tos\n  local.get $tos\n  i32.const 4\n  i32.sub\n  i32.load\n  i32.store\n  local.get $tos\n  i32.const 4\n  i32.add\n)\n```\n\nNote that support for writing WebAssembly is still experimental.\nThe assembly words used in the above snippet (`$LOCAL.GET`, `$I32.*`, ...) aren't available in the WAForth core,\nand have to be manually defined using the low-level `$U,` and `$S,` words that append ([LEB128](https://en.wikipedia.org/wiki/LEB128)-encoded) bytes directly to the WebAssembly module. For example, the code above relies on the following assembly word definitions:\n\n```forth\n: $LOCAL.GET ( u -- )   32 $U, $U,         ; IMMEDIATE\n: $I32.ADD   ( -- )    106 $U,             ; IMMEDIATE\n: $I32.SUB   ( -- )    107 $U,             ; IMMEDIATE\n: $I32.CONST ( n -- )   65 $U, $S,         ; IMMEDIATE\n: $I32.LOAD  ( -- )     40 $U, 2 $U, 0 $U, ; IMMEDIATE\n: $I32.STORE ( -- )     54 $U, 2 $U, 0 $U, ; IMMEDIATE\n```\n\nThe exact opcodes and format of instructions can be found in\n[the WebAssembly spec](https://webassembly.github.io/spec/core/binary/instructions.html). In the future, I'll probably make all WebAssembly assembly instructions available somewhere. Using WebAssembly locals also currently isn't possible, although this can\nbe added in the future. \n\n\n## Notebooks\n\nThe [WAForth Visual Studio Code Extension](https://marketplace.visualstudio.com/items?itemName=remko.waforth-vscode-extension) adds support\nfor interactive Forth notebooks powered by WAForth. This lets you create documents that combine rich text with executable Forth code.\nYou can execute both text-based Forth code, as well as [Thurtle](https://mko.re/thurtle/) graphics.\n\nBecause it is powered by WebAssembly, this extension works both in the desktop version of Visual Studio Code and in [the browser version of Visual Studio Code](https://code.visualstudio.com/docs/editor/vscode-web) (e.g. https://github.dev, https://vscode.dev).\n\nYou can also convert the notebook into a lightweight self-contained page using [`wafnb2html`](https://github.com/remko/waforth/tree/master/src/web/notebook).\nAn example can be seen [here](https://mko.re/wafnb/drawing-with-forth).\n\n\u003cdiv align=\"center\"\u003e\n\u003cdiv\u003e\n\u003ca href=\"https://github.dev/remko/waforth/blob/master/src/web/notebook/examples/drawing-with-forth.wafnb\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/remko/waforth/master/src/web/vscode-extension/doc/notebook.gif\" alt=\"WAForth notebook\"\u003e\u003c/a\u003e\n\u003c/div\u003e\n\u003cfigcaption\u003e\u003cem\u003e\u003ca href=\"https://github.dev/remko/waforth/blob/master/src/web/notebook/examples/drawing-with-forth.wafnb\"\u003eWAForth notebook\u003c/a\u003e\u003c/em\u003e\u003c/figcaption\u003e\n\u003c/div\u003e\n\n\n## Goals\n\nHere are some of the goals (and non-goals) of WAForth:\n\n- ✅ **WebAssembly-first**: Implement as much as possible in (raw) WebAssembly. Only call out to JavaScript (or whatever the host language is) for functionality that is not available in WebAssembly (I/O, loading compiled WebAssembly code).\n- ✅ **Simplicity**: Keep the code as simple and clean as possible. Raw WebAssembly requires more effort to maintain than code in a high level language, so avoid complexity if you can.\n- ✅ **Completeness**: Implement a complete (and correct) Forth system, following the [ANS Standard](http://lars.nocrew.org/dpans/dpans.htm), including all [ANS Core words](http://lars.nocrew.org/dpans/dpans6.htm#6.1).\n- ❓ **Speed**: If some speed gains can be gotten without paying much in simplicity (e.g. better design of the system, more efficient implementation of words, simple compiler improvements, ...), then I do it. However, generating the most efficient code would require a smart compiler, and a smart compiler would introduce a lot of complexity if implemented in raw WebAssembly, so speed is not an ultimate goal. Although the low level of WebAssembly gives some speed advantages, the design of the system will cause execution to consist almost exclusively of indirect calls to small functions, so there will be languages targeting WebAssembly that run faster.\n- ❓ **Binary size**: Since the entire system is written in raw WebAssembly, and since one of the main goals is simplicity, the resulting binary size is naturally quite small (±12k). However, I don't do any special efforts to save bytes here and there in the code (or the generated code) if it makes things more complex.\n- ❓ **Ease of use**: Like most Forths, I currently don't do much effort to provide functionality to make Forth programming easy and safe (helpful errors, stacktraces, strict bounds checks, ...). However, the compiler emits debug information to help step through the WebAssembly code of words, and I hope to add more debugging aids to the compiler in the future (if it doesn't add too much complexity)\n\n![Debugger view of a compiled word](doc/debugger.png \"Debugger view of a compiled word\")\n\n## Development\n\n### Install Dependencies\n\nThe build uses the [WebAssembly Binary\nToolkit](https://github.com/WebAssembly/wabt) for converting raw WebAssembly\ntext format into the binary format, and [Node.JS](https://nodejs.org/en/) for\nmanaging the build process and the dependencies of the shell.\n\n    brew install wabt node\n    npm install\n\n\n### Building \u0026 Running\n\nTo build everything:\n    \n    npm run build\n\nTo run the development server:\n\n    npm run dev\n\n### Testing\n\nThe tests are served from `/waforth/tests` by the development server.\n\nYou can also run the tests in Node.JS by running\n\n    npm test\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fremko%2Fwaforth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fremko%2Fwaforth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fremko%2Fwaforth/lists"}