Switch to Preserves for Record and Bytes
This commit is contained in:
parent
ea907c5aee
commit
44c1d425b3
|
@ -21,6 +21,7 @@
|
|||
"nyc": "^13.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"immutable": "^3.8.2"
|
||||
"immutable": "^3.8.2",
|
||||
"preserves": "^0.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,13 +17,10 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
var Struct = require('./struct.js');
|
||||
var { Record } = require('preserves');
|
||||
|
||||
function Seal(contents) {
|
||||
if (this === void 0) {
|
||||
return new Seal(contents);
|
||||
}
|
||||
|
||||
if (!(this instanceof Seal)) return new Seal(contents);
|
||||
this.contents = contents;
|
||||
}
|
||||
|
||||
|
@ -35,10 +32,12 @@ Seal.prototype.toJSON = function () {
|
|||
return { '@seal': 0 };
|
||||
};
|
||||
|
||||
module.exports.Discard = Struct.makeConstructor('discard', []);
|
||||
module.exports.Capture = Struct.makeConstructor('capture', ['specification']);
|
||||
module.exports.Observe = Struct.makeConstructor('observe', ['specification']);
|
||||
module.exports.Discard = Record.makeConstructor('discard', []);
|
||||
module.exports.Capture = Record.makeConstructor('capture', ['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.Inbound = Struct.makeConstructor('inbound', ['assertion']);
|
||||
module.exports.Outbound = Struct.makeConstructor('outbound', ['assertion']);
|
||||
module.exports.Instance = Struct.makeConstructor('instance', ['uniqueId']);
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
// Bags and Deltas (which are Bags where item-counts can be negative).
|
||||
|
||||
const Immutable = require("immutable");
|
||||
const { fromJS } = require("preserves");
|
||||
|
||||
const PRESENT_TO_ABSENT = -1;
|
||||
const ABSENT_TO_ABSENT = 0;
|
||||
|
@ -77,7 +78,7 @@ const Bag = Immutable.Map;
|
|||
function fromSet(s) {
|
||||
return Bag().withMutations(function (b) {
|
||||
for (let v of Immutable.Set(s)) {
|
||||
b = b.set(Immutable.fromJS(v), 1);
|
||||
b = b.set(fromJS(v), 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
//---------------------------------------------------------------------------
|
||||
|
||||
const Immutable = require("immutable");
|
||||
const Struct = require('./struct.js');
|
||||
const { fromJS } = require("preserves");
|
||||
|
||||
const Skeleton = require('./skeleton.js');
|
||||
const $Special = require('./special.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.adhocRetract = function (a) {
|
||||
a = Immutable.fromJS(a);
|
||||
a = fromJS(a);
|
||||
if (this.adhocAssertions.change(a, -1, true) === Bag.PRESENT_TO_ABSENT) {
|
||||
this.retract(a);
|
||||
}
|
||||
};
|
||||
|
||||
Actor.prototype.adhocAssert = function (a) {
|
||||
a = Immutable.fromJS(a);
|
||||
a = fromJS(a);
|
||||
if (this.adhocAssertions.change(a, +1) === Bag.ABSENT_TO_PRESENT) {
|
||||
this.assert(a);
|
||||
}
|
||||
|
@ -371,8 +372,10 @@ Patch.prototype.perform = function (ds, ac) {
|
|||
};
|
||||
|
||||
Patch.prototype.adjust = function (a, count) {
|
||||
var _net;
|
||||
({bag: this.changes, net: _net} = Bag.change(this.changes, Immutable.fromJS(a), count));
|
||||
if (a !== void 0) {
|
||||
var _net;
|
||||
({bag: this.changes, net: _net} = Bag.change(this.changes, fromJS(a), count));
|
||||
}
|
||||
};
|
||||
|
||||
function Message(body) {
|
||||
|
@ -381,7 +384,7 @@ function Message(body) {
|
|||
|
||||
Message.prototype.perform = function (ds, ac) {
|
||||
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) {
|
||||
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)) {
|
||||
this._uninstall(ds, ac, true);
|
||||
this._install(ds, ac, newAssertion, newHandler);
|
||||
|
|
|
@ -19,6 +19,7 @@ Ground.prototype = new Dataspace(null);
|
|||
Ground._resolved = Promise.resolve();
|
||||
Ground.laterCall = function (thunk) {
|
||||
Ground._resolved.then(() => {
|
||||
Error.stackTraceLimit = 100;
|
||||
try {
|
||||
thunk();
|
||||
} catch (e) {
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
const Struct = require('./struct.js');
|
||||
const Skeleton = require('./skeleton.js');
|
||||
const RandomID = require('./randomid.js');
|
||||
const Dataspace = require('./dataspace.js');
|
||||
|
@ -25,11 +24,12 @@ const Ground = require('./ground.js');
|
|||
const Assertions = require('./assertions.js');
|
||||
const Relay = require('./relay.js');
|
||||
|
||||
Object.assign(module.exports, require("preserves"));
|
||||
|
||||
module.exports.Immutable = require('immutable');
|
||||
// ^ for use by import machinery in syntactic extensions
|
||||
|
||||
module.exports.Bag = require("./bag.js");
|
||||
module.exports.Struct = Struct;
|
||||
module.exports.Skeleton = Skeleton;
|
||||
module.exports.RandomID = RandomID;
|
||||
|
||||
|
|
|
@ -47,18 +47,18 @@ NestedDataspace.prototype.sendMessage = function (m) {
|
|||
NestedDataspace.prototype.endpointHook = function (facet, innerEp) {
|
||||
const innerDs = this;
|
||||
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(...)).
|
||||
// Also, if innerEp.handler exists, it will be consonant with innerEp.assertion.
|
||||
// Beware of completely-constant patterns, which cause skeleton to be null!
|
||||
this.hookEndpointLifecycle(innerEp, this.outerFacet.addEndpoint(() => {
|
||||
const h = innerEp.handler;
|
||||
return [Observe(innerEp.assertion[0][0]),
|
||||
return [Observe(innerEp.assertion.get(0).get(0)),
|
||||
h && (h.skeleton === null
|
||||
? {
|
||||
skeleton: null,
|
||||
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()),
|
||||
callback: function (evt, captures) {
|
||||
h.callback.call(this, evt, captures);
|
||||
|
@ -85,13 +85,13 @@ NestedDataspace.prototype.adjustIndex = function (a, count) {
|
|||
switch (net) {
|
||||
case Bag.ABSENT_TO_PRESENT:
|
||||
this.outerFacet.actor.pushScript(() => {
|
||||
this.outerFacet.actor.adhocAssert(a[0]);
|
||||
this.outerFacet.actor.adhocAssert(a.get(0));
|
||||
});
|
||||
this.outerFacet.actor.dataspace.start();
|
||||
break;
|
||||
case Bag.PRESENT_TO_ABSENT:
|
||||
this.outerFacet.actor.pushScript(() => {
|
||||
this.outerFacet.actor.adhocRetract(a[0]);
|
||||
this.outerFacet.actor.adhocRetract(a.get(0));
|
||||
});
|
||||
this.outerFacet.actor.dataspace.start();
|
||||
break;
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
//---------------------------------------------------------------------------
|
||||
|
||||
const Immutable = require("immutable");
|
||||
const Struct = require('./struct.js');
|
||||
const { Record } = require("preserves");
|
||||
|
||||
const $Special = require('./special.js');
|
||||
const Bag = require('./bag.js');
|
||||
const { Capture, Discard } = require('./assertions.js');
|
||||
|
@ -66,8 +67,8 @@ function Handler(cachedCaptures) {
|
|||
}
|
||||
|
||||
function classOf(v) {
|
||||
if (v instanceof Struct.Structure) {
|
||||
return v.meta;
|
||||
if (v instanceof Record) {
|
||||
return v.getConstructorInfo();
|
||||
} else if (v instanceof Immutable.List) {
|
||||
return v.size;
|
||||
} else {
|
||||
|
@ -104,7 +105,7 @@ Node.prototype.extend = function(skeleton) {
|
|||
if (!nextNode) {
|
||||
nextNode = new Node(new Continuation(
|
||||
node.continuation.cachedAssertions.filter(
|
||||
(a) => classOf(projectPath(a, path)) === cls)));
|
||||
(a) => Immutable.is(classOf(projectPath(a, path)), cls))));
|
||||
table = table.set(cls, nextNode);
|
||||
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(); }
|
||||
});
|
||||
let nextValue = step(nextStack.first(), selector.index);
|
||||
let cls = classOf(nextValue);
|
||||
let nextNode = table.get(cls, false);
|
||||
let nextNode = table.get(classOf(nextValue), false);
|
||||
if (nextNode) {
|
||||
walkNode(nextNode, nextStack.push(nextValue));
|
||||
}
|
||||
|
@ -279,6 +279,15 @@ function analyzeAssertion(a) {
|
|||
let capturePaths = Immutable.List();
|
||||
|
||||
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);
|
||||
if (cls !== null) {
|
||||
let arity = (typeof cls === 'number') ? cls : cls.arity;
|
||||
|
@ -287,18 +296,11 @@ function analyzeAssertion(a) {
|
|||
result.push(walk(path.push(i), step(a, i)));
|
||||
}
|
||||
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);
|
||||
|
|
|
@ -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;
|
|
@ -24,28 +24,27 @@ chai.use(require('chai-immutable'));
|
|||
const Immutable = require('immutable');
|
||||
|
||||
const Syndicate = require('../src/index.js');
|
||||
const Skeleton = Syndicate.Skeleton;
|
||||
const Dataspace = Syndicate.Dataspace;
|
||||
const Struct = Syndicate.Struct;
|
||||
const __ = Syndicate.__;
|
||||
const _$ = Syndicate._$;
|
||||
const { Skeleton, Dataspace, Observe, Capture, Discard } = Syndicate;
|
||||
|
||||
describe('dataspace', () => {
|
||||
it('should boot and run', () => {
|
||||
// TODO: convert this into even a rudimentary somewhat-real test case
|
||||
// (change console.log into gathering a trace)
|
||||
let ds = new Dataspace(null, () => {
|
||||
let ds = new Dataspace(() => {
|
||||
// console.log('boot');
|
||||
Dataspace.currentFacet().addEndpoint(() => {
|
||||
let handler = Skeleton.analyzeAssertion(_$);
|
||||
let handler = Skeleton.analyzeAssertion(Capture(Discard()));
|
||||
handler.callback = (evt, vs) => {
|
||||
if (Syndicate.Observe.isClassOf(vs.get(0))) {
|
||||
// console.log('OBSERVATION EVENT', evt, vs, vs.get(0).get(0) === _$);
|
||||
if (Observe.isClassOf(vs.get(0))) {
|
||||
// console.log('OBSERVATION EVENT',
|
||||
// evt,
|
||||
// vs,
|
||||
// Immutable.is(vs.get(0).get(0), Capture(Discard())));
|
||||
} else {
|
||||
// console.log('EVENT', evt, vs);
|
||||
}
|
||||
};
|
||||
return [Syndicate.Observe(_$), handler];
|
||||
return [Observe(Capture(Discard())), handler];
|
||||
});
|
||||
Dataspace.deferTurn(() => {
|
||||
// console.log('after defer');
|
||||
|
|
|
@ -24,12 +24,12 @@ chai.use(require('chai-immutable'));
|
|||
const Immutable = require('immutable');
|
||||
|
||||
const Syndicate = require('../src/index.js');
|
||||
const Skeleton = Syndicate.Skeleton;
|
||||
const Struct = Syndicate.Struct;
|
||||
const __ = Syndicate.__;
|
||||
const _$ = Syndicate._$;
|
||||
const { Seal, Skeleton, Capture, Discard, Record } = 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) {
|
||||
return (e, vs) => { traceHolder.push(Event(label, e, vs)) };
|
||||
|
@ -51,21 +51,21 @@ function _analyzeAssertion(a) {
|
|||
|
||||
describe('skeleton', () => {
|
||||
|
||||
const A = Struct.makeConstructor('A', ['x', 'y']);
|
||||
const B = Struct.makeConstructor('B', ['v']);
|
||||
const C = Struct.makeConstructor('C', ['v']);
|
||||
const A = Record.makeConstructor('A', ['x', 'y']);
|
||||
const B = Record.makeConstructor('B', ['v']);
|
||||
const C = Record.makeConstructor('C', ['v']);
|
||||
|
||||
describe('pattern analysis', () => {
|
||||
it('should handle leaf captures', () => {
|
||||
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([]),
|
||||
constVals: Immutable.fromJS([]),
|
||||
capturePaths: Immutable.fromJS([[0, 0], [1]])}));
|
||||
});
|
||||
it('should handle atomic constants', () => {
|
||||
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]]),
|
||||
constVals: Immutable.fromJS(["x"]),
|
||||
capturePaths: Immutable.fromJS([[1]])}));
|
||||
|
@ -77,12 +77,15 @@ describe('skeleton', () => {
|
|||
// will end up being complex at runtime. We can't properly test
|
||||
// that situation without the static analysis half of the code.
|
||||
// TODO later.
|
||||
let complexPlaceholder = new Object();
|
||||
expect(Immutable.fromJS(_analyzeAssertion(A(complexPlaceholder, C(_$)))))
|
||||
.to.equal(Immutable.fromJS({skeleton: [A.meta, null, [C.meta, null]],
|
||||
constPaths: Immutable.fromJS([[0]]),
|
||||
constVals: Immutable.fromJS([complexPlaceholder]),
|
||||
capturePaths: Immutable.fromJS([[1, 0]])}));
|
||||
const complexPlaceholder = new Object();
|
||||
const analysis = Immutable.fromJS(_analyzeAssertion(A(complexPlaceholder, C(_$))));
|
||||
const expected = Immutable.fromJS({
|
||||
skeleton: [A.constructorInfo, null, [C.constructorInfo, null]],
|
||||
constPaths: Immutable.fromJS([[0]]),
|
||||
constVals: Immutable.List([complexPlaceholder]),
|
||||
capturePaths: Immutable.fromJS([[1, 0]]),
|
||||
});
|
||||
expect(analysis).to.equal(expected);
|
||||
});
|
||||
it('should handle complex constants (2)', () => {
|
||||
// Marker: (***)
|
||||
|
@ -91,8 +94,10 @@ describe('skeleton', () => {
|
|||
// will end up being complex at runtime. We can't properly test
|
||||
// that situation without the static analysis half of the code.
|
||||
// TODO later.
|
||||
expect(Immutable.fromJS(_analyzeAssertion(A(B(B("y")), _$("rhs", C(__))))))
|
||||
.to.equal(Immutable.fromJS({skeleton: [A.meta, [B.meta, [B.meta, null]], [C.meta, null]],
|
||||
expect(Immutable.fromJS(_analyzeAssertion(A(B(B("y")), Capture(C(__))))))
|
||||
.to.equal(Immutable.fromJS({skeleton: [A.constructorInfo,
|
||||
[B.constructorInfo, [B.constructorInfo, null]],
|
||||
[C.constructorInfo, null]],
|
||||
constPaths: Immutable.fromJS([[0, 0, 0]]),
|
||||
constVals: Immutable.fromJS(["y"]),
|
||||
capturePaths: Immutable.fromJS([[1]])}));
|
||||
|
@ -117,13 +122,15 @@ describe('skeleton', () => {
|
|||
let trace = skeletonTrace((i, traceHolder) => {
|
||||
i.addHandler(_analyzeAssertion(A(B(_$), _$)), eventCallback(traceHolder, "AB"));
|
||||
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]]),
|
||||
constVals: Immutable.fromJS([B("y")]),
|
||||
capturePaths: Immutable.fromJS([[1, 0]])};
|
||||
// ^ See comment in 'should handle complex constants (1)' test above (marked (***)).
|
||||
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]]),
|
||||
constVals: Immutable.fromJS([B("y")]),
|
||||
capturePaths: Immutable.fromJS([[1]])};
|
||||
|
@ -135,7 +142,7 @@ describe('skeleton', () => {
|
|||
i.addAssertion(Immutable.fromJS(A(B("z"),C(3))));
|
||||
});
|
||||
|
||||
// trace.forEach((e) => { console.log(e.toString()) });
|
||||
// trace.forEach((e) => { console.log(e) });
|
||||
|
||||
expect(trace)
|
||||
.to.equal(Immutable.List([
|
||||
|
@ -236,7 +243,7 @@ describe('skeleton', () => {
|
|||
expect(trace.size).to.equal(8);
|
||||
});
|
||||
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([
|
||||
Event("3-EVENT", Skeleton.EVENT_ADDED, [123, 234]),
|
||||
Event("3-EVENT", Skeleton.EVENT_ADDED, [999, 999]),
|
||||
|
@ -245,7 +252,7 @@ describe('skeleton', () => {
|
|||
Event("3-EVENT", Skeleton.EVENT_REMOVED, [123, 234])]));
|
||||
});
|
||||
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([
|
||||
Event("2-EVENT", Skeleton.EVENT_ADDED, []),
|
||||
Event("2-EVENT", Skeleton.EVENT_MESSAGE, []),
|
||||
|
|
|
@ -57,17 +57,18 @@ export function htmlToString(j) {
|
|||
|
||||
function walk(j) {
|
||||
if (htmlTag.isClassOf(j)) {
|
||||
pieces.push('<', j[0]);
|
||||
j[1].forEach((p) => pieces.push(' ', escapeHtml(p[0]), '="', escapeHtml(p[1]), '"'));
|
||||
pieces.push('<', j.get(0));
|
||||
j.get(1).forEach(
|
||||
(p) => pieces.push(' ', escapeHtml(p.get(0)), '="', escapeHtml(p.get(1)), '"'));
|
||||
pieces.push('>');
|
||||
j[2].forEach(walk);
|
||||
if (!(j[0] in emptyHtmlElements)) {
|
||||
pieces.push('</', j[0], '>');
|
||||
j.get(2).forEach(walk);
|
||||
if (!(j.get(0) in emptyHtmlElements)) {
|
||||
pieces.push('</', j.get(0), '>');
|
||||
}
|
||||
} else if (htmlFragment.isClassOf(j)) {
|
||||
j[0].forEach(walk);
|
||||
j.get(0).forEach(walk);
|
||||
} 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') {
|
||||
for (let k of j) { walk(k); }
|
||||
} else {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
// 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";
|
||||
|
||||
const http = require('http');
|
||||
|
@ -40,14 +40,14 @@ Object.assign(module.exports, {
|
|||
Response, DataOut,
|
||||
});
|
||||
|
||||
spawn named 'HttpServerFactory' {
|
||||
spawn named 'driver/HttpServerFactory' {
|
||||
during Observe(Request(_, HttpServer($h, $p), _, _, _, _)) assert HttpServer(h, p);
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ function _server(host, port, httpsOptions) {
|
|||
facet.stop();
|
||||
}
|
||||
on message DataOut(id, $chunk) {
|
||||
res.write(chunk);
|
||||
res.write(Bytes.toIO(chunk));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -218,12 +218,12 @@ function _server(host, port, httpsOptions) {
|
|||
|
||||
on asserted Observe(DataIn(id, _)) {
|
||||
ws.on('message', Dataspace.wrapExternal((message) => {
|
||||
^ DataIn(id, message);
|
||||
^ DataIn(id, Bytes.fromIO(message));
|
||||
}));
|
||||
}
|
||||
|
||||
on message DataOut(id, $message) {
|
||||
ws.send(message);
|
||||
ws.send(Bytes.toIO(message));
|
||||
}
|
||||
|
||||
stop on retracted Observe(WebSocket(_, server, pathPattern, _));
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
// 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 { sleep } = activate require("@syndicate-lang/driver-timer");
|
||||
|
@ -38,14 +38,14 @@ export {
|
|||
TcpAddress, TcpListener,
|
||||
};
|
||||
|
||||
spawn named 'TcpDriver' {
|
||||
during Observe(TcpConnection(_, TcpListener($port))) spawn named ['TcpListener', port] {
|
||||
spawn named 'driver/TcpDriver' {
|
||||
during Observe(TcpConnection(_, TcpListener($port))) spawn named ['driver/TcpListener', port] {
|
||||
let finish = Dataspace.backgroundTask();
|
||||
on stop finish();
|
||||
|
||||
let server = net.createServer(Dataspace.wrapExternal((socket) => {
|
||||
let id = genUuid('tcp' + port);
|
||||
spawn named ['TcpInbound', id] {
|
||||
spawn named ['driver/TcpInbound', id] {
|
||||
assert TcpConnection(id, TcpListener(port));
|
||||
on asserted TcpAccepted(id) _connectionCommon.call(this, currentFacet(), id, socket, true);
|
||||
stop on retracted TcpAccepted(id);
|
||||
|
@ -57,7 +57,8 @@ spawn named 'TcpDriver' {
|
|||
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();
|
||||
on stop finish();
|
||||
|
||||
|
@ -69,9 +70,9 @@ spawn named 'TcpDriver' {
|
|||
}
|
||||
}
|
||||
|
||||
during Observe(LineIn($id, _)) spawn named ['TcpLineReader', id] {
|
||||
field this.buffer = Buffer.alloc(0);
|
||||
on message DataIn(id, $data) this.buffer = Buffer.concat([this.buffer, data]);
|
||||
during Observe(LineIn($id, _)) spawn named ['driver/TcpLineReader', id] {
|
||||
field this.buffer = Bytes();
|
||||
on message DataIn(id, $data) this.buffer = Bytes.concat([this.buffer, data]);
|
||||
dataflow {
|
||||
const pos = this.buffer.indexOf(10);
|
||||
if (pos !== -1) {
|
||||
|
@ -107,12 +108,12 @@ function _connectionCommon(rootFacet, id, socket, established) {
|
|||
|
||||
on start react stop on asserted Observe(DataIn(id, _)) {
|
||||
socket.on('data', Dataspace.wrapExternal((data) => {
|
||||
^ DataIn(id, data);
|
||||
^ DataIn(id, Bytes.fromIO(data));
|
||||
}));
|
||||
}
|
||||
|
||||
on message DataOut(id, $data) {
|
||||
socket.write(data);
|
||||
socket.write(Bytes.toIO(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
// 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";
|
||||
|
||||
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('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);
|
||||
|
||||
on message UdpPacket(addr, UdpPeer($host, $port), $payload) {
|
||||
payload = Bytes.toIO(payload);
|
||||
socket.send(payload, 0, payload.length, port, host, Dataspace.wrapExternal((err) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
// 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 _WebSocket = require('isomorphic-ws');
|
||||
|
@ -52,7 +52,7 @@ spawn named 'WebSocketFactory' {
|
|||
|
||||
ws.onopen = Dataspace.wrapExternal(() => { this.connected = true; });
|
||||
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 = () => {
|
||||
|
@ -70,7 +70,7 @@ spawn named 'WebSocketFactory' {
|
|||
|
||||
on message DataOut(id, $data) {
|
||||
if (this.connected) {
|
||||
ws.send(data);
|
||||
ws.send(Bytes.toIO(data));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
// 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 { PeriodicTick } = activate require("@syndicate-lang/driver-timer");
|
||||
|
@ -34,7 +34,7 @@ spawn named 'demo' {
|
|||
}
|
||||
|
||||
on message PeriodicTick(1000) {
|
||||
^ WS.DataOut(wsId, genUuid('timestamp'));
|
||||
^ WS.DataOut(wsId, Bytes.from(genUuid('timestamp')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ spawn named 'multicast_demo' {
|
|||
assert U.UdpMulticastLoopback(HANDLE, true);
|
||||
|
||||
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) {
|
||||
|
|
|
@ -74,6 +74,15 @@ function counter() {
|
|||
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' {
|
||||
let counters = {};
|
||||
on asserted Counter($id) counters[id] = true;
|
||||
|
|
|
@ -66,9 +66,9 @@ function discardAst(state) {
|
|||
return _discardAst({ SYNDICATE: state.SyndicateID });
|
||||
}
|
||||
|
||||
const _listAst = template.expression(`IMMUTABLE.fromJS(VS)`);
|
||||
const _listAst = template.expression(`SYNDICATE.fromJS(VS)`);
|
||||
function listAst(state, vs) {
|
||||
return _listAst({ IMMUTABLE: state.ImmutableID, VS: vs });
|
||||
return _listAst({ SYNDICATE: state.SyndicateID, VS: vs });
|
||||
}
|
||||
|
||||
function captureWrap(state, ast) {
|
||||
|
@ -123,7 +123,7 @@ function compilePattern(state, patternPath) {
|
|||
// constPaths/constVals.
|
||||
if (hasCapturesOrDiscards(patternPath)) {
|
||||
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 = [];
|
||||
for (let i = 0; i < arity; i++) {
|
||||
syndicatePath.push(i);
|
||||
|
@ -295,25 +295,22 @@ export default declare((api, options) => {
|
|||
visitor: {
|
||||
Program(path, state) {
|
||||
let savedGlobalFacetUid = path.scope.generateUidIdentifier("savedGlobalFacet");
|
||||
state.ImmutableID = path.scope.generateUidIdentifier("Immutable");
|
||||
state.SyndicateID = path.scope.generateUidIdentifier("Syndicate");
|
||||
state.DataspaceID = path.scope.generateUidIdentifier("Dataspace");
|
||||
state.SkeletonID = path.scope.generateUidIdentifier("Skeleton");
|
||||
state.StructID = path.scope.generateUidIdentifier("Struct");
|
||||
state.RecordID = path.scope.generateUidIdentifier("Record");
|
||||
path.unshiftContainer(
|
||||
'body',
|
||||
template(`const SYNDICATE = require("@syndicate-lang/core");
|
||||
const IMMUTABLE = SYNDICATE.Immutable;
|
||||
const DATASPACE = SYNDICATE.Dataspace;
|
||||
const SKELETON = SYNDICATE.Skeleton;
|
||||
const STRUCT = SYNDICATE.Struct;
|
||||
const RECORD = SYNDICATE.Record;
|
||||
let SAVEDGLOBALFACET = DATASPACE._currentFacet;
|
||||
DATASPACE._currentFacet = new SYNDICATE._Dataspace.ActionCollector();`)({
|
||||
IMMUTABLE: state.ImmutableID,
|
||||
SYNDICATE: state.SyndicateID,
|
||||
DATASPACE: state.DataspaceID,
|
||||
SKELETON: state.SkeletonID,
|
||||
STRUCT: state.StructID,
|
||||
RECORD: state.RecordID,
|
||||
SAVEDGLOBALFACET: savedGlobalFacetUid,
|
||||
}));
|
||||
path.pushContainer(
|
||||
|
@ -346,8 +343,8 @@ export default declare((api, options) => {
|
|||
PROC: node.bootProc
|
||||
}),
|
||||
ASSERTIONS: node.initialAssertions.length === 0 ? null :
|
||||
template.expression(`IMMUTABLE.Set(SEQ)`)({
|
||||
IMMUTABLE: state.ImmutableID,
|
||||
template.expression(`SYNDICATE.Set(SEQ)`)({
|
||||
SYNDICATE: state.SyndicateID,
|
||||
SEQ: t.arrayExpression(node.initialAssertions)
|
||||
}),
|
||||
}));
|
||||
|
@ -453,9 +450,9 @@ export default declare((api, options) => {
|
|||
|
||||
SyndicateTypeDefinition(path, state) {
|
||||
const { node } = path;
|
||||
path.replaceWith(template(`const ID = STRUCT.makeConstructor(WIRE, FORMALS);`)({
|
||||
path.replaceWith(template(`const ID = RECORD.makeConstructor(WIRE, FORMALS);`)({
|
||||
ID: node.id,
|
||||
STRUCT: state.StructID,
|
||||
RECORD: state.RecordID,
|
||||
WIRE: node.wireName || t.stringLiteral(node.id.name),
|
||||
FORMALS: t.arrayExpression(node.formals.map((f) => t.stringLiteral(f.name))),
|
||||
}));
|
||||
|
|
Loading…
Reference in New Issue