webpack -> esbuild

This commit is contained in:
Remko Tronçon 2022-04-13 17:02:46 +02:00
parent e50fbe7e1c
commit 6b9de10002
34 changed files with 2568 additions and 5149 deletions

View file

@ -1 +1 @@
dist public/waforth/dist

View file

@ -1,14 +0,0 @@
env:
browser: true
es6: true
extends: eslint:recommended
parserOptions:
sourceType: module
globals:
WebAssembly: true
require: true
rules:
no-console: 0
settings:
react:
version: "16.0"

23
.eslintrc.yml Normal file
View file

@ -0,0 +1,23 @@
env:
browser: true
es2021: true
node: true
extends:
- 'eslint:recommended'
- 'plugin:react/recommended'
- "plugin:react-hooks/recommended"
parserOptions:
ecmaFeatures:
jsx: true
ecmaVersion: 12
sourceType: module
plugins:
- prettier
- react
settings:
react:
version: detect
rules:
"prettier/prettier": "error"
react/display-name: 0
react/prop-types: 0

20
.github/workflows/build.yml vendored Normal file
View file

@ -0,0 +1,20 @@
name: Build
on:
push:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: 14
cache: 'yarn'
- run: sudo apt-get install wabt
- run: yarnpkg --pure-lockfile
- run: yarnpkg build
- run: yarnpkg lint
- run: yarnpkg test --coverage

3
.gitignore vendored
View file

@ -1,7 +1,6 @@
.DS_Store .DS_Store
.cache/
node_modules/ node_modules/
dist/ public/waforth/
src/waforth.bulkmem.wat src/waforth.bulkmem.wat
src/waforth.vanilla.wat src/waforth.vanilla.wat
*.wasm *.wasm

36
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,36 @@
{
"editor.tabSize": 2,
"files.exclude": {
"bin/": true,
"build/": true,
"**/node_modules/": true,
"coverage/": true
},
"search.exclude": {
"**/node_modules": true
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}
}

View file

@ -5,15 +5,19 @@ ifeq ($(DEBUG),1)
WAT2WASM_FLAGS:=$(WAT2WASM_FLAGS) --debug-names WAT2WASM_FLAGS:=$(WAT2WASM_FLAGS) --debug-names
endif endif
WASM_FILES=src/waforth.wasm src/waforth.bulkmem.wasm tests/benchmarks/sieve-vanilla.wasm all:
all: $(WASM_FILES)
yarn -s build yarn -s build
dev-server: $(WASM_FILES) dev:
yarn -s dev-server yarn -s dev
wasm: $(WASM_FILES) src/waforth.assembled.wat src/tools/quadruple.wasm.hex check: src/waforth.wasm
yarn -s test
check-watch: src/waforth.wasm
yarn -s test-watch
wasm: src/waforth.assembled.wat src/tools/quadruple.wasm.hex
process: src/waforth.vanilla.wat process: src/waforth.vanilla.wat
cp $< src/waforth.wat cp $< src/waforth.wat
@ -30,7 +34,7 @@ src/waforth.bulkmem.wasm: src/waforth.bulkmem.wat
src/waforth.bulkmem.wat: src/waforth.wat src/waforth.bulkmem.wat: src/waforth.wat
./src/tools/process.js --enable-bulk-memory $< > $@ ./src/tools/process.js --enable-bulk-memory $< > $@
tests/benchmarks/sieve-vanilla.wasm: tests/benchmarks/sieve-vanilla.wat src/benchmarks/sieve-vanilla.wasm: src/benchmarks/sieve-vanilla.wat
$(WAT2WASM) $(WAT2WASM_FLAGS) -o $@ $< $(WAT2WASM) $(WAT2WASM_FLAGS) -o $@ $<
src/tools/quadruple.wasm: src/tools/quadruple.wat src/tools/quadruple.wasm: src/tools/quadruple.wat
@ -42,11 +46,5 @@ src/tools/quadruple.wasm.hex: src/tools/quadruple.wasm
clean: clean:
-rm -rf $(WASM_FILES) src/tools/quadruple.wasm src/tools/quadruple.wasm.hex src/waforth.wat.tmp dist -rm -rf $(WASM_FILES) src/tools/quadruple.wasm src/tools/quadruple.wasm.hex src/waforth.wat.tmp dist
check: $(WASM_FILES)
yarn -s test
check-watch: $(WASM_FILES)
yarn -s test-watch
lint: lint:
yarn -s lint yarn -s lint

