'From Cuis 2.6 of 10 August 2010 [latest update: #540] on 2 September 2010 at 12:42:51 am'!
!classDefinition: #ParseNodeEnumerator category: #'Compiler-Support'!
ParseNodeVisitor subclass: #ParseNodeEnumerator
	instanceVariableNames: 'theBlock theSelectBlock '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-Support'!

!ParseNodeEnumerator commentStamp: 'jmv 9/2/2010 00:39' prior: 0!
ParseNodeEnumerator implements ParseNode>>nodesDo:.  It can be used to enumerate an entire tree via
	aParseNode accept: (ParseNodeEnumerator ofBlock: aBlock)
or selectively, excluding the node and subnodes for which selectBlock answers false, via
	aParseNode accept: (ParseNodeEnumerator
							ofBlock: aBlock
							select: selectBlock)

Here's a doIt that generates and compiles the visiting methods:

self superclass selectors do:
	[:s|
	self compile: (String streamContents:
		[:str| | arg |
		arg := 'a', (s allButFirst: 5) allButLast.
		str nextPutAll: s, ' ', arg; crtab;
			nextPutAll: '(theSelectBlock isNil or: [theSelectBlock value: '; nextPutAll: arg; nextPutAll: ']) ifFalse:'; crtab;
			tab: 2; nextPutAll: '[^nil].'; crtab;
			nextPutAll: 'theBlock value: '; nextPutAll: arg; nextPut: $.; crtab;
			nextPutAll: '^super '; nextPutAll: s, ' ', arg])]!


!Compiler methodsFor: 'private' stamp: 'eem 8/30/2010 17:57'!
format: aStream noPattern: noPattern ifFail: failBlock
	^(self parser
		parse: aStream
		class: class
		noPattern: noPattern
		context: context
		notifying: requestor
		ifFail: [^failBlock value]) preen! !


!Decompiler methodsFor: 'public access' stamp: 'eem 8/30/2010 17:57'!
decompile: aSelector in: aClass method: aMethod using: aConstructor

	| block node |
	constructor := aConstructor.
	method := aMethod.
	self initSymbols: aClass.  "create symbol tables"
	method isQuick
		ifTrue: [block := self quickMethod]
		ifFalse: 
			[stack := OrderedCollection new: method frameSize.
			caseExits := OrderedCollection new.
			statements := OrderedCollection new: 20.
			numLocalTemps := 0.
			super method: method pc: method initialPC.
			"skip primitive error code store if necessary"
			(method primitive ~= 0 and: [self willStore]) ifTrue:
				[pc := pc + 2.
				 tempVars := tempVars asOrderedCollection].
			block := self blockTo: method endPC + 1.
			stack isEmpty ifFalse: [self error: 'stack not empty']].
	node := constructor
				codeMethod: aSelector
				block: block
				tempVars: tempVars
				primitive: method primitive
				class: aClass.
	method primitive > 0 ifTrue:
		[node removeAndRenameLastTempIfErrorCode].
	^node preen! !


!ParseNode methodsFor: 'testing' stamp: 'eem 8/31/2010 11:34'!
isOnlySubnodeOf: aSubtree "<ParseNode>" in: aParseTree "<ParseNode>"
	"Answer if the receiver only occurs within aSubtree of aParseTree, not in the rest of aParseTree.
	 Assumes that aSubtree is in fact a subnode of aParseTree."
	| isSubnode |
	isSubnode := false.
	aSubtree accept: (ParseNodeEnumerator
							ofBlock: [:node| node == self ifTrue: [isSubnode := true]]).
	isSubnode ifFalse:
		[^false].
	aParseTree accept: (ParseNodeEnumerator
							ofBlock: [:node| node == self ifTrue: [^false]]
							select: [:node| node ~= aSubtree]).
	^true! !


