diff --git a/assets/deps.png b/assets/deps.png index d8a37799b1..f228279a7a 100644 Binary files a/assets/deps.png and b/assets/deps.png differ diff --git a/examples/pointfree-svg/src/index.ts b/examples/pointfree-svg/src/index.ts index c0344b768a..1f6ade2f57 100644 --- a/examples/pointfree-svg/src/index.ts +++ b/examples/pointfree-svg/src/index.ts @@ -52,12 +52,12 @@ const usersrc = ` only creates circles for grid cells where x <= y ) : circlegrid ( res -- ) - dup [dup2 lteq [xy 10 v* 3 circle] [drop2] if] loop2 ; + dup [dup2 <= [xy 10 v* 3 circle] [drop2] if] loop2 ; grid circlegrid ( create SVG root element in hiccup format ) -@svg.svgdoc [{width: 200, height: 200, stroke: "#f04", fill: "none"}] pushl +[@svg.svgdoc {width: 200, height: 200, stroke: "#f04", fill: "none"}] ( concat with generated shapes ) @shapes cat ( serialize hiccup format to SVG and write to disk ) diff --git a/packages/api/CHANGELOG.md b/packages/api/CHANGELOG.md index 3535577db4..e773ace164 100644 --- a/packages/api/CHANGELOG.md +++ b/packages/api/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [2.1.3](https://github.com/thi-ng/umbrella/compare/@thi.ng/api@2.1.2...@thi.ng/api@2.1.3) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/api + ## [2.1.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/api@2.1.1...@thi.ng/api@2.1.2) (2018-04-01) diff --git a/packages/api/package.json b/packages/api/package.json index 15e2dba323..3e3ae37f22 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/api", - "version": "2.1.2", + "version": "2.1.3", "description": "Common, generic types & interfaces for thi.ng projects", "main": "./index.js", "typings": "./index.d.ts", @@ -24,7 +24,7 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/checks": "^1.3.1" + "@thi.ng/checks": "^1.3.2" }, "keywords": [ "compare", diff --git a/packages/atom/CHANGELOG.md b/packages/atom/CHANGELOG.md index ea2cdb385a..489f59e02d 100644 --- a/packages/atom/CHANGELOG.md +++ b/packages/atom/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.2.3](https://github.com/thi-ng/umbrella/compare/@thi.ng/atom@1.2.2...@thi.ng/atom@1.2.3) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/atom + ## [1.2.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/atom@1.2.1...@thi.ng/atom@1.2.2) (2018-04-01) diff --git a/packages/atom/package.json b/packages/atom/package.json index b6ffc91cef..b1df2e044b 100644 --- a/packages/atom/package.json +++ b/packages/atom/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/atom", - "version": "1.2.2", + "version": "1.2.3", "description": "Mutable wrapper for immutable values", "main": "./index.js", "typings": "./index.d.ts", @@ -24,8 +24,8 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/api": "^2.1.2", - "@thi.ng/paths": "^1.1.3" + "@thi.ng/api": "^2.1.3", + "@thi.ng/paths": "^1.1.4" }, "keywords": [ "cursor", diff --git a/packages/bitstream/CHANGELOG.md b/packages/bitstream/CHANGELOG.md index 54247a9d63..a8b0a47fdd 100644 --- a/packages/bitstream/CHANGELOG.md +++ b/packages/bitstream/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.4.3](https://github.com/thi-ng/umbrella/compare/@thi.ng/bitstream@0.4.2...@thi.ng/bitstream@0.4.3) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/bitstream + ## [0.4.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/bitstream@0.4.1...@thi.ng/bitstream@0.4.2) (2018-04-01) diff --git a/packages/bitstream/package.json b/packages/bitstream/package.json index 5ea52d6fc0..59e3b10dd5 100644 --- a/packages/bitstream/package.json +++ b/packages/bitstream/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/bitstream", - "version": "0.4.2", + "version": "0.4.3", "description": "ES6 iterator based read/write bit streams & support for variable word widths", "main": "./index.js", "typings": "./index.d.ts", @@ -16,7 +16,7 @@ "test": "rm -rf build && tsc -p test && nyc mocha build/test/*.js" }, "dependencies": { - "@thi.ng/api": "^2.1.2" + "@thi.ng/api": "^2.1.3" }, "devDependencies": { "@types/mocha": "^5.0.0", diff --git a/packages/checks/CHANGELOG.md b/packages/checks/CHANGELOG.md index afbd6168c1..57140251a7 100644 --- a/packages/checks/CHANGELOG.md +++ b/packages/checks/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.3.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/checks@1.3.1...@thi.ng/checks@1.3.2) (2018-04-04) + + +### Bug Fixes + +* **checks:** add prototype check for isPlainObject(), add tests ([bffc443](https://github.com/thi-ng/umbrella/commit/bffc443)) + + + + ## [1.3.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/checks@1.3.0...@thi.ng/checks@1.3.1) (2018-04-01) diff --git a/packages/checks/package.json b/packages/checks/package.json index f14c641465..26b3d84ca2 100644 --- a/packages/checks/package.json +++ b/packages/checks/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/checks", - "version": "1.3.1", + "version": "1.3.2", "description": "Single-function sub-modules for type, feature & value checks", "main": "./index.js", "typings": "./index.d.ts", diff --git a/packages/checks/src/is-plain-object.ts b/packages/checks/src/is-plain-object.ts index 352b0972c4..54d021677b 100644 --- a/packages/checks/src/is-plain-object.ts +++ b/packages/checks/src/is-plain-object.ts @@ -1,3 +1,11 @@ +/** + * Similar to `isObject()`, but also checks if prototype is that of + * `Object` (or `null`). + * + * @param x + */ export function isPlainObject(x: any): x is Object { - return Object.prototype.toString.call(x) === "[object Object]"; + let proto; + return Object.prototype.toString.call(x) === "[object Object]" && + (proto = Object.getPrototypeOf(x), proto === null || proto === Object.getPrototypeOf({})); } diff --git a/packages/checks/test/index.ts b/packages/checks/test/index.ts index 13ecf8c890..98fb3b7059 100644 --- a/packages/checks/test/index.ts +++ b/packages/checks/test/index.ts @@ -13,6 +13,7 @@ import { isTransferable } from "../src/is-transferable"; import { isTypedArray } from "../src/is-typedarray"; describe("checks", function () { + it("existsAndNotNull", () => { assert(existsAndNotNull([]), "empty array"); assert(existsAndNotNull(new Uint8Array(1)), "typedarray"); @@ -23,6 +24,7 @@ describe("checks", function () { assert(!existsAndNotNull(null), "null"); assert(!existsAndNotNull(undefined), "null"); }); + it("isArray", () => { assert(isArray([]), "empty array"); assert(!isArray(new Uint8Array(1)), "typedarray"); @@ -32,6 +34,7 @@ describe("checks", function () { assert(!isArray(null), "null"); assert(!isArray(undefined), "null"); }); + it("isTypedArray", () => { assert(isTypedArray(new Uint8Array(1)), "u8"); assert(isTypedArray(new Uint8ClampedArray(1)), "u8c"); @@ -49,6 +52,7 @@ describe("checks", function () { assert(!isTypedArray(null), "null"); assert(!isTypedArray(undefined), "null"); }); + it("isArrayLike", () => { assert(isArrayLike([]), "empty array"); assert(isArrayLike(new Uint8Array(1)), "typedarray"); @@ -59,18 +63,26 @@ describe("checks", function () { assert(!isArrayLike(null), "null"); assert(!isArrayLike(undefined), "null"); }); + it("isObject", () => { + function Foo() { }; assert(isObject([]), "empty array"); assert(isObject(new Uint8Array(1)), "typedarray"); assert(isObject({}), "obj"); + assert(isObject(new Foo()), "class"); + assert(!isObject(Foo), "fn"); assert(!isObject("[]"), "string"); assert(!isObject(0), "zero"); assert(!isObject(null), "null"); assert(!isObject(undefined), "null"); }); + it("isPlainObject", () => { + function Foo() { }; assert(isPlainObject({}), "obj"); assert(isPlainObject(new Object()), "obj ctor"); + assert(!isPlainObject(Foo), "fn"); + assert(!isPlainObject(new Foo()), "class"); assert(!isPlainObject([]), "empty array"); assert(!isPlainObject(new Uint8Array(1)), "typedarray"); assert(!isPlainObject("[]"), "string"); @@ -78,6 +90,7 @@ describe("checks", function () { assert(!isPlainObject(null), "null"); assert(!isPlainObject(undefined), "null"); }); + it("isString", () => { assert(isString(""), "empty string"); assert(isString("a"), "empty string"); @@ -88,6 +101,7 @@ describe("checks", function () { assert(!isString(null), "null"); assert(!isString(undefined), "null"); }); + it("isFunction", () => { assert(isFunction((_) => null), "fn"); assert(isFunction(Uint8Array), "ctor"); @@ -100,6 +114,7 @@ describe("checks", function () { assert(!isFunction(null), "null"); assert(!isFunction(undefined), "undefined"); }); + it("implementsFunction", () => { assert(implementsFunction({ a: () => true }, "a"), "obj"); assert(implementsFunction([], Symbol.iterator), "arr iterator"); @@ -108,6 +123,7 @@ describe("checks", function () { assert(!implementsFunction(null, Symbol.iterator), "null"); assert(!implementsFunction(undefined, Symbol.iterator), "undefined"); }); + it("isSymbol", () => { assert(isSymbol(Symbol.iterator), "iterator"); assert(!isSymbol("iterator"), "string"); @@ -115,6 +131,7 @@ describe("checks", function () { assert(!isFunction(null), "null"); assert(!isFunction(undefined), "undefined"); }); + it("isTransferable", () => { assert(isTransferable(new ArrayBuffer(4)), "arraybuffer"); assert(!isTransferable(new Uint8Array(4)), "typedarray"); @@ -124,4 +141,5 @@ describe("checks", function () { assert(!isTransferable(null), "null"); assert(!isTransferable(undefined), "undefined"); }); + }); diff --git a/packages/csp/CHANGELOG.md b/packages/csp/CHANGELOG.md index 92e5057afe..1fd4ce95fb 100644 --- a/packages/csp/CHANGELOG.md +++ b/packages/csp/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.3.24](https://github.com/thi-ng/umbrella/compare/@thi.ng/csp@0.3.23...@thi.ng/csp@0.3.24) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/csp + ## [0.3.23](https://github.com/thi-ng/umbrella/compare/@thi.ng/csp@0.3.22...@thi.ng/csp@0.3.23) (2018-04-01) diff --git a/packages/csp/package.json b/packages/csp/package.json index c79154eadb..4ebaa6c109 100644 --- a/packages/csp/package.json +++ b/packages/csp/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/csp", - "version": "0.3.23", + "version": "0.3.24", "description": "ES6 promise based CSP implementation", "main": "./index.js", "typings": "./index.d.ts", @@ -28,8 +28,8 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/dcons": "^0.1.17", - "@thi.ng/transducers": "^1.7.3" + "@thi.ng/dcons": "^0.1.18", + "@thi.ng/transducers": "^1.7.4" }, "keywords": [ "async", diff --git a/packages/dcons/CHANGELOG.md b/packages/dcons/CHANGELOG.md index b1f80b3efc..bfb31c9e3b 100644 --- a/packages/dcons/CHANGELOG.md +++ b/packages/dcons/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.1.18](https://github.com/thi-ng/umbrella/compare/@thi.ng/dcons@0.1.17...@thi.ng/dcons@0.1.18) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/dcons + ## [0.1.17](https://github.com/thi-ng/umbrella/compare/@thi.ng/dcons@0.1.16...@thi.ng/dcons@0.1.17) (2018-04-01) diff --git a/packages/dcons/package.json b/packages/dcons/package.json index 01242ce091..f319e7931e 100644 --- a/packages/dcons/package.json +++ b/packages/dcons/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/dcons", - "version": "0.1.17", + "version": "0.1.18", "description": "Comprehensive doubly linked list structure w/ iterator support", "main": "./index.js", "typings": "./index.d.ts", @@ -24,7 +24,7 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/api": "^2.1.2" + "@thi.ng/api": "^2.1.3" }, "keywords": [ "datastructure", diff --git a/packages/dgraph/CHANGELOG.md b/packages/dgraph/CHANGELOG.md new file mode 100644 index 0000000000..0842d922ef --- /dev/null +++ b/packages/dgraph/CHANGELOG.md @@ -0,0 +1,12 @@ +# Change Log + +All notable changes to this project will be documented in this file. +See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + + +## 0.0.2 (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/dgraph diff --git a/packages/dgraph/package.json b/packages/dgraph/package.json new file mode 100644 index 0000000000..4972a6738b --- /dev/null +++ b/packages/dgraph/package.json @@ -0,0 +1,36 @@ +{ + "name": "@thi.ng/dgraph", + "version": "0.0.2", + "description": "TODO", + "main": "./index.js", + "typings": "./index.d.ts", + "repository": "https://github.com/thi-ng/umbrella", + "author": "Karsten Schmidt ", + "license": "Apache-2.0", + "scripts": { + "build": "yarn run clean && tsc --declaration", + "clean": "rm -rf *.js *.d.ts build doc", + "cover": "yarn test && nyc report --reporter=lcov", + "doc": "node_modules/.bin/typedoc --mode modules --out doc src", + "pub": "yarn run build && yarn publish --access public", + "test": "rm -rf build && tsc -p test && nyc mocha build/test/*.js" + }, + "devDependencies": { + "@types/mocha": "^5.0.0", + "@types/node": "^9.6.1", + "mocha": "^5.0.5", + "nyc": "^11.6.0", + "typedoc": "^0.11.1", + "typescript": "^2.8.1" + }, + "dependencies": { + "@thi.ng/api": "^2.1.3" + }, + "keywords": [ + "ES6", + "typescript" + ], + "publishConfig": { + "access": "public" + } +} diff --git a/packages/diff/CHANGELOG.md b/packages/diff/CHANGELOG.md index 7547bcb489..4979ef88da 100644 --- a/packages/diff/CHANGELOG.md +++ b/packages/diff/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.0.5](https://github.com/thi-ng/umbrella/compare/@thi.ng/diff@1.0.4...@thi.ng/diff@1.0.5) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/diff + ## [1.0.4](https://github.com/thi-ng/umbrella/compare/@thi.ng/diff@1.0.3...@thi.ng/diff@1.0.4) (2018-04-01) diff --git a/packages/diff/package.json b/packages/diff/package.json index 3d7b8bd0a8..b8e22dbb8a 100644 --- a/packages/diff/package.json +++ b/packages/diff/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/diff", - "version": "1.0.4", + "version": "1.0.5", "description": "Array & object Diff", "main": "./index.js", "typings": "./index.d.ts", @@ -22,7 +22,7 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/api": "^2.1.2" + "@thi.ng/api": "^2.1.3" }, "keywords": [ "array", diff --git a/packages/hdom-components/CHANGELOG.md b/packages/hdom-components/CHANGELOG.md index 6b9fcbec9d..e43a889ba2 100644 --- a/packages/hdom-components/CHANGELOG.md +++ b/packages/hdom-components/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.1.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/hdom-components@1.1.1...@thi.ng/hdom-components@1.1.2) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/hdom-components + ## [1.1.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/hdom-components@1.1.0...@thi.ng/hdom-components@1.1.1) (2018-04-01) diff --git a/packages/hdom-components/package.json b/packages/hdom-components/package.json index 388ad3e646..a6244b5bec 100644 --- a/packages/hdom-components/package.json +++ b/packages/hdom-components/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/hdom-components", - "version": "1.1.1", + "version": "1.1.2", "description": "Raw, skinnable UI & SVG components for @thi.ng/hdom", "main": "./index.js", "typings": "./index.d.ts", @@ -24,8 +24,8 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/checks": "^1.3.1", - "@thi.ng/hiccup": "^1.3.2" + "@thi.ng/checks": "^1.3.2", + "@thi.ng/hiccup": "^1.3.3" }, "keywords": [ "ES6", diff --git a/packages/hdom/CHANGELOG.md b/packages/hdom/CHANGELOG.md index 4cf89950ed..deab86aa31 100644 --- a/packages/hdom/CHANGELOG.md +++ b/packages/hdom/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [2.3.3](https://github.com/thi-ng/umbrella/compare/@thi.ng/hdom@2.3.2...@thi.ng/hdom@2.3.3) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/hdom + ## [2.3.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/hdom@2.3.1...@thi.ng/hdom@2.3.2) (2018-04-01) diff --git a/packages/hdom/package.json b/packages/hdom/package.json index fd997853c4..208ab91a3d 100644 --- a/packages/hdom/package.json +++ b/packages/hdom/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/hdom", - "version": "2.3.2", + "version": "2.3.3", "description": "Lightweight vanilla ES6 UI component & virtual DOM system", "main": "./index.js", "typings": "./index.d.ts", @@ -16,7 +16,7 @@ "test": "rm -rf build && tsc -p test && nyc mocha build/test/*.js" }, "devDependencies": { - "@thi.ng/atom": "^1.2.2", + "@thi.ng/atom": "^1.2.3", "@types/mocha": "^5.0.0", "@types/node": "^9.6.1", "mocha": "^5.0.5", @@ -25,10 +25,10 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/api": "^2.1.2", - "@thi.ng/diff": "^1.0.4", - "@thi.ng/hiccup": "^1.3.2", - "@thi.ng/iterators": "^4.1.2" + "@thi.ng/api": "^2.1.3", + "@thi.ng/diff": "^1.0.5", + "@thi.ng/hiccup": "^1.3.3", + "@thi.ng/iterators": "^4.1.3" }, "keywords": [ "browser", diff --git a/packages/hiccup-css/CHANGELOG.md b/packages/hiccup-css/CHANGELOG.md index 4d323cfae3..3092195152 100644 --- a/packages/hiccup-css/CHANGELOG.md +++ b/packages/hiccup-css/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.1.8](https://github.com/thi-ng/umbrella/compare/@thi.ng/hiccup-css@0.1.7...@thi.ng/hiccup-css@0.1.8) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/hiccup-css + ## [0.1.7](https://github.com/thi-ng/umbrella/compare/@thi.ng/hiccup-css@0.1.6...@thi.ng/hiccup-css@0.1.7) (2018-04-01) diff --git a/packages/hiccup-css/package.json b/packages/hiccup-css/package.json index 933f5dcd3f..87cd0b29fd 100644 --- a/packages/hiccup-css/package.json +++ b/packages/hiccup-css/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/hiccup-css", - "version": "0.1.7", + "version": "0.1.8", "description": "CSS from nested JS data structures", "main": "./index.js", "typings": "./index.d.ts", @@ -24,8 +24,8 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/api": "^2.1.2", - "@thi.ng/transducers": "^1.7.3" + "@thi.ng/api": "^2.1.3", + "@thi.ng/transducers": "^1.7.4" }, "keywords": [ "clojure", diff --git a/packages/hiccup/CHANGELOG.md b/packages/hiccup/CHANGELOG.md index 83517e3f06..8340b3f539 100644 --- a/packages/hiccup/CHANGELOG.md +++ b/packages/hiccup/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.3.3](https://github.com/thi-ng/umbrella/compare/@thi.ng/hiccup@1.3.2...@thi.ng/hiccup@1.3.3) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/hiccup + ## [1.3.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/hiccup@1.3.1...@thi.ng/hiccup@1.3.2) (2018-04-01) diff --git a/packages/hiccup/package.json b/packages/hiccup/package.json index 4e675b3d73..1d967a129a 100644 --- a/packages/hiccup/package.json +++ b/packages/hiccup/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/hiccup", - "version": "1.3.2", + "version": "1.3.3", "description": "HTML/SVG/XML serialization of nested data structures, iterables & closures", "main": "./index.js", "typings": "./index.d.ts", @@ -16,7 +16,7 @@ "test": "rm -rf build && tsc -p test && nyc mocha build/test/*.js" }, "devDependencies": { - "@thi.ng/atom": "^1.2.2", + "@thi.ng/atom": "^1.2.3", "@types/mocha": "^5.0.0", "@types/node": "^9.6.1", "mocha": "^5.0.5", @@ -25,8 +25,8 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/api": "^2.1.2", - "@thi.ng/checks": "^1.3.1" + "@thi.ng/api": "^2.1.3", + "@thi.ng/checks": "^1.3.2" }, "keywords": [ "clojure", diff --git a/packages/interceptors/CHANGELOG.md b/packages/interceptors/CHANGELOG.md index deb8b1417e..ae0fbfb505 100644 --- a/packages/interceptors/CHANGELOG.md +++ b/packages/interceptors/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.1.3](https://github.com/thi-ng/umbrella/compare/@thi.ng/interceptors@1.1.2...@thi.ng/interceptors@1.1.3) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/interceptors + ## [1.1.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/interceptors@1.1.1...@thi.ng/interceptors@1.1.2) (2018-04-01) diff --git a/packages/interceptors/package.json b/packages/interceptors/package.json index c0e21bd2b0..a95fb24666 100644 --- a/packages/interceptors/package.json +++ b/packages/interceptors/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/interceptors", - "version": "1.1.2", + "version": "1.1.3", "description": "Interceptor based event bus, side effect & immutable state handling", "main": "./index.js", "typings": "./index.d.ts", @@ -24,9 +24,9 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/api": "^2.1.2", - "@thi.ng/atom": "^1.2.2", - "@thi.ng/paths": "^1.1.3" + "@thi.ng/api": "^2.1.3", + "@thi.ng/atom": "^1.2.3", + "@thi.ng/paths": "^1.1.4" }, "keywords": [ "ES6", diff --git a/packages/iterators/CHANGELOG.md b/packages/iterators/CHANGELOG.md index ceca16f87e..21a252f24d 100644 --- a/packages/iterators/CHANGELOG.md +++ b/packages/iterators/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [4.1.3](https://github.com/thi-ng/umbrella/compare/@thi.ng/iterators@4.1.2...@thi.ng/iterators@4.1.3) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/iterators + ## [4.1.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/iterators@4.1.1...@thi.ng/iterators@4.1.2) (2018-04-01) diff --git a/packages/iterators/package.json b/packages/iterators/package.json index 9e86ae945c..36612a7c5f 100644 --- a/packages/iterators/package.json +++ b/packages/iterators/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/iterators", - "version": "4.1.2", + "version": "4.1.3", "description": "clojure.core inspired, composable ES6 iterators & generators", "main": "./index.js", "typings": "./index.d.ts", @@ -24,8 +24,8 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/api": "^2.1.2", - "@thi.ng/dcons": "^0.1.17" + "@thi.ng/api": "^2.1.3", + "@thi.ng/dcons": "^0.1.18" }, "keywords": [ "clojure", diff --git a/packages/paths/CHANGELOG.md b/packages/paths/CHANGELOG.md index fbc7f205ab..e50db1de2e 100644 --- a/packages/paths/CHANGELOG.md +++ b/packages/paths/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.1.4](https://github.com/thi-ng/umbrella/compare/@thi.ng/paths@1.1.3...@thi.ng/paths@1.1.4) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/paths + ## [1.1.3](https://github.com/thi-ng/umbrella/compare/@thi.ng/paths@1.1.2...@thi.ng/paths@1.1.3) (2018-04-01) diff --git a/packages/paths/package.json b/packages/paths/package.json index 577bdde580..96a9f3ee01 100644 --- a/packages/paths/package.json +++ b/packages/paths/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/paths", - "version": "1.1.3", + "version": "1.1.4", "description": "immutable, optimized path-based object property / array accessors", "main": "./index.js", "typings": "./index.d.ts", @@ -24,7 +24,7 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/checks": "^1.3.1" + "@thi.ng/checks": "^1.3.2" }, "keywords": [ "accessors", diff --git a/packages/pointfree-lang/CHANGELOG.md b/packages/pointfree-lang/CHANGELOG.md index 9d9a388312..a4c8382505 100644 --- a/packages/pointfree-lang/CHANGELOG.md +++ b/packages/pointfree-lang/CHANGELOG.md @@ -3,6 +3,32 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.2.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/pointfree-lang@0.2.0...@thi.ng/pointfree-lang@0.2.1) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/pointfree-lang + + +# [0.2.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/pointfree-lang@0.1.3...@thi.ng/pointfree-lang@0.2.0) (2018-04-03) + + +### Bug Fixes + +* **pointfree-lang:** update grammar (parse order), add tests ([5450e50](https://github.com/thi-ng/umbrella/commit/5450e50)) + + +### Features + +* **pointfree-lang:** implement dynamic var scoping & local var grammar ([3310ec3](https://github.com/thi-ng/umbrella/commit/3310ec3)) +* **pointfree-lang:** overhaul visitor quote/array & map handling, grammar ([769e84d](https://github.com/thi-ng/umbrella/commit/769e84d)) +* **pointfree-lang:** update grammar, aliases, ASTNode, NodeType ([ee684c7](https://github.com/thi-ng/umbrella/commit/ee684c7)) + + + + ## [0.1.3](https://github.com/thi-ng/umbrella/compare/@thi.ng/pointfree-lang@0.1.2...@thi.ng/pointfree-lang@0.1.3) (2018-04-01) diff --git a/packages/pointfree-lang/README.md b/packages/pointfree-lang/README.md index abedcfcd52..b8da12cbd2 100644 --- a/packages/pointfree-lang/README.md +++ b/packages/pointfree-lang/README.md @@ -15,14 +15,15 @@ This project is part of the [@thi.ng/umbrella](https://github.com/thi-ng/umbrell - [Identifiers](#identifiers) - [Word definitions](#word-definitions) - [Hyperstatic words](#hyperstatic-words) + - [Local variables](#local-variables) - [Boolean](#boolean) - [Numbers](#numbers) - [Strings](#strings) - [Quotations (Arrays)](#quotations-arrays) - [Literal quotes](#literal-quotes) - [Variables](#variables) + - [Dynamic scoping](#dynamic-scoping) - [Objects](#objects) - - [Sets](#sets) - [Ideas / Todos](#ideas--todos) - [Authors](#authors) - [License](#license) @@ -42,9 +43,10 @@ an ES6 embedded DSL for concatenative programming: & parser - untyped, interpreted, but with AOT compilation of user defined words - hyperstatic word definitions -- support for custom / externally defined vocabularies (word sets / JS functions) -- scoped variables (stored in environment object) -- nested quotations (code as data) +- support for custom / externally defined words (JS functions) +- dynamically scoped variables (stored in environment object) + - syntax sugar for declaring & autobinding local vars w/ stack values +- nested quotations (code as data, vanilla JS arrays) - array & object literals (optionally w/ computed properties) - all other features of @thi.ng/pointfree (combinators, array/vector ops etc.) @@ -158,21 +160,24 @@ pf.runWord("hairx", env, [100, 200, 640, 480]); As noted previously, the syntax is closely based on Forth (and other concatenative languages), however since this implementation is targetted to ES6 environments, the semantics and actual implementation differ -drastically. In @thi.ng/pointfree (and therefore also in this DSL layer): +drastically. In this DSL (and most aspects also in @thi.ng/pointfree): - words and programs are implemented as functional compositions of vanilla JS functions, i.e. `1 2 +` => `add(push(1)(push(2)(ctx)))` - therefore no user controlled context switch between immediate & compile modes, as in Forth - - parsing of word definitions and quotations triggers compile mode - automatically -- both stacks (D & R stacks) can store any valid JS data type + - parsing of word definitions triggers compile mode automatically +- variables and both stacks (D & R stacks) can store any valid JS data + type - no linear memory as in Forth, instead variables and the dictionary of (custom / FFI or user defined) words is stored in a separate environment object, which is passed to each word/function - the DSL has syntax sugar for variable value lookups & assignments -- the DSL allows nested quotations & object literals, optionally with - lazily resolved computed properties and/or values +- within word definitions the DSL supports binding stack values to local + vars at the beginning of the word +- the DSL supports nested quotations (array) & object literals, incl. + support for computed properties and/or values (lazily resolved within + words) - all symbols are separated by whitespace (like in Clojure, commas are considered whitespace too) @@ -183,8 +188,8 @@ includes the `--` string, it's marked as a [stack effect comment](https://github.com/thi-ng/umbrella/tree/master/packages/pointfree#about-stack-effects) in preparation for future tooling additions. -Comments can span multiple lines. There's no special syntax for single -line comments: +Comments current cannot contain `(` or `)`, but can span multiple lines. +There's no special syntax for single line comments: ``` ( multiline: @@ -203,8 +208,8 @@ ______ ____ |__| _____/ |__/ ____\______ ____ ____ ### Identifiers Word identifiers can contain any alhpanumeric character and these -additional ones: `*?$%&/|~<>=._+-`. Digits are not allowed as first -char. +additional ones: `*?$%&/|~<>=._+-`. However, digits are not allowed as +first char. All 100+ built-in words defined by [@thi.ng/pointfree](https://github.com/thi-ng/umbrella/tree/master/packages/pointfree) @@ -213,29 +218,39 @@ aren't valid names in the ES6 context): | Alias | Original name | | --- | --- | -| ?drop | dropif | -| ?dup | dupif | -| -rot | invrot | -| >r | movdr | -| >r2 | movdr2 | -| r> | movrd | -| r2> | movrd2 | -| if | condq | -| switch | casesq | -| while | loopq | -| + | add | -| - | sub | -| * | mul | -| / | div | -| 1+ | inc | -| 1- | dec | -| v+ | vadd | -| v- | vsub | -| v* | vmul | -| v/ | vdiv | -| . | print | -| .s | printds | -| .r | printrs | +| `?drop` | `dropif` | +| `?dup` | `dupif` | +| `-rot` | `invrot` | +| `>r` | `movdr` | +| `>r2` | `movdr2` | +| `r>` | `movrd` | +| `r2>` | `movrd2` | +| `if` | `condq` | +| `switch` | `casesq` | +| `while` | `loopq` | +| `+` | `add` | +| `-` | `sub` | +| `*` | `mul` | +| `/` | `div` | +| `v+` | `vadd` | +| `v-` | `vsub` | +| `v*` | `vmul` | +| `v/` | `vdiv` | +| `=` | `eq` | +| `not=` | `neq` | +| `<=` | `lteq` | +| `>=` | `gteq` | +| `<` | `lt` | +| `>` | `gt` | +| `pos?` | `ispos` | +| `neg?` | `isneg` | +| `nil?` | `isnil` | +| `zero?` | `iszero` | +| `pi` | `Math.PI` | +| `tau` | `2 * Math.PI` | +| `.` | `print` | +| `.s` | `printds` | +| `.r` | `printrs` | The ID resolution priority is: @@ -255,17 +270,29 @@ As in Forth, new words can be defined using the `: name ... ;` form. Will result in `100`. +There're no formatting rules enforced (yet, but under consideration). +However, it's strongly encouraged to include [stack effect +comments](https://github.com/thi-ng/umbrella/tree/master/packages/pointfree#about-stack-effects) +as shown in the examples above. + +**Word definitions MUST be terminated with `;`.** + #### Hyperstatic words -Unlike [variables](#variables), words are defined in a +Unlike [variables](#variables), which are [dynamically +scoped](https://en.wikipedia.org/wiki/Scope_(computer_science)#Dynamic_scoping), +words are defined in a [hyper-static](http://wiki.c2.com/?HyperStaticGlobalEnvironment) environment, meaning new versions of existing words can be defined, however any other word (incl. the new version of same word) which uses -the earlier version will continue to do so. By implication, this too -means that attempting to use undefined words inside a word definition -will fail, even if they'd be defined later on. +the earlier version will continue to use that older version. +Consequently, this too means that attempting to use undefined words +inside a word definition will fail, even if they'd be defined later on. +In these cases, use of variables and/or quotations is encouraged to +implement dynamic programming techniques. ```ts +// hyperstaticness by example pf.run(` : foo "foo1" ; : bar foo "bar" + ; @@ -279,12 +306,35 @@ foo bar // [ 'foo1foo2', 'foo1bar' ] ``` -There're no formatting rules enforced (yet, but under consideration). -However, it's strongly encouraged to include [stack effect -comments](https://github.com/thi-ng/umbrella/tree/master/packages/pointfree#about-stack-effects) -as shown in the examples above. +#### Local variables -**Word definitions MUST be terminated with `;`.** +A word definition can include an optional declaration of local +variables, which are automatically bound to stack values each time the +word is invoked. The declarations are given via the form: + +``` +: wordname ^{ name1 name2 ... } ... ; +``` + +If used, the declaration MUST be given as first element of the word, +even before the optional stack comment: + +```ts +// word with 2 local vars binding: a & b +// when the word is used, first pops 2 values from stack +// and stores them in local vars (in right to left order) +pf.run(` +: add ^{ a b } ( a b -- a+b ) + "a=" @a + . + "b=" @b + . ; + +1 2 add +`); +// a=1 +// b=2 +``` + +See [section about variables](#variables) for further details... ### Boolean @@ -318,16 +368,16 @@ A single element quotation can be formed by prefixing a term with `'`. Nestable. ### Variables Variables can be looked up & resolved via the currently active -environment by prefixing their name with `@`. Attempting to resolve an -unknown var will result in an error. +environment and scope by prefixing their name with `@`. Attempting to +resolve an unknown var will result in an error. ```ts pf.runU(`@a @b +`, {a: 10, b: 20}); // 30 ``` -Storing a stack value in a variable (in the the current environment) is -done via the `!` suffix: +Assigning a value to a variable (in the the current scope) is done via +the `!` suffix: ```ts pf.runE(`1 2 + a!`) @@ -338,7 +388,87 @@ Furthermore, readonly variables can be defined via words. In this case no prefix must be used and these kind of variables are [hyperstatic](#hyperstatic-words). -TODO add info about scoping and resolution in words / quotations... +```ts +pf.run(`: pi 3.1415 ; "π=" pi + .`); +// π=3.1415 +``` + +#### Dynamic scoping + +Each variable is resolved via its own stack of binding scopes, and +therefore technically results in [dynamic +scoping](https://en.wikipedia.org/wiki/Scope_(computer_science)#Dynamic_scoping). +However, in this DSL a new scope is only introduced when a word defines +a local var with an already existing name, so in practice the effect is +more like lexical scoping. + +Var assignment always only impacts the current scope of a var. + +```ts +// predefined global scope (via env binding) +pf.runU(`@a`, {a: 1}); +// 1 + +// dynamically created global var, then used in quotation +pf.runU(`1 a! [@a @a]`); +// [1, 1] + +// var lookup inside word +pf.runU(`: foo @a ; foo`, {a: 1}); +// 1 + +// global & word local vars +// local var (value obtained from stack) takes precendence inside word +pf.runU(`: foo ^{ a } @a ; 2 foo, 3 foo, @a vec3`, {a: 1}); +// [2, 3, 1] + +// nested local var scopes +// both `foo` & `bar` define a local var `a` +pf.run(` +: foo ^{ a } + "foo a=" @a + . + ( since 'a' is declared as local var ) + ( assignment is only to local scope ) + 100 a! ; + +: bar ^{ a } + "bar1 a=" @a + . + @a inc foo ( call 'foo' w/ new value ) + "bar2 a=" @a + . ; ( @a still has same value here ) + +1 bar +"global a=" @a + . ( global @a never modified ) +`, { a: 0 }); +// bar1 a=1 +// foo a=2 +// bar2 a=1 +// global a=0 + +// since `b` is NOT declared as local var inside `foo` +// assigning a value to `b` (even inside `foo`) will be treated as global +pf.runE(`: foo @a b! ; foo`, {a: 1}) +// { a: 1, b: 1, __words: { foo: [Function] } } + +// here `foo` doesn't declare any locals +// so assignment to `a` will impact parent scope: +// - when `foo` is called from `bar`, bar's `a` var is modified +// - when `foo` is called from root level, global var `a` is created/modified +pf.runE(` +: foo 10 a! ; + +: bar ^{ a } + "before foo a=" @a + . + foo + "after foo a=" @a + . ; + +1 bar + +foo` +); +// before foo a=1 +// after foo a=10 +{ a: 10 ... } +``` ### Objects @@ -347,10 +477,10 @@ Plain objects literals can be created similarly as in JS, i.e. `{key1: value, key2: val2 ...}` (again commas are optional) Keys can be given with or without doublequotes (string literals). Quotes -are only needed if: +for keys are only needed if: - the key contains spaces, has `@` prefix or `!` suffix -- is binary/hex number +- is a binary / hex number - a number in scientific notation Furthermore, variables can be used both as keys and/or values: @@ -367,9 +497,6 @@ pf.runU(src, {bingo: 42}, [43]); // nope ``` -### Sets - -TODO ## Ideas / Todos diff --git a/packages/pointfree-lang/package.json b/packages/pointfree-lang/package.json index 2e30330bfc..1a2d1b28c5 100644 --- a/packages/pointfree-lang/package.json +++ b/packages/pointfree-lang/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/pointfree-lang", - "version": "0.1.3", + "version": "0.2.1", "description": "Forth style syntax layer/compiler for the @thi.ng/pointfree DSL", "main": "./index.js", "typings": "./index.d.ts", @@ -26,8 +26,8 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/api": "^2.1.2", - "@thi.ng/pointfree": "^0.6.2" + "@thi.ng/api": "^2.1.3", + "@thi.ng/pointfree": "^0.7.1" }, "keywords": [ "concatenative", diff --git a/packages/pointfree-lang/src/api.ts b/packages/pointfree-lang/src/api.ts index 77ae75a8c7..5deee4d70f 100644 --- a/packages/pointfree-lang/src/api.ts +++ b/packages/pointfree-lang/src/api.ts @@ -4,13 +4,18 @@ import * as pf from "@thi.ng/pointfree"; export interface ASTNode { type: NodeType; body: any; + loc: [number, number]; id?: string; + locals: string[]; +} + +export interface VisitorState { + word: boolean; } export enum NodeType { SYM = 1, WORD, - QUOT, VAR_DEREF, VAR_STORE, @@ -19,8 +24,8 @@ export enum NodeType { NUMBER, BOOLEAN, STRING, - MAP, - SET, + ARRAY, + OBJ, COMMENT, STACK_COMMENT, @@ -41,12 +46,22 @@ export const ALIASES: IObjectOf = { "-": pf.sub, "*": pf.mul, "/": pf.div, - "1+": pf.inc, - "1-": pf.dec, "v+": pf.vadd, "v-": pf.vsub, "v*": pf.vmul, "v/": pf.vdiv, + "=": pf.eq, + "not=": pf.neq, + "<=": pf.lteq, + ">=": pf.gteq, + "<": pf.lt, + ">": pf.gt, + "pos?": pf.ispos, + "neg?": pf.isneg, + "nil?": pf.isnull, + "zero?": pf.iszero, + "pi": pf.push(Math.PI), + "tau": pf.push(2 * Math.PI), ".": pf.print, ".s": pf.printds, ".r": pf.printrs, diff --git a/packages/pointfree-lang/src/grammar.pegjs b/packages/pointfree-lang/src/grammar.pegjs index 3ddc0dedff..bf2866fbec 100644 --- a/packages/pointfree-lang/src/grammar.pegjs +++ b/packages/pointfree-lang/src/grammar.pegjs @@ -4,75 +4,84 @@ // const NodeType = {}; // NodeType[NodeType["SYM"] = 1] = "SYM"; // NodeType[NodeType["WORD"] = 2] = "WORD"; - // NodeType[NodeType["QUOT"] = 3] = "QUOT"; - // NodeType[NodeType["VAR_DEREF"] = 4] = "VAR_DEREF"; - // NodeType[NodeType["VAR_STORE"] = 5] = "VAR_STORE"; - // NodeType[NodeType["NIL"] = 6] = "NIL"; - // NodeType[NodeType["NUMBER"] = 7] = "NUMBER"; - // NodeType[NodeType["BOOLEAN"] = 8] = "BOOLEAN"; - // NodeType[NodeType["STRING"] = 9] = "STRING"; - // NodeType[NodeType["MAP"] = 10] = "MAP"; - // NodeType[NodeType["SET"] = 11] = "SET"; - // NodeType[NodeType["COMMENT"] = 12] = "COMMENT"; - // NodeType[NodeType["STACK_COMMENT"] = 13] = "STACK_COMMENT"; + // NodeType[NodeType["VAR_DEREF"] = 3] = "VAR_DEREF"; + // NodeType[NodeType["VAR_STORE"] = 4] = "VAR_STORE"; + // NodeType[NodeType["NIL"] = 5] = "NIL"; + // NodeType[NodeType["NUMBER"] = 6] = "NUMBER"; + // NodeType[NodeType["BOOLEAN"] = 7] = "BOOLEAN"; + // NodeType[NodeType["STRING"] = 8] = "STRING"; + // NodeType[NodeType["ARRAY"] = 9] = "ARRAY"; + // NodeType[NodeType["OBJ"] = 10] = "OBJ"; + // NodeType[NodeType["COMMENT"] = 11] = "COMMENT"; + // NodeType[NodeType["STACK_COMMENT"] = 12] = "STACK_COMMENT"; const ast = (node) => { - // const loc = location().start; - // node.loc = [loc.offset, loc.line, loc.column]; + const loc = location().start; + node.loc = [loc.line, loc.column]; return node; }; } Root - = exrp:Expr* + = expr:Expr* Expr + = _ expr:( Word / NonWordExpr ) _ { + return ast(expr); + } + +NonWordExpr = _ expr:( - Word - / Quot - / LitQuote + LitQuote / Var / Comment + / Array + / Obj / Atom - / Map -// / Set ) _ { return ast(expr); } Word - = ":" __ id:Sym body:Expr+ ";" { - return { type: NodeType.WORD, id: id.id, body}; + = ":" __ id:Sym locals:LocalVars? body:NonWordExpr+ ";" { + return { type: NodeType.WORD, id: id.id, locals, body}; } -Quot - = "[" body:Expr* "]" { - return { type: NodeType.QUOT, body }; +LocalVars + = _ "^{" body:SymList+ "}" { + return body; } -Set - = "#{" body:Expr* "}" { - return { type: NodeType.SET, body }; +SymList + = _ id:Sym _ { return id.id; } + +Array + = "[" body:NonWordExpr* "]" { + return { type: NodeType.ARRAY, body }; } -Map - = "{" _ body:MapPair* "}" { - return { type: NodeType.MAP, body }; +Obj + = "{" _ body:ObjPair* "}" { + return { type: NodeType.OBJ, body }; } -MapPair - = k:MapKey v:MapVal { return [ k, v ]; } +ObjPair + = k:ObjKey v:ObjVal { return [ k, v ]; } -MapKey - = k:(String / Sym / Number / VarDeref) ":" { return k; } +ObjKey + = k:( + String + / Number + / VarDeref + / Sym + ) ":" { return ast(k); } -MapVal +ObjVal = _ val:( Atom - / Quot / LitQuote / VarDeref - / Map -// / Set - ) _ { return val; } + / Array + / Obj + ) _ { return ast(val); } Atom = String @@ -92,20 +101,12 @@ Boolean } Sym - = id:$(SymInit SymRest*) { + = id:$((Alpha / SymChars) (AlphaNum / SymChars)*) { return {type: NodeType.SYM, id}; } -SymInit - = Alpha - / SymChars - -SymRest - = AlphaNum - / SymChars - SymChars - = [*?$%&/\|~<>=._+\-] + = [*?$%&/\|~<>=_.+\-] Var = VarDeref @@ -122,17 +123,21 @@ VarStore } LitQuote - = "'" body:Expr { - return {type: NodeType.QUOT, body: [body]}; + = "'" body:NonWordExpr { + return {type: NodeType.ARRAY, body: [body]}; } Comment = "("+ body:$(!")" .)* ")" { return body.indexOf("--") > 0 ? - { type: NodeType.STACK_COMMENT, - body: body.split("--").map(x => x.trim().split(" ")) + { + type: NodeType.STACK_COMMENT, + body: body.split("--").map(x => x.trim()) } : - { type: NodeType.COMMENT, body: body.trim()}; + { + type: NodeType.COMMENT, + body: body.trim() + }; } String @@ -151,6 +156,7 @@ Binary = "0b" n:$[01]+ { return {type: NodeType.NUMBER, radix: 2, body: parseInt(n, 2)}; } + Hex = "0x" n:$[0-9a-fA-F]+ { return {type: NodeType.NUMBER, radix: 16, body: parseInt(n, 16)}; diff --git a/packages/pointfree-lang/src/index.ts b/packages/pointfree-lang/src/index.ts index 4e6a5e3329..b7dd41e05a 100644 --- a/packages/pointfree-lang/src/index.ts +++ b/packages/pointfree-lang/src/index.ts @@ -2,60 +2,222 @@ import { IObjectOf } from "@thi.ng/api/api"; import { illegalArgs, illegalState } from "@thi.ng/api/error"; import * as pf from "@thi.ng/pointfree"; -import { ASTNode, NodeType, ALIASES } from "./api"; -import { parse } from "./parser"; +import { ASTNode, NodeType, ALIASES, VisitorState } from "./api"; +import { parse, SyntaxError } from "./parser"; let DEBUG = false; export const setDebug = (state: boolean) => DEBUG = state; +const nodeLoc = (node: ASTNode) => + node.loc ? + `line ${node.loc.join(":")} -` : + ""; + +/** + * Looks up given symbol (word name) in this order of priority: + * - current `env.__words` + * - `ALIASES` + * - @thi.ng/pointfree built-ins + * + * Throws error if symbol can't be resolved. + * + * @param node + * @param ctx + */ const resolveSym = (node: ASTNode, ctx: pf.StackContext) => { const id = node.id; let w = (ctx[2].__words[id] || ALIASES[id] || pf[id]); if (!w) { - illegalArgs(`unknown symbol: ${id}`); + illegalArgs(`${nodeLoc(node)} unknown symbol: ${id}`); } return w; }; -const resolveVar = (id: string, ctx: pf.StackContext) => { - const w = ctx[2][id]; - if (w === undefined) { - illegalArgs(`unknown var: ${id}`); +/** + * Looks up given variable in current `env.__vars` object and returns + * its value. Throws error if var can't be resolved, either because it's + * undefined or there's scoping error. Each var uses its own (reverse) + * stack of scopes (prepared in `ensureEnv()`), and the current scope's + * value is always at the TOS element (`scopes[0]`). + * + * @param id + * @param ctx + */ +const resolveVar = (node: ASTNode, ctx: pf.StackContext) => { + const id = node.id; + const v = ctx[2].__vars[id]; + if (!v) { + illegalArgs(`${nodeLoc(node)} unknown var: ${id}`); } - return w; + if (!v.length) { + illegalState(`${nodeLoc(node)} missing bindings for var: ${id}`); + } + return v[0]; }; -const visit = (node: ASTNode, ctx: pf.StackContext, isQuote = false) => { - DEBUG && console.log("visit", NodeType[node.type], node, ctx); +/** + * Resolves given node's value. Used by `resolveArray` & `resolveObject` + * to process internal values (and in the latter case also their keys). + * + * @param node + * @param ctx + */ +const resolveNode = (node: ASTNode, ctx: pf.StackContext) => { switch (node.type) { case NodeType.SYM: - return visitSym(node, ctx, isQuote); + return resolveSym(node, ctx); + case NodeType.VAR_DEREF: + return resolveVar(node, ctx); + case NodeType.VAR_STORE: + return storevar(node.id); + case NodeType.ARRAY: + return resolveArray(node, ctx); + case NodeType.OBJ: + return resolveObject(node, ctx); + default: + return node.body; + } +}; + +/** + * Constructs an array literal (quotation) from given AST node. + * + * @param node + * @param ctx + */ +const resolveArray = (node: ASTNode, ctx: pf.StackContext) => { + const res = []; + for (let n of node.body) { + res.push(resolveNode(n, ctx)); + } + return res; +}; + +/** + * Constructs object literal from given AST node. + * + * @param node + * @param ctx + */ +const resolveObject = (node: ASTNode, ctx: pf.StackContext) => { + const res = {}; + for (let [k, v] of node.body) { + res[k.type === NodeType.SYM ? k.id : resolveNode(k, ctx)] = resolveNode(v, ctx); + } + return res; +}; + +/** + * HOF word function. Calls `resolveVar` and pushes result on stack. + * + * @param node + */ +const loadvar = (node: ASTNode) => (ctx: pf.StackContext) => + (ctx[0].push(resolveVar(node, ctx)), ctx); + +/** + * HOF word function. Pops TOS and stores value in current scope of + * var's stack of bindings, i.e. `scopes[0] = val`. Creates new scope + * stack for hitherto unknown vars. + * + * @param id + */ +const storevar = (id: string) => (ctx: pf.StackContext) => { + pf.ensureStack(ctx[0], 1); + let v = ctx[2].__vars[id]; + if (v === undefined) { + ctx[2].__vars[id] = [ctx[0].pop()]; + } else { + v[0] = ctx[0].pop(); + } + return ctx; +}; + +/** + * HOF word function used by `visitWord` to create local variables. Pops + * TOS and adds it as value for a new scope in stack of bindings for + * given var. + * + * @param id + */ +const beginvar = (id: string) => (ctx: pf.StackContext) => { + pf.ensureStack(ctx[0], 1); + let v = ctx[2].__vars[id]; + if (v === undefined) { + ctx[2].__vars[id] = [ctx[0].pop()]; + } else { + v.unshift(ctx[0].pop()); + } + return ctx; +}; + +/** + * HOF word function used by `visitWord` to end local variables. Removes + * scope from given var's stack of bindings. Throws error if for some + * reason the scope stack has become corrupted (i.e. no more scopes left + * to remove). + * + * @param id + */ +const endvar = (id: string) => (ctx: pf.StackContext) => { + const v = ctx[2].__vars[id]; + if (v === undefined || v.length === 0) { + illegalState(`can't end scope for var: ${id}`); + } + v.shift(); + if (!v.length) { + delete ctx[2].__vars[id]; + } + return ctx; +}; + +/** + * Main AST node visitor dispatcher. + * + * @param node + * @param ctx + * @param state + */ +const visit = (node: ASTNode, ctx: pf.StackContext, state: VisitorState) => { + DEBUG && console.log("visit", NodeType[node.type], node, ctx[0].toString()); + switch (node.type) { + case NodeType.SYM: + return visitSym(node, ctx, state); case NodeType.NUMBER: case NodeType.BOOLEAN: case NodeType.STRING: case NodeType.NIL: ctx[0].push(node.body); return ctx; - case NodeType.MAP: - return visitMap(node, ctx, isQuote); - case NodeType.QUOT: - return visitQuot(node, ctx); + case NodeType.ARRAY: + return visitArray(node, ctx, state); + case NodeType.OBJ: + return visitObject(node, ctx, state); case NodeType.VAR_DEREF: - return visitDeref(node, ctx, isQuote); + return visitDeref(node, ctx, state); case NodeType.VAR_STORE: - return visitStore(node, ctx, isQuote); + return visitStore(node, ctx, state); case NodeType.WORD: - return visitWord(node, ctx, isQuote); + return visitWord(node, ctx, state); default: DEBUG && console.log("skipping node..."); } return ctx; }; -const visitSym = (node: ASTNode, ctx: pf.StackContext, isQuote: boolean) => { +/** + * SYM visitor. Looks up symbol (word name) and if `state.word` is true, + * pushes word on (temp) stack (created by `visitWord`), else executes + * word. Throws error if unknown word. + * + * @param node + * @param ctx + * @param state + */ +const visitSym = (node: ASTNode, ctx: pf.StackContext, state: VisitorState) => { const w = resolveSym(node, ctx); - if (isQuote) { + if (state.word) { ctx[0].push(w); return ctx; } else { @@ -63,146 +225,275 @@ const visitSym = (node: ASTNode, ctx: pf.StackContext, isQuote: boolean) => { } }; -const visitQuot = (node: ASTNode, ctx: pf.StackContext) => { - let qctx = pf.ctx([], ctx[2]); - for (let n of node.body) { - qctx = visit(n, qctx, true); +/** + * VAR_DEREF visitor. If `state.word` is true, pushes `loadvar(id)` on + * (temp) stack (created by `visitWord`), else attempts to resolve var + * and pushes its value on stack. Throws error if unknown var. + * + * @param node + * @param ctx + * @param state + */ +const visitDeref = (node: ASTNode, ctx: pf.StackContext, state: VisitorState) => { + if (state.word) { + ctx[0].push(loadvar(node)); + } else { + ctx[0].push(resolveVar(node, ctx)); } - ctx[0].push(qctx[0]); - return ctx; -}; - -const visitDeref = (node: ASTNode, ctx: pf.StackContext, isQuote: boolean) => { - const id = node.id; - ctx[0].push(isQuote ? pf.loadkey(id) : resolveVar(id, ctx)); return ctx; }; -const visitStore = (node: ASTNode, ctx: pf.StackContext, isQuote: boolean) => { +/** + * VAR_STORE visitor. If `state.word` is true, pushes `storevar(id)` on + * (temp) stack (created by `visitWord`), else executes `storevar` + * directly to save value in env. + * + * @param node + * @param ctx + * @param state + */ +const visitStore = (node: ASTNode, ctx: pf.StackContext, state: VisitorState) => { const id = node.id; - if (isQuote) { - ctx[0].push(pf.storekey(id)); + if (state.word) { + ctx[0].push(storevar(id)); return ctx; } else { - ctx[0].push(id); - return pf.store(ctx); + return storevar(id)(ctx); } }; -const visitWord = (node: ASTNode, ctx: pf.StackContext, isQuote: boolean) => { +/** + * WORD visitor to create new word definition. Sets `state.word` to + * true, builds temp stack context and calls `visit()` for all child + * nodes. Then calls `word()` to compile function and stores it in + * `env.__words` object. + * + * root: {a: 1, b: 2} + * word1: {a: 2, b: 2} (a is local, b from root) + * word2: {c: 3, a: 2, b: 2} (c is local, called from w1, a from w1, b: from root) + * + * @param node + * @param ctx + * @param state + */ +const visitWord = (node: ASTNode, ctx: pf.StackContext, state: VisitorState) => { const id = node.id; - if (isQuote) { - illegalState(`can't define words inside quotations (${id})`); + if (state.word) { + illegalState(`${nodeLoc(node)}: can't define words inside quotations (${id})`); + } + let wctx = pf.ctx([], ctx[2]); + state.word = true; + if (node.locals) { + for (let l = node.locals, stack = wctx[0], i = l.length - 1; i >= 0; i--) { + stack.push(beginvar(l[i])); + } } - let wctx = pf.ctx([], { ...ctx[2] }); for (let n of node.body) { - wctx = visit(n, wctx, true); + wctx = visit(n, wctx, state); + } + if (node.locals) { + for (let l = node.locals, stack = wctx[0], i = l.length - 1; i >= 0; i--) { + stack.push(endvar(l[i])); + } } - const w = pf.word(wctx[0], wctx[2]); - // TODO add stack comment as meta + const w = pf.word(wctx[0]); ctx[2].__words[id] = w; + state.word = false; return ctx; } -const visitMap = (node: ASTNode, ctx: pf.StackContext, isQuote: boolean) => { - const res = {}; - let k, v; - for (let pair of node.body) { - [k, v] = pair; - let deferV: ASTNode, deferK: ASTNode; - switch (v.type) { - case NodeType.QUOT: - v = pf.unwrap(visitQuot(v, pf.ctx([], { ...ctx[2] }))); - break; - case NodeType.MAP: - v = visitMap(v, pf.ctx([], { ...ctx[2] }), isQuote)[0]; - if (isQuote) { - ctx[0].push(...v.slice(0, v.length - 1)); - } - v = v[v.length - 1]; - break; - case NodeType.SYM: - v = resolveSym(v, ctx); - break; - case NodeType.VAR_DEREF: - if (isQuote) { - deferV = v; - } else { - v = resolveVar(v.id, ctx); - } - break; - default: - v = v.body; - } - switch (k.type) { - case NodeType.VAR_DEREF: - if (isQuote) { - deferK = k; - } else { - res[resolveVar(k.id, ctx)] = v; - } - break; - case NodeType.SYM: - if (deferV) { - deferK = k.id; - } else { - res[k.id] = v; - } - break; - default: - if (deferV) { - deferK = k.body; - } else { - res[k.body] = v; - } - } - if (deferK !== undefined || deferV !== undefined) { - ctx[0].push(deferedPair(res, deferK, deferV || v)); - } +/** + * ARRAY visitor for arrays/quotations. If `state.word` is true, pushes + * call to `resolveArray` on temp word stack, else calls `resolveArray` + * and pushes result on stack. + * + * @param node + * @param ctx + * @param state + */ +const visitArray = (node: ASTNode, ctx: pf.StackContext, state: VisitorState) => { + if (state.word) { + ctx[0].push((_ctx) => (_ctx[0].push(resolveArray(node, _ctx)), _ctx)); + } else { + ctx[0].push(resolveArray(node, ctx)); } - ctx[0].push(res); return ctx; }; -const deferedPair = (res: any, k, v) => { - return (k.type === NodeType.VAR_DEREF) ? - (v != null && v.type === NodeType.VAR_DEREF) ? - (ctx: pf.StackContext) => (res[resolveVar(k.id, ctx)] = resolveVar(v.id, ctx), ctx) : - (ctx: pf.StackContext) => (res[resolveVar(k.id, ctx)] = v, ctx) : - (ctx: pf.StackContext) => (res[k] = resolveVar(v.id, ctx), ctx); +/** + * OBJ visitor for object literals. If `state.word` is true, pushes call + * to `resolveObject` on temp word stack, else calls `resolveObject` and + * pushes result on stack. + * + * @param node + * @param ctx + * @param state + */ +const visitObject = (node: ASTNode, ctx: pf.StackContext, state: VisitorState) => { + if (state.word) { + ctx[0].push((_ctx) => (_ctx[0].push(resolveObject(node, _ctx)), _ctx)); + } else { + ctx[0].push(resolveObject(node, ctx)); + } + return ctx; }; -export const ensureEnv = (env?: pf.StackEnv) => { +/** + * Prepares a the given environment object and if needed injects/updates + * these keys: + * + * - `__words`: dictionary of user defined and FFI words + * - `__vars`: individual stacks for each defined var name + * + * The user pre-defines variables at the root level of the env object, + * e.g. `{a: 1}`. For each defined var a stack is built inside the + * `__vars` sub-object, which only exists during runtime and will be + * removed before returning the env back to the user (handled by + * `finalizeEnv`). The name stacks are used to implement dynamic scoping + * of all variables. + * + * ``` + * // foo uses local var `a` with same name as global + * // foo also writes to `b` (a new global) + * // b=12 because foo's local `a` takes precedence over global `a` + * // during `foo` execution the stack for var `a` is: + * // {... __vars: {a: [2, 1]}} + * + * run(`: foo ^{ a } @a 10 + b!; 2 foo`, {a: 1}); + * // [ [], [], { a: 1, b: 12, __words: { foo: [Function] } } ] + * ``` + * + * Also see: `loadvar`, `storevar`, `beginvar`, `endvar` + * + * @param env + */ +const ensureEnv = (env?: pf.StackEnv) => { env = env || {}; if (!env.__words) { env.__words = {}; } + if (!env.__vars) { + env.__vars = {}; + } + const vars = env.__vars; + for (let k in env) { + if (k !== "__words" && k !== "__vars") { + vars[k] = [env[k]]; + } + } return env; }; +/** + * Copies current scope values for all vars back into main env object + * and removes `env.__vars`. Called from all `run*()` functions. + * + * @param ctx + */ +const finalizeEnv = (ctx: pf.StackContext) => { + const env = ctx[2]; + const vars = env.__vars; + delete env.__vars; + for (let k in vars) { + const v = vars[k]; + if (v.length !== 1) { + illegalState(`dangling or missing scopes for var: ${k}`); + } + env[k] = v[0]; + } + return ctx; +}; + +/** + * Main user function. Takes a string w/ DSL source code and optional + * env and stack. Prepares env using `ensureEnv()`, parses, compiles and + * executes source, then returns resulting `StackContext` tuple. + * + * @param src + * @param env + * @param stack + */ export const run = (src: string, env?: pf.StackEnv, stack: pf.Stack = []) => { let ctx = pf.ctx(stack, ensureEnv(env)); - for (let node of parse(src)) { - ctx = visit(node, ctx); + const state = { word: false }; + try { + for (let node of parse(src)) { + ctx = visit(node, ctx, state); + } + return finalizeEnv(ctx); + } catch (e) { + if (e instanceof SyntaxError) { + throw new Error(`line ${e.location.start.line}:${e.location.start.column}: ${e.message}`); + } else { + throw e; + } } - return ctx; }; +/** + * Like `run()`, but returns unwrapped value(s) from result data stack. + * + * @param src + * @param env + * @param stack + * @param n + */ export const runU = (src: string, env?: pf.StackEnv, stack?: pf.Stack, n = 1) => pf.unwrap(run(src, env, stack), n); +/** + * Like `run`, but returns resulting env object only. + * + * @param src + * @param env + * @param stack + */ export const runE = (src: string, env?: pf.StackEnv, stack?: pf.Stack) => run(src, env, stack)[2]; -export const runWord = (id: string, env?: pf.StackEnv, stack: pf.Stack = []) => - env.__words[id](pf.ctx(stack, ensureEnv(env))); +/** + * Executes word with given name, defined in supplied `env` object and + * with given optional initial stack. Returns resulting `StackContext` + * tuple. + * + * @param id + * @param env + * @param stack + */ +export const runWord = (id: string, env: pf.StackEnv, stack: pf.Stack = []) => + finalizeEnv(env.__words[id](pf.ctx(stack, ensureEnv(env)))); -export const runWordU = (id: string, env?: pf.StackEnv, stack: pf.Stack = [], n = 1) => - pf.unwrap(env.__words[id](pf.ctx(stack, ensureEnv(env))), n); +/** + * Like `runWord()`, but returns unwrapped value(s) from result data + * stack. + * + * @param id + * @param env + * @param stack + * @param n + */ +export const runWordU = (id: string, env: pf.StackEnv, stack: pf.Stack = [], n = 1) => + pf.unwrap(finalizeEnv(env.__words[id](pf.ctx(stack, ensureEnv(env)))), n); -export const runWordE = (id: string, env?: pf.StackEnv, stack: pf.Stack = []) => - env.__words[id](pf.ctx(stack, ensureEnv(env)))[2]; +/** + * Like `runWord()`, but returns resulting env object only. + * + * @param id + * @param env + * @param stack + */ +export const runWordE = (id: string, env: pf.StackEnv, stack: pf.Stack = []) => + finalizeEnv(env.__words[id](pf.ctx(stack, ensureEnv(env))))[2]; +/** + * Takes an environment object and injects given custom word + * definitions. `words` is an object with keys representing word names + * and their values `StackFn`s. See @thi.ng/pointfree package for more + * details about stack functions. + * + * @param env + * @param words + */ export const ffi = (env: any, words: IObjectOf) => { env = ensureEnv(env); env.__words = { ...env.__words, ...words }; @@ -213,4 +504,4 @@ export { ensureStack, ensureStackN, unwrap, -} from "@thi.ng/pointfree"; \ No newline at end of file +} from "@thi.ng/pointfree"; diff --git a/packages/pointfree-lang/test/index.ts b/packages/pointfree-lang/test/index.ts index c962debf83..1e8e19e91e 100644 --- a/packages/pointfree-lang/test/index.ts +++ b/packages/pointfree-lang/test/index.ts @@ -9,6 +9,18 @@ describe("pointfree-lang", () => { assert.deepEqual(run(`'nil dup`)[0], [[null], [null]]); }); + it("number (hex)", () => { + assert.deepEqual(run(`0x1 0xa 0xff 0xdecafbad`)[0], [1, 10, 255, 0xdecafbad]); + }); + + it("number (decimal)", () => { + assert.deepEqual(run(`0 -1 +2`)[0], [0, -1, 2]); + assert.deepEqual(run(`-123. +12.3`)[0], [-123, 12.3]); + assert.deepEqual(run(`-123e4`)[0], [-1230000]); + assert.deepEqual(run(`+1.23e-2`)[0], [0.0123]); + assert.deepEqual(run(`+1.23e-2 0.0123 =`)[0], [true]); + }); + it("litquote", () => { assert.deepEqual(runU(`'nil`), [null]); assert.deepEqual(runU(`'+`), [pf.add]); @@ -17,6 +29,23 @@ describe("pointfree-lang", () => { assert.deepEqual(run(`1 2 '+ exec`)[0], [3]); }); + it("var deref (quote)", () => { + assert.deepEqual(runU(`[@a [@a {@a: @a} {@a: [@a]}]]`, { a: 1 }), [1, [1, { 1: 1 }, { 1: [1] }]]); + }); + + it("var deref (litquote)", () => { + assert.deepEqual(runU(`'@a`, { a: 1 }), [1]); + assert.deepEqual(runU(`'[@a]`, { a: 1 }), [[1]]); + assert.deepEqual(runU(`''@a`, { a: 1 }), [[1]]); + }); + + it("var deref (word)", () => { + assert.deepEqual(runU(`: foo [@a [@a {@a: @a} {@a: [@a]}]]; foo`, { a: 1 }), [1, [1, { 1: 1 }, { 1: [1] }]]); + assert.deepEqual( + run(`: foo [@a [@a {@a: @a} {@a: [@a]}]]; foo 2 a! foo`, { a: 1 })[0], + [[1, [1, { 1: 1 }, { 1: [1] }]], [2, [2, { 2: 2 }, { 2: [2] }]]]); + }); + // setDebug(true); // console.log(run(`"result: " 1 2 + + .`)); diff --git a/packages/pointfree/CHANGELOG.md b/packages/pointfree/CHANGELOG.md index 878a31b1bd..e86b0e7b00 100644 --- a/packages/pointfree/CHANGELOG.md +++ b/packages/pointfree/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.7.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/pointfree@0.7.0...@thi.ng/pointfree@0.7.1) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/pointfree + + +# [0.7.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/pointfree@0.6.2...@thi.ng/pointfree@0.7.0) (2018-04-03) + + +### Features + +* **pointfree:** add copy() word ([68a8dba](https://github.com/thi-ng/umbrella/commit/68a8dba)) +* **pointfree:** add math ops, update load/loadkey, update tests ([2101e92](https://github.com/thi-ng/umbrella/commit/2101e92)) + + + + ## [0.6.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/pointfree@0.6.1...@thi.ng/pointfree@0.6.2) (2018-04-01) diff --git a/packages/pointfree/README.md b/packages/pointfree/README.md index 8b556e11a5..4d7b16db34 100644 --- a/packages/pointfree/README.md +++ b/packages/pointfree/README.md @@ -890,7 +890,7 @@ at word construction time and return a pre-configured stack function. | `pull3` | `( arr -- x y z arr )` | short for: `[pull2, pull]` | | `pull4` | `( arr -- a b c d arr )` | short for: `[pull2, pull2]` | | `split` | `( arr x -- [...] [...] )` | split array at index `x` | -| `storeat` | `( val obj k -- )` | `obj` can be array/obj | +| `setat` | `( val obj k -- obj )` | `obj` can be array/obj | | `tuple(n)` | `( ... -- [...] )` | HOF, like `collect`, but w/ predefined size | | `vec2` | `( x y -- [x, y] )` | same as `tuple(2)` | | `vec3` | `( x y z -- [x, y, z] )` | same as `tuple(3)` | diff --git a/packages/pointfree/package.json b/packages/pointfree/package.json index 7b20b9eabb..c3e315acb6 100644 --- a/packages/pointfree/package.json +++ b/packages/pointfree/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/pointfree", - "version": "0.6.2", + "version": "0.7.1", "description": "Pointfree functional composition / Forth style stack execution engine", "main": "./index.js", "typings": "./index.d.ts", @@ -24,7 +24,7 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/api": "^2.1.2" + "@thi.ng/api": "^2.1.3" }, "keywords": [ "composition", diff --git a/packages/pointfree/src/index.ts b/packages/pointfree/src/index.ts index 08df2d675f..8e81ff65eb 100644 --- a/packages/pointfree/src/index.ts +++ b/packages/pointfree/src/index.ts @@ -3,6 +3,7 @@ import { illegalState, illegalArgs } from "@thi.ng/api/error"; import { equiv as _equiv } from "@thi.ng/api/equiv"; import { isArray } from "@thi.ng/checks/is-array"; import { isFunction } from "@thi.ng/checks/is-function"; +import { isPlainObject } from "@thi.ng/checks/is-plain-object"; import { StackContext, StackProc, StackEnv, StackProgram, StackFn, Stack } from "./api"; import { comp } from "./comp"; @@ -705,14 +706,81 @@ export const pow = op2((b, a) => Math.pow(a, b)); */ export const sqrt = op1(Math.sqrt); +/** + * ( x -- exp(x) ) + * + * @param ctx + */ +export const exp = op1(Math.exp); + +/** + * ( x -- log(x) ) + * + * @param ctx + */ export const log = op1(Math.log); +/** + * ( x -- sin(x) ) + * + * @param ctx + */ export const sin = op1(Math.sin); +/** + * ( x -- cos(x) ) + * + * @param ctx + */ export const cos = op1(Math.cos); +/** + * ( x -- tan(x) ) + * + * @param ctx + */ +export const tan = op1(Math.tan); + +/** + * ( x -- tanh(x) ) + * + * @param ctx + */ +export const tanh = op1(Math.tanh); + +/** + * ( x -- floor(x) ) + * + * @param ctx + */ +export const floor = op1(Math.floor); + +/** + * ( x -- ceil(x) ) + * + * @param ctx + */ +export const ceil = op1(Math.ceil); + +/** + * ( x y -- sqrt(x*x+y*y) ) + * + * @param ctx + */ +export const hypot = op2(Math.hypot); + +/** + * ( x y -- atan2(y,x) ) + * + * @param ctx + */ export const atan2 = op2(Math.atan2); +/** + * ( -- Math.random() ) + * + * @param ctx + */ export const rand = (ctx: StackContext) => (ctx[0].push(Math.random()), ctx); @@ -1539,6 +1607,17 @@ export const join = (sep = "") => op1((x) => x.join(sep)); */ export const length = op1((x) => x.length); +/** + * Replaces TOS with its shallow copy. MUST be an array or plain object. + * + * ( x -- copy ) + */ +export const copy = op1((x) => + isArray(x) ? + [...x] : + isPlainObject(x) ? { ...x } : + illegalArgs(`can't copy type ${typeof x}`)); + /** * Reads key/index from object/array. * @@ -1551,16 +1630,17 @@ export const at = op2((b, a) => a[b]); /** * Writes `val` at key/index in object/array. * - * ( val obj k -- ) + * ( val obj k -- obj ) * * @param ctx */ -export const storeat = (ctx: StackContext) => { +export const setat = (ctx: StackContext) => { const stack = ctx[0]; const n = stack.length - 3; $n(n, 0); stack[n + 1][stack[n + 2]] = stack[n]; - stack.length = n; + stack[n] = stack[n + 1]; + stack.length -= 2; return ctx; }; @@ -1608,15 +1688,22 @@ export const pushenv = (ctx: StackContext) => (ctx[0].push(ctx[2]), ctx); /** - * Loads value for `key` from env and pushes it on d-stack. + * Loads value for `key` from current env and pushes it on d-stack. + * Throws error if var doesn't exist. * * ( key -- env[key] ) * * @param ctx * @param env */ -export const load = (ctx: StackContext) => - ($(ctx[0], 1), ctx[0].push(ctx[2][ctx[0].pop()]), ctx); +export const load = (ctx: StackContext) => { + const stack = ctx[0]; + $(stack, 1); + const id = stack.pop(); + !ctx[2].hasOwnProperty(id) && illegalArgs(`unknown var: ${id}`); + stack.push(ctx[2][id]); + return ctx; +}; /** * Stores `val` under `key` in env. @@ -1631,20 +1718,24 @@ export const store = (ctx: StackContext) => /** * Higher order word. Similar to `load`, but always uses given - * preconfigured `key` instead of reading it from d-stack at runtime (also - * slightly faster). + * preconfigured `key` instead of reading it from d-stack at runtime + * (also slightly faster). Throws error if var doesn't exist. * * ( -- env[key] ) * @param ctx * @param env */ export const loadkey = (key: PropertyKey) => - (ctx: StackContext) => (ctx[0].push(ctx[2][key]), ctx); + (ctx: StackContext) => { + !ctx[2].hasOwnProperty(key) && illegalArgs(`unknown var: ${key}`); + ctx[0].push(ctx[2][key]); + return ctx; + }; /** * Higher order word. Similar to `store`, but always uses given - * preconfigure `key` instead of reading it from d-stack at runtime (also - * slightly faster). + * preconfigure `key` instead of reading it from d-stack at runtime + * (also slightly faster). * * ( val -- ) * diff --git a/packages/pointfree/test/index.ts b/packages/pointfree/test/index.ts index c81669547a..0d3f356ada 100644 --- a/packages/pointfree/test/index.ts +++ b/packages/pointfree/test/index.ts @@ -457,23 +457,23 @@ describe("pointfree", () => { assert.deepEqual(pf.at($([{ id: 42 }, "id"]))[0], [42]); }); - it("storeat", () => { - assert.throws(() => pf.storeat($([1, 2]))); + it("setat", () => { + assert.throws(() => pf.setat($([1, 2]))); let a: any = [10, 20]; - assert.deepEqual(pf.storeat($([30, a, 0]))[0], []); + assert.deepEqual(pf.setat($([30, a, 0]))[0], [a]); assert.deepEqual(a, [30, 20]); a = [10, 20]; - assert.deepEqual(pf.storeat($([30, a, 3]))[0], []); + assert.deepEqual(pf.setat($([30, a, 3]))[0], [a]); assert.deepEqual(a, [10, 20, , 30]); a = {}; - assert.deepEqual(pf.storeat($([30, a, "a"]))[0], []); + assert.deepEqual(pf.setat($([30, a, "a"]))[0], [a]); assert.deepEqual(a, { a: 30 }); }); it("load", () => { assert.throws(() => pf.load($())); assert.deepEqual(pf.load([["a"], [], { a: 1 }])[0], [1]); - assert.deepEqual(pf.load([["b"], [], { a: 1 }])[0], [undefined]); + assert.throws(() => pf.load([["b"], [], { a: 1 }])); }); it("store", () => { @@ -484,7 +484,7 @@ describe("pointfree", () => { it("loadkey", () => { assert.deepEqual(pf.loadkey("a")([[0], [], { a: 1 }])[0], [0, 1]); - assert.deepEqual(pf.loadkey("b")([[0], [], { a: 1 }])[0], [0, undefined]); + assert.throws(() => pf.loadkey("a")(pf.ctx())); }); it("storekey", () => { diff --git a/packages/resolve-map/CHANGELOG.md b/packages/resolve-map/CHANGELOG.md index 986ae44e5c..d4d2022b4f 100644 --- a/packages/resolve-map/CHANGELOG.md +++ b/packages/resolve-map/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.1.5](https://github.com/thi-ng/umbrella/compare/@thi.ng/resolve-map@0.1.4...@thi.ng/resolve-map@0.1.5) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/resolve-map + ## [0.1.4](https://github.com/thi-ng/umbrella/compare/@thi.ng/resolve-map@0.1.3...@thi.ng/resolve-map@0.1.4) (2018-04-01) diff --git a/packages/resolve-map/package.json b/packages/resolve-map/package.json index b631b93394..c0288935e5 100644 --- a/packages/resolve-map/package.json +++ b/packages/resolve-map/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/resolve-map", - "version": "0.1.4", + "version": "0.1.5", "description": "DAG resolution of vanilla objects & arrays with internally linked values", "main": "./index.js", "typings": "./index.d.ts", @@ -22,8 +22,8 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/checks": "^1.3.1", - "@thi.ng/paths": "^1.1.3" + "@thi.ng/checks": "^1.3.2", + "@thi.ng/paths": "^1.1.4" }, "keywords": [ "configuration", diff --git a/packages/rle-pack/CHANGELOG.md b/packages/rle-pack/CHANGELOG.md index 1fd9087730..6a5fe55f3d 100644 --- a/packages/rle-pack/CHANGELOG.md +++ b/packages/rle-pack/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.2.12](https://github.com/thi-ng/umbrella/compare/@thi.ng/rle-pack@0.2.11...@thi.ng/rle-pack@0.2.12) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/rle-pack + ## [0.2.11](https://github.com/thi-ng/umbrella/compare/@thi.ng/rle-pack@0.2.10...@thi.ng/rle-pack@0.2.11) (2018-04-01) diff --git a/packages/rle-pack/package.json b/packages/rle-pack/package.json index 27562db6e4..26e77d58de 100644 --- a/packages/rle-pack/package.json +++ b/packages/rle-pack/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/rle-pack", - "version": "0.2.11", + "version": "0.2.12", "description": "Binary run-length encoding packer w/ flexible repeat bit widths", "main": "./index.js", "typings": "./index.d.ts", @@ -25,7 +25,7 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/bitstream": "^0.4.2" + "@thi.ng/bitstream": "^0.4.3" }, "keywords": [ "binary", diff --git a/packages/router/CHANGELOG.md b/packages/router/CHANGELOG.md index cda78c8729..6319d1d8be 100644 --- a/packages/router/CHANGELOG.md +++ b/packages/router/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.1.4](https://github.com/thi-ng/umbrella/compare/@thi.ng/router@0.1.3...@thi.ng/router@0.1.4) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/router + ## [0.1.3](https://github.com/thi-ng/umbrella/compare/@thi.ng/router@0.1.2...@thi.ng/router@0.1.3) (2018-04-01) diff --git a/packages/router/package.json b/packages/router/package.json index fd87634f3c..e2e6858b68 100644 --- a/packages/router/package.json +++ b/packages/router/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/router", - "version": "0.1.3", + "version": "0.1.4", "description": "Generic router for browser & non-browser based applications", "main": "./index.js", "typings": "./index.d.ts", @@ -23,7 +23,7 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/api": "^2.1.2" + "@thi.ng/api": "^2.1.3" }, "keywords": [ "declarative", diff --git a/packages/rstream-csp/CHANGELOG.md b/packages/rstream-csp/CHANGELOG.md index 976d932985..9b68cce7fe 100644 --- a/packages/rstream-csp/CHANGELOG.md +++ b/packages/rstream-csp/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.1.43](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-csp@0.1.42...@thi.ng/rstream-csp@0.1.43) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/rstream-csp + ## [0.1.42](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-csp@0.1.41...@thi.ng/rstream-csp@0.1.42) (2018-04-01) diff --git a/packages/rstream-csp/package.json b/packages/rstream-csp/package.json index 9e24eff0f0..25787dc1b3 100644 --- a/packages/rstream-csp/package.json +++ b/packages/rstream-csp/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/rstream-csp", - "version": "0.1.42", + "version": "0.1.43", "description": "@thi.ng/csp bridge module for @thi.ng/rstream", "main": "./index.js", "typings": "./index.d.ts", @@ -24,8 +24,8 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/csp": "^0.3.23", - "@thi.ng/rstream": "^1.2.2" + "@thi.ng/csp": "^0.3.24", + "@thi.ng/rstream": "^1.2.3" }, "keywords": [ "bridge", diff --git a/packages/rstream-log/CHANGELOG.md b/packages/rstream-log/CHANGELOG.md index 79133bf9a0..d3d4f1cc64 100644 --- a/packages/rstream-log/CHANGELOG.md +++ b/packages/rstream-log/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [0.6.3](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-log@0.6.2...@thi.ng/rstream-log@0.6.3) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/rstream-log + ## [0.6.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream-log@0.6.1...@thi.ng/rstream-log@0.6.2) (2018-04-01) diff --git a/packages/rstream-log/package.json b/packages/rstream-log/package.json index 4114e87268..2c29b97f54 100644 --- a/packages/rstream-log/package.json +++ b/packages/rstream-log/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/rstream-log", - "version": "0.6.2", + "version": "0.6.3", "description": "Structured, multilevel & hierarchical loggers based on @thi.ng/rstream", "main": "./index.js", "typings": "./index.d.ts", @@ -24,8 +24,8 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/api": "^2.1.2", - "@thi.ng/rstream": "^1.2.2" + "@thi.ng/api": "^2.1.3", + "@thi.ng/rstream": "^1.2.3" }, "keywords": [ "ES6", diff --git a/packages/rstream/CHANGELOG.md b/packages/rstream/CHANGELOG.md index 4cf7b442b6..10ebc4b3d5 100644 --- a/packages/rstream/CHANGELOG.md +++ b/packages/rstream/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.2.3](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream@1.2.2...@thi.ng/rstream@1.2.3) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/rstream + ## [1.2.2](https://github.com/thi-ng/umbrella/compare/@thi.ng/rstream@1.2.1...@thi.ng/rstream@1.2.2) (2018-04-01) diff --git a/packages/rstream/package.json b/packages/rstream/package.json index 7649354281..3887e23567 100644 --- a/packages/rstream/package.json +++ b/packages/rstream/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/rstream", - "version": "1.2.2", + "version": "1.2.3", "description": "Reactive multi-tap streams, dataflow & transformation pipeline constructs", "main": "./index.js", "typings": "./index.d.ts", @@ -24,9 +24,9 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/api": "^2.1.2", - "@thi.ng/atom": "^1.2.2", - "@thi.ng/transducers": "^1.7.3" + "@thi.ng/api": "^2.1.3", + "@thi.ng/atom": "^1.2.3", + "@thi.ng/transducers": "^1.7.4" }, "keywords": [ "datastructure", diff --git a/packages/transducers/CHANGELOG.md b/packages/transducers/CHANGELOG.md index d601db11f6..ec108e993b 100644 --- a/packages/transducers/CHANGELOG.md +++ b/packages/transducers/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. + +## [1.7.4](https://github.com/thi-ng/umbrella/compare/@thi.ng/transducers@1.7.3...@thi.ng/transducers@1.7.4) (2018-04-04) + + + + +**Note:** Version bump only for package @thi.ng/transducers + ## [1.7.3](https://github.com/thi-ng/umbrella/compare/@thi.ng/transducers@1.7.2...@thi.ng/transducers@1.7.3) (2018-04-01) diff --git a/packages/transducers/package.json b/packages/transducers/package.json index 38b23f9246..5dfc669c12 100644 --- a/packages/transducers/package.json +++ b/packages/transducers/package.json @@ -1,6 +1,6 @@ { "name": "@thi.ng/transducers", - "version": "1.7.3", + "version": "1.7.4", "description": "Lightweight transducer implementations for ES6 / TypeScript", "main": "./index.js", "typings": "./index.d.ts", @@ -24,7 +24,7 @@ "typescript": "^2.8.1" }, "dependencies": { - "@thi.ng/api": "^2.1.2" + "@thi.ng/api": "^2.1.3" }, "keywords": [ "ES6",