diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..e1d47aa --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +# Doesn't work yet, because there is no 'wabt' package in Ubuntu yet +# This will have to wait until disco. +# TODO: Add racket. +language: node_js +node_js: + - "node" +script: + - yarn build + - yarn test +cache: yarn +addons: + apt: + packages: + - wabt diff --git a/Makefile b/Makefile index 2863b50..5a8d7a7 100644 --- a/Makefile +++ b/Makefile @@ -3,16 +3,14 @@ WAT2WASM_FLAGS= ifeq ($(DEBUG),1) WAT2WASM_FLAGS=--debug-names endif -WEBPACK=yarn exec webpack -WEBPACK_DEV_SERVER=yarn exec webpack-dev-server WASM_FILES=src/waforth.wasm tests/benchmarks/sieve-vanilla.wasm all: $(WASM_FILES) - $(WEBPACK) --mode=production + yarn -s build dev-server: $(WASM_FILES) - $(WEBPACK_DEV_SERVER) --open --openPage waforth --content-base public + yarn -s dev-server wasm: $(WASM_FILES) src/tools/quadruple.wasm.hex @@ -35,5 +33,8 @@ src/tools/quadruple.wasm.hex: src/tools/quadruple.wasm clean: -rm -rf $(WASM_FILES) src/tools/quadruple.wasm src/tools/quadruple.wasm.hex src/waforth.wat.tmp dist +check: + yarn -s test + lint: - yarn run -s eslint . + yarn -s lint diff --git a/README.md b/README.md index 02ce05e..a8f217f 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,11 @@ To run the development server: ## Testing -The tests are served from `/tests`. +The tests are served from `/tests` by the development server. + +You can also run the tests in Node.JS by running + + make check ## Design diff --git a/package.json b/package.json index 3f26370..9b62b8b 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@babel/core": "^7.3.4", "@babel/preset-env": "^7.3.4", "@babel/preset-react": "^7.0.0", + "@babel/register": "^7.0.0", "babel-loader": "^8.0.5", "bin-loader": "^0.1.0", "chai": "^4.2.0", @@ -24,5 +25,12 @@ "webpack": "^4.29.6", "webpack-cli": "^3.2.3", "webpack-dev-server": "^3.2.1" + }, + "scripts": { + "build": "webpack --mode=production", + "test": "mocha tests/index-node.js", + "lint": "eslint .", + "dev-server": + "webpack-dev-server --open --openPage waforth --content-base public" } } diff --git a/src/shell/WAForth.js b/src/shell/WAForth.js index f350121..e0376e0 100644 --- a/src/shell/WAForth.js +++ b/src/shell/WAForth.js @@ -1,6 +1,6 @@ -import wasmModule from "../waforth.wasm"; - -const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent); +const isSafari = + typeof navigator != "undefined" && + /^((?!chrome|android).)*safari/i.test(navigator.userAgent); // eslint-disable-next-line no-unused-vars function arrayToBase64(bytes) { @@ -13,13 +13,21 @@ function arrayToBase64(bytes) { } class WAForth { + constructor(wasmModule) { + if (wasmModule == null) { + this.wasmModule = require("../waforth.wasm"); + } else { + this.wasmModule = wasmModule; + } + } + start(options = {}) { const { skipPrelude } = options; let table; let memory; const buffer = (this.buffer = []); - return WebAssembly.instantiate(wasmModule, { + return WebAssembly.instantiate(this.wasmModule, { shell: { //////////////////////////////////////// // I/O diff --git a/tests/index-node.js b/tests/index-node.js new file mode 100644 index 0000000..17338f7 --- /dev/null +++ b/tests/index-node.js @@ -0,0 +1,9 @@ +/* global __dirname */ +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); diff --git a/tests/index.js b/tests/index.js index c14df06..c16a033 100644 --- a/tests/index.js +++ b/tests/index.js @@ -1,1251 +1,8 @@ -import WAForth from "../src/shell/WAForth"; import { mocha } from "mocha"; -import { expect } from "chai"; -import sieve from "../src/shell/sieve"; +import loadTests from "./tests"; + mocha.setup("bdd"); - -describe("WAForth", () => { - let forth, stack, output, core, memory, memory8; - - beforeEach(() => { - forth = new WAForth(); - forth.onEmit = c => { - output = output + String.fromCharCode(c); - }; - const x = forth.start({ skipPrelude: true }).then( - () => { - core = forth.core.exports; - - output = ""; - memory = new Int32Array(core.memory.buffer, 0, 0x30000); - memory8 = new Uint8Array(core.memory.buffer, 0, 0x30000); - // dictionary = new Uint8Array(core.memory.buffer, 0x1000, 0x1000); - stack = new Int32Array(core.memory.buffer, core.tos(), 0x100); - }, - err => { - console.error(err); - } - ); - return x; - }); - - // eslint-disable-next-line no-unused-vars - function dumpTable() { - for (let i = 0; i < core.table.length; ++i) { - console.log("table", i, core.table.get(i)); - } - } - - function getString(p, n) { - let name = []; - for (let i = 0; i < n; ++i) { - name.push(String.fromCharCode(memory8[p + i])); - } - return name.join(""); - } - - // function getCountedString(p) { - // return getString(p + 4, memory[p / 4]); - // } - - function loadString(s) { - run("HERE"); - run(`${s.length} ,`); - for (let i = 0; i < s.length; ++i) { - run(`${s.charCodeAt(i)} C,`); - } - } - - // eslint-disable-next-line no-unused-vars - function dumpWord(w) { - let end = here(); - let p = latest(); - while (p != w) { - console.log("SEARCH", p, w); - end = p; - p = memory[p / 4]; - } - const previous = memory[p / 4]; - const length = memory8[p + 4]; - const name = getString(p + 4 + 1, length); - const codeP = (p + 4 + 1 + length + 3) & ~3; - const code = memory[codeP / 4]; - const data = []; - for (let i = codeP + 4; i < end; ++i) { - data.push(memory8[i]); - } - console.log("Entry:", p, previous, length, name, code, data, end); - } - - function run(s) { - const r = forth.run(s); - expect(r).to.not.be.below(0); - output = output.substr(0, output.length - 3); // Strip 'ok\n' from output - return r; - } - - function here() { - run("HERE"); - const result = memory[core.tos() / 4 - 1]; - run("DROP"); - return result; - } - - function latest() { - run("LATEST"); - const result = memory[core.tos() / 4 - 1]; - run("DROP"); - return result; - } - - describe("leb128", () => { - it("should convert 0x0", () => { - const r = core.leb128(0x0, 0x0); - expect(r).to.eql(0x1); - expect(memory8[0]).to.eql(0x0); - }); - it("should convert 0x17", () => { - const r = core.leb128(0x0, 0x17); - expect(r).to.eql(0x1); - expect(memory8[0]).to.eql(0x17); - }); - it("should convert 0x80", () => { - const r = core.leb128(0x0, 0x80); - expect(r).to.eql(0x2); - expect(memory8[0]).to.eql(0x80); - expect(memory8[1]).to.eql(0x01); - }); - it("should convert 0x12345", () => { - const r = core.leb128(0x0, 0x12345); - expect(r).to.eql(0x3); - expect(memory8[0]).to.eql(0xc5); - expect(memory8[1]).to.eql(0xc6); - expect(memory8[2]).to.eql(0x04); - }); - it("should convert -1", () => { - const r = core.leb128(0x0, -1); - expect(r).to.eql(0x1); - expect(memory8[0]).to.eql(0x7f); - }); - it("should convert -0x12345", () => { - const r = core.leb128(0x0, -0x12345); - expect(r).to.eql(0x3); - expect(memory8[0]).to.eql(0xbb); - expect(memory8[1]).to.eql(0xb9); - expect(memory8[2]).to.eql(0x7b); - }); - }); - - describe("leb128-4p", () => { - it("should convert 0x0", () => { - expect(core.leb128_4p(0x0)).to.eql(0x808080); - }); - it("should convert 0x17", () => { - expect(core.leb128_4p(0x17)).to.eql(0x808097); - }); - it("should convert 0x80", () => { - expect(core.leb128_4p(0x80)).to.eql(0x808180); - }); - it("should convert 0xBADF00D", () => { - expect(core.leb128_4p(0xbadf00d)).to.eql(0x5db7e08d); - }); - it("should convert 0xFFFFFFF", () => { - expect(core.leb128_4p(0xfffffff)).to.eql(0x7fffffff); - }); - }); - - describe("interpret", () => { - it("should return an error when word is not found", () => { - forth.read("BADWORD"); - expect(core.interpret()).to.eql(-1); - }); - - it("should interpret a positive number", () => { - forth.read("123"); - expect(core.interpret()).to.eql(0); - expect(stack[0]).to.eql(123); - }); - - it("should interpret a negative number", () => { - forth.read("-123"); - expect(core.interpret()).to.eql(0); - expect(stack[0]).to.eql(-123); - }); - it("should interpret a hex", () => { - forth.read("16 BASE ! DF"); - expect(core.interpret()).to.eql(0); - expect(stack[0]).to.eql(223); - }); - it("should not interpret hex in decimal mode", () => { - forth.read("DF"); - expect(core.interpret()).to.eql(-1); - }); - - it("should fail on half a word", () => { - forth.read("23FOO"); - expect(core.interpret()).to.eql(-1); - }); - }); - - describe("DUP", () => { - it("should work", () => { - run("121"); - run("DUP"); - expect(stack[0]).to.eql(121); - expect(stack[1]).to.eql(121); - }); - }); - - describe("?DUP", () => { - it("should duplicate when not 0", () => { - run("121"); - run("?DUP 5"); - expect(stack[0]).to.eql(121); - expect(stack[1]).to.eql(121); - expect(stack[2]).to.eql(5); - }); - - it("should not duplicate when 0", () => { - run("0"); - run("?DUP 5"); - expect(stack[0]).to.eql(0); - expect(stack[1]).to.eql(5); - }); - }); - - describe("2DUP", () => { - it("should work", () => { - run("222"); - run("173"); - run("2DUP"); - run("5"); - expect(stack[0]).to.eql(222); - expect(stack[1]).to.eql(173); - expect(stack[2]).to.eql(222); - expect(stack[3]).to.eql(173); - expect(stack[4]).to.eql(5); - }); - }); - - describe("ROT", () => { - it("should work", () => { - run("1 2 3 ROT 5"); - expect(stack[0]).to.eql(2); - expect(stack[1]).to.eql(3); - expect(stack[2]).to.eql(1); - expect(stack[3]).to.eql(5); - }); - }); - - describe("*", () => { - it("should multiply", () => { - run("3"); - run("4"); - run("*"); - run("5"); - expect(stack[0]).to.eql(12); - expect(stack[1]).to.eql(5); - }); - - it("should multiply negative", () => { - run("-3"); - run("4"); - run("*"); - run("5"); - expect(stack[0]).to.eql(-12); - expect(stack[1]).to.eql(5); - }); - }); - - describe("+", () => { - it("should add", () => { - run("3"); - run("4"); - run("+"); - run("5"); - expect(stack[0]).to.eql(7); - expect(stack[1]).to.eql(5); - }); - }); - - describe("-", () => { - it("should subtract", () => { - run("8 5 - 5"); - expect(stack[0]).to.eql(3); - expect(stack[1]).to.eql(5); - }); - - it("should subtract to negative", () => { - run("8 13 - 5"); - expect(stack[0]).to.eql(-5); - expect(stack[1]).to.eql(5); - }); - - it("should subtract negative", () => { - run("8 -3 - 5"); - expect(stack[0]).to.eql(11); - expect(stack[1]).to.eql(5); - }); - }); - - describe("/", () => { - it("should divide", () => { - run("15 5 / 5"); - expect(stack[0]).to.eql(3); - expect(stack[1]).to.eql(5); - }); - - it("should divide negative", () => { - run("15 -5 / 5"); - expect(stack[0]).to.eql(-3); - expect(stack[1]).to.eql(5); - }); - }); - - describe("/MOD", () => { - it("should work", () => { - run("15 6 /MOD 5"); - expect(stack[0]).to.eql(3); - expect(stack[1]).to.eql(2); - expect(stack[2]).to.eql(5); - }); - }); - - describe("*/", () => { - it("should work with small numbers", () => { - run("10 3 5 */ 5"); - expect(stack[0]).to.eql(6); - expect(stack[1]).to.eql(5); - }); - - it("should work with large numbers", () => { - run("268435455 1000 5000 */"); - expect(stack[0]).to.eql(53687091); - }); - }); - - describe("*/MOD", () => { - it("should work with small numbers", () => { - run("9 3 5 */MOD 7"); - expect(stack[0]).to.eql(2); - expect(stack[1]).to.eql(5); - expect(stack[2]).to.eql(7); - }); - - it("should work with large numbers", () => { - run("268435455 1000 3333 */MOD"); - expect(stack[0]).to.eql(1230); - expect(stack[1]).to.eql(80538690); - }); - }); - - describe("1+", () => { - it("should work with positive numbers", () => { - run("3"); - run("1+"); - run("5"); - expect(stack[0]).to.eql(4); - expect(stack[1]).to.eql(5); - }); - - it("should work with negative numbers", () => { - run("-3"); - run("1+"); - run("5"); - expect(stack[0]).to.eql(-2); - expect(stack[1]).to.eql(5); - }); - }); - - describe("1-", () => { - it("should work with positive numbers", () => { - run("3"); - run("1-"); - run("5"); - expect(stack[0]).to.eql(2); - expect(stack[1]).to.eql(5); - }); - - it("should work with negative numbers", () => { - run("-3"); - run("1-"); - run("5"); - expect(stack[0]).to.eql(-4); - expect(stack[1]).to.eql(5); - }); - }); - - describe(">", () => { - it("should test true when greater", () => { - run("5"); - run("3"); - run(">"); - run("5"); - expect(stack[0]).to.eql(-1); - expect(stack[1]).to.eql(5); - }); - - it("should test false when smaller", () => { - run("3"); - run("5"); - run(">"); - run("5"); - expect(stack[0]).to.eql(0); - expect(stack[1]).to.eql(5); - }); - - it("should test false when equal", () => { - run("5"); - run("5"); - run(">"); - run("5"); - expect(stack[0]).to.eql(0); - expect(stack[1]).to.eql(5); - }); - - it("should work with negative numbers", () => { - run("5"); - run("-3"); - run(">"); - run("5"); - expect(stack[0]).to.eql(-1); - expect(stack[1]).to.eql(5); - }); - }); - - describe("NEGATE", () => { - it("should negate positive number", () => { - run("7 NEGATE 5"); - expect(stack[0]).to.eql(-7); - expect(stack[1]).to.eql(5); - }); - - it("should negate negative number", () => { - run("-7 NEGATE 5"); - expect(stack[0]).to.eql(7); - expect(stack[1]).to.eql(5); - }); - - it("should negate negative zero", () => { - run("0 NEGATE 5"); - expect(stack[0]).to.eql(0); - expect(stack[1]).to.eql(5); - }); - }); - - describe("0=", () => { - it("should test true", () => { - run("0"); - run("0="); - run("5"); - expect(stack[0]).to.eql(-1); - expect(stack[1]).to.eql(5); - }); - - it("should test false", () => { - run("23"); - run("0="); - run("5"); - expect(stack[0]).to.eql(0); - expect(stack[1]).to.eql(5); - }); - }); - - describe("0>", () => { - it("should test true", () => { - run("2"); - run("0>"); - run("5"); - expect(stack[0]).to.eql(-1); - expect(stack[1]).to.eql(5); - }); - - it("should test false", () => { - run("-3"); - run("0>"); - run("5"); - expect(stack[0]).to.eql(0); - expect(stack[1]).to.eql(5); - }); - }); - - describe("OVER", () => { - it("should work", () => { - run("12"); - run("34"); - run("OVER"); - run("5"); - expect(stack[0]).to.eql(12); - expect(stack[1]).to.eql(34); - expect(stack[2]).to.eql(12); - expect(stack[3]).to.eql(5); - }); - }); - - describe("SWAP", () => { - it("should work", () => { - run("12"); - run("34"); - run("SWAP"); - run("5"); - expect(stack[0]).to.eql(34); - expect(stack[1]).to.eql(12); - expect(stack[2]).to.eql(5); - }); - }); - - describe("EMIT", () => { - it("should work once", () => { - run("87"); - run("EMIT"); - expect(output).to.eql("W"); - }); - - it("should work twice", () => { - run("97"); - run("87"); - run("EMIT"); - run("EMIT"); - expect(output).to.eql("Wa"); - }); - }); - - describe("DROP", () => { - it("should drop", () => { - run("222"); - run("173"); - run("DROP"); - run("190"); - expect(stack[0]).to.eql(222); - expect(stack[1]).to.eql(190); - }); - }); - - describe("ERASE", () => { - it("should erase", () => { - const ptr = here(); - memory8[ptr] = 222; - memory8[ptr + 1] = 173; - memory8[ptr + 2] = 190; - memory8[ptr + 3] = 239; - run((ptr + 1).toString(10)); - run("2 ERASE 5"); - - expect(memory8[ptr + 0]).to.eql(222); - expect(memory8[ptr + 1]).to.eql(0x00); - expect(memory8[ptr + 2]).to.eql(0x00); - expect(memory8[ptr + 3]).to.eql(239); - expect(stack[0]).to.eql(5); - }); - }); - - describe("IF/ELSE/THEN", () => { - it("should take the then branch without else", () => { - run(`: FOO IF 8 THEN ;`); - run("1 FOO 5"); - expect(stack[0]).to.eql(8); - expect(stack[1]).to.eql(5); - }); - - it("should not take the then branch without else", () => { - run(": FOO"); - run("IF"); - run("8"); - run("THEN"); - run("0"); - run(";"); - run("FOO"); - run("5"); - expect(stack[0]).to.eql(5); - }); - - it("should take the then branch with else", () => { - run(": FOO"); - run("IF"); - run("8"); - run("ELSE"); - run("9"); - run("THEN"); - run(";"); - run("1"); - run("FOO"); - run("5"); - expect(stack[0]).to.eql(8); - expect(stack[1]).to.eql(5); - }); - - it("should take the else branch with else", () => { - run(": FOO"); - run("IF"); - run("8"); - run("ELSE"); - run("9"); - run("THEN"); - run(";"); - run("0"); - run("FOO"); - run("5"); - expect(stack[0]).to.eql(9); - expect(stack[1]).to.eql(5); - }); - - it("should support nested if", () => { - run(`: FOO - IF - IF 8 ELSE 9 THEN - 10 - ELSE - 11 - THEN - ;`); - run("0 1 FOO 5"); - expect(stack[0]).to.eql(9); - expect(stack[1]).to.eql(10); - expect(stack[2]).to.eql(5); - }); - }); - - describe("DO/LOOP", () => { - it("should run a loop", () => { - run(`: FOO 4 0 DO 3 LOOP ;`); - run("FOO 5"); - expect(stack[0]).to.eql(3); - expect(stack[1]).to.eql(3); - expect(stack[2]).to.eql(3); - expect(stack[3]).to.eql(3); - expect(stack[4]).to.eql(5); - }); - - it("should run a nested loop", () => { - run(`: FOO 3 0 DO 2 0 DO 3 LOOP LOOP ;`); - run("FOO 5"); - expect(stack[0]).to.eql(3); - expect(stack[1]).to.eql(3); - expect(stack[2]).to.eql(3); - expect(stack[3]).to.eql(3); - expect(stack[4]).to.eql(3); - expect(stack[5]).to.eql(3); - expect(stack[6]).to.eql(5); - }); - }); - - describe("LEAVE", () => { - it("should leave", () => { - run(`: FOO 4 0 DO 3 LEAVE 6 LOOP 4 ;`); - run("FOO 5"); - expect(stack[0]).to.eql(3); - expect(stack[1]).to.eql(4); - expect(stack[2]).to.eql(5); - }); - }); - - describe("+LOOP", () => { - it("should increment a loop", () => { - run(`: FOO 10 0 DO 3 2 +LOOP ;`); - run("FOO 5"); - expect(stack[0]).to.eql(3); - expect(stack[1]).to.eql(3); - expect(stack[2]).to.eql(3); - expect(stack[3]).to.eql(3); - expect(stack[4]).to.eql(3); - expect(stack[5]).to.eql(5); - }); - - it("should increment a loop beyond the index", () => { - run(`: FOO 10 0 DO 3 8 +LOOP ;`); - run("FOO 5"); - expect(stack[0]).to.eql(3); - expect(stack[1]).to.eql(3); - expect(stack[2]).to.eql(5); - }); - }); - - describe("I", () => { - it("should work", () => { - run(`: FOO 4 0 DO I LOOP ;`); - run("FOO 5"); - expect(stack[0]).to.eql(0); - expect(stack[1]).to.eql(1); - expect(stack[2]).to.eql(2); - expect(stack[3]).to.eql(3); - expect(stack[4]).to.eql(5); - }); - - it("should work in a nested loop", () => { - run(`: FOO 3 0 DO 2 0 DO I LOOP LOOP ;`); - run("FOO 5"); - expect(stack[0]).to.eql(0); - expect(stack[1]).to.eql(1); - expect(stack[2]).to.eql(0); - expect(stack[3]).to.eql(1); - expect(stack[4]).to.eql(0); - expect(stack[5]).to.eql(1); - expect(stack[6]).to.eql(5); - }); - }); - - describe("J", () => { - it("should work", () => { - run(`: FOO 3 0 DO 2 0 DO J LOOP LOOP ;`); - run("FOO 5"); - expect(stack[0]).to.eql(0); - expect(stack[1]).to.eql(0); - expect(stack[2]).to.eql(1); - expect(stack[3]).to.eql(1); - expect(stack[4]).to.eql(2); - expect(stack[5]).to.eql(2); - expect(stack[6]).to.eql(5); - }); - - it("should work in a nested loop", () => { - run(`: FOO 3 0 DO 2 0 DO J LOOP LOOP ;`); - run("FOO 5"); - expect(stack[0]).to.eql(0); - expect(stack[1]).to.eql(0); - expect(stack[2]).to.eql(1); - expect(stack[3]).to.eql(1); - expect(stack[4]).to.eql(2); - expect(stack[5]).to.eql(2); - expect(stack[6]).to.eql(5); - }); - }); - - describe("BEGIN / WHILE / REPEAT", () => { - it("should work", () => { - run(`: FOO BEGIN DUP 2 * DUP 16 < WHILE DUP REPEAT 7 ;`); - run("1 FOO 5"); - expect(stack[0]).to.eql(1); - expect(stack[1]).to.eql(2); - expect(stack[2]).to.eql(2); - expect(stack[3]).to.eql(4); - expect(stack[4]).to.eql(4); - expect(stack[5]).to.eql(8); - expect(stack[6]).to.eql(8); - expect(stack[7]).to.eql(16); - expect(stack[8]).to.eql(7); - expect(stack[9]).to.eql(5); - }); - }); - - describe("BEGIN / UNTIL", () => { - it("should work", () => { - run(`: FOO BEGIN DUP 2 * DUP 16 > UNTIL 7 ;`); - run("1 FOO 5"); - expect(stack[0]).to.eql(1); - expect(stack[1]).to.eql(2); - expect(stack[2]).to.eql(4); - expect(stack[3]).to.eql(8); - expect(stack[4]).to.eql(16); - expect(stack[5]).to.eql(32); - expect(stack[6]).to.eql(7); - expect(stack[7]).to.eql(5); - }); - }); - - describe("EXIT", () => { - it("should work", () => { - run(`: FOO IF 3 EXIT 4 THEN 5 ;`); - run("1 FOO 6"); - expect(stack[0]).to.eql(3); - expect(stack[1]).to.eql(6); - }); - }); - - describe("( / )", () => { - beforeEach(() => { - core.loadPrelude(); - }); - - it("should work", () => { - run(": FOO ( bad -- x ) 7 ;"); - run("1 FOO 5"); - expect(stack[0]).to.eql(1); - expect(stack[1]).to.eql(7); - expect(stack[2]).to.eql(5); - }); - }); - - describe("CHAR", () => { - it("should work with a single character", () => { - run("CHAR A 5"); - expect(stack[0]).to.eql(65); - expect(stack[1]).to.eql(5); - }); - - it("should work with multiple characters", () => { - run("CHAR ABC 5"); - expect(stack[0]).to.eql(65); - expect(stack[1]).to.eql(5); - }); - }); - - // describe("word", () => { - // it("should read a word", () => { - // forth.read(" FOO BAR BAZ "); - // core.WORD(); - // expect(getCountedString(stack[0])).to.eql("FOO"); - // }); - // - // it("should read two words", () => { - // forth.read(" FOO BAR BAZ "); - // core.WORD(); - // core.WORD(); - // expect(getCountedString(stack[1])).to.eql("BAR"); - // }); - // - // it("should skip comments", () => { - // forth.read(" \\ FOO BAZ\n BART BAZ"); - // core.WORD(); - // expect(getCountedString(stack[0])).to.eql("BART"); - // }); - // - // it("should stop at end of buffer while parsing word", () => { - // forth.read("FOO"); - // core.WORD(); - // expect(getCountedString(stack[0])).to.eql("FOO"); - // }); - // - // it("should stop at end of buffer while parsing comments", () => { - // forth.read(" \\FOO"); - // core.WORD(); - // expect(getCountedString()).to.eql(""); - // }); - // - // it("should stop when parsing empty line", () => { - // forth.read(" "); - // core.WORD(); - // expect(getCountedString()).to.eql(""); - // }); - // - // it("should stop when parsing nothing", () => { - // forth.read(""); - // core.WORD(); - // expect(getCountedString()).to.eql(""); - // }); - // }); - - describe("FIND", () => { - it("should find a word", () => { - loadString("DUP"); - run("FIND"); - expect(stack[0]).to.eql(131768); - expect(stack[1]).to.eql(-1); - }); - - it("should find a short word", () => { - loadString("!"); - run("FIND"); - expect(stack[0]).to.eql(131072); - expect(stack[1]).to.eql(-1); - }); - - it("should find an immediate word", () => { - loadString("+LOOP"); - run("FIND"); - expect(stack[0]).to.eql(131160); - expect(stack[1]).to.eql(1); - }); - - it("should not find an unexisting word", () => { - loadString("BADWORD"); - run("FIND"); - expect(stack[1]).to.eql(0); - }); - - it("should not find a very long unexisting word", () => { - loadString("VERYVERYVERYBADWORD"); - run("FIND"); - expect(stack[1]).to.eql(0); - }); - }); - - describe("BASE", () => { - it("should contain the base", () => { - run("BASE @ 5"); - expect(stack[0]).to.eql(10); - expect(stack[1]).to.eql(5); - }); - }); - - describe("KEY", () => { - it("should read a key", () => { - run("KEY F"); - run("5"); - expect(stack[0]).to.eql(70); - expect(stack[1]).to.eql(5); - }); - }); - - describe("LITERAL", () => { - it("should put a literal on the stack", () => { - run("20 : FOO LITERAL ;"); - run("5 FOO"); - expect(stack[0]).to.eql(5); - expect(stack[1]).to.eql(20); - }); - }); - - describe("[ ]", () => { - it("should work", () => { - run(": FOO [ 20 5 * ] LITERAL ;"); - run("5 FOO 6"); - expect(stack[0]).to.eql(5); - expect(stack[1]).to.eql(100); - expect(stack[2]).to.eql(6); - }); - }); - - describe("C@", () => { - it("should fetch an aligned character", () => { - const ptr = here(); - memory8[ptr] = 222; - memory8[ptr + 1] = 173; - run(ptr.toString()); - run("C@"); - expect(stack[0]).to.eql(222); - }); - - it("should fetch an unaligned character", () => { - const ptr = here(); - memory8[ptr] = 222; - memory8[ptr + 1] = 173; - run((ptr + 1).toString()); - run("C@"); - expect(stack[0]).to.eql(173); - }); - }); - - describe("C!", () => { - it("should store an aligned character", () => { - const ptr = here(); - memory8[ptr] = 222; - memory8[ptr + 1] = 173; - run("190"); - run(ptr.toString()); - run("C! 5"); - expect(stack[0]).to.eql(5); - expect(memory8[ptr]).to.eql(190); - expect(memory8[ptr + 1]).to.eql(173); - }); - - it("should store an unaligned character", () => { - const ptr = here(); - memory8[ptr] = 222; - memory8[ptr + 1] = 173; - run("190"); - run((ptr + 1).toString()); - run("C! 5"); - expect(stack[0]).to.eql(5); - expect(memory8[ptr]).to.eql(222); - expect(memory8[ptr + 1]).to.eql(190); - }); - }); - - describe("@", () => { - it("should fetch", () => { - const ptr = here(); - memory[ptr / 4] = 123456; - run(ptr.toString()); - run("@ 5"); - expect(stack[0]).to.eql(123456); - expect(stack[1]).to.eql(5); - }); - }); - - describe("!", () => { - it("should store", () => { - const ptr = here(); - run("12345"); - run(ptr.toString()); - run("! 5"); - expect(stack[0]).to.eql(5); - expect(memory[ptr / 4]).to.eql(12345); - }); - }); - - describe(",", () => { - it("should add word", () => { - run("HERE"); - run("1234"); - run(","); - run("HERE"); - expect(stack[1] - stack[0]).to.eql(4); - expect(memory[stack[0] / 4]).to.eql(1234); - }); - }); - - describe('S"', () => { - it("should work", () => { - run(': FOO S" Foo Bar" ;'); - run("FOO"); - expect(stack[1]).to.eql(7); - expect(getString(stack[0], stack[1])).to.eql("Foo Bar"); - }); - - it("should work with 2 strings", () => { - run(': FOO S" Foo Bar" ;'); - run(': BAR S" Baz Ba" ;'); - run("FOO BAR 5"); - expect(stack[1]).to.eql(7); - expect(getString(stack[0], stack[1])).to.eql("Foo Bar"); - expect(stack[3]).to.eql(6); - expect(getString(stack[2], stack[3])).to.eql("Baz Ba"); - }); - }); - - describe('."', () => { - it("should work", () => { - run(': FOO ." Foo Bar" ;'); - run("FOO 5"); - expect(stack[0]).to.eql(5); - expect(output).to.eql("Foo Bar"); - }); - }); - - describe("MOVE", () => { - it("should work with non-overlapping regions", () => { - const ptr = here(); - memory8[ptr] = 1; - memory8[ptr + 1] = 2; - memory8[ptr + 2] = 3; - memory8[ptr + 3] = 4; - memory8[ptr + 4] = 5; - run("HERE HERE 10 + 4 MOVE 5"); - - expect(stack[0]).to.eql(5); - expect(memory8[ptr + 10]).to.eql(1); - expect(memory8[ptr + 11]).to.eql(2); - expect(memory8[ptr + 12]).to.eql(3); - expect(memory8[ptr + 13]).to.eql(4); - expect(memory8[ptr + 14]).to.eql(0); - }); - - it("should work with begin-overlapping regions", () => { - const ptr = here(); - memory8[ptr] = 1; - memory8[ptr + 1] = 2; - memory8[ptr + 2] = 3; - memory8[ptr + 3] = 4; - memory8[ptr + 4] = 5; - run("HERE HERE 2 + 4 MOVE 5"); - - expect(stack[0]).to.eql(5); - expect(memory8[ptr + 0]).to.eql(1); - expect(memory8[ptr + 1]).to.eql(2); - expect(memory8[ptr + 2]).to.eql(1); - expect(memory8[ptr + 3]).to.eql(2); - expect(memory8[ptr + 4]).to.eql(3); - expect(memory8[ptr + 5]).to.eql(4); - expect(memory8[ptr + 6]).to.eql(0); - }); - - it("should work with end-overlapping regions", () => { - const ptr = here(); - memory8[ptr + 10] = 1; - memory8[ptr + 11] = 2; - memory8[ptr + 12] = 3; - memory8[ptr + 13] = 4; - memory8[ptr + 14] = 5; - run("HERE 10 + DUP 2 - 4 MOVE 5"); - - expect(stack[0]).to.eql(5); - expect(memory8[ptr + 8]).to.eql(1); - expect(memory8[ptr + 9]).to.eql(2); - expect(memory8[ptr + 10]).to.eql(3); - expect(memory8[ptr + 11]).to.eql(4); - expect(memory8[ptr + 12]).to.eql(3); - expect(memory8[ptr + 13]).to.eql(4); - expect(memory8[ptr + 14]).to.eql(5); - }); - }); - - describe("RECURSE", () => { - it("should recurse", () => { - run(": FOO DUP 4 < IF DUP 1+ RECURSE ELSE 12 THEN 13 ;"); - run("1 FOO 5"); - expect(stack[0]).to.eql(1); - expect(stack[1]).to.eql(2); - expect(stack[2]).to.eql(3); - expect(stack[3]).to.eql(4); - expect(stack[4]).to.eql(12); - expect(stack[5]).to.eql(13); - expect(stack[6]).to.eql(13); - expect(stack[7]).to.eql(13); - expect(stack[8]).to.eql(13); - expect(stack[9]).to.eql(5); - }); - }); - - describe("CREATE", () => { - it("should create words", () => { - run("HERE"); - run("LATEST"); - run("CREATE DUP"); - run("HERE"); - run("LATEST"); - expect(stack[2] - stack[0]).to.eql(4 + 4 + 4); - expect(stack[3]).to.eql(stack[0]); - expect(stack[3]).to.not.eql(stack[1]); - }); - - it("should create findable words", () => { - run("CREATE FOOBAR"); - run("LATEST"); - run("CREATE BAM"); - loadString("FOOBAR"); - run("FIND"); - expect(stack[1]).to.eql(stack[0]); - expect(stack[2]).to.eql(-1); - }); - - it("should align unaligned words", () => { - run("CREATE DUPE"); - run("HERE"); - expect(stack[0] % 4).to.eql(0); - }); - - it("should align aligned words", () => { - run("CREATE DUP"); - run("HERE"); - expect(stack[0] % 4).to.eql(0); - }); - }); - - describe("IMMEDIATE", () => { - it("should make words immediate", () => { - run("CREATE FOOBAR IMMEDIATE"); - loadString("FOOBAR"); - run("FIND"); - expect(stack[1]).to.eql(1); - }); - }); - - describe(":", () => { - it("should compile multiple instructions", () => { - run(": FOOBAR 4 * ;"); - run("3 FOOBAR"); - expect(stack[0]).to.eql(12); - }); - - it("should compile negative numbers", () => { - run(": FOOBAR -4 * ;"); - run("3 FOOBAR"); - expect(stack[0]).to.eql(-12); - }); - - it("should compile large numbers", () => { - run(": FOOBAR 111111 * ;"); - run("3 FOOBAR"); - expect(stack[0]).to.eql(333333); - }); - - it("should skip comments", () => { - run(": FOOBAR\n\n \\ Test string \n 4 * ;"); - run("3 FOOBAR 5"); - expect(stack[0]).to.eql(12); - expect(stack[1]).to.eql(5); - }); - - it("should override", () => { - run(": FOOBAR 3 ;"); - run(": FOOBAR FOOBAR 4 ;"); - run("FOOBAR 5"); - expect(stack[0]).to.eql(3); - expect(stack[1]).to.eql(4); - expect(stack[2]).to.eql(5); - }); - - it("should compile a name with an illegal WASM character", () => { - run(': F" 3 0 DO 2 LOOP ;'); - }); - }); - - describe("VARIABLE", () => { - it("should work with one variable", () => { - run("VARIABLE FOO"); - run("12 FOO !"); - run("FOO @ 5"); - expect(stack[0]).to.eql(12); - expect(stack[1]).to.eql(5); - }); - - it("should work with two variables", () => { - run("VARIABLE FOO VARIABLE BAR"); - run("12 FOO ! 13 BAR !"); - run("FOO @ BAR @ 5"); - expect(stack[0]).to.eql(12); - expect(stack[1]).to.eql(13); - expect(stack[2]).to.eql(5); - }); - }); - - describe("CONSTANT", () => { - it("should work", () => { - run("12 CONSTANT FOO"); - run("FOO 5"); - expect(stack[0]).to.eql(12); - expect(stack[1]).to.eql(5); - }); - }); - - describe("VALUE", () => { - it("should store a value", () => { - run("12 VALUE FOO"); - run("FOO 5"); - expect(stack[0]).to.eql(12); - expect(stack[1]).to.eql(5); - }); - - it("should update a value", () => { - run("12 VALUE FOO"); - run("13 TO FOO"); - run("FOO 5"); - expect(stack[0]).to.eql(13); - expect(stack[1]).to.eql(5); - }); - }); - - // describe.only("DOES>", () => { - // it("should work", () => { - // run(": ID CREATE 1 , DOES> @"); - // }); - // }); - - describe("UWIDTH", () => { - beforeEach(() => { - core.loadPrelude(); - }); - - it("should work with 3 digits", () => { - run("123 UWIDTH"); - expect(stack[0]).to.eql(3); - }); - - it("should work with 4 digits", () => { - run("1234 UWIDTH"); - expect(stack[0]).to.eql(4); - }); - }); - - describe("system", () => { - beforeEach(() => { - core.loadPrelude(); - }); - - it("should run sieve", () => { - run(sieve); - run("100 sieve"); - expect(output.trim()).to.eql("97"); - }); - - it("should run direct sieve", () => { - run(sieve); - run("100 sieve_direct"); - expect(stack[0]).to.eql(97); - }); - }); -}); - +loadTests(); // mocha.checkLeaks(); mocha.globals(["jQuery"]); mocha.run(); diff --git a/tests/tests.js b/tests/tests.js new file mode 100644 index 0000000..a562a4e --- /dev/null +++ b/tests/tests.js @@ -0,0 +1,1249 @@ +import WAForth from "../src/shell/WAForth"; +import sieve from "../src/shell/sieve"; +import { expect } from "chai"; + +function loadTests(wasmModule) { + describe("WAForth", () => { + let forth, stack, output, core, memory, memory8; + + beforeEach(() => { + forth = new WAForth(wasmModule); + forth.onEmit = c => { + output = output + String.fromCharCode(c); + }; + const x = forth.start({ skipPrelude: true }).then( + () => { + core = forth.core.exports; + + output = ""; + memory = new Int32Array(core.memory.buffer, 0, 0x30000); + memory8 = new Uint8Array(core.memory.buffer, 0, 0x30000); + // dictionary = new Uint8Array(core.memory.buffer, 0x1000, 0x1000); + stack = new Int32Array(core.memory.buffer, core.tos(), 0x100); + }, + err => { + console.error(err); + } + ); + return x; + }); + + // eslint-disable-next-line no-unused-vars + function dumpTable() { + for (let i = 0; i < core.table.length; ++i) { + console.log("table", i, core.table.get(i)); + } + } + + function getString(p, n) { + let name = []; + for (let i = 0; i < n; ++i) { + name.push(String.fromCharCode(memory8[p + i])); + } + return name.join(""); + } + + // function getCountedString(p) { + // return getString(p + 4, memory[p / 4]); + // } + + function loadString(s) { + run("HERE"); + run(`${s.length} ,`); + for (let i = 0; i < s.length; ++i) { + run(`${s.charCodeAt(i)} C,`); + } + } + + // eslint-disable-next-line no-unused-vars + function dumpWord(w) { + let end = here(); + let p = latest(); + while (p != w) { + console.log("SEARCH", p, w); + end = p; + p = memory[p / 4]; + } + const previous = memory[p / 4]; + const length = memory8[p + 4]; + const name = getString(p + 4 + 1, length); + const codeP = (p + 4 + 1 + length + 3) & ~3; + const code = memory[codeP / 4]; + const data = []; + for (let i = codeP + 4; i < end; ++i) { + data.push(memory8[i]); + } + console.log("Entry:", p, previous, length, name, code, data, end); + } + + function run(s) { + const r = forth.run(s); + expect(r).to.not.be.below(0); + output = output.substr(0, output.length - 3); // Strip 'ok\n' from output + return r; + } + + function here() { + run("HERE"); + const result = memory[core.tos() / 4 - 1]; + run("DROP"); + return result; + } + + function latest() { + run("LATEST"); + const result = memory[core.tos() / 4 - 1]; + run("DROP"); + return result; + } + + describe("leb128", () => { + it("should convert 0x0", () => { + const r = core.leb128(0x0, 0x0); + expect(r).to.eql(0x1); + expect(memory8[0]).to.eql(0x0); + }); + it("should convert 0x17", () => { + const r = core.leb128(0x0, 0x17); + expect(r).to.eql(0x1); + expect(memory8[0]).to.eql(0x17); + }); + it("should convert 0x80", () => { + const r = core.leb128(0x0, 0x80); + expect(r).to.eql(0x2); + expect(memory8[0]).to.eql(0x80); + expect(memory8[1]).to.eql(0x01); + }); + it("should convert 0x12345", () => { + const r = core.leb128(0x0, 0x12345); + expect(r).to.eql(0x3); + expect(memory8[0]).to.eql(0xc5); + expect(memory8[1]).to.eql(0xc6); + expect(memory8[2]).to.eql(0x04); + }); + it("should convert -1", () => { + const r = core.leb128(0x0, -1); + expect(r).to.eql(0x1); + expect(memory8[0]).to.eql(0x7f); + }); + it("should convert -0x12345", () => { + const r = core.leb128(0x0, -0x12345); + expect(r).to.eql(0x3); + expect(memory8[0]).to.eql(0xbb); + expect(memory8[1]).to.eql(0xb9); + expect(memory8[2]).to.eql(0x7b); + }); + }); + + describe("leb128-4p", () => { + it("should convert 0x0", () => { + expect(core.leb128_4p(0x0)).to.eql(0x808080); + }); + it("should convert 0x17", () => { + expect(core.leb128_4p(0x17)).to.eql(0x808097); + }); + it("should convert 0x80", () => { + expect(core.leb128_4p(0x80)).to.eql(0x808180); + }); + it("should convert 0xBADF00D", () => { + expect(core.leb128_4p(0xbadf00d)).to.eql(0x5db7e08d); + }); + it("should convert 0xFFFFFFF", () => { + expect(core.leb128_4p(0xfffffff)).to.eql(0x7fffffff); + }); + }); + + describe("interpret", () => { + it("should return an error when word is not found", () => { + forth.read("BADWORD"); + expect(core.interpret()).to.eql(-1); + }); + + it("should interpret a positive number", () => { + forth.read("123"); + expect(core.interpret()).to.eql(0); + expect(stack[0]).to.eql(123); + }); + + it("should interpret a negative number", () => { + forth.read("-123"); + expect(core.interpret()).to.eql(0); + expect(stack[0]).to.eql(-123); + }); + it("should interpret a hex", () => { + forth.read("16 BASE ! DF"); + expect(core.interpret()).to.eql(0); + expect(stack[0]).to.eql(223); + }); + it("should not interpret hex in decimal mode", () => { + forth.read("DF"); + expect(core.interpret()).to.eql(-1); + }); + + it("should fail on half a word", () => { + forth.read("23FOO"); + expect(core.interpret()).to.eql(-1); + }); + }); + + describe("DUP", () => { + it("should work", () => { + run("121"); + run("DUP"); + expect(stack[0]).to.eql(121); + expect(stack[1]).to.eql(121); + }); + }); + + describe("?DUP", () => { + it("should duplicate when not 0", () => { + run("121"); + run("?DUP 5"); + expect(stack[0]).to.eql(121); + expect(stack[1]).to.eql(121); + expect(stack[2]).to.eql(5); + }); + + it("should not duplicate when 0", () => { + run("0"); + run("?DUP 5"); + expect(stack[0]).to.eql(0); + expect(stack[1]).to.eql(5); + }); + }); + + describe("2DUP", () => { + it("should work", () => { + run("222"); + run("173"); + run("2DUP"); + run("5"); + expect(stack[0]).to.eql(222); + expect(stack[1]).to.eql(173); + expect(stack[2]).to.eql(222); + expect(stack[3]).to.eql(173); + expect(stack[4]).to.eql(5); + }); + }); + + describe("ROT", () => { + it("should work", () => { + run("1 2 3 ROT 5"); + expect(stack[0]).to.eql(2); + expect(stack[1]).to.eql(3); + expect(stack[2]).to.eql(1); + expect(stack[3]).to.eql(5); + }); + }); + + describe("*", () => { + it("should multiply", () => { + run("3"); + run("4"); + run("*"); + run("5"); + expect(stack[0]).to.eql(12); + expect(stack[1]).to.eql(5); + }); + + it("should multiply negative", () => { + run("-3"); + run("4"); + run("*"); + run("5"); + expect(stack[0]).to.eql(-12); + expect(stack[1]).to.eql(5); + }); + }); + + describe("+", () => { + it("should add", () => { + run("3"); + run("4"); + run("+"); + run("5"); + expect(stack[0]).to.eql(7); + expect(stack[1]).to.eql(5); + }); + }); + + describe("-", () => { + it("should subtract", () => { + run("8 5 - 5"); + expect(stack[0]).to.eql(3); + expect(stack[1]).to.eql(5); + }); + + it("should subtract to negative", () => { + run("8 13 - 5"); + expect(stack[0]).to.eql(-5); + expect(stack[1]).to.eql(5); + }); + + it("should subtract negative", () => { + run("8 -3 - 5"); + expect(stack[0]).to.eql(11); + expect(stack[1]).to.eql(5); + }); + }); + + describe("/", () => { + it("should divide", () => { + run("15 5 / 5"); + expect(stack[0]).to.eql(3); + expect(stack[1]).to.eql(5); + }); + + it("should divide negative", () => { + run("15 -5 / 5"); + expect(stack[0]).to.eql(-3); + expect(stack[1]).to.eql(5); + }); + }); + + describe("/MOD", () => { + it("should work", () => { + run("15 6 /MOD 5"); + expect(stack[0]).to.eql(3); + expect(stack[1]).to.eql(2); + expect(stack[2]).to.eql(5); + }); + }); + + describe("*/", () => { + it("should work with small numbers", () => { + run("10 3 5 */ 5"); + expect(stack[0]).to.eql(6); + expect(stack[1]).to.eql(5); + }); + + it("should work with large numbers", () => { + run("268435455 1000 5000 */"); + expect(stack[0]).to.eql(53687091); + }); + }); + + describe("*/MOD", () => { + it("should work with small numbers", () => { + run("9 3 5 */MOD 7"); + expect(stack[0]).to.eql(2); + expect(stack[1]).to.eql(5); + expect(stack[2]).to.eql(7); + }); + + it("should work with large numbers", () => { + run("268435455 1000 3333 */MOD"); + expect(stack[0]).to.eql(1230); + expect(stack[1]).to.eql(80538690); + }); + }); + + describe("1+", () => { + it("should work with positive numbers", () => { + run("3"); + run("1+"); + run("5"); + expect(stack[0]).to.eql(4); + expect(stack[1]).to.eql(5); + }); + + it("should work with negative numbers", () => { + run("-3"); + run("1+"); + run("5"); + expect(stack[0]).to.eql(-2); + expect(stack[1]).to.eql(5); + }); + }); + + describe("1-", () => { + it("should work with positive numbers", () => { + run("3"); + run("1-"); + run("5"); + expect(stack[0]).to.eql(2); + expect(stack[1]).to.eql(5); + }); + + it("should work with negative numbers", () => { + run("-3"); + run("1-"); + run("5"); + expect(stack[0]).to.eql(-4); + expect(stack[1]).to.eql(5); + }); + }); + + describe(">", () => { + it("should test true when greater", () => { + run("5"); + run("3"); + run(">"); + run("5"); + expect(stack[0]).to.eql(-1); + expect(stack[1]).to.eql(5); + }); + + it("should test false when smaller", () => { + run("3"); + run("5"); + run(">"); + run("5"); + expect(stack[0]).to.eql(0); + expect(stack[1]).to.eql(5); + }); + + it("should test false when equal", () => { + run("5"); + run("5"); + run(">"); + run("5"); + expect(stack[0]).to.eql(0); + expect(stack[1]).to.eql(5); + }); + + it("should work with negative numbers", () => { + run("5"); + run("-3"); + run(">"); + run("5"); + expect(stack[0]).to.eql(-1); + expect(stack[1]).to.eql(5); + }); + }); + + describe("NEGATE", () => { + it("should negate positive number", () => { + run("7 NEGATE 5"); + expect(stack[0]).to.eql(-7); + expect(stack[1]).to.eql(5); + }); + + it("should negate negative number", () => { + run("-7 NEGATE 5"); + expect(stack[0]).to.eql(7); + expect(stack[1]).to.eql(5); + }); + + it("should negate negative zero", () => { + run("0 NEGATE 5"); + expect(stack[0]).to.eql(0); + expect(stack[1]).to.eql(5); + }); + }); + + describe("0=", () => { + it("should test true", () => { + run("0"); + run("0="); + run("5"); + expect(stack[0]).to.eql(-1); + expect(stack[1]).to.eql(5); + }); + + it("should test false", () => { + run("23"); + run("0="); + run("5"); + expect(stack[0]).to.eql(0); + expect(stack[1]).to.eql(5); + }); + }); + + describe("0>", () => { + it("should test true", () => { + run("2"); + run("0>"); + run("5"); + expect(stack[0]).to.eql(-1); + expect(stack[1]).to.eql(5); + }); + + it("should test false", () => { + run("-3"); + run("0>"); + run("5"); + expect(stack[0]).to.eql(0); + expect(stack[1]).to.eql(5); + }); + }); + + describe("OVER", () => { + it("should work", () => { + run("12"); + run("34"); + run("OVER"); + run("5"); + expect(stack[0]).to.eql(12); + expect(stack[1]).to.eql(34); + expect(stack[2]).to.eql(12); + expect(stack[3]).to.eql(5); + }); + }); + + describe("SWAP", () => { + it("should work", () => { + run("12"); + run("34"); + run("SWAP"); + run("5"); + expect(stack[0]).to.eql(34); + expect(stack[1]).to.eql(12); + expect(stack[2]).to.eql(5); + }); + }); + + describe("EMIT", () => { + it("should work once", () => { + run("87"); + run("EMIT"); + expect(output).to.eql("W"); + }); + + it("should work twice", () => { + run("97"); + run("87"); + run("EMIT"); + run("EMIT"); + expect(output).to.eql("Wa"); + }); + }); + + describe("DROP", () => { + it("should drop", () => { + run("222"); + run("173"); + run("DROP"); + run("190"); + expect(stack[0]).to.eql(222); + expect(stack[1]).to.eql(190); + }); + }); + + describe("ERASE", () => { + it("should erase", () => { + const ptr = here(); + memory8[ptr] = 222; + memory8[ptr + 1] = 173; + memory8[ptr + 2] = 190; + memory8[ptr + 3] = 239; + run((ptr + 1).toString(10)); + run("2 ERASE 5"); + + expect(memory8[ptr + 0]).to.eql(222); + expect(memory8[ptr + 1]).to.eql(0x00); + expect(memory8[ptr + 2]).to.eql(0x00); + expect(memory8[ptr + 3]).to.eql(239); + expect(stack[0]).to.eql(5); + }); + }); + + describe("IF/ELSE/THEN", () => { + it("should take the then branch without else", () => { + run(`: FOO IF 8 THEN ;`); + run("1 FOO 5"); + expect(stack[0]).to.eql(8); + expect(stack[1]).to.eql(5); + }); + + it("should not take the then branch without else", () => { + run(": FOO"); + run("IF"); + run("8"); + run("THEN"); + run("0"); + run(";"); + run("FOO"); + run("5"); + expect(stack[0]).to.eql(5); + }); + + it("should take the then branch with else", () => { + run(": FOO"); + run("IF"); + run("8"); + run("ELSE"); + run("9"); + run("THEN"); + run(";"); + run("1"); + run("FOO"); + run("5"); + expect(stack[0]).to.eql(8); + expect(stack[1]).to.eql(5); + }); + + it("should take the else branch with else", () => { + run(": FOO"); + run("IF"); + run("8"); + run("ELSE"); + run("9"); + run("THEN"); + run(";"); + run("0"); + run("FOO"); + run("5"); + expect(stack[0]).to.eql(9); + expect(stack[1]).to.eql(5); + }); + + it("should support nested if", () => { + run(`: FOO + IF + IF 8 ELSE 9 THEN + 10 + ELSE + 11 + THEN + ;`); + run("0 1 FOO 5"); + expect(stack[0]).to.eql(9); + expect(stack[1]).to.eql(10); + expect(stack[2]).to.eql(5); + }); + }); + + describe("DO/LOOP", () => { + it("should run a loop", () => { + run(`: FOO 4 0 DO 3 LOOP ;`); + run("FOO 5"); + expect(stack[0]).to.eql(3); + expect(stack[1]).to.eql(3); + expect(stack[2]).to.eql(3); + expect(stack[3]).to.eql(3); + expect(stack[4]).to.eql(5); + }); + + it("should run a nested loop", () => { + run(`: FOO 3 0 DO 2 0 DO 3 LOOP LOOP ;`); + run("FOO 5"); + expect(stack[0]).to.eql(3); + expect(stack[1]).to.eql(3); + expect(stack[2]).to.eql(3); + expect(stack[3]).to.eql(3); + expect(stack[4]).to.eql(3); + expect(stack[5]).to.eql(3); + expect(stack[6]).to.eql(5); + }); + }); + + describe("LEAVE", () => { + it("should leave", () => { + run(`: FOO 4 0 DO 3 LEAVE 6 LOOP 4 ;`); + run("FOO 5"); + expect(stack[0]).to.eql(3); + expect(stack[1]).to.eql(4); + expect(stack[2]).to.eql(5); + }); + }); + + describe("+LOOP", () => { + it("should increment a loop", () => { + run(`: FOO 10 0 DO 3 2 +LOOP ;`); + run("FOO 5"); + expect(stack[0]).to.eql(3); + expect(stack[1]).to.eql(3); + expect(stack[2]).to.eql(3); + expect(stack[3]).to.eql(3); + expect(stack[4]).to.eql(3); + expect(stack[5]).to.eql(5); + }); + + it("should increment a loop beyond the index", () => { + run(`: FOO 10 0 DO 3 8 +LOOP ;`); + run("FOO 5"); + expect(stack[0]).to.eql(3); + expect(stack[1]).to.eql(3); + expect(stack[2]).to.eql(5); + }); + }); + + describe("I", () => { + it("should work", () => { + run(`: FOO 4 0 DO I LOOP ;`); + run("FOO 5"); + expect(stack[0]).to.eql(0); + expect(stack[1]).to.eql(1); + expect(stack[2]).to.eql(2); + expect(stack[3]).to.eql(3); + expect(stack[4]).to.eql(5); + }); + + it("should work in a nested loop", () => { + run(`: FOO 3 0 DO 2 0 DO I LOOP LOOP ;`); + run("FOO 5"); + expect(stack[0]).to.eql(0); + expect(stack[1]).to.eql(1); + expect(stack[2]).to.eql(0); + expect(stack[3]).to.eql(1); + expect(stack[4]).to.eql(0); + expect(stack[5]).to.eql(1); + expect(stack[6]).to.eql(5); + }); + }); + + describe("J", () => { + it("should work", () => { + run(`: FOO 3 0 DO 2 0 DO J LOOP LOOP ;`); + run("FOO 5"); + expect(stack[0]).to.eql(0); + expect(stack[1]).to.eql(0); + expect(stack[2]).to.eql(1); + expect(stack[3]).to.eql(1); + expect(stack[4]).to.eql(2); + expect(stack[5]).to.eql(2); + expect(stack[6]).to.eql(5); + }); + + it("should work in a nested loop", () => { + run(`: FOO 3 0 DO 2 0 DO J LOOP LOOP ;`); + run("FOO 5"); + expect(stack[0]).to.eql(0); + expect(stack[1]).to.eql(0); + expect(stack[2]).to.eql(1); + expect(stack[3]).to.eql(1); + expect(stack[4]).to.eql(2); + expect(stack[5]).to.eql(2); + expect(stack[6]).to.eql(5); + }); + }); + + describe("BEGIN / WHILE / REPEAT", () => { + it("should work", () => { + run(`: FOO BEGIN DUP 2 * DUP 16 < WHILE DUP REPEAT 7 ;`); + run("1 FOO 5"); + expect(stack[0]).to.eql(1); + expect(stack[1]).to.eql(2); + expect(stack[2]).to.eql(2); + expect(stack[3]).to.eql(4); + expect(stack[4]).to.eql(4); + expect(stack[5]).to.eql(8); + expect(stack[6]).to.eql(8); + expect(stack[7]).to.eql(16); + expect(stack[8]).to.eql(7); + expect(stack[9]).to.eql(5); + }); + }); + + describe("BEGIN / UNTIL", () => { + it("should work", () => { + run(`: FOO BEGIN DUP 2 * DUP 16 > UNTIL 7 ;`); + run("1 FOO 5"); + expect(stack[0]).to.eql(1); + expect(stack[1]).to.eql(2); + expect(stack[2]).to.eql(4); + expect(stack[3]).to.eql(8); + expect(stack[4]).to.eql(16); + expect(stack[5]).to.eql(32); + expect(stack[6]).to.eql(7); + expect(stack[7]).to.eql(5); + }); + }); + + describe("EXIT", () => { + it("should work", () => { + run(`: FOO IF 3 EXIT 4 THEN 5 ;`); + run("1 FOO 6"); + expect(stack[0]).to.eql(3); + expect(stack[1]).to.eql(6); + }); + }); + + describe("( / )", () => { + beforeEach(() => { + core.loadPrelude(); + }); + + it("should work", () => { + run(": FOO ( bad -- x ) 7 ;"); + run("1 FOO 5"); + expect(stack[0]).to.eql(1); + expect(stack[1]).to.eql(7); + expect(stack[2]).to.eql(5); + }); + }); + + describe("CHAR", () => { + it("should work with a single character", () => { + run("CHAR A 5"); + expect(stack[0]).to.eql(65); + expect(stack[1]).to.eql(5); + }); + + it("should work with multiple characters", () => { + run("CHAR ABC 5"); + expect(stack[0]).to.eql(65); + expect(stack[1]).to.eql(5); + }); + }); + + // describe("word", () => { + // it("should read a word", () => { + // forth.read(" FOO BAR BAZ "); + // core.WORD(); + // expect(getCountedString(stack[0])).to.eql("FOO"); + // }); + // + // it("should read two words", () => { + // forth.read(" FOO BAR BAZ "); + // core.WORD(); + // core.WORD(); + // expect(getCountedString(stack[1])).to.eql("BAR"); + // }); + // + // it("should skip comments", () => { + // forth.read(" \\ FOO BAZ\n BART BAZ"); + // core.WORD(); + // expect(getCountedString(stack[0])).to.eql("BART"); + // }); + // + // it("should stop at end of buffer while parsing word", () => { + // forth.read("FOO"); + // core.WORD(); + // expect(getCountedString(stack[0])).to.eql("FOO"); + // }); + // + // it("should stop at end of buffer while parsing comments", () => { + // forth.read(" \\FOO"); + // core.WORD(); + // expect(getCountedString()).to.eql(""); + // }); + // + // it("should stop when parsing empty line", () => { + // forth.read(" "); + // core.WORD(); + // expect(getCountedString()).to.eql(""); + // }); + // + // it("should stop when parsing nothing", () => { + // forth.read(""); + // core.WORD(); + // expect(getCountedString()).to.eql(""); + // }); + // }); + + describe("FIND", () => { + it("should find a word", () => { + loadString("DUP"); + run("FIND"); + expect(stack[0]).to.eql(131768); + expect(stack[1]).to.eql(-1); + }); + + it("should find a short word", () => { + loadString("!"); + run("FIND"); + expect(stack[0]).to.eql(131072); + expect(stack[1]).to.eql(-1); + }); + + it("should find an immediate word", () => { + loadString("+LOOP"); + run("FIND"); + expect(stack[0]).to.eql(131160); + expect(stack[1]).to.eql(1); + }); + + it("should not find an unexisting word", () => { + loadString("BADWORD"); + run("FIND"); + expect(stack[1]).to.eql(0); + }); + + it("should not find a very long unexisting word", () => { + loadString("VERYVERYVERYBADWORD"); + run("FIND"); + expect(stack[1]).to.eql(0); + }); + }); + + describe("BASE", () => { + it("should contain the base", () => { + run("BASE @ 5"); + expect(stack[0]).to.eql(10); + expect(stack[1]).to.eql(5); + }); + }); + + describe("KEY", () => { + it("should read a key", () => { + run("KEY F"); + run("5"); + expect(stack[0]).to.eql(70); + expect(stack[1]).to.eql(5); + }); + }); + + describe("LITERAL", () => { + it("should put a literal on the stack", () => { + run("20 : FOO LITERAL ;"); + run("5 FOO"); + expect(stack[0]).to.eql(5); + expect(stack[1]).to.eql(20); + }); + }); + + describe("[ ]", () => { + it("should work", () => { + run(": FOO [ 20 5 * ] LITERAL ;"); + run("5 FOO 6"); + expect(stack[0]).to.eql(5); + expect(stack[1]).to.eql(100); + expect(stack[2]).to.eql(6); + }); + }); + + describe("C@", () => { + it("should fetch an aligned character", () => { + const ptr = here(); + memory8[ptr] = 222; + memory8[ptr + 1] = 173; + run(ptr.toString()); + run("C@"); + expect(stack[0]).to.eql(222); + }); + + it("should fetch an unaligned character", () => { + const ptr = here(); + memory8[ptr] = 222; + memory8[ptr + 1] = 173; + run((ptr + 1).toString()); + run("C@"); + expect(stack[0]).to.eql(173); + }); + }); + + describe("C!", () => { + it("should store an aligned character", () => { + const ptr = here(); + memory8[ptr] = 222; + memory8[ptr + 1] = 173; + run("190"); + run(ptr.toString()); + run("C! 5"); + expect(stack[0]).to.eql(5); + expect(memory8[ptr]).to.eql(190); + expect(memory8[ptr + 1]).to.eql(173); + }); + + it("should store an unaligned character", () => { + const ptr = here(); + memory8[ptr] = 222; + memory8[ptr + 1] = 173; + run("190"); + run((ptr + 1).toString()); + run("C! 5"); + expect(stack[0]).to.eql(5); + expect(memory8[ptr]).to.eql(222); + expect(memory8[ptr + 1]).to.eql(190); + }); + }); + + describe("@", () => { + it("should fetch", () => { + const ptr = here(); + memory[ptr / 4] = 123456; + run(ptr.toString()); + run("@ 5"); + expect(stack[0]).to.eql(123456); + expect(stack[1]).to.eql(5); + }); + }); + + describe("!", () => { + it("should store", () => { + const ptr = here(); + run("12345"); + run(ptr.toString()); + run("! 5"); + expect(stack[0]).to.eql(5); + expect(memory[ptr / 4]).to.eql(12345); + }); + }); + + describe(",", () => { + it("should add word", () => { + run("HERE"); + run("1234"); + run(","); + run("HERE"); + expect(stack[1] - stack[0]).to.eql(4); + expect(memory[stack[0] / 4]).to.eql(1234); + }); + }); + + describe('S"', () => { + it("should work", () => { + run(': FOO S" Foo Bar" ;'); + run("FOO"); + expect(stack[1]).to.eql(7); + expect(getString(stack[0], stack[1])).to.eql("Foo Bar"); + }); + + it("should work with 2 strings", () => { + run(': FOO S" Foo Bar" ;'); + run(': BAR S" Baz Ba" ;'); + run("FOO BAR 5"); + expect(stack[1]).to.eql(7); + expect(getString(stack[0], stack[1])).to.eql("Foo Bar"); + expect(stack[3]).to.eql(6); + expect(getString(stack[2], stack[3])).to.eql("Baz Ba"); + }); + }); + + describe('."', () => { + it("should work", () => { + run(': FOO ." Foo Bar" ;'); + run("FOO 5"); + expect(stack[0]).to.eql(5); + expect(output).to.eql("Foo Bar"); + }); + }); + + describe("MOVE", () => { + it("should work with non-overlapping regions", () => { + const ptr = here(); + memory8[ptr] = 1; + memory8[ptr + 1] = 2; + memory8[ptr + 2] = 3; + memory8[ptr + 3] = 4; + memory8[ptr + 4] = 5; + run("HERE HERE 10 + 4 MOVE 5"); + + expect(stack[0]).to.eql(5); + expect(memory8[ptr + 10]).to.eql(1); + expect(memory8[ptr + 11]).to.eql(2); + expect(memory8[ptr + 12]).to.eql(3); + expect(memory8[ptr + 13]).to.eql(4); + expect(memory8[ptr + 14]).to.eql(0); + }); + + it("should work with begin-overlapping regions", () => { + const ptr = here(); + memory8[ptr] = 1; + memory8[ptr + 1] = 2; + memory8[ptr + 2] = 3; + memory8[ptr + 3] = 4; + memory8[ptr + 4] = 5; + run("HERE HERE 2 + 4 MOVE 5"); + + expect(stack[0]).to.eql(5); + expect(memory8[ptr + 0]).to.eql(1); + expect(memory8[ptr + 1]).to.eql(2); + expect(memory8[ptr + 2]).to.eql(1); + expect(memory8[ptr + 3]).to.eql(2); + expect(memory8[ptr + 4]).to.eql(3); + expect(memory8[ptr + 5]).to.eql(4); + expect(memory8[ptr + 6]).to.eql(0); + }); + + it("should work with end-overlapping regions", () => { + const ptr = here(); + memory8[ptr + 10] = 1; + memory8[ptr + 11] = 2; + memory8[ptr + 12] = 3; + memory8[ptr + 13] = 4; + memory8[ptr + 14] = 5; + run("HERE 10 + DUP 2 - 4 MOVE 5"); + + expect(stack[0]).to.eql(5); + expect(memory8[ptr + 8]).to.eql(1); + expect(memory8[ptr + 9]).to.eql(2); + expect(memory8[ptr + 10]).to.eql(3); + expect(memory8[ptr + 11]).to.eql(4); + expect(memory8[ptr + 12]).to.eql(3); + expect(memory8[ptr + 13]).to.eql(4); + expect(memory8[ptr + 14]).to.eql(5); + }); + }); + + describe("RECURSE", () => { + it("should recurse", () => { + run(": FOO DUP 4 < IF DUP 1+ RECURSE ELSE 12 THEN 13 ;"); + run("1 FOO 5"); + expect(stack[0]).to.eql(1); + expect(stack[1]).to.eql(2); + expect(stack[2]).to.eql(3); + expect(stack[3]).to.eql(4); + expect(stack[4]).to.eql(12); + expect(stack[5]).to.eql(13); + expect(stack[6]).to.eql(13); + expect(stack[7]).to.eql(13); + expect(stack[8]).to.eql(13); + expect(stack[9]).to.eql(5); + }); + }); + + describe("CREATE", () => { + it("should create words", () => { + run("HERE"); + run("LATEST"); + run("CREATE DUP"); + run("HERE"); + run("LATEST"); + expect(stack[2] - stack[0]).to.eql(4 + 4 + 4); + expect(stack[3]).to.eql(stack[0]); + expect(stack[3]).to.not.eql(stack[1]); + }); + + it("should create findable words", () => { + run("CREATE FOOBAR"); + run("LATEST"); + run("CREATE BAM"); + loadString("FOOBAR"); + run("FIND"); + expect(stack[1]).to.eql(stack[0]); + expect(stack[2]).to.eql(-1); + }); + + it("should align unaligned words", () => { + run("CREATE DUPE"); + run("HERE"); + expect(stack[0] % 4).to.eql(0); + }); + + it("should align aligned words", () => { + run("CREATE DUP"); + run("HERE"); + expect(stack[0] % 4).to.eql(0); + }); + }); + + describe("IMMEDIATE", () => { + it("should make words immediate", () => { + run("CREATE FOOBAR IMMEDIATE"); + loadString("FOOBAR"); + run("FIND"); + expect(stack[1]).to.eql(1); + }); + }); + + describe(":", () => { + it("should compile multiple instructions", () => { + run(": FOOBAR 4 * ;"); + run("3 FOOBAR"); + expect(stack[0]).to.eql(12); + }); + + it("should compile negative numbers", () => { + run(": FOOBAR -4 * ;"); + run("3 FOOBAR"); + expect(stack[0]).to.eql(-12); + }); + + it("should compile large numbers", () => { + run(": FOOBAR 111111 * ;"); + run("3 FOOBAR"); + expect(stack[0]).to.eql(333333); + }); + + it("should skip comments", () => { + run(": FOOBAR\n\n \\ Test string \n 4 * ;"); + run("3 FOOBAR 5"); + expect(stack[0]).to.eql(12); + expect(stack[1]).to.eql(5); + }); + + it("should override", () => { + run(": FOOBAR 3 ;"); + run(": FOOBAR FOOBAR 4 ;"); + run("FOOBAR 5"); + expect(stack[0]).to.eql(3); + expect(stack[1]).to.eql(4); + expect(stack[2]).to.eql(5); + }); + + it("should compile a name with an illegal WASM character", () => { + run(': F" 3 0 DO 2 LOOP ;'); + }); + }); + + describe("VARIABLE", () => { + it("should work with one variable", () => { + run("VARIABLE FOO"); + run("12 FOO !"); + run("FOO @ 5"); + expect(stack[0]).to.eql(12); + expect(stack[1]).to.eql(5); + }); + + it("should work with two variables", () => { + run("VARIABLE FOO VARIABLE BAR"); + run("12 FOO ! 13 BAR !"); + run("FOO @ BAR @ 5"); + expect(stack[0]).to.eql(12); + expect(stack[1]).to.eql(13); + expect(stack[2]).to.eql(5); + }); + }); + + describe("CONSTANT", () => { + it("should work", () => { + run("12 CONSTANT FOO"); + run("FOO 5"); + expect(stack[0]).to.eql(12); + expect(stack[1]).to.eql(5); + }); + }); + + describe("VALUE", () => { + it("should store a value", () => { + run("12 VALUE FOO"); + run("FOO 5"); + expect(stack[0]).to.eql(12); + expect(stack[1]).to.eql(5); + }); + + it("should update a value", () => { + run("12 VALUE FOO"); + run("13 TO FOO"); + run("FOO 5"); + expect(stack[0]).to.eql(13); + expect(stack[1]).to.eql(5); + }); + }); + + // describe.only("DOES>", () => { + // it("should work", () => { + // run(": ID CREATE 1 , DOES> @"); + // }); + // }); + + describe("UWIDTH", () => { + beforeEach(() => { + core.loadPrelude(); + }); + + it("should work with 3 digits", () => { + run("123 UWIDTH"); + expect(stack[0]).to.eql(3); + }); + + it("should work with 4 digits", () => { + run("1234 UWIDTH"); + expect(stack[0]).to.eql(4); + }); + }); + + describe("system", () => { + beforeEach(() => { + core.loadPrelude(); + }); + + it("should run sieve", () => { + run(sieve); + run("100 sieve"); + expect(output.trim()).to.eql("97"); + }); + + it("should run direct sieve", () => { + run(sieve); + run("100 sieve_direct"); + expect(stack[0]).to.eql(97); + }); + }); + }); +} + +export default loadTests; diff --git a/yarn.lock b/yarn.lock index 000ddf2..aab41df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -617,6 +617,19 @@ "@babel/plugin-transform-react-jsx-self" "^7.0.0" "@babel/plugin-transform-react-jsx-source" "^7.0.0" +"@babel/register@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.0.0.tgz#fa634bae1bfa429f60615b754fc1f1d745edd827" + integrity sha512-f/+CRmaCe7rVEvcvPvxeA8j5aJhHC3aJie7YuqcMDhUOuyWLA7J/aNrTaHIzoWPEhpHA54mec4Mm8fv8KBlv3g== + dependencies: + core-js "^2.5.7" + find-cache-dir "^1.0.0" + home-or-tmp "^3.0.0" + lodash "^4.17.10" + mkdirp "^0.5.1" + pirates "^4.0.0" + source-map-support "^0.5.9" + "@babel/template@^7.1.0", "@babel/template@^7.1.2", "@babel/template@^7.2.2": version "7.2.2" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907" @@ -1510,6 +1523,11 @@ copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" +core-js@^2.5.7: + version "2.6.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895" + integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A== + core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -2254,6 +2272,15 @@ finalhandler@1.1.1: statuses "~1.4.0" unpipe "~1.0.0" +find-cache-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" + integrity sha1-kojj6ePMN0hxfTnq3hfPcfww7m8= + dependencies: + commondir "^1.0.1" + make-dir "^1.0.0" + pkg-dir "^2.0.0" + find-cache-dir@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.0.0.tgz#4c1faed59f45184530fb9d7fa123a4d04a98472d" @@ -2263,6 +2290,13 @@ find-cache-dir@^2.0.0: make-dir "^1.0.0" pkg-dir "^3.0.0" +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -2573,6 +2607,11 @@ hmac-drbg@^1.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" +home-or-tmp@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-3.0.0.tgz#57a8fe24cf33cdd524860a15821ddc25c86671fb" + integrity sha1-V6j+JM8zzdUkhgoVgh3cJchmcfs= + homedir-polyfill@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc" @@ -3193,6 +3232,14 @@ loader-utils@^1.2.3: emojis-list "^2.0.0" json5 "^1.0.1" +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -3574,6 +3621,11 @@ node-libs-browser@^2.0.0: util "^0.10.3" vm-browserify "0.0.4" +node-modules-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" + integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= + node-pre-gyp@^0.10.0: version "0.10.0" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.0.tgz#6e4ef5bb5c5203c6552448828c852c40111aac46" @@ -3802,6 +3854,13 @@ p-is-promise@^2.0.0: resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.0.0.tgz#7554e3d572109a87e1f3f53f6a7d85d1b194f4c5" integrity sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg== +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + p-limit@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.0.tgz#417c9941e6027a9abcba5092dd2904e255b5fbc2" @@ -3809,6 +3868,13 @@ p-limit@^2.0.0: dependencies: p-try "^2.0.0" +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -3820,6 +3886,11 @@ p-map@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + p-try@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" @@ -3938,6 +4009,20 @@ pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" +pirates@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" + integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA== + dependencies: + node-modules-regexp "^1.0.0" + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + pkg-dir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" @@ -4619,7 +4704,7 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@~0.5.9: +source-map-support@^0.5.9, source-map-support@~0.5.9: version "0.5.10" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.10.tgz#2214080bc9d51832511ee2bab96e3c2f9353120c" integrity sha512-YfQ3tQFTK/yzlGJuX8pTwa4tifQj4QS2Mj7UegOu8jAz59MqIiMGPXxQhVQiIMNzayuUSF/jEuVnfFF5JqybmQ==