Browse Source

Abstract DAG visitor.

Herbert Vojčík 7 years ago
parent
commit
ba2929eb2b
2 changed files with 457 additions and 0 deletions
  1. 359 0
      src/Kernel-Dag.js
  2. 98 0
      src/Kernel-Dag.st

+ 359 - 0
src/Kernel-Dag.js

@@ -6,10 +6,369 @@ $core.addPackage('Kernel-Dag');
 $core.packages["Kernel-Dag"].innerEval = function (expr) { return eval(expr); };
 $core.packages["Kernel-Dag"].transport = {"type":"amd","amdNamespace":"amber_core"};
 
+$core.addClass('AbstractDagVisitor', $globals.Object, [], 'Kernel-Dag');
+//>>excludeStart("ide", pragmas.excludeIdeData);
+$globals.AbstractDagVisitor.comment="I am base class of `DagNode` visitor.\x0a\x0aConcrete classes should implement `visitDagNode:`,\x0athey can reuse possible variants of implementation\x0aoffered directly: `visitDagNodeVariantSimple:`\x0aand `visitDagNodeVariantRedux:`.";
+//>>excludeEnd("ide");
+$core.addMethod(
+$core.method({
+selector: "visit:",
+protocol: 'visiting',
+fn: function (aNode){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $recv(aNode)._acceptDagVisitor_(self);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"visit:",{aNode:aNode},$globals.AbstractDagVisitor)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aNode"],
+source: "visit: aNode\x0a\x09^ aNode acceptDagVisitor: self",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["acceptDagVisitor:"]
+}),
+$globals.AbstractDagVisitor);
+
+$core.addMethod(
+$core.method({
+selector: "visitAll:",
+protocol: 'visiting',
+fn: function (aCollection){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $recv(aCollection)._collect_((function(each){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return self._visit_(each);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({each:each},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"visitAll:",{aCollection:aCollection},$globals.AbstractDagVisitor)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aCollection"],
+source: "visitAll: aCollection\x0a\x09^ aCollection collect: [ :each | self visit: each ]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["collect:", "visit:"]
+}),
+$globals.AbstractDagVisitor);
+
+$core.addMethod(
+$core.method({
+selector: "visitAllChildren:",
+protocol: 'visiting',
+fn: function (aDagNode){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return self._visitAll_($recv(aDagNode)._dagChildren());
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"visitAllChildren:",{aDagNode:aDagNode},$globals.AbstractDagVisitor)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aDagNode"],
+source: "visitAllChildren: aDagNode\x0a\x09^ self visitAll: aDagNode dagChildren",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["visitAll:", "dagChildren"]
+}),
+$globals.AbstractDagVisitor);
+
+$core.addMethod(
+$core.method({
+selector: "visitDagNode:",
+protocol: 'visiting',
+fn: function (aNode){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+self._subclassResponsibility();
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"visitDagNode:",{aNode:aNode},$globals.AbstractDagVisitor)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aNode"],
+source: "visitDagNode: aNode\x0a\x09self subclassResponsibility",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["subclassResponsibility"]
+}),
+$globals.AbstractDagVisitor);
+
+$core.addMethod(
+$core.method({
+selector: "visitDagNodeVariantRedux:",
+protocol: 'visiting',
+fn: function (aNode){
+var self=this;
+var newChildren,oldChildren;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $2,$3,$1,$4,$5;
+var $early={};
+try {
+oldChildren=$recv(aNode)._dagChildren();
+newChildren=self._visitAllChildren_(aNode);
+$2=$recv(oldChildren)._size();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["size"]=1;
+//>>excludeEnd("ctx");
+$3=$recv(newChildren)._size();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.sendIdx["size"]=2;
+//>>excludeEnd("ctx");
+$1=$recv($2).__eq($3);
+if($core.assert($1)){
+$recv((1)._to_($recv(oldChildren)._size()))._detect_ifNone_((function(i){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$4=$recv(oldChildren)._at_(i);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.sendIdx["at:"]=1;
+//>>excludeEnd("ctx");
+return $recv($4).__tild_eq($recv(newChildren)._at_(i));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({i:i},$ctx1,2)});
+//>>excludeEnd("ctx");
+}),(function(){
+throw $early=[aNode];
+
+}));
+};
+$5=$recv(aNode)._copy();
+$recv($5)._dagChildren_(newChildren);
+return $recv($5)._yourself();
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"visitDagNodeVariantRedux:",{aNode:aNode,newChildren:newChildren,oldChildren:oldChildren},$globals.AbstractDagVisitor)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aNode"],
+source: "visitDagNodeVariantRedux: aNode\x0a\x09\x22Immutable-guarded implementation of visitDagNode:.\x0a\x09Visits all children and checks if there were changes.\x0a\x09If not, returns aNode.\x0a\x09If yes, returns copy of aNode with new children.\x22\x0a\x0a\x09| newChildren oldChildren |\x0a\x09oldChildren := aNode dagChildren.\x0a\x09newChildren := self visitAllChildren: aNode.\x0a\x09oldChildren size = newChildren size ifTrue: [\x0a\x09\x09(1 to: oldChildren size) detect: [ :i |\x0a\x09\x09\x09(oldChildren at: i) ~= (newChildren at: i)\x0a\x09\x09] ifNone: [ \x22no change\x22 ^ aNode ] ].\x0a\x09^ aNode copy dagChildren: newChildren; yourself",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["dagChildren", "visitAllChildren:", "ifTrue:", "=", "size", "detect:ifNone:", "to:", "~=", "at:", "dagChildren:", "copy", "yourself"]
+}),
+$globals.AbstractDagVisitor);
+
+$core.addMethod(
+$core.method({
+selector: "visitDagNodeVariantSimple:",
+protocol: 'visiting',
+fn: function (aNode){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+self._visitAllChildren_(aNode);
+return aNode;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"visitDagNodeVariantSimple:",{aNode:aNode},$globals.AbstractDagVisitor)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aNode"],
+source: "visitDagNodeVariantSimple: aNode\x0a\x09\x22Simple implementation of visitDagNode:.\x0a\x09Visits children, then returns aNode\x22\x0a\x0a\x09self visitAllChildren: aNode.\x0a\x09^ aNode",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["visitAllChildren:"]
+}),
+$globals.AbstractDagVisitor);
+
+
+
+$core.addClass('PathDagVisitor', $globals.AbstractDagVisitor, ['path'], 'Kernel-Dag');
+//>>excludeStart("ide", pragmas.excludeIdeData);
+$globals.PathDagVisitor.comment="I am base class of `DagNode` visitor.\x0a\x0aI hold the path of ancestors up to actual node\x0ain `self path`.";
+//>>excludeEnd("ide");
+$core.addMethod(
+$core.method({
+selector: "initialize",
+protocol: 'initialization',
+fn: function (){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+(
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = true,
+//>>excludeEnd("ctx");
+($globals.PathDagVisitor.superclass||$boot.nilAsClass).fn.prototype._initialize.apply($recv(self), []));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = false;
+//>>excludeEnd("ctx");;
+self["@path"]=[];
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"initialize",{},$globals.PathDagVisitor)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "initialize\x0a\x09super initialize.\x0a\x0a\x09path := #()",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["initialize"]
+}),
+$globals.PathDagVisitor);
+
+$core.addMethod(
+$core.method({
+selector: "path",
+protocol: 'accessing',
+fn: function (){
+var self=this;
+return self["@path"];
+
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "path\x0a\x09^ path",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: []
+}),
+$globals.PathDagVisitor);
+
+$core.addMethod(
+$core.method({
+selector: "visit:",
+protocol: 'visiting',
+fn: function (aNode){
+var self=this;
+var oldPath;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+var $early={};
+try {
+oldPath=self["@path"];
+self["@path"]=$recv(self["@path"]).__comma([aNode]);
+$recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+$1=(
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.supercall = true,
+//>>excludeEnd("ctx");
+($globals.PathDagVisitor.superclass||$boot.nilAsClass).fn.prototype._visit_.apply($recv(self), [aNode]));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx2.supercall = false;
+//>>excludeEnd("ctx");;
+throw $early=[$1];
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}))._ensure_((function(){
+self["@path"]=oldPath;
+return self["@path"];
+
+}));
+return self;
+}
+catch(e) {if(e===$early)return e[0]; throw e}
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"visit:",{aNode:aNode,oldPath:oldPath},$globals.PathDagVisitor)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aNode"],
+source: "visit: aNode\x0a\x09| oldPath |\x0a\x09oldPath := path.\x0a\x09path := path, {aNode}.\x0a\x09[ ^ super visit: aNode ] ensure: [ path := oldPath ]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: [",", "ensure:", "visit:"]
+}),
+$globals.PathDagVisitor);
+
+$core.addMethod(
+$core.method({
+selector: "visitDagNodeVariantRedux:",
+protocol: 'visiting',
+fn: function (aNode){
+var self=this;
+var newNode;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+var $1;
+newNode=(
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = true,
+//>>excludeEnd("ctx");
+($globals.PathDagVisitor.superclass||$boot.nilAsClass).fn.prototype._visitDagNodeVariantRedux_.apply($recv(self), [aNode]));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+$ctx1.supercall = false;
+//>>excludeEnd("ctx");;
+$1=$recv(aNode).__eq_eq(newNode);
+if(!$core.assert($1)){
+$recv(self["@path"])._at_put_($recv(self["@path"])._size(),newNode);
+};
+return newNode;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"visitDagNodeVariantRedux:",{aNode:aNode,newNode:newNode},$globals.PathDagVisitor)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aNode"],
+source: "visitDagNodeVariantRedux: aNode\x0a\x09| newNode |\x0a\x09newNode := super visitDagNodeVariantRedux: aNode.\x0a\x09aNode == newNode ifFalse: [ path at: path size put: newNode ].\x0a\x09^ newNode",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["visitDagNodeVariantRedux:", "ifFalse:", "==", "at:put:", "size"]
+}),
+$globals.PathDagVisitor);
+
+
+
 $core.addClass('DagNode', $globals.Object, [], 'Kernel-Dag');
 //>>excludeStart("ide", pragmas.excludeIdeData);
 $globals.DagNode.comment="I am the abstract root class of any directed acyclic graph.\x0a\x0aConcrete classes should implement `dagChildren` and `dagChildren:`\x0ato get / set direct successor nodes (aka child nodes / subnodes).";
 //>>excludeEnd("ide");
+$core.addMethod(
+$core.method({
+selector: "acceptDagVisitor:",
+protocol: 'visiting',
+fn: function (aVisitor){
+var self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $recv(aVisitor)._visitDagNode_(self);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"acceptDagVisitor:",{aVisitor:aVisitor},$globals.DagNode)});
+//>>excludeEnd("ctx");
+},
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aVisitor"],
+source: "acceptDagVisitor: aVisitor\x0a\x09^ aVisitor visitDagNode: self",
+referencedClasses: [],
+//>>excludeEnd("ide");
+messageSends: ["visitDagNode:"]
+}),
+$globals.DagNode);
+
 $core.addMethod(
 $core.method({
 selector: "allDagChildren",

+ 98 - 0
src/Kernel-Dag.st

@@ -1,4 +1,96 @@
 Smalltalk createPackage: 'Kernel-Dag'!
+Object subclass: #AbstractDagVisitor
+	instanceVariableNames: ''
+	package: 'Kernel-Dag'!
+!AbstractDagVisitor commentStamp!
+I am base class of `DagNode` visitor.
+
+Concrete classes should implement `visitDagNode:`,
+they can reuse possible variants of implementation
+offered directly: `visitDagNodeVariantSimple:`
+and `visitDagNodeVariantRedux:`.!
+
+!AbstractDagVisitor methodsFor: 'visiting'!
+
+visit: aNode
+	^ aNode acceptDagVisitor: self
+!
+
+visitAll: aCollection
+	^ aCollection collect: [ :each | self visit: each ]
+!
+
+visitAllChildren: aDagNode
+	^ self visitAll: aDagNode dagChildren
+!
+
+visitDagNode: aNode
+	self subclassResponsibility
+!
+
+visitDagNodeVariantRedux: aNode
+	"Immutable-guarded implementation of visitDagNode:.
+	Visits all children and checks if there were changes.
+	If not, returns aNode.
+	If yes, returns copy of aNode with new children."
+
+	| newChildren oldChildren |
+	oldChildren := aNode dagChildren.
+	newChildren := self visitAllChildren: aNode.
+	oldChildren size = newChildren size ifTrue: [
+		(1 to: oldChildren size) detect: [ :i |
+			(oldChildren at: i) ~= (newChildren at: i)
+		] ifNone: [ "no change" ^ aNode ] ].
+	^ aNode copy dagChildren: newChildren; yourself
+!
+
+visitDagNodeVariantSimple: aNode
+	"Simple implementation of visitDagNode:.
+	Visits children, then returns aNode"
+
+	self visitAllChildren: aNode.
+	^ aNode
+! !
+
+AbstractDagVisitor subclass: #PathDagVisitor
+	instanceVariableNames: 'path'
+	package: 'Kernel-Dag'!
+!PathDagVisitor commentStamp!
+I am base class of `DagNode` visitor.
+
+I hold the path of ancestors up to actual node
+in `self path`.!
+
+!PathDagVisitor methodsFor: 'accessing'!
+
+path
+	^ path
+! !
+
+!PathDagVisitor methodsFor: 'initialization'!
+
+initialize
+	super initialize.
+
+	path := #()
+! !
+
+!PathDagVisitor methodsFor: 'visiting'!
+
+visit: aNode
+	| oldPath |
+	oldPath := path.
+	path := path, {aNode}.
+	[ ^ super visit: aNode ] ensure: [ path := oldPath ]
+!
+
+visitDagNodeVariantRedux: aNode
+	| newNode |
+	newNode := super visitDagNodeVariantRedux: aNode.
+	aNode == newNode ifFalse: [ path at: path size put: newNode ].
+	^ newNode
+! !
+
 Object subclass: #DagNode
 	instanceVariableNames: ''
 	package: 'Kernel-Dag'!
@@ -34,6 +126,12 @@ isDagNode
 	^ true
 ! !
 
+!DagNode methodsFor: 'visiting'!
+
+acceptDagVisitor: aVisitor
+	^ aVisitor visitDagNode: self
+! !
+
 DagNode subclass: #DagParentNode
 	instanceVariableNames: 'nodes'
 	package: 'Kernel-Dag'!