Initial commit.

This commit is contained in:
Tony Garnock-Jones 2016-01-30 21:58:59 -05:00
commit 1107483c86
12 changed files with 1546 additions and 0 deletions

2
js/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
scratch/
node_modules/

23
js/Makefile Normal file
View File

@ -0,0 +1,23 @@
all:
npm install .
keys: private-key.pem server-cert.pem
private-key.pem:
openssl genrsa -des3 -passout pass:a -out $@ 1024
openssl rsa -passin pass:a -in $@ -out $@
server-cert.pem: private-key.pem
openssl req -new -x509 -nodes -sha1 -days 365 \
-subj /CN=server.minimart.leastfixedpoint.com \
-passin pass:a \
-key private-key.pem > $@
clean-keys:
rm -f private-key.pem server-cert.pem
clean:
rm -f dist/*.js
veryclean: clean
rm -rf node_modules/

11
js/README.md Normal file
View File

@ -0,0 +1,11 @@
# Syndicate-JS: Syndicate for Javascript environments
## A walk through the code
Source files in `src/`, from most general to most specific:
- `reflect.js`: Reflection on function formal parameter lists.
- `util.js`: Functions `extend` and `kwApply`.
- `route.js`: Implementation of dataspace trie structure.
- `syndicate.js`: Implementation of core leaf actors and networks.
- `main.js`: Main package entry point.

1
js/dist/README.md vendored Normal file
View File

@ -0,0 +1 @@
Directory for build products, checked in to the repo for ease-of-use.

29
js/package.json Normal file
View File

@ -0,0 +1,29 @@
{
"name": "syndicate-js",
"version": "0.0.0",
"description": "Syndicate in the browser",
"homepage": "https://github.com/tonyg/syndicate",
"main": "src/main.js",
"repository": {
"type": "git",
"url": "git://github.com/tonyg/syndicate"
},
"scripts": {
"clean": "rm -f dist/*",
"build-debug": "browserify src/main.js -d -s Syndicate -o dist/syndicate.js",
"build-min": "browserify src/main.js -s Syndicate -o dist/_syndicate.js && uglifyjs dist/_syndicate.js -o dist/syndicate.min.js && rm dist/_syndicate.js",
"build": "npm run build-debug && npm run build-min",
"watch": "watchify src/main.js -d -s Syndicate -o dist/syndicate.js",
"test": "mocha",
"prepublish": "npm run build"
},
"author": "Tony Garnock-Jones <tonyg@ccs.neu.edu>",
"devDependencies": {
"watchify": "^3.7.0",
"uglify-js": "^2.6.1",
"browserify": "^13.0.0",
"mocha": "^2.4.5",
"expect.js": "^0.3.1",
"immutable": "^3.7.6"
}
}

16
js/src/main.js Normal file
View File

@ -0,0 +1,16 @@
module.exports = require("./syndicate.js");
// module.exports.DOM = require("./dom-driver.js");
// module.exports.JQuery = require("./jquery-driver.js");
// module.exports.RoutingTableWidget = require("./routing-table-widget.js");
// module.exports.WebSocket = require("./websocket-driver.js");
module.exports.Reflect = require("./reflect.js");
// module.exports.Ground = require("./ground.js").Ground;
// module.exports.Actor = require("./actor.js").Actor;
// module.exports.Spy = require("./spy.js").Spy;
// module.exports.WakeDetector = require("./wake-detector.js").WakeDetector;
// var Worker = require("./worker.js");
// module.exports.Worker = Worker.Worker;
// module.exports.WorkerGround = Worker.WorkerGround;

26
js/src/reflect.js Normal file
View File

@ -0,0 +1,26 @@
// Reflection on function formal parameter lists.
// This module is based on Angular's "injector" code,
// https://github.com/angular/angular.js/blob/master/src/auto/injector.js,
// MIT licensed, and hence:
// Copyright (c) 2010-2014 Google, Inc. http://angularjs.org
// Copyright (c) 2014 Tony Garnock-Jones
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
var FN_ARG_SPLIT = /,/;
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
function formalParameters(fn) {
var result = [];
var fnText = fn.toString().replace(STRIP_COMMENTS, '');
var argDecl = fnText.match(FN_ARGS);
var args = argDecl[1].split(FN_ARG_SPLIT);
for (var i = 0; i < args.length; i++) {
var trimmed = args[i].trim();
if (trimmed) { result.push(trimmed); }
}
return result;
}
module.exports.formalParameters = formalParameters;

1004
js/src/route.js Normal file

File diff suppressed because it is too large Load Diff

27
js/src/syndicate.js Normal file
View File

@ -0,0 +1,27 @@
var Route = require("./route.js");
var Util = require("./util.js");
///////////////////////////////////////////////////////////////////////////
/*---------------------------------------------------------------------------*/
/* Events and Actions */
var __ = Route.__;
var _$ = Route._$;
///////////////////////////////////////////////////////////////////////////
module.exports.__ = __;
module.exports._$ = _$;
// module.exports.sub = sub;
// module.exports.pub = pub;
// module.exports.spawn = spawn;
// module.exports.updateRoutes = updateRoutes;
// module.exports.sendMessage = sendMessage;
// module.exports.shutdownWorld = shutdownWorld;
// module.exports.World = World;
// module.exports.DemandMatcher = DemandMatcher;
// module.exports.Deduplicator = Deduplicator;
module.exports.Route = Route;

