From 4048ce3ba2df2520c80dbcbabed6c3f126788ddb Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Tue, 20 Nov 2018 14:17:10 +0000 Subject: [PATCH] Add VisibilityRestriction --- packages/broker/src/client.js | 4 +-- packages/core/src/skeleton.js | 65 +++++++++++++++++++++++++---------- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/packages/broker/src/client.js b/packages/broker/src/client.js index 4c15ced..1dec222 100644 --- a/packages/broker/src/client.js +++ b/packages/broker/src/client.js @@ -62,12 +62,12 @@ spawn named "BrokerClientFactory" { on stop w(Clear(ep)); on message _BrokerPacket(url, Add(ep, $vs)) { react { - assert FromBroker(url, Skeleton.instantiateAssertion(spec, vs)); + assert Skeleton.instantiateAssertion(FromBroker(url, spec), vs); stop on message _BrokerPacket(url, Del(ep, vs)); } } on message _BrokerPacket(url, Msg(ep, $vs)) { - send FromBroker(url, Skeleton.instantiateAssertion(spec, vs)); + send Skeleton.instantiateAssertion(FromBroker(url, spec), vs); } } } diff --git a/packages/core/src/skeleton.js b/packages/core/src/skeleton.js index 9e07302..a0f38f7 100644 --- a/packages/core/src/skeleton.js +++ b/packages/core/src/skeleton.js @@ -105,7 +105,7 @@ Node.prototype.extend = function(skeleton) { if (!nextNode) { nextNode = new Node(new Continuation( node.continuation.cachedAssertions.filter( - (a) => Immutable.is(classOf(projectPath(a, path)), cls)))); + (a) => Immutable.is(classOf(projectPath(unscope(a), path)), cls)))); table = table.set(cls, nextNode); node.edges = node.edges.set(selector, table); } @@ -137,7 +137,7 @@ Index.prototype.addHandler = function(analysisResults, callback) { let leaf = constValMap.get(constVals, false); if (!leaf) { leaf = new Leaf(continuation.cachedAssertions.filter( - (a) => projectPaths(a, constPaths).equals(constVals))); + (a) => projectPaths(unscope(a), constPaths).equals(constVals))); constValMap = constValMap.set(constVals, leaf); continuation.leafMap = continuation.leafMap.set(constPaths, constValMap); } @@ -145,9 +145,13 @@ Index.prototype.addHandler = function(analysisResults, callback) { if (!handler) { let cachedCaptures = Bag.Bag().withMutations((mutable) => { leaf.cachedAssertions.forEach((a) => { - let captures = projectPaths(a, capturePaths); - mutable.set(captures, mutable.get(captures, 0) + 1); - return true; + return unpackScoped(a, (restrictionPaths, term) => { + if (restrictionPaths === false || restrictionPaths.equals(capturePaths)) { + let captures = projectPaths(a, capturePaths); + mutable.set(captures, mutable.get(captures, 0) + 1); + } + return true; + }); }) }); handler = new Handler(cachedCaptures); @@ -184,6 +188,8 @@ Index.prototype.removeHandler = function(analysisResults, callback) { }; Node.prototype.modify = function(outerValue, m_cont, m_leaf, m_handler) { + const [restrictionPaths, outerValueTerm] = unpackScoped(outerValue, (p,t) => [p,t]); + function walkNode(node, termStack) { walkContinuation(node.continuation); node.edges.forEach((table, selector) => { @@ -203,12 +209,14 @@ Node.prototype.modify = function(outerValue, m_cont, m_leaf, m_handler) { function walkContinuation(continuation) { m_cont(continuation, outerValue); continuation.leafMap.forEach((constValMap, constPaths) => { - let constVals = projectPaths(outerValue, constPaths); + let constVals = projectPaths(outerValueTerm, constPaths); let leaf = constValMap.get(constVals, false); if (leaf) { m_leaf(leaf, outerValue); leaf.handlerMap.forEach((handler, capturePaths) => { - m_handler(handler, projectPaths(outerValue, capturePaths)); + if (restrictionPaths === false || restrictionPaths.equals(capturePaths)) { + m_handler(handler, projectPaths(outerValueTerm, capturePaths)); + } return true; }); } @@ -216,7 +224,7 @@ Node.prototype.modify = function(outerValue, m_cont, m_leaf, m_handler) { }); } - walkNode(this, Immutable.Stack().push(Immutable.List([outerValue]))); + walkNode(this, Immutable.Stack().push(Immutable.List([outerValueTerm]))); }; function add_to_cont(c, v) { c.cachedAssertions = c.cachedAssertions.add(v); } @@ -308,38 +316,57 @@ function analyzeAssertion(a) { return { skeleton, constPaths, constVals, capturePaths }; } -function VisibilityRestriction() {} +function OpaquePlaceholder() {} function instantiateAssertion(a, vs) { + let capturePaths = Immutable.List(); let remaining = vs; - function walk(a) { + + function walk(path, a) { if (Capture.isClassOf(a)) { + capturePaths = capturePaths.push(path); const v = remaining.first(); remaining = remaining.shift(); - walk(a.get(0)); + walk(path, a.get(0)); return v; } if (Discard.isClassOf(a)) { - return new VisibilityRestriction(); - // ^ Doesn't match ANYTHING ELSE, even other - // VisibilityRestriction instances. This does the equivalent of - // the Racket implementation's `visibility-restriction` stuff, - // to a degree. + return new OpaquePlaceholder(); + // ^ Doesn't match ANYTHING ELSE, even other `OpaquePlaceholder` + // instances. This prevents unwanted matching against + // "don't-care" positions when `VisibilityRestriction`s are in + // play. } let cls = classOf(a); if (cls !== null) { if (typeof cls === 'number') { - return a.map(walk); + return a.map((v, i) => walk(path.push(i), v)); } else { - return new Record(a.label, a.fields.map(walk)); + return new Record(a.label, a.fields.map((v, i) => walk(path.push(i), v))); } } return a; } - return walk(a); + + const instantiated = walk(Immutable.List(), a); + // ^ Compute `instantiated` completely before retrieving the imperatively-updated `capturePaths`. + return new VisibilityRestriction(capturePaths, instantiated); +} + +function VisibilityRestriction(paths, term) { + this.paths = paths; + this.term = term; +} + +function unscope(a) { + return (a instanceof VisibilityRestriction) ? a.term : a; +} + +function unpackScoped(a, k) { + return (a instanceof VisibilityRestriction) ? k(a.paths, a.term) : k(false, a); } ///////////////////////////////////////////////////////////////////////////