From a2bb2f0f9288521c640cf894ab081b890ae48777 Mon Sep 17 00:00:00 2001 From: Chien Tran Date: Wed, 5 Jan 2022 11:52:34 +0700 Subject: [PATCH] fix workflow rename namelist to chain chain works for array, string and object --- .github/workflows/npm-publish.yml | 10 ++-- package.json | 4 +- src/components/EArray.test.js | 2 +- src/components/EChain.js | 37 ++++++++++++++ .../{ENameList.test.js => EChain.test.js} | 49 +++++++++++++++---- src/components/EExpression.js | 4 +- src/components/EInvoke.js | 7 ++- src/components/ENameList.js | 32 ------------ src/components/ESet.js | 6 +-- src/components/ESet.test.js | 2 +- src/components/ETuple.js | 6 +-- src/components/ETuple.test.js | 2 +- src/components/operators.js | 10 ++-- src/components/operators.test.js | 23 ++++++--- src/core/error.js | 4 ++ src/core/index.js | 7 ++- src/escript.test.js | 5 +- 17 files changed, 130 insertions(+), 80 deletions(-) create mode 100644 src/components/EChain.js rename src/components/{ENameList.test.js => EChain.test.js} (59%) delete mode 100644 src/components/ENameList.js diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index e0404a4..129c5d4 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -1,4 +1,4 @@ -name: Node.js Package +name: Publish to NPM on: release: @@ -9,14 +9,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - run: npm i + - run: npm run lint + - run: npm test + - run: npm run cleanForPublish - uses: actions/setup-node@v2 with: node-version: 16 registry-url: https://registry.npmjs.org/ - - run: npm i - - run: npm run lint - - run: npm test - - run: npm run removeDocsAndTests - run: npm publish env: NODE_AUTH_TOKEN: ${{secrets.npm_token}} diff --git a/package.json b/package.json index b1fab6d..cfe58db 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@chientrm/es", "type": "module", - "version": "0.0.12-alpha15", + "version": "0.0.12-alpha16", "description": "", "main": "index.js", "directories": { @@ -16,7 +16,7 @@ "testLoc": "find src/*.test.js src/**/*.test.js | xargs wc -l", "lcov": "npx c8 -r lcovonly npm test", "coverage": "npx c8 npm test", - "removeDocsAndTests": "rm -rf docs src/*.test.js src/**/*.test.js" + "cleanForPublish": "rm -rf docs src/*.test.js src/**/*.test.js .github" }, "author": "Chien Tran", "license": "MIT", diff --git a/src/components/EArray.test.js b/src/components/EArray.test.js index 0f68c97..c2f9de6 100644 --- a/src/components/EArray.test.js +++ b/src/components/EArray.test.js @@ -14,7 +14,7 @@ describe("EArray", () => { "alice", ]); }); - it("name list", () => { + it("chain", () => { expect( run( [{ a: { b: 1 } }], diff --git a/src/components/EChain.js b/src/components/EChain.js new file mode 100644 index 0000000..4d6f91a --- /dev/null +++ b/src/components/EChain.js @@ -0,0 +1,37 @@ +import { invalidChain } from "../core/error.js"; +import { EName } from "./EName.js"; +import { EReference } from "./EReference.js"; +import { eRun } from "./utils.js"; + +export class EChain extends EName { + constructor(names) { + super({ names }); + } + getNameKey(i) { + const name = this.names[i]; + name instanceof EName || invalidChain(name); + return [name, name.name]; + } + run(contexts) { + let result = eRun(contexts, this.names[0]); + if (this.names.length > 1) + for (let i = 1; i < this.names.length; i++) { + const [name, key] = this.getNameKey(i); + result = name.run([...contexts, { [key]: result[key] }], result); + } + return result; + } + assign(contexts, value) { + if (this.names.length > 1) { + let result = eRun(contexts, this.names[0]); + for (let i = 1; i < this.names.length - 1; i++) { + const [name, key] = this.getNameKey(i); + result = name.run([...contexts, { [key]: result[key] }], result); + } + const [name, key] = this.getNameKey(this.names.length - 1); + return name instanceof EReference + ? (result[key] = value) + : name.assign([...contexts, { [key]: result[key] }], value); + } else return this.names[0].assign(contexts, value); + } +} diff --git a/src/components/ENameList.test.js b/src/components/EChain.test.js similarity index 59% rename from src/components/ENameList.test.js rename to src/components/EChain.test.js index a40fc72..bc1a8d0 100644 --- a/src/components/ENameList.test.js +++ b/src/components/EChain.test.js @@ -1,20 +1,46 @@ import { expect } from "chai"; import { describe, it } from "mocha"; import { EIndexing } from "./EIndexing.js"; -import { ENameList } from "./ENameList.js"; +import { EInvoke } from "./EInvoke.js"; +import { EChain } from "./EChain.js"; import { EReference } from "./EReference.js"; -describe("ENameList", () => { +describe("EChain", () => { describe("run", () => { - const run = (contexts, names) => new ENameList(names).run(contexts); + const run = (contexts, names) => new EChain(names).run(contexts); + it("invalid", () => { + expect(() => run([{}], [[], 1])).to.throw("Unknown chain 1"); + }); it("undefined", () => { - expect(run([{}], [new EReference("a")])).to.be.undefined; expect(run([{ a: 1 }], [new EReference("a"), new EReference("b")])).to.be .undefined; expect(() => run([{}], [new EReference("a"), new EReference("b")]) ).to.throw("read properties of undefined (reading 'b')"); }); + it("string", () => { + expect(run([{}], ["abc", new EInvoke("includes", ["a"])])).to.be.true; + expect( + run( + [{ s: "abc" }], + [new EReference("s"), new EInvoke("includes", ["a"])] + ) + ).to.be.true; + }); + it("array", () => { + expect( + run( + [{ f: (a) => a * 2 }], + [[2, 3], new EInvoke("map", [new EReference("f")])] + ) + ).to.deep.equal([4, 6]); + expect( + run( + [{ a: [2, 3], f: (i) => i * 2 }], + [new EReference("a"), new EInvoke("map", [new EReference("f")])] + ) + ).to.deep.equal([4, 6]); + }); it("1 reference", () => { const contexts = [{ a: 1 }]; expect(run(contexts, [new EReference("a")])).to.equal(1); @@ -27,17 +53,20 @@ describe("ENameList", () => { }); describe("assign", () => { const assign = (contexts, names, value) => - new ENameList(names).assign(contexts, value); + new EChain(names).assign(contexts, value); it("undefined", () => { const contexts = [{}]; expect(() => assign(contexts, [new EReference("a"), new EReference("b")], 1) - ).to.throw("Cannot read properties of undefined (reading 'b')"); + ).to.throw("Cannot set properties of undefined (setting 'b')"); }); - it("1 reference", () => { - const contexts = [{}]; - expect(assign(contexts, [new EReference("a")], 1)).to.equal(1); - expect(contexts).to.deep.equal([{ a: 1 }]); + it("string", () => { + expect(() => assign([{}], ["", new EReference("a")], 1)).to.throw( + "Cannot create property 'a' on string ''" + ); + }); + it("array", () => { + expect(assign([{}], [[], new EReference("a")], 1)).to.equal(1); }); it("2 references", () => { const contexts = [{ a: {} }]; diff --git a/src/components/EExpression.js b/src/components/EExpression.js index 6042abc..e90f039 100644 --- a/src/components/EExpression.js +++ b/src/components/EExpression.js @@ -1,7 +1,7 @@ import { EObject } from "./EObject.js"; import { operators as funcs } from "./operators.js"; import { invalidOperator } from "../core/index.js"; -import { ENameList } from "./ENameList.js"; +import { EChain } from "./EChain.js"; class Node extends EObject { constructor(left, right, func) { @@ -46,7 +46,7 @@ export class EExpression extends EObject { : stack.push(p) ); let result = stack.pop(); - result instanceof ENameList && (result = result.run(ctxs)); + result instanceof EChain && (result = result.run(ctxs)); return result; } } diff --git a/src/components/EInvoke.js b/src/components/EInvoke.js index 726cf88..3ad99a8 100644 --- a/src/components/EInvoke.js +++ b/src/components/EInvoke.js @@ -7,10 +7,13 @@ export class EInvoke extends EName { constructor(name, operands) { super({ name, operands }); } - run(ctxs) { + run(ctxs, _this) { const func = search(ctxs, this.name); isFunction(func) || invalidFunction(this.name); - return func(...this.operands.map((o) => eRun(ctxs, eRun(ctxs, o)))); + return func.apply( + _this, + this.operands.map((o) => eRun(ctxs, eRun(ctxs, o))) + ); } assign() { invalidLvalue(`invoke ${this.name}`); diff --git a/src/components/ENameList.js b/src/components/ENameList.js deleted file mode 100644 index 5cf7969..0000000 --- a/src/components/ENameList.js +++ /dev/null @@ -1,32 +0,0 @@ -import { EName } from "./EName.js"; -import { EReference } from "./EReference.js"; - -export class ENameList extends EName { - constructor(names) { - super({ names }); - } - run(contexts) { - if (this.names.length === 1) return this.names[0].run(contexts); - let result = this.names[0].run(contexts); - for (let i = 1; i < this.names.length; i++) { - const key = this.names[i].name; - result = this.names[i].run([...contexts, { [key]: result[key] }]); - } - return result; - } - assign(contexts, value) { - if (this.names.length === 1) return this.names[0].assign(contexts, value); - let object = this.names[0].run(contexts); - for (let i = 1; i < this.names.length; i++) { - const key = this.names[i].name; - const newContexts = [...contexts, { [key]: object[key] }]; - if (i === this.names.length - 1) - object = - this.names[i] instanceof EReference - ? (object[key] = value) - : this.names[i].assign(newContexts, value); - else object = this.names[i].run(newContexts); - } - return object; - } -} diff --git a/src/components/ESet.js b/src/components/ESet.js index 71cacef..fa05c85 100644 --- a/src/components/ESet.js +++ b/src/components/ESet.js @@ -1,4 +1,3 @@ -import { ENameList } from "./ENameList.js"; import { EObject } from "./EObject.js"; export class ESet extends EObject { @@ -8,10 +7,7 @@ export class ESet extends EObject { run(contexts) { const result = {}; contexts = [...contexts, result]; - this.operands.forEach((operand) => { - const _result = operand.run(contexts); - _result instanceof ENameList && _result.run(contexts); - }); + this.operands.forEach((operand) => operand.run(contexts)); return result; } } diff --git a/src/components/ESet.test.js b/src/components/ESet.test.js index 5ec7f2d..cd4936e 100644 --- a/src/components/ESet.test.js +++ b/src/components/ESet.test.js @@ -30,7 +30,7 @@ describe("ESet", () => { ) ).to.deep.equal({ a: 1, b: 3 }); }); - it("name list", () => { + it("chain", () => { expect( run( [{ o: { f: () => 1 } }], diff --git a/src/components/ETuple.js b/src/components/ETuple.js index fe878ec..6be8631 100644 --- a/src/components/ETuple.js +++ b/src/components/ETuple.js @@ -1,4 +1,3 @@ -import { ENameList } from "./ENameList.js"; import { EObject } from "./EObject.js"; export class ETuple extends EObject { @@ -7,10 +6,7 @@ export class ETuple extends EObject { } run(contexts) { let result = undefined; - this.operands.forEach((operand) => { - result = operand.run(contexts); - result instanceof ENameList && (result = result.run(contexts)); - }); + this.operands.forEach((operand) => (result = operand.run(contexts))); return result; } } diff --git a/src/components/ETuple.test.js b/src/components/ETuple.test.js index 69023a1..0425af3 100644 --- a/src/components/ETuple.test.js +++ b/src/components/ETuple.test.js @@ -27,7 +27,7 @@ describe("ETuple", () => { ) ).to.equal(3); }); - it("name list", () => { + it("chain", () => { expect( run( [{ o: { f: () => 1 } }], diff --git a/src/components/operators.js b/src/components/operators.js index d7c44cc..fef0797 100644 --- a/src/components/operators.js +++ b/src/components/operators.js @@ -1,8 +1,8 @@ import { invalidLvalue, invalidParam, notImplemented } from "../core/error.js"; import { EArray } from "./EArray.js"; import { EName } from "./EName.js"; -import { ENameList } from "./ENameList.js"; import { EObject } from "./EObject.js"; +import { EChain } from "./EChain.js"; import { EReference } from "./EReference.js"; import { eRun, isObject } from "./utils.js"; @@ -10,9 +10,9 @@ export const operators = { ".": { i: -2, f: (_, b, a) => - a instanceof EName && b instanceof EName - ? new ENameList([...(a instanceof ENameList ? a.names : [a]), b]) - : `${a}.${b}` * 1, + typeof a === "number" + ? `${a}.${b}` * 1 + : new EChain([...(a instanceof EChain ? a.names : [a]), b]), }, "=>": { i: -1, @@ -62,7 +62,7 @@ export const operators = { "=": { i: 10, f: (ctxs, b, a) => { - a instanceof ENameList || (a = new ENameList([a])); + a instanceof EChain || (a = new EChain([a])); a.names.forEach((name) => name instanceof EName || invalidLvalue(a)); return a.assign(ctxs, eRun(ctxs, b)); }, diff --git a/src/components/operators.test.js b/src/components/operators.test.js index 0356ad2..591f8b4 100644 --- a/src/components/operators.test.js +++ b/src/components/operators.test.js @@ -1,10 +1,10 @@ import { expect } from "chai"; import { describe, it } from "mocha"; import { EArray } from "./EArray.js"; +import { EChain } from "./EChain.js"; import { EExpression } from "./EExpression.js"; import { EIndexing } from "./EIndexing.js"; import { EInvoke } from "./EInvoke.js"; -import { ENameList } from "./ENameList.js"; import { EReference } from "./EReference.js"; import { ETuple } from "./ETuple.js"; import { operators } from "./operators.js"; @@ -12,16 +12,25 @@ import { operators } from "./operators.js"; describe("operators", () => { const run = (operator, ctxs, a, b) => operators[operator].f(ctxs, b, a); - describe(". name list", () => { + describe(". chain", () => { it("number", () => { expect(run(".", [{}], 0, 14)).to.equal(0.14); }); it("NaN", () => { expect(run(".", [{}], 0, {})).to.be.NaN; expect(run(".", [{}], 0, new EReference())).to.be.NaN; - expect(run(".", [{}], new EReference()), 0).to.be.NaN; }); - it("name list", () => { + it("string", () => { + expect(run(".", [{}], "abc", new EReference("length"))).to.deep.equal({ + names: ["abc", { name: "length" }], + }); + }); + it("array", () => { + expect(run(".", [{}], [], new EReference("length"))).to.deep.equal({ + names: [[], { name: "length" }], + }); + }); + it("chain", () => { expect( run(".", [{}], new EReference("a"), new EReference("b")) ).to.deep.equal({ @@ -31,7 +40,7 @@ describe("operators", () => { run( ".", [{}], - new ENameList([new EReference("a"), new EReference("b")]), + new EChain([new EReference("a"), new EReference("b")]), new EReference("c") ) ).to.deep.equal({ @@ -134,12 +143,12 @@ describe("operators", () => { it("l-value indexing", () => { expect(run("=", [{ a: [1] }], new EIndexing("a", [0]), 2)).to.equal(2); }); - it("l-value name list", () => { + it("l-value chain", () => { expect(() => run( "=", [{}], - new ENameList([new EReference("a"), new EReference("b")]), + new EChain([new EReference("a"), new EReference("b")]), 1 ).to.throw("Cannot read properties of undefined (reading 'b')") ); diff --git a/src/core/error.js b/src/core/error.js index 7675472..0311657 100644 --- a/src/core/error.js +++ b/src/core/error.js @@ -2,6 +2,10 @@ export const syntaxExpected = (filename, lineNo, what) => { throw new SyntaxError(`${filename}:${lineNo}\n${what} is expected`); }; +export const invalidChain = (name) => { + throw new Error(`Unknown chain ${name}`); +}; + export const invalidFunction = (name) => { throw new Error(`Unknown function ${name}`); }; diff --git a/src/core/index.js b/src/core/index.js index 2538038..e9ab9a3 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -1,2 +1,7 @@ export { assign, search } from "./context.js"; -export { invalidOperator, notImplemented, syntaxExpected } from "./error.js"; +export { + invalidOperator, + notImplemented, + syntaxExpected, + invalidChain, +} from "./error.js"; diff --git a/src/escript.test.js b/src/escript.test.js index 755e98f..32989a7 100644 --- a/src/escript.test.js +++ b/src/escript.test.js @@ -16,9 +16,12 @@ describe("escript", () => { expect(run("inc = a => (a + 1) inc(1)")).to.equal(2); expect(run("sum = [a b] => (a + b) sum(1, 2.14)")).to.equal(3.14); }); - it("name list", () => { + it("chain", () => { expect(run("a = {v = 1} r = a.v")).to.equal(1); expect(run("a = 0 o = {f = _ => (a = 1)} o.f() r = a")).to.equal(1); + expect(run("r = 'abc'.includes('a')")).to.be.true; + expect(run("r = [2 3].map(a => (a * 2))")).to.deep.equal([4, 6]); + expect(run("a = [2 3] a.map(a => (a * 2))")).to.deep.equal([4, 6]); }); it("logical", () => { expect(run("a = 0 false && (a = 1) r = a")).to.equal(0);