23
js/src/util.js Normal file
View File

@ -0,0 +1,23 @@
var Reflect = require("./reflect.js");
module.exports.extend = function (what, _with) {
for (var prop in _with) {
if (_with.hasOwnProperty(prop)) {
what[prop] = _with[prop];
}
}
return what;
};
module.exports.kwApply = function (f, thisArg, args) {
var formals = Reflect.formalParameters(f);
var actuals = []
for (var i = 0; i < formals.length; i++) {
var formal = formals[i];
if (!(formal in args)) {
throw new Error("Function parameter '"+formal+"' not present in args");
}
actuals.push(args[formal]);
}
return f.apply(thisArg, actuals);
};

380
js/test/test-route.js Normal file
View File

@ -0,0 +1,380 @@
var Immutable = require('immutable');
var expect = require('expect.js');
var util = require('util');
var r = require("../src/route.js");
function checkPrettyTrie(m, expected) {
expect(r.prettyTrie(m)).to.equal(expected.join('\n'));
}
function checkTrieKeys(actual, expected) {
expect((Immutable.Set(actual).map(Immutable.List))
.equals(Immutable.Set(expected).map(Immutable.List))).to.be(true);
}
describe("basic pattern compilation", function () {
var sAny = Immutable.Set(['mAny']);
var sAAny = Immutable.Set(['mAAny']);
var mAny = r.compilePattern(sAny, r.__);
var mAAny = r.compilePattern(sAAny, ['A', r.__]);
it("should print as expected", function () {
checkPrettyTrie(mAny, [' ★ >{["mAny"]}']);
checkPrettyTrie(mAAny, [' < "A" ★ > >{["mAAny"]}']);
});
describe("of wildcard", function () {
it("should match anything", function () {
expect(r.matchValue(mAny, 'hi')).to.eql(sAny);
expect(r.matchValue(mAny, ['A', 'hi'])).to.eql(sAny);
expect(r.matchValue(mAny, ['B', 'hi'])).to.eql(sAny);
expect(r.matchValue(mAny, ['A', [['hi']]])).to.eql(sAny);
});
});
describe("of A followed by wildcard", function () {
it("should match A followed by anything", function () {
expect(r.matchValue(mAAny, 'hi')).to.be(null);
expect(r.matchValue(mAAny, ['A', 'hi'])).to.eql(sAAny);
expect(r.matchValue(mAAny, ['B', 'hi'])).to.be(null);
expect(r.matchValue(mAAny, ['A', [['hi']]])).to.eql(sAAny);
});
});
it("should observe basic (in)equivalences", function () {
expect(Immutable.is(mAny, mAAny)).to.be(false);
expect(Immutable.is(mAny, mAny)).to.be(true);
expect(Immutable.is(mAAny, mAAny)).to.be(true);
});
});
describe("unions", function () {
it("should collapse common prefix wildcard", function () {
checkPrettyTrie(r.union(r.compilePattern(Immutable.Set(['A']), [r.__, 'A']),
r.compilePattern(Immutable.Set(['B']), [r.__, 'B'])),
[' < ★ "A" > >{["A"]}',
' "B" > >{["B"]}']);
});
it("should unroll wildcard unioned with nonwildcard", function () {
checkPrettyTrie(r.union(r.compilePattern(Immutable.Set(['A']), [r.__, 'A']),
r.compilePattern(Immutable.Set(['W']), r.__)),
[' ★ >{["W"]}',
' < ★ "A" ★...> >{["W"]}',
' > >{["A","W"]}',
' ★...> >{["W"]}',
' > >{["W"]}',
' > >{["W"]}']);
});
it("should properly multiply out", function () {
checkPrettyTrie(r.union(r.compilePattern(Immutable.Set(['A']), [r.__, 2]),
r.compilePattern(Immutable.Set(['C']), [1, 3]),
r.compilePattern(Immutable.Set(['B']), [3, 4])),
[' < ★ 2 > >{["A"]}',
' 1 2 > >{["A"]}',
' 3 > >{["C"]}',
' 3 2 > >{["A"]}',
' 4 > >{["B"]}']);
checkPrettyTrie(r.union(r.compilePattern(Immutable.Set(['C']), [1, 3]),
r.compilePattern(Immutable.Set(['B']), [3, 4])),
[' < 1 3 > >{["C"]}',
' 3 4 > >{["B"]}']);
checkPrettyTrie(r.union(r.compilePattern(Immutable.Set(['A']), [r.__, 2]),
r.compilePattern(Immutable.Set(['C']), [1, 3])),
[' < ★ 2 > >{["A"]}',
' 1 2 > >{["A"]}',
' 3 > >{["C"]}']);
checkPrettyTrie(r.union(r.compilePattern(Immutable.Set(['A']), [r.__, 2]),
r.compilePattern(Immutable.Set(['B']), [3, 4])),
[' < ★ 2 > >{["A"]}',
' 3 2 > >{["A"]}',
' 4 > >{["B"]}']);
});
it("should correctly construct intermediate values", function () {
var MU = r.emptyTrie;
MU = r.union(MU, r.compilePattern(Immutable.Set(['A']), [r.__, 2]));
checkPrettyTrie(MU, [' < ★ 2 > >{["A"]}']);
MU = r.union(MU, r.compilePattern(Immutable.Set(['C']), [1, 3]));
checkPrettyTrie(MU, [' < ★ 2 > >{["A"]}',
' 1 2 > >{["A"]}',
' 3 > >{["C"]}']);
MU = r.union(MU, r.compilePattern(Immutable.Set(['B']), [3, 4]));
checkPrettyTrie(MU, [' < ★ 2 > >{["A"]}',
' 1 2 > >{["A"]}',
' 3 > >{["C"]}',
' 3 2 > >{["A"]}',
' 4 > >{["B"]}']);
});
it("should handle identical patterns with different pids", function () {
var m = r.union(r.compilePattern(Immutable.Set('B'), [2]),
r.compilePattern(Immutable.Set('C'), [3]));
checkPrettyTrie(m, [' < 2 > >{["B"]}',
' 3 > >{["C"]}']);
m = r.union(r.compilePattern(Immutable.Set('A'), [2]), m);
checkPrettyTrie(m, [' < 2 > >{["A","B"]}',
' 3 > >{["C"]}']);
});
});
describe("projections", function () {
describe("with picky structure", function () {
var proj = r.compileProjection(r._$("v", [[r.__]]));
it("should include things that match as well as wildcards", function () {
checkPrettyTrie(r.project(r.union(r.compilePattern(Immutable.Set(['A']), r.__),
r.compilePattern(Immutable.Set(['B']), [['b']])),
proj),
[' < < ★ > > >{["A"]}',
' "b" > > >{["B","A"]}']);
});
it("should exclude things that lack the required structure", function () {
checkPrettyTrie(r.project(r.union(r.compilePattern(Immutable.Set(['A']), r.__),
r.compilePattern(Immutable.Set(['B']), ['b'])),
proj),
[' < < ★ > > >{["A"]}']);
});
});
describe("simple positional", function () {
var proj = r.compileProjection([r._$, r._$]);
it("should collapse common prefixes", function () {
checkPrettyTrie(r.project(r.union(r.compilePattern(Immutable.Set(['A']), [1, 2]),
r.compilePattern(Immutable.Set(['C']), [1, 3]),
r.compilePattern(Immutable.Set(['B']), [3, 4])),
proj),
[' 1 2 >{["A"]}',
' 3 >{["C"]}',
' 3 4 >{["B"]}']);
});
it("should yield a correct set of results", function () {
var u = r.union(r.compilePattern(Immutable.Set(['A']), [1, 2]),
r.compilePattern(Immutable.Set(['C']), [1, 3]),
r.compilePattern(Immutable.Set(['B']), [3, 4]));
checkTrieKeys(r.trieKeys(r.project(u, proj)), [[1, 2], [1, 3], [3, 4]]);
});
});
});
describe("subtract after union", function () {
var R1 = r.compilePattern(Immutable.Set(['A']), [r.__, "B"]);
var R2 = r.compilePattern(Immutable.Set(['B']), ["A", r.__]);
var R12 = r.union(R1, R2);
it("should have sane preconditions", function () { // Am I doing this right?
checkPrettyTrie(R1, [' < ★ "B" > >{["A"]}']);
checkPrettyTrie(R2, [' < "A" ★ > >{["B"]}']);
checkPrettyTrie(R12, [' < "A" "B" > >{["B","A"]}',
' ★ > >{["B"]}',
' ★ "B" > >{["A"]}']);
});
it("should yield the remaining ingredients of the union", function () {
expect(Immutable.is(r.subtract(R12, R1), R2)).to.be(true);
expect(Immutable.is(r.subtract(R12, R2), R1)).to.be(true);
expect(Immutable.is(r.subtract(R12, R1), R1)).to.be(false);
});
});
describe("trie equality", function () {
it("should not rely on object identity", function () {
expect(Immutable.is(r.union(r.compilePattern(Immutable.Set(['A']), [r.__, 'A']),
r.compilePattern(Immutable.Set(['B']), [r.__, 'B'])),
r.union(r.compilePattern(Immutable.Set(['A']), [r.__, 'A']),
r.compilePattern(Immutable.Set(['B']), [r.__, 'B']))))
.to.be(true);
});
it("should respect commutativity of union", function () {
expect(Immutable.is(r.union(r.compilePattern(Immutable.Set(['A']), [r.__, 'A']),
r.compilePattern(Immutable.Set(['B']), [r.__, 'B'])),
r.union(r.compilePattern(Immutable.Set(['B']), [r.__, 'B']),
r.compilePattern(Immutable.Set(['A']), [r.__, 'A']))))
.to.be(true);
});
});
describe("trieKeys on wild tries", function () {
var M = r.union(r.compilePattern(Immutable.Set(['A']), [r.__, 2]),
r.compilePattern(Immutable.Set(['C']), [1, 3]),
r.compilePattern(Immutable.Set(['B']), [3, 4]));
it("should yield null to signal an infinite result", function () {
expect(r.trieKeys(r.project(M, r.compileProjection([r._$, r._$])))).to.be(null);
});
it("should extract just the second array element successfully", function () {
checkTrieKeys(r.trieKeys(r.project(M, r.compileProjection([r.__, r._$]))),
[[2],[3],[4]]);
});
var M2 = r.project(M, r.compileProjection([r._$, r._$]));
it("should survive double-projection", function () {
checkTrieKeys(r.trieKeys(r.project(M2, r.compileProjection(r.__, r._$))),
[[2],[3],[4]]);
});
it("should survive embedding and reprojection", function () {
checkTrieKeys(r.trieKeys(r.project(r.compilePattern(true, [r.embeddedTrie(M2)]),
r.compileProjection([r.__, r._$]))),
[[2],[3],[4]]);
checkTrieKeys(r.trieKeys(r.project(r.compilePattern(true, [[r.embeddedTrie(M2)]]),
r.compileProjection([[r.__, r._$]]))),
[[2],[3],[4]]);
});
});
describe("trieKeys using multiple-values in projections", function () {
var M = r.union(r.compilePattern(Immutable.Set(['A']), [1, 2]),
r.compilePattern(Immutable.Set(['C']), [1, 3]),
r.compilePattern(Immutable.Set(['B']), [3, 4]));
var proj = r.compileProjection([r._$, r._$]);
var M2 = r.project(M, proj);
it("should be able to extract ordinary values", function () {
checkTrieKeys(r.trieKeys(M2), [[1,2],[1,3],[3,4]]);
});
it("should be able to be reprojected as a sequence of more than one value", function () {
checkTrieKeys(r.trieKeys(r.project(M2, r.compileProjection(r._$, r._$))),
[[1,2],[1,3],[3,4]]);
});
it("should be convertible into objects with $-indexed fields", function () {
expect(r.trieKeysToObjects(r.trieKeys(M2), proj))
.to.eql([{'$0': 3, '$1': 4}, {'$0': 1, '$1': 2}, {'$0': 1, '$1': 3}]);
expect(r.projectObjects(M, proj))
.to.eql([{'$0': 3, '$1': 4}, {'$0': 1, '$1': 2}, {'$0': 1, '$1': 3}]);
});
});
describe("trieKeys using multiple-values in projections, with names", function () {
var M = r.union(r.compilePattern(Immutable.Set(['A']), [1, 2]),
r.compilePattern(Immutable.Set(['C']), [1, 3]),
r.compilePattern(Immutable.Set(['B']), [3, 4]));
it("should yield named fields", function () {
expect(r.projectObjects(M, r.compileProjection([r._$("fst"), r._$("snd")])))
.to.eql([{'fst': 3, 'snd': 4}, {'fst': 1, 'snd': 2}, {'fst': 1, 'snd': 3}]);
});
it("should yield numbered fields where names are missing", function () {
expect(r.projectObjects(M, r.compileProjection([r._$, r._$("snd")])))
.to.eql([{'$0': 3, 'snd': 4}, {'$0': 1, 'snd': 2}, {'$0': 1, 'snd': 3}]);
expect(r.projectObjects(M, r.compileProjection([r._$("fst"), r._$])))
.to.eql([{'fst': 3, '$1': 4}, {'fst': 1, '$1': 2}, {'fst': 1, '$1': 3}]);
});
});
describe("complex erasure", function () {
var A = r.compilePattern(Immutable.Set(['A']), r.__);
var B = r.union(r.compilePattern(Immutable.Set(['B']), [[[["foo"]]]]),
r.compilePattern(Immutable.Set(['B']), [[[["bar"]]]]));
describe("after a union", function () {
var R0 = r.union(A, B);
var R1a = r.subtract(R0, B);
var R1b = r.subtract(R0, A);
it("should yield the other parts of the union", function () {
expect(Immutable.is(R1a, A)).to.be(true);
expect(Immutable.is(R1b, B)).to.be(true);
});
});
});
describe("embedding tries in patterns", function () {
var M1a =
r.compilePattern(Immutable.Set(['A']),
[1, r.embeddedTrie(r.compilePattern(Immutable.Set(['B']), [2, 3])), 4]);
var M1b =
r.compilePattern(Immutable.Set(['A']), [1, [2, 3], 4]);
var M2a =
r.compilePattern(Immutable.Set(['A']),
[r.embeddedTrie(r.compilePattern(Immutable.Set(['B']), [1, 2])),
r.embeddedTrie(r.compilePattern(Immutable.Set(['C']), [3, 4]))]);
var M2b =
r.compilePattern(Immutable.Set(['A']), [[1, 2], [3, 4]]);
it("should yield tries equivalent to the original patterns", function () {
expect(Immutable.is(M1a, M1b)).to.be(true);
expect(Immutable.is(M2a, M2b)).to.be(true);
});
});
describe("calls to matchPattern", function () {
it("should yield appropriately-named/-numbered fields", function () {
expect(r.matchPattern([1, 2, 3], [r.__, 2, r._$])).to.eql({'$0': 3, 'length': 1});
expect(r.matchPattern([1, 2, 3], [r.__, 2, r._$("three")])).to.eql({'three': 3, 'length': 1});
expect(r.matchPattern([1, 2, 3], [r._$, 2, r._$("three")]))
.to.eql({'$0': 1, 'three': 3, 'length': 2});
expect(r.matchPattern([1, 2, 3], [r._$("one"), 2, r._$]))
.to.eql({'one': 1, '$1': 3, 'length': 2});
expect(r.matchPattern([1, 2, 3], [r._$("one"), 2, r._$("three")]))
.to.eql({'one': 1, 'three': 3, 'length': 2});
});
it("should fail on value mismatch", function () {
expect(r.matchPattern([1, 2, 3], [r.__, 999, r._$("three")])).to.be(null);
});
it("should fail on array length mismatch", function () {
expect(r.matchPattern([1, 2, 3], [r.__, 2, r._$("three"), 4])).to.be(null);
});
it("matches substructure", function () {
expect(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$(null, [2, r.__]), r._$("three")]))
.to.eql({ one: 1, '$1': [ 2, 999 ], three: 3, length: 3 });
expect(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$("two", [2, r.__]), r._$("three")]))
.to.eql({ one: 1, two: [ 2, 999 ], three: 3, length: 3 });
expect(r.matchPattern([1, [999, 2], 3], [r._$("one"), r._$(null, [2, r.__]), r._$("three")]))
.to.be(null);
expect(r.matchPattern([1, [999, 2], 3], [r._$("one"), r._$("two", [2, r.__]), r._$("three")]))
.to.be(null);
});
it("matches nested captures", function () {
expect(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$(null, [2, r._$]), r._$("three")]))
.to.eql({ one: 1, '$2': 999, '$1': [ 2, 999 ], three: 3, length: 4 });
expect(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$("two", [2, r._$]), r._$("three")]))
.to.eql({ one: 1, '$2': 999, two: [ 2, 999 ], three: 3, length: 4 });
});
});
describe("Projection with no captures", function () {
it("should yield the empty sequence when there's a match", function () {
var emptySequence = [' >{["A"]}'];
checkPrettyTrie(r.project(r.compilePattern(Immutable.Set(['A']), ["X", r.__]),
r.compileProjection(r.__)),
emptySequence);
checkPrettyTrie(r.project(r.compilePattern(Immutable.Set(['A']), ["X", r.__]),
r.compileProjection([r.__, r.__])),
emptySequence);
checkPrettyTrie(r.project(r.compilePattern(Immutable.Set(['A']), ["X", r.__]),
r.compileProjection(["X", r.__])),
emptySequence);
});
it("should yield the empty trie when there's no match", function () {
expect(r.project(r.compilePattern(Immutable.Set(['A']), ["X", r.__]),
r.compileProjection(["Y", r.__]))).to.be(r.emptyTrie);
});
it("should yield nonempty sequences when there are captures after all", function () {
checkPrettyTrie(r.project(r.compilePattern(Immutable.Set(['A']), ["X", r.__]),
r.compileProjection([r.__, r._$])),
[' ★ >{["A"]}']);
checkPrettyTrie(r.project(r.compilePattern(Immutable.Set(['A']), ["X", r.__]),
r.compileProjection([r._$, r._$])),
[' "X" ★ >{["A"]}']);
});
});

4
js/third-party/jquery-2.2.0.min.js vendored Normal file

File diff suppressed because one or more lines are too long