View file

@ -40,7 +40,7 @@ To build everything:
To run the development server: To run the development server:
make dev-server make dev
## Testing ## Testing

161
build.js Executable file
View file

@ -0,0 +1,161 @@
#!/usr/bin/env node
/* eslint-env node */
const esbuild = require("esbuild");
const path = require("path");
const fs = require("fs");
const { createServer } = require("http");
const nstatic = require("node-static");
const { promisify } = require("util");
const exec = promisify(require("child_process").exec);
function withWatcher(config, handleBuildFinished = () => {}, port = 8880) {
const watchClients = [];
createServer((req, res) => {
return watchClients.push(
res.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
"Access-Control-Allow-Origin": "*",
Connection: "keep-alive",
})
);
}).listen(port);
return {
...config,
banner: {
js: `(function () { new EventSource("http://localhost:${port}").onmessage = function() { location.reload();};})();`,
},
watch: {
async onRebuild(error, result) {
if (error) {
console.error(error);
} else {
// Doing this first, because this may do some ES5 transformations
await handleBuildFinished(result);
watchClients.forEach((res) => res.write("data: update\n\n"));
watchClients.length = 0;
}
},
},
};
}
let dev = false;
let watch = false;
for (const arg of process.argv.slice(2)) {
switch (arg) {
case "--development":
dev = true;
break;
case "--watch":
watch = true;
break;
}
}
let buildConfig = {
bundle: true,
logLevel: "info",
entryPoints: [
path.join(__dirname, "src", "shell", "shell"),
path.join(__dirname, "src", "tests", "tests"),
path.join(__dirname, "src", "benchmarks", "benchmarks"),
],
entryNames: dev ? "[name]" : "[name]-c$[hash]",
assetNames: "[name]-c$[hash]",
// target: "es6",
outdir: path.join(__dirname, "public/waforth/dist"),
external: ["fs", "stream", "util", "events"],
minify: !dev,
loader: {
".wasm": "binary",
".js": "jsx",
".png": "file",
".m4a": "file",
".woff2": "file",
".svg": "file",
".woff": "file",
".ttf": "file",
".eot": "file",
".md": "text",
},
sourcemap: true,
metafile: true,
plugins: [
{
name: "wat",
setup(build) {
build.onResolve({ filter: /.\.wat$/ }, async (args) => {
if (args.resolveDir === "") {
return;
}
const watPath = path.isAbsolute(args.path)
? args.path
: path.join(args.resolveDir, args.path);
return {
path: watPath,
namespace: "wat",
watchFiles: [watPath],
};
});
build.onLoad({ filter: /.*/, namespace: "wat" }, async (args) => {
// Would be handy if we could get output from stdout without going through file
const out = args.path.replace(".wat", ".wasm");
const flags = "";
// flags = --debug-names
// console.log("wat: compiling %s", args.path);
await exec(`wat2wasm ${flags} --output=${out} ${args.path}`);
return {
contents: await fs.promises.readFile(out),
loader: "binary",
};
});
},
},
],
};
const INDEX_TEMPLATE = `<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="/waforth/dist/$BASE.css" rel="stylesheet" />
<title></title>
</head>
<body>
<script type="text/javascript" src="/waforth/dist/$BASE.js"></script>
</body>
</html>
`;
async function handleBuildFinished(result) {
let index = INDEX_TEMPLATE.replace(/\$BASE/g, "shell");
let testIndex = INDEX_TEMPLATE.replace(/\$BASE/g, "tests");
let benchmarksIndex = INDEX_TEMPLATE.replace(/\$BASE/g, "benchmarks");
// console.log(JSON.stringify(result.metafile.outputs, undefined, 2));
for (const [out] of Object.entries(result.metafile.outputs)) {
const outfile = path.basename(out);
const sourcefile = outfile.replace(/-c\$[^.]+\./, ".");
// console.log("%s -> %s", sourcefile, outfile);
index = index.replace(`/${sourcefile}`, `/${outfile}`);
testIndex = testIndex.replace(`/${sourcefile}`, `/${outfile}`);
benchmarksIndex = benchmarksIndex.replace(`/${sourcefile}`, `/${outfile}`);
}
fs.writeFileSync("public/waforth/index.html", index);
fs.mkdirSync("public/waforth/tests", { recursive: true });
fs.writeFileSync("public/waforth/tests/index.html", testIndex);
fs.mkdirSync("public/waforth/benchmarks", { recursive: true });
fs.writeFileSync("public/waforth/benchmarks/index.html", benchmarksIndex);
}
if (watch) {
const file = new nstatic.Server(path.join(__dirname, "public"));
createServer(function (req, res) {
file.serve(req, res);
}).listen(8080);
console.log("listening on port 8080");
buildConfig = withWatcher(buildConfig, handleBuildFinished, 8081);
}
esbuild.build(buildConfig).then(handleBuildFinished, () => process.exit(1));

