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",