!BlockNode methodsFor: 'accessing' stamp: 'eem 8/31/2010 12:31'!
arguments
	^arguments ifNil: [#()]! !

!BlockNode methodsFor: 'accessing' stamp: 'eem 8/31/2010 12:30'!
temporaries
	^temporaries ifNil: [#()]! !


!MethodNode methodsFor: 'converting' stamp: 'eem 8/31/2010 11:54'!
preen
	"Preen for pretty-printing and/or decompilation.
	 i.e. post-process to cover up for inadequacies in both algorithms.
	 Currently one case, hiding the assignment to the arg of an inlined block arg to ifNotNil:,
		(var := expr) ifNil: [...] ifNotNil: [...]    =>    expr ifNil: [...] ifNotNil: [:var| ...]."

	self preenLocalIfNotNilArg! !

!MethodNode methodsFor: 'converting' stamp: 'eem 8/31/2010 12:36'!
preenLocalIfNotNilArg
	"Try and spot a (var := expr) ifNil: [...] ifNotNil: [...] where var is only used in the ifNotNil: block
	 and convert it to expr ifNil: [...] ifNotNil: [:var| ...].  Deal both with the pretty-print case where
	 the block already declares the variable and the decompile case where it does not."

	| varsToHide |
	varsToHide := Set new.
	self nodesDo:
		[:node| | variable |
		(node isMessageNode
		and: [node macroPrinter == #printIfNilNotNil:indent:
		and: [node receiver isMessageNode
		and: [node receiver selector key == #==
		and: [node receiver receiver isAssignmentNode
		and: [(variable := node receiver receiver variable) isTemp
		and: [variable isRemote not
		and: [variable isOnlySubnodeOf: node in: self]]]]]]]) ifTrue:
			[node arguments last arguments isEmpty
				ifTrue: [node arguments last arguments: { variable }.
						varsToHide add: variable]
				ifFalse: [self assert: node arguments last arguments asArray =  { variable }].
			 node receiver receiver: node receiver receiver value]].
	varsToHide notEmpty ifTrue:
		[self nodesDo:
			[:node|
			((node == self or: [node isBlockNode])
			and: [node temporaries anySatisfy: [:temp| varsToHide includes: temp]]) ifTrue:
				[node temporaries: (node temporaries reject: [:temp| varsToHide includes: temp])]]]! !


!ParseNodeEnumerator methodsFor: 'initialize-release' stamp: 'eem 8/31/2010 11:24'!
ofBlock: aBlock select: aSelectBlock
	theBlock := aBlock.
	theSelectBlock := aSelectBlock! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitAssignmentNode: anAssignmentNode
	(theSelectBlock isNil or: [theSelectBlock value: anAssignmentNode]) ifFalse:
		[^nil].
	theBlock value: anAssignmentNode.
	^super visitAssignmentNode: anAssignmentNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitBlockNode: aBlockNode
	(theSelectBlock isNil or: [theSelectBlock value: aBlockNode]) ifFalse:
		[^nil].
	theBlock value: aBlockNode.
	^super visitBlockNode: aBlockNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitBraceNode: aBraceNode
	(theSelectBlock isNil or: [theSelectBlock value: aBraceNode]) ifFalse:
		[^nil].
	theBlock value: aBraceNode.
	^super visitBraceNode: aBraceNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitCascadeNode: aCascadeNode
	(theSelectBlock isNil or: [theSelectBlock value: aCascadeNode]) ifFalse:
		[^nil].
	theBlock value: aCascadeNode.
	^super visitCascadeNode: aCascadeNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitCommentNode: aCommentNode
	(theSelectBlock isNil or: [theSelectBlock value: aCommentNode]) ifFalse:
		[^nil].
	theBlock value: aCommentNode.
	^super visitCommentNode: aCommentNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitFieldNode: aFieldNode
	(theSelectBlock isNil or: [theSelectBlock value: aFieldNode]) ifFalse:
		[^nil].
	theBlock value: aFieldNode.
	^super visitFieldNode: aFieldNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitFutureNode: aFutureNode
	(theSelectBlock isNil or: [theSelectBlock value: aFutureNode]) ifFalse:
		[^nil].
	theBlock value: aFutureNode.
	^super visitFutureNode: aFutureNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitInstanceVariableNode: anInstanceVariableNode
	(theSelectBlock isNil or: [theSelectBlock value: anInstanceVariableNode]) ifFalse:
		[^nil].
	theBlock value: anInstanceVariableNode.
	^super visitInstanceVariableNode: anInstanceVariableNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitLiteralNode: aLiteralNode
	(theSelectBlock isNil or: [theSelectBlock value: aLiteralNode]) ifFalse:
		[^nil].
	theBlock value: aLiteralNode.
	^super visitLiteralNode: aLiteralNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitLiteralVariableNode: aLiteralVariableNode
	(theSelectBlock isNil or: [theSelectBlock value: aLiteralVariableNode]) ifFalse:
		[^nil].
	theBlock value: aLiteralVariableNode.
	^super visitLiteralVariableNode: aLiteralVariableNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitMessageNode: aMessageNode
	(theSelectBlock isNil or: [theSelectBlock value: aMessageNode]) ifFalse:
		[^nil].
	theBlock value: aMessageNode.
	^super visitMessageNode: aMessageNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitMessageNodeInCascade: aMessageNodeInCascade
	(theSelectBlock isNil or: [theSelectBlock value: aMessageNodeInCascade]) ifFalse:
		[^nil].
	theBlock value: aMessageNodeInCascade.
	^super visitMessageNodeInCascade: aMessageNodeInCascade! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitMethodNode: aMethodNode
	(theSelectBlock isNil or: [theSelectBlock value: aMethodNode]) ifFalse:
		[^nil].
	theBlock value: aMethodNode.
	^super visitMethodNode: aMethodNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitNewArrayNode: aNewArrayNode
	(theSelectBlock isNil or: [theSelectBlock value: aNewArrayNode]) ifFalse:
		[^nil].
	theBlock value: aNewArrayNode.
	^super visitNewArrayNode: aNewArrayNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitRemoteTempVectorNode: aRemoteTempVectorNode
	(theSelectBlock isNil or: [theSelectBlock value: aRemoteTempVectorNode]) ifFalse:
		[^nil].
	theBlock value: aRemoteTempVectorNode.
	^super visitRemoteTempVectorNode: aRemoteTempVectorNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitReturnNode: aReturnNode
	(theSelectBlock isNil or: [theSelectBlock value: aReturnNode]) ifFalse:
		[^nil].
	theBlock value: aReturnNode.
	^super visitReturnNode: aReturnNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitSelectorNode: aSelectorNode
	(theSelectBlock isNil or: [theSelectBlock value: aSelectorNode]) ifFalse:
		[^nil].
	theBlock value: aSelectorNode.
	^super visitSelectorNode: aSelectorNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitTempVariableNode: aTempVariableNode
	(theSelectBlock isNil or: [theSelectBlock value: aTempVariableNode]) ifFalse:
		[^nil].
	theBlock value: aTempVariableNode.
	^super visitTempVariableNode: aTempVariableNode! !

!ParseNodeEnumerator methodsFor: 'visiting' stamp: 'eem 8/31/2010 11:13'!
visitVariableNode: aVariableNode
	(theSelectBlock isNil or: [theSelectBlock value: aVariableNode]) ifFalse:
		[^nil].
	theBlock value: aVariableNode.
	^super visitVariableNode: aVariableNode! !


!ParseNodeEnumerator class methodsFor: 'instance creation' stamp: 'eem 8/31/2010 11:43'!
ofBlock: aBlock select: selectBlock
	^self new ofBlock: aBlock select: selectBlock! !

!classDefinition: #ParseNodeEnumerator category: #'Compiler-Support'!
ParseNodeVisitor subclass: #ParseNodeEnumerator
	instanceVariableNames: 'theBlock theSelectBlock'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-Support'!
