Switch to Preserves for Record and Bytes

This commit is contained in:
Tony Garnock-Jones 2018-11-15 23:24:58 +00:00
parent ea907c5aee
commit 44c1d425b3
20 changed files with 144 additions and 291 deletions

View File

@ -21,6 +21,7 @@
"nyc": "^13.1.0" "nyc": "^13.1.0"
}, },
"dependencies": { "dependencies": {
"immutable": "^3.8.2" "immutable": "^3.8.2",
"preserves": "^0.0.2"
} }
} }

View File

@ -17,13 +17,10 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
var Struct = require('./struct.js'); var { Record } = require('preserves');
function Seal(contents) { function Seal(contents) {
if (this === void 0) { if (!(this instanceof Seal)) return new Seal(contents);
return new Seal(contents);
}
this.contents = contents; this.contents = contents;
} }
@ -35,10 +32,12 @@ Seal.prototype.toJSON = function () {
return { '@seal': 0 }; return { '@seal': 0 };
}; };
module.exports.Discard = Struct.makeConstructor('discard', []); module.exports.Discard = Record.makeConstructor('discard', []);
module.exports.Capture = Struct.makeConstructor('capture', ['specification']); module.exports.Capture = Record.makeConstructor('capture', ['specification']);
module.exports.Observe = Struct.makeConstructor('observe', ['specification']); module.exports.Observe = Record.makeConstructor('observe', ['specification']);
module.exports.Inbound = Record.makeConstructor('inbound', ['assertion']);
module.exports.Outbound = Record.makeConstructor('outbound', ['assertion']);
module.exports.Instance = Record.makeConstructor('instance', ['uniqueId']);
module.exports.Seal = Seal; module.exports.Seal = Seal;
module.exports.Inbound = Struct.makeConstructor('inbound', ['assertion']);
module.exports.Outbound = Struct.makeConstructor('outbound', ['assertion']);
module.exports.Instance = Struct.makeConstructor('instance', ['uniqueId']);

View File

@ -20,6 +20,7 @@
// Bags and Deltas (which are Bags where item-counts can be negative). // Bags and Deltas (which are Bags where item-counts can be negative).
const Immutable = require("immutable"); const Immutable = require("immutable");
const { fromJS } = require("preserves");
const PRESENT_TO_ABSENT = -1; const PRESENT_TO_ABSENT = -1;
const ABSENT_TO_ABSENT = 0; const ABSENT_TO_ABSENT = 0;
@ -77,7 +78,7 @@ const Bag = Immutable.Map;
function fromSet(s) { function fromSet(s) {
return Bag().withMutations(function (b) { return Bag().withMutations(function (b) {
for (let v of Immutable.Set(s)) { for (let v of Immutable.Set(s)) {
b = b.set(Immutable.fromJS(v), 1); b = b.set(fromJS(v), 1);
} }
}); });
} }

View File