View file

@ -40,7 +40,7 @@ function here() {
WebAssembly.instantiate(coreWasm, { WebAssembly.instantiate(coreWasm, {
shell: { shell: {
emit: c => { emit: (c) => {
process.stdout.write(String.fromCharCode(c)); process.stdout.write(String.fromCharCode(c));
}, },
@ -51,7 +51,7 @@ WebAssembly.instantiate(coreWasm, {
return buffer.pop(); return buffer.pop();
}, },
debug: c => { debug: (c) => {
process.stderr.write(String.fromCharCode(c)); process.stderr.write(String.fromCharCode(c));
}, },
@ -64,11 +64,11 @@ WebAssembly.instantiate(coreWasm, {
var module = new WebAssembly.Module(data); var module = new WebAssembly.Module(data);
modules.push(new Uint8Array(Array.from(data))); modules.push(new Uint8Array(Array.from(data)));
new WebAssembly.Instance(module, { new WebAssembly.Instance(module, {
env: { table, memory, tos: -1 } env: { table, memory, tos: -1 },
}); });
} },
} },
}).then(instance => { }).then((instance) => {
core = instance.instance; core = instance.instance;
table = core.exports.table; table = core.exports.table;
memory = core.exports.memory; memory = core.exports.memory;
@ -99,7 +99,7 @@ WebAssembly.instantiate(coreWasm, {
"#define WAFORTH_HERE " + savedHere + "\n", "#define WAFORTH_HERE " + savedHere + "\n",
"#define WAFORTH_TABLE_SIZE " + tableSize + "\n", "#define WAFORTH_TABLE_SIZE " + tableSize + "\n",
"void waforth_modules_init();", "void waforth_modules_init();",
"#undef WASM_RT_MODULE_PREFIX" "#undef WASM_RT_MODULE_PREFIX",
]; ];
const init = [ const init = [
"#include <memory.h>", "#include <memory.h>",
@ -112,7 +112,7 @@ WebAssembly.instantiate(coreWasm, {
dictionaryStart + dictionaryStart +
"], dictionary, " + "], dictionary, " +
(savedHere - dictionaryStart) + (savedHere - dictionaryStart) +
");" ");",
]; ];
const moduleFiles = []; const moduleFiles = [];
for (let i = 0; i < modules.length; ++i) { for (let i = 0; i < modules.length; ++i) {

View file

@ -2,37 +2,34 @@
"private": true, "private": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"immutability-helper": "^3.0.0",
"jq-console": "^2.13.2", "jq-console": "^2.13.2",
"jquery": "^3.5.0", "jquery": "^3.5.0",
"preact": "^8.2.9",
"promise-polyfill": "^7.1.2" "promise-polyfill": "^7.1.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.3.4", "@babel/core": "^7.17.9",
"@babel/preset-env": "^7.3.4", "@babel/preset-env": "^7.16.11",
"@babel/register": "^7.0.0", "@babel/register": "^7.17.7",
"babel-loader": "^8.0.5", "chai": "^4.3.6",
"babel-preset-preact": "^2.0.0", "commander": "^9.1.0",
"bin-loader": "^0.1.0", "esbuild": "^0.14.36",
"chai": "^4.2.0", "eslint": "^8.13.0",
"commander": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
"css-loader": "^2.1.1", "eslint-plugin-react": "^7.29.4",
"eslint": "5.15.1", "eslint-plugin-react-hooks": "^4.4.0",
"eslint-plugin-react": "^7.12.4", "immutability-helper": "^3.1.1",
"html-webpack-plugin": "^3.2.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"mocha": "^6.1.4", "mocha": "^9.2.2",
"style-loader": "^0.23.1", "node-static": "^0.7.11",
"webpack": "^4.41.5", "prettier": "^2.6.2",
"webpack-cli": "^3.2.3", "react": "^18.0.0",
"webpack-dev-server": "^3.2.1" "react-dom": "^18.0.0"
}, },
"scripts": { "scripts": {
"build": "webpack --mode=production", "build": "./build.js",
"test": "mocha tests/index-node.js", "dev": "./build.js --watch --development",
"test-watch": "mocha --watch tests/index-node.js", "test": "mocha src/tests/tests.node.js",
"lint": "eslint .", "test-watch": "mocha --watch src/tests/tests.node.js",
"dev-server": "webpack-dev-server --open --openPage waforth --content-base public" "lint": "eslint ."
} }
} }

View file

@ -1,9 +1,10 @@
import React from "react";
import { createRoot } from "react-dom/client";
import WAForth from "../../src/shell/WAForth"; import WAForth from "../../src/shell/WAForth";
import sieve from "../../src/shell/sieve"; import sieve from "../../src/shell/sieve";
import sieveVanillaModule from "./sieve-vanilla.wasm"; import sieveVanillaModule from "./sieve-vanilla.wat";
import { Component, render, h } from "preact";
import update from "immutability-helper"; import update from "immutability-helper";
import "./index.css"; import "./benchmarks.css";
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Initial setup // Initial setup
@ -13,7 +14,7 @@ const setup = [];
const forth = new WAForth(); const forth = new WAForth();
let outputBuffer = []; let outputBuffer = [];
forth.onEmit = c => { forth.onEmit = (c) => {
outputBuffer.push(String.fromCharCode(c)); outputBuffer.push(String.fromCharCode(c));
}; };
setup.push( setup.push(
@ -26,9 +27,9 @@ let sieveVanilla;
setup.push( setup.push(
WebAssembly.instantiate(sieveVanillaModule, { WebAssembly.instantiate(sieveVanillaModule, {
js: { js: {
print: x => console.log(x) print: (x) => console.log(x),
} },
}).then(instance => { }).then((instance) => {
sieveVanilla = instance.instance.exports.sieve; sieveVanilla = instance.instance.exports.sieve;
}) })
); );
@ -44,7 +45,7 @@ const benchmarks = [
outputBuffer = []; outputBuffer = [];
forth.run(`${LIMIT} sieve`); forth.run(`${LIMIT} sieve`);
return outputBuffer.join(""); return outputBuffer.join("");
} },
}, },
{ {
name: "sieve-direct", name: "sieve-direct",
@ -52,21 +53,21 @@ const benchmarks = [
outputBuffer = []; outputBuffer = [];
forth.run(`${LIMIT} sieve_direct .`); forth.run(`${LIMIT} sieve_direct .`);
return outputBuffer.join(""); return outputBuffer.join("");
} },
}, },
{ {
name: "sieve-vanilla", name: "sieve-vanilla",
fn: () => { fn: () => {
return sieveVanilla(LIMIT); return sieveVanilla(LIMIT);
} },
} },
]; ];
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
const iterations = Array.from(Array(ITERATIONS).keys()); const iterations = Array.from(Array(ITERATIONS).keys());
class Benchmarks extends Component { class Benchmarks extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
const results = {}; const results = {};
@ -74,7 +75,7 @@ class Benchmarks extends Component {
this.state = { this.state = {
initialized: false, initialized: false,
done: false, done: false,
results results,
}; };
} }
@ -91,10 +92,10 @@ class Benchmarks extends Component {
results: update(this.state.results, { results: update(this.state.results, {
[benchmarks[benchmarkIndex].name]: { [benchmarks[benchmarkIndex].name]: {
[benchmarkIteration]: { [benchmarkIteration]: {
$set: { time: (t2 - t1) / 1000.0, output } $set: { time: (t2 - t1) / 1000.0, output },
} },
} },
}) }),
}); });
if (benchmarkIteration < ITERATIONS - 1) { if (benchmarkIteration < ITERATIONS - 1) {
benchmarkIteration += 1; benchmarkIteration += 1;
@ -122,7 +123,9 @@ class Benchmarks extends Component {
<thead> <thead>
<tr> <tr>
<th /> <th />
{iterations.map(i => <th key={i}>{i}</th>)} {iterations.map((i) => (
<th key={i}>{i}</th>
))}
<th>Avg</th> <th>Avg</th>
</tr> </tr>
</thead> </thead>
@ -133,7 +136,7 @@ class Benchmarks extends Component {
return [ return [
<tr key={`${name}-time`}> <tr key={`${name}-time`}>
<th>{name}</th> <th>{name}</th>
{iterations.map(i => ( {iterations.map((i) => (
<td key={i}> <td key={i}>
{benchmark[i] == null ? null : ( {benchmark[i] == null ? null : (
<span>{benchmark[i].time.toFixed(2)}s</span> <span>{benchmark[i].time.toFixed(2)}s</span>
@ -150,14 +153,14 @@ class Benchmarks extends Component {
</tr>, </tr>,
<tr key={`${name}-output`}> <tr key={`${name}-output`}>
<th /> <th />
{iterations.map(i => ( {iterations.map((i) => (
<td key={i}> <td key={i}>
<pre className="output"> <pre className="output">
{benchmark[i] == null ? null : benchmark[i].output} {benchmark[i] == null ? null : benchmark[i].output}
</pre> </pre>
</td> </td>
))} ))}
</tr> </tr>,
]; ];
})} })}
</tbody> </tbody>
@ -166,4 +169,7 @@ class Benchmarks extends Component {
); );
} }
} }
render(<Benchmarks />, document.body);
const rootEl = document.createElement("div");
document.body.appendChild(rootEl);
createRoot(rootEl).render(<Benchmarks />);

View file

@ -5,7 +5,7 @@ const isSafari =
class WAForth { class WAForth {
constructor(wasmModule, arrayToBase64) { constructor(wasmModule, arrayToBase64) {
if (wasmModule == null) { if (wasmModule == null) {
this.wasmModule = require("../waforth.wasm"); this.wasmModule = require("../waforth.wat");
} else { } else {
this.wasmModule = wasmModule; this.wasmModule = wasmModule;
} }
@ -41,7 +41,7 @@ class WAForth {
return buffer.pop(); return buffer.pop();
}, },
debug: d => { debug: (d) => {
console.log("DEBUG: ", d, String.fromCharCode(d)); console.log("DEBUG: ", d, String.fromCharCode(d));
}, },
@ -88,11 +88,11 @@ class WAForth {
// console.log("Load", index, this.arrayToBase64(data)); // console.log("Load", index, this.arrayToBase64(data));
var module = new WebAssembly.Module(data); var module = new WebAssembly.Module(data);
new WebAssembly.Instance(module, { new WebAssembly.Instance(module, {
env: { table, memory, tos: -1 } env: { table, memory, tos: -1 },
}); });
} },
} },
}).then(instance => { }).then((instance) => {
this.core = instance.instance; this.core = instance.instance;
table = this.core.exports.table; table = this.core.exports.table;
memory = this.core.exports.memory; memory = this.core.exports.memory;

View file

@ -1,13 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="console"></div>
<div class="messageContainer">
<div class="message" id="message"></div>
</div>
</body>
</html>

View file

@ -20,15 +20,15 @@ body {
} }
.message.error { .message.error {
color: red; color: red;
} }
/* The console container element */ /* The console container element */
#console { .console {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color:black; background-color: black;
} }
/* The inner console element. */ /* The inner console element. */
@ -49,31 +49,31 @@ body {
/* The cursor. */ /* The cursor. */
.jqconsole-cursor { .jqconsole-cursor {
background-color: gray; background-color: gray;
} }
/* The cursor color when the console looses focus. */ /* The cursor color when the console looses focus. */
.jqconsole-blurred .jqconsole-cursor { .jqconsole-blurred .jqconsole-cursor {
background-color: #666; background-color: #666;
} }
/* The current prompt text color */ /* The current prompt text color */
.jqconsole-prompt { .jqconsole-prompt {
color: #dd0; color: #dd0;
} }
/* The command history */ /* The command history */
.jqconsole-old-prompt { .jqconsole-old-prompt {
color: #bb0; color: #bb0;
font-weight: normal; font-weight: normal;
} }
/* The text color when in input mode. */ /* The text color when in input mode. */
.jqconsole-input { .jqconsole-input {
color: #0d0; color: #0d0;
} }
/* Previously entered input. */ /* Previously entered input. */
.jqconsole-old-input { .jqconsole-old-input {
color: #0b0; color: #0b0;
font-weight: normal; font-weight: normal;
} }
/* The text color of the output. */ /* The text color of the output. */
.jqconsole-output { .jqconsole-output {
color: white; color: white;
} }

View file

@ -2,25 +2,38 @@ import "promise-polyfill/src/polyfill";
import $ from "jquery"; import $ from "jquery";
import WAForth from "./WAForth"; import WAForth from "./WAForth";
import sieve from "./sieve"; import sieve from "./sieve";
import "./index.css"; import "./shell.css";
window.jQuery = $; window.jQuery = $;
require("jq-console"); require("jq-console");
document.title = "WAForth";
const forth = new WAForth(); const forth = new WAForth();
let jqconsole = $("#console").jqconsole("WAForth\n", ""); const consoleEl = document.createElement("div");
$("#console").hide(); consoleEl.className = "console";
document.body.appendChild(consoleEl);
const messageContainerEl = document.createElement("div");
messageContainerEl.className = "messageContainer";
const messageEl = document.createElement("div");
messageEl.className = "message";
messageContainerEl.appendChild(messageEl);
document.body.appendChild(messageContainerEl);
let jqconsole = $(consoleEl).jqconsole("WAForth\n", "");
$(consoleEl).hide();
$(".jqconsole-header").html( $(".jqconsole-header").html(
"<span><a target='_blank' href='https://github.com/remko/waforth'>WAForth</a>\n</span>" "<span><a target='_blank' href='https://github.com/remko/waforth'>WAForth</a>\n</span>"
); );
let outputBuffer = []; let outputBuffer = [];
forth.onEmit = c => { forth.onEmit = (c) => {
outputBuffer.push(String.fromCharCode(c)); outputBuffer.push(String.fromCharCode(c));
}; };
function prompt() { function prompt() {
jqconsole.Prompt(false, input => { jqconsole.Prompt(false, (input) => {
jqconsole.Write(" "); jqconsole.Write(" ");
// Avoid console inserting a newline // Avoid console inserting a newline
@ -38,18 +51,16 @@ function prompt() {
}); });
} }
$("#message").text("Loading..."); $(messageEl).text("Loading...");
forth.start().then( forth.start().then(
() => { () => {
forth.run(sieve); forth.run(sieve);
outputBuffer = []; outputBuffer = [];
$("#message").hide(); $(messageEl).hide();
$("#console").show(); $(consoleEl).show();
prompt(); prompt();
}, },
() => { () => {
$("#message") $(messageEl).addClass("error").text("Error");
.addClass("error")
.text("Error");
} }
); );

View file

@ -1,5 +1,5 @@
import WAForth from "../src/shell/WAForth"; import WAForth from "../shell/WAForth";
import sieve from "../src/shell/sieve"; import sieve from "../shell/sieve";
import standardTestSuiteTester from "./standard-testsuite/tester.f"; import standardTestSuiteTester from "./standard-testsuite/tester.f";
import standardCoreWordsTestSuite from "./standard-testsuite/core.f"; import standardCoreWordsTestSuite from "./standard-testsuite/core.f";
import { expect, assert } from "chai"; import { expect, assert } from "chai";
@ -10,7 +10,7 @@ function loadTests(wasmModule, arrayToBase64) {
beforeEach(() => { beforeEach(() => {
forth = new WAForth(wasmModule, arrayToBase64); forth = new WAForth(wasmModule, arrayToBase64);
forth.onEmit = c => { forth.onEmit = (c) => {
output = output + String.fromCharCode(c); output = output + String.fromCharCode(c);
// console.log(output); // console.log(output);
}; };
@ -25,7 +25,7 @@ function loadTests(wasmModule, arrayToBase64) {
stack = new Int32Array(core.memory.buffer, core.tos(), 0x100); stack = new Int32Array(core.memory.buffer, core.tos(), 0x100);
initialTOS = core.tos(); initialTOS = core.tos();
}, },
err => { (err) => {
console.error(err); console.error(err);
} }
); );
@ -81,7 +81,7 @@ function loadTests(wasmModule, arrayToBase64) {
} }
function run(ss, expectErrors = false) { function run(ss, expectErrors = false) {
ss.split("\n").forEach(s => { ss.split("\n").forEach((s) => {
// console.log("Running: ", s); // console.log("Running: ", s);
const r = forth.run(s); const r = forth.run(s);
if (expectErrors) { if (expectErrors) {

18
src/tests/tests.js Normal file
View file

@ -0,0 +1,18 @@
import mocha from "mocha/mocha.js";
import loadTests from "./suite";
import "mocha/mocha.css";
const h1El = document.createElement("h1");
h1El.style = "font-family: sans-serif; margin: 1rem;";
h1El.appendChild(document.createTextNode("WAForth Unit Tests"));
document.body.appendChild(h1El);
const mochaEl = document.createElement("div");
mochaEl.id = "mocha";
document.body.appendChild(mochaEl);
mocha.setup("bdd");
loadTests();
// mocha.checkLeaks();
mocha.globals(["jQuery"]);
mocha.run();

10
src/tests/tests.node.js Normal file
View file

@ -0,0 +1,10 @@
const fs = require("fs");
const path = require("path");
require("@babel/register")({
presets: ["@babel/preset-env"],
});
const loadTests = require("./suite.js").default;
const wasmModule = fs.readFileSync(path.join(__dirname, "../waforth.wasm"));
loadTests(wasmModule, (s) => {
return Buffer.from(s).toString("base64");
});

View file

@ -26,7 +26,7 @@ const dictionaryEntry = [
encodeLE(latest, 4), encodeLE(latest, 4),
encodeLE(name.length | flags, 1), encodeLE(name.length | flags, 1),
_.padEnd(name, 4 * Math.floor((name.length + 4) / 4) - 1, "0"), _.padEnd(name, 4 * Math.floor((name.length + 4) / 4) - 1, "0"),
encodeLE(nextTableIndex, 4) encodeLE(nextTableIndex, 4),
]; ];
console.log( console.log(
"(data (i32.const 0x" + "(data (i32.const 0x" +

View file

@ -1,8 +1,8 @@
TODO // TODO
;;; !moduleHeaderSize := (string-length !moduleHeader)) // ;;; !moduleHeaderSize := (string-length !moduleHeader))
;;; !moduleHeaderCodeSizeOffset := (char-index (string->list !moduleHeader) \FF 0)) // ;;; !moduleHeaderCodeSizeOffset := (char-index (string->list !moduleHeader) \FF 0))
;;; !moduleHeaderBodySizeOffset := (char-index (string->list !moduleHeader) \FE 0)) // ;;; !moduleHeaderBodySizeOffset := (char-index (string->list !moduleHeader) \FE 0))
;;; !moduleHeaderLocalCountOffset := (char-index (string->list !moduleHeader) \FD 0)) // ;;; !moduleHeaderLocalCountOffset := (char-index (string->list !moduleHeader) \FD 0))
;;; !moduleHeaderTableIndexOffset := (char-index (string->list !moduleHeader) \FC 0)) // ;;; !moduleHeaderTableIndexOffset := (char-index (string->list !moduleHeader) \FC 0))
;;; !moduleHeaderTableInitialSizeOffset := (char-index (string->list !moduleHeader) \FB 0)) // ;;; !moduleHeaderTableInitialSizeOffset := (char-index (string->list !moduleHeader) \FB 0))
;;; !moduleHeaderFunctionTypeOffset := (char-index (string->list !moduleHeader) \FA 0)) // ;;; !moduleHeaderFunctionTypeOffset := (char-index (string->list !moduleHeader) \FA 0))

View file

@ -12,22 +12,19 @@ program
"--enable-bulk-memory", "--enable-bulk-memory",
"use bulk memory operations instead of own implementation" "use bulk memory operations instead of own implementation"
) )
.action(f => { .action((f) => {
file = f; file = f;
}); });
program.parse(process.argv); program.parse(process.argv);
const lines = fs const lines = fs.readFileSync(file).toString().split("\n");
.readFileSync(file)
.toString()
.split("\n");
const definitions = {}; const definitions = {};
let skipLevel = 0; let skipLevel = 0;
let skippingDefinition = false; let skippingDefinition = false;
lines.forEach(line => { lines.forEach((line) => {
// Constants // Constants
Object.keys(definitions).forEach(k => { Object.keys(definitions).forEach((k) => {
line = line.replace( line = line.replace(
new RegExp( new RegExp(
"(\\s)([^\\s])+(\\s)+\\(; = " + _.escapeRegExp(k) + " ;\\)", "(\\s)([^\\s])+(\\s)+\\(; = " + _.escapeRegExp(k) + " ;\\)",

View file

@ -1,11 +0,0 @@
/* global __dirname, Buffer */
const fs = require("fs");
const path = require("path");
require("@babel/register")({
presets: ["@babel/preset-env"]
});
const loadTests = require("./tests.js").default;
const wasmModule = fs.readFileSync(path.join(__dirname, "../src/waforth.wasm"));
loadTests(wasmModule, s => {
return Buffer.from(s).toString("base64");
});

View file

@ -1,14 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title><%= htmlWebpackPlugin.options.title %></title>
<link href="https://cdn.jsdelivr.net/gh/mochajs/mocha@2.2.5/mocha.css" rel="stylesheet" />
</head>
<body>
<h1 style="font-family: sans-serif; margin: 1rem; ">
<%= htmlWebpackPlugin.options.title %>
</h1>
<div style="margin-top: 30px;" id="mocha"></div>
</body>
</html>

View file

@ -1,8 +0,0 @@
import { mocha } from "mocha";
import loadTests from "./tests";
mocha.setup("bdd");
loadTests();
// mocha.checkLeaks();
mocha.globals(["jQuery"]);
mocha.run();

View file

@ -1,91 +0,0 @@
/*eslint-env node*/
const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const webpack = require("webpack");
function config({ entry, outputDir, title, template, mode }) {
mode = mode || "development";
const result = {
mode,
entry,
output: {
filename: "index.js",
path: path.resolve(__dirname, "dist", outputDir),
publicPath: "/" + outputDir
},
module: {
rules: [
{
test: /\.js$|\.jsx$/,
exclude: /node_modules/,
use: {
loader: "babel-loader",
options: {
presets: [["@babel/preset-env", { modules: false }], "preact"]
}
}
},
{
test: /\.css$/,
use: [{ loader: "style-loader" }, { loader: "css-loader" }]
},
{
test: /\.wasm$/,
exclude: /node_modules/,
type: "javascript/auto",
use: { loader: "bin-loader" }
}
]
},
plugins: [
new webpack.ContextReplacementPlugin(/mocha\/lib/, "", false),
new HtmlWebpackPlugin(
Object.assign(
{
title,
meta: {
viewport: "width=device-width, initial-scale=1, shrink-to-fit=no"
}
},
template ? { template } : {}
)
)
],
// Mocha requires this
node: {
fs: "empty"
},
// Mocha requires this
performance: { hints: false }
};
if (mode === "development") {
result.devtool = "cheap-module-eval-source-map";
} else {
result.devtool = "source-map";
}
return result;
}
module.exports = (env, argv) => [
config({
title: "WAForth",
template: "./src/shell/index.html",
entry: "./src/shell/index.js",
outputDir: "waforth",
mode: argv.mode
}),
config({
title: "WAForth Unit Tests",
template: "./tests/index.html",
entry: "./tests/index.js",
outputDir: "tests",
mode: argv.mode
}),
config({
title: "Benchmarks",
entry: "./tests/benchmarks/index.js",
outputDir: "benchmarks",
mode: argv.mode
})
];

7046
yarn.lock

File diff suppressed because it is too large Load diff