@ -18,7 +18,8 @@
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
const Immutable = require("immutable"); const Immutable = require("immutable");
const Struct = require('./struct.js'); const { fromJS } = require("preserves");
const Skeleton = require('./skeleton.js'); const Skeleton = require('./skeleton.js');
const $Special = require('./special.js'); const $Special = require('./special.js');
const Bag = require('./bag.js'); const Bag = require('./bag.js');
@ -343,14 +344,14 @@ Actor.prototype.assert = function (a) { this.pendingPatch().adjust(a, +1); };
Actor.prototype.retract = function (a) { this.pendingPatch().adjust(a, -1); }; Actor.prototype.retract = function (a) { this.pendingPatch().adjust(a, -1); };
Actor.prototype.adhocRetract = function (a) { Actor.prototype.adhocRetract = function (a) {
a = Immutable.fromJS(a); a = fromJS(a);
if (this.adhocAssertions.change(a, -1, true) === Bag.PRESENT_TO_ABSENT) { if (this.adhocAssertions.change(a, -1, true) === Bag.PRESENT_TO_ABSENT) {
this.retract(a); this.retract(a);
} }
}; };
Actor.prototype.adhocAssert = function (a) { Actor.prototype.adhocAssert = function (a) {
a = Immutable.fromJS(a); a = fromJS(a);
if (this.adhocAssertions.change(a, +1) === Bag.ABSENT_TO_PRESENT) { if (this.adhocAssertions.change(a, +1) === Bag.ABSENT_TO_PRESENT) {
this.assert(a); this.assert(a);
} }
@ -371,8 +372,10 @@ Patch.prototype.perform = function (ds, ac) {
}; };
Patch.prototype.adjust = function (a, count) { Patch.prototype.adjust = function (a, count) {
var _net; if (a !== void 0) {
({bag: this.changes, net: _net} = Bag.change(this.changes, Immutable.fromJS(a), count)); var _net;
({bag: this.changes, net: _net} = Bag.change(this.changes, fromJS(a), count));
}
}; };
function Message(body) { function Message(body) {
@ -381,7 +384,7 @@ function Message(body) {
Message.prototype.perform = function (ds, ac) { Message.prototype.perform = function (ds, ac) {
if (this.body !== void 0) { if (this.body !== void 0) {
ds.sendMessage(Immutable.fromJS(this.body)); ds.sendMessage(fromJS(this.body));
} }
}; };
@ -612,7 +615,7 @@ Endpoint.prototype._uninstall = function (ds, ac, emitPatches) {
Endpoint.prototype.refresh = function (ds, ac, facet) { Endpoint.prototype.refresh = function (ds, ac, facet) {
let [newAssertion, newHandler] = this.updateFun.call(facet.fields); let [newAssertion, newHandler] = this.updateFun.call(facet.fields);
newAssertion = Immutable.fromJS(newAssertion); if (newAssertion !== void 0) newAssertion = fromJS(newAssertion);
if (!Immutable.is(newAssertion, this.assertion)) { if (!Immutable.is(newAssertion, this.assertion)) {
this._uninstall(ds, ac, true); this._uninstall(ds, ac, true);
this._install(ds, ac, newAssertion, newHandler); this._install(ds, ac, newAssertion, newHandler);

View File

@ -19,6 +19,7 @@ Ground.prototype = new Dataspace(null);
Ground._resolved = Promise.resolve(); Ground._resolved = Promise.resolve();
Ground.laterCall = function (thunk) { Ground.laterCall = function (thunk) {
Ground._resolved.then(() => { Ground._resolved.then(() => {
Error.stackTraceLimit = 100;
try { try {
thunk(); thunk();
} catch (e) { } catch (e) {

View File

@ -17,7 +17,6 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
const Struct = require('./struct.js');
const Skeleton = require('./skeleton.js'); const Skeleton = require('./skeleton.js');
const RandomID = require('./randomid.js'); const RandomID = require('./randomid.js');
const Dataspace = require('./dataspace.js'); const Dataspace = require('./dataspace.js');
@ -25,11 +24,12 @@ const Ground = require('./ground.js');
const Assertions = require('./assertions.js'); const Assertions = require('./assertions.js');
const Relay = require('./relay.js'); const Relay = require('./relay.js');
Object.assign(module.exports, require("preserves"));
module.exports.Immutable = require('immutable'); module.exports.Immutable = require('immutable');
// ^ for use by import machinery in syntactic extensions // ^ for use by import machinery in syntactic extensions
module.exports.Bag = require("./bag.js"); module.exports.Bag = require("./bag.js");
module.exports.Struct = Struct;
module.exports.Skeleton = Skeleton; module.exports.Skeleton = Skeleton;
module.exports.RandomID = RandomID; module.exports.RandomID = RandomID;

View File

@ -47,18 +47,18 @@ NestedDataspace.prototype.sendMessage = function (m) {
NestedDataspace.prototype.endpointHook = function (facet, innerEp) { NestedDataspace.prototype.endpointHook = function (facet, innerEp) {
const innerDs = this; const innerDs = this;
Dataspace.prototype.endpointHook.call(this, facet, innerEp); Dataspace.prototype.endpointHook.call(this, facet, innerEp);
if (Observe.isClassOf(innerEp.assertion) && Inbound.isClassOf(innerEp.assertion[0])) { if (Observe.isClassOf(innerEp.assertion) && Inbound.isClassOf(innerEp.assertion.get(0))) {
// We know that innerEp.assertion is an Observe(Inbound(...)). // We know that innerEp.assertion is an Observe(Inbound(...)).
// Also, if innerEp.handler exists, it will be consonant with innerEp.assertion. // Also, if innerEp.handler exists, it will be consonant with innerEp.assertion.
// Beware of completely-constant patterns, which cause skeleton to be null! // Beware of completely-constant patterns, which cause skeleton to be null!
this.hookEndpointLifecycle(innerEp, this.outerFacet.addEndpoint(() => { this.hookEndpointLifecycle(innerEp, this.outerFacet.addEndpoint(() => {
const h = innerEp.handler; const h = innerEp.handler;
return [Observe(innerEp.assertion[0][0]), return [Observe(innerEp.assertion.get(0).get(0)),
h && (h.skeleton === null h && (h.skeleton === null
? { ? {
skeleton: null, skeleton: null,
constPaths: h.constPaths, constPaths: h.constPaths,
constVals: h.constVals.map((v) => v[0]), constVals: h.constVals.map((v) => v.get(0)),
capturePaths: h.capturePaths.map((p) => p.shift()), capturePaths: h.capturePaths.map((p) => p.shift()),
callback: function (evt, captures) { callback: function (evt, captures) {
h.callback.call(this, evt, captures); h.callback.call(this, evt, captures);
@ -85,13 +85,13 @@ NestedDataspace.prototype.adjustIndex = function (a, count) {
switch (net) { switch (net) {
case Bag.ABSENT_TO_PRESENT: case Bag.ABSENT_TO_PRESENT:
this.outerFacet.actor.pushScript(() => { this.outerFacet.actor.pushScript(() => {
this.outerFacet.actor.adhocAssert(a[0]); this.outerFacet.actor.adhocAssert(a.get(0));
}); });
this.outerFacet.actor.dataspace.start(); this.outerFacet.actor.dataspace.start();
break; break;
case Bag.PRESENT_TO_ABSENT: case Bag.PRESENT_TO_ABSENT:
this.outerFacet.actor.pushScript(() => { this.outerFacet.actor.pushScript(() => {
this.outerFacet.actor.adhocRetract(a[0]); this.outerFacet.actor.adhocRetract(a.get(0));
}); });
this.outerFacet.actor.dataspace.start(); this.outerFacet.actor.dataspace.start();
break; break;

View File

@ -18,7 +18,8 @@
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
const Immutable = require("immutable"); const Immutable = require("immutable");
const Struct = require('./struct.js'); const { Record } = require("preserves");
const $Special = require('./special.js'); const $Special = require('./special.js');
const Bag = require('./bag.js'); const Bag = require('./bag.js');
const { Capture, Discard } = require('./assertions.js'); const { Capture, Discard } = require('./assertions.js');
@ -66,8 +67,8 @@ function Handler(cachedCaptures) {
} }
function classOf(v) { function classOf(v) {
if (v instanceof Struct.Structure) { if (v instanceof Record) {
return v.meta; return v.getConstructorInfo();
} else if (v instanceof Immutable.List) { } else if (v instanceof Immutable.List) {
return v.size; return v.size;
} else { } else {
@ -104,7 +105,7 @@ Node.prototype.extend = function(skeleton) {
if (!nextNode) { if (!nextNode) {
nextNode = new Node(new Continuation( nextNode = new Node(new Continuation(
node.continuation.cachedAssertions.filter( node.continuation.cachedAssertions.filter(
(a) => classOf(projectPath(a, path)) === cls))); (a) => Immutable.is(classOf(projectPath(a, path)), cls))));
table = table.set(cls, nextNode); table = table.set(cls, nextNode);
node.edges = node.edges.set(selector, table); node.edges = node.edges.set(selector, table);
} }
@ -191,8 +192,7 @@ Node.prototype.modify = function(outerValue, m_cont, m_leaf, m_handler) {
while (i--) { mutable.pop(); } while (i--) { mutable.pop(); }
}); });
let nextValue = step(nextStack.first(), selector.index); let nextValue = step(nextStack.first(), selector.index);
let cls = classOf(nextValue); let nextNode = table.get(classOf(nextValue), false);
let nextNode = table.get(cls, false);
if (nextNode) { if (nextNode) {
walkNode(nextNode, nextStack.push(nextValue)); walkNode(nextNode, nextStack.push(nextValue));
} }
@ -279,6 +279,15 @@ function analyzeAssertion(a) {
let capturePaths = Immutable.List(); let capturePaths = Immutable.List();
function walk(path, a) { function walk(path, a) {
if (Capture.isClassOf(a)) {
capturePaths = capturePaths.push(path);
return walk(path, a.get(0));
}
if (Discard.isClassOf(a)) {
return null;
}
let cls = classOf(a); let cls = classOf(a);
if (cls !== null) { if (cls !== null) {
let arity = (typeof cls === 'number') ? cls : cls.arity; let arity = (typeof cls === 'number') ? cls : cls.arity;
@ -287,18 +296,11 @@ function analyzeAssertion(a) {
result.push(walk(path.push(i), step(a, i))); result.push(walk(path.push(i), step(a, i)));
} }
return result; return result;
} else {
if (Capture.isClassOf(a)) {
capturePaths = capturePaths.push(path);
return walk(path, a.get(0));
} else if (Discard.isClassOf(a)) {
return null;
} else {
constPaths = constPaths.push(path);
constVals = constVals.push(a);
return null;
}
} }
constPaths = constPaths.push(path);
constVals = constVals.push(a);
return null;
} }
let skeleton = walk(Immutable.List(), a); let skeleton = walk(Immutable.List(), a);

View File

@ -1,169 +0,0 @@
"use strict";
//---------------------------------------------------------------------------
// @syndicate-lang/core, an implementation of Syndicate dataspaces for JS.
// Copyright (C) 2016-2018 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//---------------------------------------------------------------------------
// "Structures": Simple named-tuple-like records.
const Immutable = require("immutable");
const $Special = require('./special.js');
function StructureType(label, arity) {
this.label = label;
this.arity = arity;
var self = this;
this.ctor = function () {
return self.instantiate(Array.prototype.slice.call(arguments));
};
this.ctor.meta = this;
this.ctor.isClassOf = function (v) { return self.isClassOf(v); };
}
function makeConstructor(label, fieldNames) {
return new StructureType(label, fieldNames.length).ctor;
}
StructureType.prototype.equals = function (other) {
if (!(other instanceof StructureType)) return false;
return this.arity === other.arity && this.label === other.label;
};
StructureType.prototype.hashCode = function () {
return Immutable.List([this.label, this.arity]).hashCode();
};
StructureType.prototype.instantiate = function (fields) {
return new Structure(this, fields);
};
StructureType.prototype.isClassOf = function (v) {
return v && (v instanceof Structure) && (v.meta.equals(this));
};
function Structure(meta, fields) {
if (!isStructureType(meta)) {
throw new Error("Structure: requires structure type");
}
if (fields.length !== meta.arity) {
throw new Error("Structure: cannot instantiate meta "+JSON.stringify(meta.label)+
" expecting "+meta.arity+" fields with "+fields.length+" fields");
}
fields = fields.slice(0);
this.meta = meta;
this.length = meta.arity;
this.fields = fields;
for (var i = 0; i < fields.length; i++) {
this[i] = fields[i] = Immutable.fromJS(fields[i]);
if (this[i] === void 0) {
throw new Error("Structure: cannot contain undefined value at field " + i);
}
}
}
Structure.prototype.clone = function () {
return new Structure(this.meta, this.fields);
};
Structure.prototype.get = function (index) {
return this[index];
};
Structure.prototype.set = function (index, value) {
var s = this.clone();
s[index] = s.fields[index] = value;
return s;
};
Structure.prototype.equals = function (other) {
if (!other) return false;
if (!(other instanceof Structure)) return false;
if (!other.meta.equals(this.meta)) return false;
for (let i = 0; i < this.length; i++) {
const a = this[i];
const b = other[i];
if (a === b) continue;
if (!a || typeof a.equals !== 'function') return false;
if (!a.equals(b)) return false;
}
return true;
};
Structure.prototype.hashCode = function () {
return Immutable.List(this.fields).unshift(this.meta).hashCode();
};
Structure.prototype.toString = function () {
let b = this.meta.label + "(";
let needComma = false;
for (let v of this.fields) {
if (needComma) b = b + ", ";
needComma = true;
b = b + JSON.stringify(v);
}
return b + ")";
};
function reviveStructs(j) {
if (Array.isArray(j)) {
return j.map(reviveStructs);
}
if ((j !== null) && typeof j === 'object') {
if ((typeof j['@type'] === 'string') && Array.isArray(j['fields'])) {
return (new StructureType(j['@type'], j['fields'].length)).instantiate(j['fields']);
} else {
for (var k in j) {
if (Object.prototype.hasOwnProperty.call(j, k)) {
j[k] = reviveStructs(j[k]);
}
}
return j;
}
}
return j;
}
function reviver(k, v) {
if (k === '') {
return reviveStructs(v);
}
return v;
};
Structure.prototype.toJSON = function () {
return { '@type': this.meta.label, 'fields': this.fields };
};
function isStructureType(v) {
return v && (v instanceof StructureType);
}
function isStructure(v) {
return v && (v instanceof Structure);
}
///////////////////////////////////////////////////////////////////////////
module.exports.StructureType = StructureType;
module.exports.makeConstructor = makeConstructor;
module.exports.Structure = Structure;
module.exports.reviveStructs = reviveStructs;
module.exports.reviver = reviver;
module.exports.isStructureType = isStructureType;
module.exports.isStructure = isStructure;

View File

@ -24,28 +24,27 @@ chai.use(require('chai-immutable'));
const Immutable = require('immutable'); const Immutable = require('immutable');
const Syndicate = require('../src/index.js'); const Syndicate = require('../src/index.js');
const Skeleton = Syndicate.Skeleton; const { Skeleton, Dataspace, Observe, Capture, Discard } = Syndicate;
const Dataspace = Syndicate.Dataspace;
const Struct = Syndicate.Struct;
const __ = Syndicate.__;
const _$ = Syndicate._$;
describe('dataspace', () => { describe('dataspace', () => {
it('should boot and run', () => { it('should boot and run', () => {
// TODO: convert this into even a rudimentary somewhat-real test case // TODO: convert this into even a rudimentary somewhat-real test case
// (change console.log into gathering a trace) // (change console.log into gathering a trace)
let ds = new Dataspace(null, () => { let ds = new Dataspace(() => {
// console.log('boot'); // console.log('boot');
Dataspace.currentFacet().addEndpoint(() => { Dataspace.currentFacet().addEndpoint(() => {
let handler = Skeleton.analyzeAssertion(_$); let handler = Skeleton.analyzeAssertion(Capture(Discard()));
handler.callback = (evt, vs) => { handler.callback = (evt, vs) => {
if (Syndicate.Observe.isClassOf(vs.get(0))) { if (Observe.isClassOf(vs.get(0))) {
// console.log('OBSERVATION EVENT', evt, vs, vs.get(0).get(0) === _$); // console.log('OBSERVATION EVENT',
// evt,
// vs,
// Immutable.is(vs.get(0).get(0), Capture(Discard())));
} else { } else {
// console.log('EVENT', evt, vs); // console.log('EVENT', evt, vs);
} }
}; };
return [Syndicate.Observe(_$), handler]; return [Observe(Capture(Discard())), handler];
}); });
Dataspace.deferTurn(() => { Dataspace.deferTurn(() => {
// console.log('after defer'); // console.log('after defer');

View File

@ -24,12 +24,12 @@ chai.use(require('chai-immutable'));
const Immutable = require('immutable'); const Immutable = require('immutable');
const Syndicate = require('../src/index.js'); const Syndicate = require('../src/index.js');
const Skeleton = Syndicate.Skeleton; const { Seal, Skeleton, Capture, Discard, Record } = Syndicate;
const Struct = Syndicate.Struct;
const __ = Syndicate.__;
const _$ = Syndicate._$;
const Event = Struct.makeConstructor('Event', ['label', 'type', 'values']); const __ = Discard();
const _$ = Capture(Discard());
const Event = Record.makeConstructor('Event', ['label', 'type', 'values']);
function eventCallback(traceHolder, label) { function eventCallback(traceHolder, label) {
return (e, vs) => { traceHolder.push(Event(label, e, vs)) }; return (e, vs) => { traceHolder.push(Event(label, e, vs)) };
@ -51,21 +51,21 @@ function _analyzeAssertion(a) {
describe('skeleton', () => { describe('skeleton', () => {
const A = Struct.makeConstructor('A', ['x', 'y']); const A = Record.makeConstructor('A', ['x', 'y']);
const B = Struct.makeConstructor('B', ['v']); const B = Record.makeConstructor('B', ['v']);
const C = Struct.makeConstructor('C', ['v']); const C = Record.makeConstructor('C', ['v']);
describe('pattern analysis', () => { describe('pattern analysis', () => {
it('should handle leaf captures', () => { it('should handle leaf captures', () => {
expect(Immutable.fromJS(_analyzeAssertion(A(B(_$), _$)))) expect(Immutable.fromJS(_analyzeAssertion(A(B(_$), _$))))
.to.equal(Immutable.fromJS({skeleton: [A.meta, [B.meta, null], null], .to.equal(Immutable.fromJS({skeleton: [A.constructorInfo, [B.constructorInfo, null], null],
constPaths: Immutable.fromJS([]), constPaths: Immutable.fromJS([]),
constVals: Immutable.fromJS([]), constVals: Immutable.fromJS([]),
capturePaths: Immutable.fromJS([[0, 0], [1]])})); capturePaths: Immutable.fromJS([[0, 0], [1]])}));
}); });
it('should handle atomic constants', () => { it('should handle atomic constants', () => {
expect(Immutable.fromJS(_analyzeAssertion(A(B("x"), _$)))) expect(Immutable.fromJS(_analyzeAssertion(A(B("x"), _$))))
.to.equal(Immutable.fromJS({skeleton: [A.meta, [B.meta, null], null], .to.equal(Immutable.fromJS({skeleton: [A.constructorInfo, [B.constructorInfo, null], null],
constPaths: Immutable.fromJS([[0, 0]]), constPaths: Immutable.fromJS([[0, 0]]),
constVals: Immutable.fromJS(["x"]), constVals: Immutable.fromJS(["x"]),
capturePaths: Immutable.fromJS([[1]])})); capturePaths: Immutable.fromJS([[1]])}));
@ -77,12 +77,15 @@ describe('skeleton', () => {
// will end up being complex at runtime. We can't properly test // will end up being complex at runtime. We can't properly test
// that situation without the static analysis half of the code. // that situation without the static analysis half of the code.
// TODO later. // TODO later.
let complexPlaceholder = new Object(); const complexPlaceholder = new Object();
expect(Immutable.fromJS(_analyzeAssertion(A(complexPlaceholder, C(_$))))) const analysis = Immutable.fromJS(_analyzeAssertion(A(complexPlaceholder, C(_$))));
.to.equal(Immutable.fromJS({skeleton: [A.meta, null, [C.meta, null]], const expected = Immutable.fromJS({
constPaths: Immutable.fromJS([[0]]), skeleton: [A.constructorInfo, null, [C.constructorInfo, null]],
constVals: Immutable.fromJS([complexPlaceholder]), constPaths: Immutable.fromJS([[0]]),
capturePaths: Immutable.fromJS([[1, 0]])})); constVals: Immutable.List([complexPlaceholder]),
capturePaths: Immutable.fromJS([[1, 0]]),
});
expect(analysis).to.equal(expected);
}); });
it('should handle complex constants (2)', () => { it('should handle complex constants (2)', () => {
// Marker: (***) // Marker: (***)
@ -91,8 +94,10 @@ describe('skeleton', () => {
// will end up being complex at runtime. We can't properly test // will end up being complex at runtime. We can't properly test
// that situation without the static analysis half of the code. // that situation without the static analysis half of the code.
// TODO later. // TODO later.
expect(Immutable.fromJS(_analyzeAssertion(A(B(B("y")), _$("rhs", C(__)))))) expect(Immutable.fromJS(_analyzeAssertion(A(B(B("y")), Capture(C(__))))))
.to.equal(Immutable.fromJS({skeleton: [A.meta, [B.meta, [B.meta, null]], [C.meta, null]], .to.equal(Immutable.fromJS({skeleton: [A.constructorInfo,
[B.constructorInfo, [B.constructorInfo, null]],
[C.constructorInfo, null]],
constPaths: Immutable.fromJS([[0, 0, 0]]), constPaths: Immutable.fromJS([[0, 0, 0]]),
constVals: Immutable.fromJS(["y"]), constVals: Immutable.fromJS(["y"]),
capturePaths: Immutable.fromJS([[1]])})); capturePaths: Immutable.fromJS([[1]])}));
@ -117,13 +122,15 @@ describe('skeleton', () => {
let trace = skeletonTrace((i, traceHolder) => { let trace = skeletonTrace((i, traceHolder) => {
i.addHandler(_analyzeAssertion(A(B(_$), _$)), eventCallback(traceHolder, "AB")); i.addHandler(_analyzeAssertion(A(B(_$), _$)), eventCallback(traceHolder, "AB"));
i.addHandler(_analyzeAssertion(A(B("x"), _$)), eventCallback(traceHolder, "ABx")); i.addHandler(_analyzeAssertion(A(B("x"), _$)), eventCallback(traceHolder, "ABx"));
let complexConstantPattern1 = {skeleton: [A.meta, null, [C.meta, null]], let complexConstantPattern1 = {skeleton: [A.constructorInfo, null, [C.constructorInfo, null]],
constPaths: Immutable.fromJS([[0]]), constPaths: Immutable.fromJS([[0]]),
constVals: Immutable.fromJS([B("y")]), constVals: Immutable.fromJS([B("y")]),
capturePaths: Immutable.fromJS([[1, 0]])}; capturePaths: Immutable.fromJS([[1, 0]])};
// ^ See comment in 'should handle complex constants (1)' test above (marked (***)). // ^ See comment in 'should handle complex constants (1)' test above (marked (***)).
i.addHandler(complexConstantPattern1, eventCallback(traceHolder, "AByC")); i.addHandler(complexConstantPattern1, eventCallback(traceHolder, "AByC"));
let complexConstantPattern2 = {skeleton: [A.meta, [B.meta, null], [C.meta, null]], let complexConstantPattern2 = {skeleton: [A.constructorInfo,
[B.constructorInfo, null],
[C.constructorInfo, null]],
constPaths: Immutable.fromJS([[0, 0]]), constPaths: Immutable.fromJS([[0, 0]]),
constVals: Immutable.fromJS([B("y")]), constVals: Immutable.fromJS([B("y")]),
capturePaths: Immutable.fromJS([[1]])}; capturePaths: Immutable.fromJS([[1]])};
@ -135,7 +142,7 @@ describe('skeleton', () => {
i.addAssertion(Immutable.fromJS(A(B("z"),C(3)))); i.addAssertion(Immutable.fromJS(A(B("z"),C(3))));
}); });
// trace.forEach((e) => { console.log(e.toString()) }); // trace.forEach((e) => { console.log(e) });
expect(trace) expect(trace)
.to.equal(Immutable.List([ .to.equal(Immutable.List([
@ -236,7 +243,7 @@ describe('skeleton', () => {
expect(trace.size).to.equal(8); expect(trace.size).to.equal(8);
}); });
it('should have a correct 3-EVENT subtrace', () => { it('should have a correct 3-EVENT subtrace', () => {
expect(trace.filter((e) => { return e[0] === "3-EVENT"; })) expect(trace.filter((e) => { return e.get(0) === "3-EVENT"; }))
.to.equal(Immutable.List([ .to.equal(Immutable.List([
Event("3-EVENT", Skeleton.EVENT_ADDED, [123, 234]), Event("3-EVENT", Skeleton.EVENT_ADDED, [123, 234]),
Event("3-EVENT", Skeleton.EVENT_ADDED, [999, 999]), Event("3-EVENT", Skeleton.EVENT_ADDED, [999, 999]),
@ -245,7 +252,7 @@ describe('skeleton', () => {
Event("3-EVENT", Skeleton.EVENT_REMOVED, [123, 234])])); Event("3-EVENT", Skeleton.EVENT_REMOVED, [123, 234])]));
}); });
it('should have a correct 2-EVENT subtrace', () => { it('should have a correct 2-EVENT subtrace', () => {
expect(trace.filter((e) => { return e[0] === "2-EVENT"; })) expect(trace.filter((e) => { return e.get(0) === "2-EVENT"; }))
.to.equal(Immutable.List([ .to.equal(Immutable.List([
Event("2-EVENT", Skeleton.EVENT_ADDED, []), Event("2-EVENT", Skeleton.EVENT_ADDED, []),
Event("2-EVENT", Skeleton.EVENT_MESSAGE, []), Event("2-EVENT", Skeleton.EVENT_MESSAGE, []),

View File

@ -57,17 +57,18 @@ export function htmlToString(j) {
function walk(j) { function walk(j) {
if (htmlTag.isClassOf(j)) { if (htmlTag.isClassOf(j)) {
pieces.push('<', j[0]); pieces.push('<', j.get(0));
j[1].forEach((p) => pieces.push(' ', escapeHtml(p[0]), '="', escapeHtml(p[1]), '"')); j.get(1).forEach(
(p) => pieces.push(' ', escapeHtml(p.get(0)), '="', escapeHtml(p.get(1)), '"'));
pieces.push('>'); pieces.push('>');
j[2].forEach(walk); j.get(2).forEach(walk);
if (!(j[0] in emptyHtmlElements)) { if (!(j.get(0) in emptyHtmlElements)) {
pieces.push('</', j[0], '>'); pieces.push('</', j.get(0), '>');
} }
} else if (htmlFragment.isClassOf(j)) { } else if (htmlFragment.isClassOf(j)) {
j[0].forEach(walk); j.get(0).forEach(walk);
} else if (htmlLiteral.isClassOf(j)) { } else if (htmlLiteral.isClassOf(j)) {
pieces.push(j[0]); pieces.push(j.get(0));
} else if (typeof j === 'object' && j && typeof j[Symbol.iterator] === 'function') { } else if (typeof j === 'object' && j && typeof j[Symbol.iterator] === 'function') {
for (let k of j) { walk(k); } for (let k of j) { walk(k); }
} else { } else {

View File

@ -16,7 +16,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
import { genUuid, Seal, Capture, Observe, Dataspace, currentFacet } from "@syndicate-lang/core"; import { genUuid, Seal, Capture, Observe, Dataspace, currentFacet, Bytes } from "@syndicate-lang/core";
import { parse as parseUrl } from "url"; import { parse as parseUrl } from "url";
const http = require('http'); const http = require('http');
@ -40,14 +40,14 @@ Object.assign(module.exports, {
Response, DataOut, Response, DataOut,
}); });
spawn named 'HttpServerFactory' { spawn named 'driver/HttpServerFactory' {
during Observe(Request(_, HttpServer($h, $p), _, _, _, _)) assert HttpServer(h, p); during Observe(Request(_, HttpServer($h, $p), _, _, _, _)) assert HttpServer(h, p);
during Observe(Request(_, HttpsServer($h, $p, $o), _, _, _, _)) assert HttpsServer(h, p, o); during Observe(Request(_, HttpsServer($h, $p, $o), _, _, _, _)) assert HttpsServer(h, p, o);
during HttpServer($host, $port) spawn named ['HttpServer', host, port] { during HttpServer($host, $port) spawn named ['driver/HttpServer', host, port] {
_server.call(this, host, port, null); _server.call(this, host, port, null);
} }
during HttpsServer($host, $port, $options) spawn named ['HttpsServer', host, port] { during HttpsServer($host, $port, $options) spawn named ['driver/HttpsServer', host, port] {
_server.call(this, host, port, options); _server.call(this, host, port, options);
} }
} }
@ -179,7 +179,7 @@ function _server(host, port, httpsOptions) {
facet.stop(); facet.stop();
} }
on message DataOut(id, $chunk) { on message DataOut(id, $chunk) {
res.write(chunk); res.write(Bytes.toIO(chunk));
} }
} }
} else { } else {
@ -218,12 +218,12 @@ function _server(host, port, httpsOptions) {
on asserted Observe(DataIn(id, _)) { on asserted Observe(DataIn(id, _)) {
ws.on('message', Dataspace.wrapExternal((message) => { ws.on('message', Dataspace.wrapExternal((message) => {
^ DataIn(id, message); ^ DataIn(id, Bytes.fromIO(message));
})); }));
} }
on message DataOut(id, $message) { on message DataOut(id, $message) {
ws.send(message); ws.send(Bytes.toIO(message));
} }
stop on retracted Observe(WebSocket(_, server, pathPattern, _)); stop on retracted Observe(WebSocket(_, server, pathPattern, _));

View File

@ -16,7 +16,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
import { currentFacet, Observe, Dataspace, genUuid } from "@syndicate-lang/core"; import { currentFacet, Observe, Dataspace, genUuid, Bytes } from "@syndicate-lang/core";
const net = require('net'); const net = require('net');
const { sleep } = activate require("@syndicate-lang/driver-timer"); const { sleep } = activate require("@syndicate-lang/driver-timer");
@ -38,14 +38,14 @@ export {
TcpAddress, TcpListener, TcpAddress, TcpListener,
}; };
spawn named 'TcpDriver' { spawn named 'driver/TcpDriver' {
during Observe(TcpConnection(_, TcpListener($port))) spawn named ['TcpListener', port] { during Observe(TcpConnection(_, TcpListener($port))) spawn named ['driver/TcpListener', port] {
let finish = Dataspace.backgroundTask(); let finish = Dataspace.backgroundTask();
on stop finish(); on stop finish();
let server = net.createServer(Dataspace.wrapExternal((socket) => { let server = net.createServer(Dataspace.wrapExternal((socket) => {
let id = genUuid('tcp' + port); let id = genUuid('tcp' + port);
spawn named ['TcpInbound', id] { spawn named ['driver/TcpInbound', id] {
assert TcpConnection(id, TcpListener(port)); assert TcpConnection(id, TcpListener(port));
on asserted TcpAccepted(id) _connectionCommon.call(this, currentFacet(), id, socket, true); on asserted TcpAccepted(id) _connectionCommon.call(this, currentFacet(), id, socket, true);
stop on retracted TcpAccepted(id); stop on retracted TcpAccepted(id);
@ -57,7 +57,8 @@ spawn named 'TcpDriver' {
on stop try { server.close() } catch (e) { console.error(e); } on stop try { server.close() } catch (e) { console.error(e); }
} }
during TcpConnection($id, TcpAddress($host, $port)) spawn named ['TcpOutbound', id, host, port] { during TcpConnection($id, TcpAddress($host, $port))
spawn named ['driver/TcpOutbound', id, host, port] {
let finish = Dataspace.backgroundTask(); let finish = Dataspace.backgroundTask();
on stop finish(); on stop finish();
@ -69,9 +70,9 @@ spawn named 'TcpDriver' {
} }
} }
during Observe(LineIn($id, _)) spawn named ['TcpLineReader', id] { during Observe(LineIn($id, _)) spawn named ['driver/TcpLineReader', id] {
field this.buffer = Buffer.alloc(0); field this.buffer = Bytes();
on message DataIn(id, $data) this.buffer = Buffer.concat([this.buffer, data]); on message DataIn(id, $data) this.buffer = Bytes.concat([this.buffer, data]);
dataflow { dataflow {
const pos = this.buffer.indexOf(10); const pos = this.buffer.indexOf(10);
if (pos !== -1) { if (pos !== -1) {
@ -107,12 +108,12 @@ function _connectionCommon(rootFacet, id, socket, established) {
on start react stop on asserted Observe(DataIn(id, _)) { on start react stop on asserted Observe(DataIn(id, _)) {
socket.on('data', Dataspace.wrapExternal((data) => { socket.on('data', Dataspace.wrapExternal((data) => {
^ DataIn(id, data); ^ DataIn(id, Bytes.fromIO(data));
})); }));
} }
on message DataOut(id, $data) { on message DataOut(id, $data) {
socket.write(data); socket.write(Bytes.toIO(data));
} }
} }
} }

View File

@ -16,7 +16,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
import { currentFacet, Observe, Dataspace } from "@syndicate-lang/core"; import { currentFacet, Observe, Dataspace, Bytes } from "@syndicate-lang/core";
import { createSocket } from "dgram"; import { createSocket } from "dgram";
const { sleep } = activate require("@syndicate-lang/driver-timer"); const { sleep } = activate require("@syndicate-lang/driver-timer");
@ -70,7 +70,7 @@ function _socket(addr, port) {
socket.on('listening', Dataspace.wrapExternal(() => { this.connected = true; })); socket.on('listening', Dataspace.wrapExternal(() => { this.connected = true; }));
socket.on('message', Dataspace.wrapExternal((message, rinfo) => { socket.on('message', Dataspace.wrapExternal((message, rinfo) => {
^ UdpPacket(UdpPeer(rinfo.address, rinfo.port), addr, message); ^ UdpPacket(UdpPeer(rinfo.address, rinfo.port), addr, Bytes.fromIO(message));
})); }));
}; };
@ -88,6 +88,7 @@ function _socket(addr, port) {
assert addr when (this.connected); assert addr when (this.connected);
on message UdpPacket(addr, UdpPeer($host, $port), $payload) { on message UdpPacket(addr, UdpPeer($host, $port), $payload) {
payload = Bytes.toIO(payload);
socket.send(payload, 0, payload.length, port, host, Dataspace.wrapExternal((err) => { socket.send(payload, 0, payload.length, port, host, Dataspace.wrapExternal((err) => {
if (err) { if (err) {
console.error(err); console.error(err);

View File

@ -16,7 +16,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
import { currentFacet, Observe, Dataspace } from "@syndicate-lang/core"; import { currentFacet, Bytes, Observe, Dataspace } from "@syndicate-lang/core";
const { sleep } = activate require("@syndicate-lang/driver-timer"); const { sleep } = activate require("@syndicate-lang/driver-timer");
const _WebSocket = require('isomorphic-ws'); const _WebSocket = require('isomorphic-ws');
@ -52,7 +52,7 @@ spawn named 'WebSocketFactory' {
ws.onopen = Dataspace.wrapExternal(() => { this.connected = true; }); ws.onopen = Dataspace.wrapExternal(() => { this.connected = true; });
ws.onclose = Dataspace.wrapExternal(() => { if (this.connected) { connect(); }}); ws.onclose = Dataspace.wrapExternal(() => { if (this.connected) { connect(); }});
ws.onmessage = Dataspace.wrapExternal((data) => { ^ DataIn(id, data.data); }); ws.onmessage = Dataspace.wrapExternal((data) => { ^ DataIn(id, Bytes.fromIO(data.data)); });
}; };
const disconnect = () => { const disconnect = () => {
@ -70,7 +70,7 @@ spawn named 'WebSocketFactory' {
on message DataOut(id, $data) { on message DataOut(id, $data) {
if (this.connected) { if (this.connected) {
ws.send(data); ws.send(Bytes.toIO(data));
} }
} }
} }

View File

@ -16,7 +16,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
import { currentFacet, genUuid } from "@syndicate-lang/core"; import { currentFacet, genUuid, Bytes } from "@syndicate-lang/core";
const WS = activate require("@syndicate-lang/driver-websocket"); const WS = activate require("@syndicate-lang/driver-websocket");
const { PeriodicTick } = activate require("@syndicate-lang/driver-timer"); const { PeriodicTick } = activate require("@syndicate-lang/driver-timer");
@ -34,7 +34,7 @@ spawn named 'demo' {
} }
on message PeriodicTick(1000) { on message PeriodicTick(1000) {
^ WS.DataOut(wsId, genUuid('timestamp')); ^ WS.DataOut(wsId, Bytes.from(genUuid('timestamp')));
} }
} }
} }

View File

@ -35,7 +35,7 @@ spawn named 'multicast_demo' {
assert U.UdpMulticastLoopback(HANDLE, true); assert U.UdpMulticastLoopback(HANDLE, true);
on message U.UdpPacket(U.UdpPeer($host, $port), HANDLE, $body) { on message U.UdpPacket(U.UdpPeer($host, $port), HANDLE, $body) {
console.log('Got', body.toString(), 'from', host, port); console.log('Got', body, 'from', host, port);
} }
on message PeriodicTick(2000) { on message PeriodicTick(2000) {

View File

@ -74,6 +74,15 @@ function counter() {
return id; return id;
} }
spawn named 'serverLogger' {
on asserted Http.Request(_, server, $method, $path, $query, $req) {
console.log(method, path.toJS(), query.toJS());
}
on asserted Http.WebSocket(_, server, $path, $query) {
console.log(path.toJS(), query.toJS());
}
}
spawn named 'rootServer' { spawn named 'rootServer' {
let counters = {}; let counters = {};
on asserted Counter($id) counters[id] = true; on asserted Counter($id) counters[id] = true;

View File

@ -66,9 +66,9 @@ function discardAst(state) {
return _discardAst({ SYNDICATE: state.SyndicateID }); return _discardAst({ SYNDICATE: state.SyndicateID });
} }
const _listAst = template.expression(`IMMUTABLE.fromJS(VS)`); const _listAst = template.expression(`SYNDICATE.fromJS(VS)`);
function listAst(state, vs) { function listAst(state, vs) {
return _listAst({ IMMUTABLE: state.ImmutableID, VS: vs }); return _listAst({ SYNDICATE: state.SyndicateID, VS: vs });
} }
function captureWrap(state, ast) { function captureWrap(state, ast) {
@ -123,7 +123,7 @@ function compilePattern(state, patternPath) {
// constPaths/constVals. // constPaths/constVals.
if (hasCapturesOrDiscards(patternPath)) { if (hasCapturesOrDiscards(patternPath)) {
let arity = pattern.arguments.length; let arity = pattern.arguments.length;
let skel = [t.memberExpression(pattern.callee, t.identifier('meta'), false, false)]; let skel = [t.memberExpression(pattern.callee, t.identifier('constructorInfo'), false, false)];
let assn = []; let assn = [];
for (let i = 0; i < arity; i++) { for (let i = 0; i < arity; i++) {
syndicatePath.push(i); syndicatePath.push(i);
@ -295,25 +295,22 @@ export default declare((api, options) => {
visitor: { visitor: {
Program(path, state) { Program(path, state) {
let savedGlobalFacetUid = path.scope.generateUidIdentifier("savedGlobalFacet"); let savedGlobalFacetUid = path.scope.generateUidIdentifier("savedGlobalFacet");
state.ImmutableID = path.scope.generateUidIdentifier("Immutable");
state.SyndicateID = path.scope.generateUidIdentifier("Syndicate"); state.SyndicateID = path.scope.generateUidIdentifier("Syndicate");
state.DataspaceID = path.scope.generateUidIdentifier("Dataspace"); state.DataspaceID = path.scope.generateUidIdentifier("Dataspace");
state.SkeletonID = path.scope.generateUidIdentifier("Skeleton"); state.SkeletonID = path.scope.generateUidIdentifier("Skeleton");
state.StructID = path.scope.generateUidIdentifier("Struct"); state.RecordID = path.scope.generateUidIdentifier("Record");
path.unshiftContainer( path.unshiftContainer(
'body', 'body',
template(`const SYNDICATE = require("@syndicate-lang/core"); template(`const SYNDICATE = require("@syndicate-lang/core");
const IMMUTABLE = SYNDICATE.Immutable;
const DATASPACE = SYNDICATE.Dataspace; const DATASPACE = SYNDICATE.Dataspace;
const SKELETON = SYNDICATE.Skeleton; const SKELETON = SYNDICATE.Skeleton;
const STRUCT = SYNDICATE.Struct; const RECORD = SYNDICATE.Record;
let SAVEDGLOBALFACET = DATASPACE._currentFacet; let SAVEDGLOBALFACET = DATASPACE._currentFacet;
DATASPACE._currentFacet = new SYNDICATE._Dataspace.ActionCollector();`)({ DATASPACE._currentFacet = new SYNDICATE._Dataspace.ActionCollector();`)({
IMMUTABLE: state.ImmutableID,
SYNDICATE: state.SyndicateID, SYNDICATE: state.SyndicateID,
DATASPACE: state.DataspaceID, DATASPACE: state.DataspaceID,
SKELETON: state.SkeletonID, SKELETON: state.SkeletonID,
STRUCT: state.StructID, RECORD: state.RecordID,
SAVEDGLOBALFACET: savedGlobalFacetUid, SAVEDGLOBALFACET: savedGlobalFacetUid,
})); }));
path.pushContainer( path.pushContainer(
@ -346,8 +343,8 @@ export default declare((api, options) => {
PROC: node.bootProc PROC: node.bootProc
}), }),
ASSERTIONS: node.initialAssertions.length === 0 ? null : ASSERTIONS: node.initialAssertions.length === 0 ? null :
template.expression(`IMMUTABLE.Set(SEQ)`)({ template.expression(`SYNDICATE.Set(SEQ)`)({
IMMUTABLE: state.ImmutableID, SYNDICATE: state.SyndicateID,
SEQ: t.arrayExpression(node.initialAssertions) SEQ: t.arrayExpression(node.initialAssertions)
}), }),
})); }));
@ -453,9 +450,9 @@ export default declare((api, options) => {
SyndicateTypeDefinition(path, state) { SyndicateTypeDefinition(path, state) {
const { node } = path; const { node } = path;
path.replaceWith(template(`const ID = STRUCT.makeConstructor(WIRE, FORMALS);`)({ path.replaceWith(template(`const ID = RECORD.makeConstructor(WIRE, FORMALS);`)({
ID: node.id, ID: node.id,
STRUCT: state.StructID, RECORD: state.RecordID,
WIRE: node.wireName || t.stringLiteral(node.id.name), WIRE: node.wireName || t.stringLiteral(node.id.name),
FORMALS: t.arrayExpression(node.formals.map((f) => t.stringLiteral(f.name))), FORMALS: t.arrayExpression(node.formals.map((f) => t.stringLiteral(f.name))),
})); }));