[Pkg] Squeak3.10bc: Compiler-kph.63.mcz

squeak-dev-noreply at lists.squeakfoundation.org squeak-dev-noreply at lists.squeakfoundation.org
Sat Dec 13 04:47:46 UTC 2008


A new version of Compiler was added to project Squeak3.10bc:
http://www.squeaksource.com/310bc/Compiler-kph.63.mcz

==================== Summary ====================

Name: Compiler-kph.63
Author: kph
Time: 13 December 2008, 4:47:42 am
UUID: 4305226f-c184-4103-893e-30c7086dcb6c
Ancestors: Compiler-edc.62

Saved from SystemVersion

==================== Snapshot ====================

SystemOrganization addCategory: #'Compiler-Kernel'!
SystemOrganization addCategory: #'Compiler-ParseNodes'!
SystemOrganization addCategory: #'Compiler-Support'!
SystemOrganization addCategory: #'Compiler-Tests'!

Object subclass: #CompiledMethodWithNode
	instanceVariableNames: 'node method'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-Support'!

----- Method: CompiledMethodWithNode class>>generateMethodFromNode:trailer: (in category 'instance creation') -----
generateMethodFromNode: aMethodNode trailer: bytes
	^ self method: (aMethodNode generate: bytes) node: aMethodNode.!

----- Method: CompiledMethodWithNode class>>method:node: (in category 'instance creation') -----
method: aCompiledMethod node: aMethodNode
	^ self new method: aCompiledMethod; node: aMethodNode.!

----- Method: CompiledMethodWithNode>>method (in category 'accessing') -----
method
	^ method!

----- Method: CompiledMethodWithNode>>method: (in category 'private') -----
method: aCompiledMethod
	method _ aCompiledMethod!

----- Method: CompiledMethodWithNode>>node (in category 'accessing') -----
node
	^ node!

----- Method: CompiledMethodWithNode>>node: (in category 'private') -----
node: aMethodNode
	node _ aMethodNode!

----- Method: CompiledMethodWithNode>>selector (in category 'accessing') -----
selector
	^ self node selector!

Object subclass: #Compiler
	instanceVariableNames: 'sourceStream requestor class category context parserClass'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-Kernel'!

!Compiler commentStamp: '<historical>' prior: 0!
The compiler accepts Smalltalk source code and compiles it with respect to a given class. The user of the compiler supplies a context so that temporary variables are accessible during compilation. If there is an error, a requestor (usually a kind of StringHolderController) is sent the message notify:at:in: so that the error message can be displayed. If there is no error, then the result of compilation is a MethodNode, which is the root of a parse tree whose nodes are kinds of ParseNodes. The parse tree can be sent messages to (1) generate code for a CompiledMethod (this is done for compiling methods or evaluating expressions); (2) pretty-print the code (for formatting); or (3) produce a map from object code back to source code (used by debugger program-counter selection). See also Parser, Encoder, ParseNode.!

----- Method: Compiler class>>closureDecompilerClass (in category 'accessing') -----
closureDecompilerClass
	^self error: 'not installed'.!

----- Method: Compiler class>>closureParserClass (in category 'accessing') -----
closureParserClass
	^self error: 'not installed'.!

----- Method: Compiler class>>couldEvaluate: (in category 'accessing') -----
couldEvaluate: anObject
	"Answer true if anObject can be passed to my various #evaluate: methods."
	^anObject isString or: [ anObject isText or: [ anObject isStream ]]!

----- Method: Compiler class>>decompilerClass (in category 'accessing') -----
decompilerClass
	^Decompiler!

----- Method: Compiler class>>evaluate: (in category 'evaluating') -----
evaluate: textOrString 
	"See Compiler|evaluate:for:notifying:logged:. If a compilation error occurs, 
	a Syntax Error view is created rather than notifying any requestor. 
	Compilation is carried out with respect to nil, i.e., no object, and the 
	invocation is not logged."

	^self evaluate: textOrString for: nil logged: false!

----- Method: Compiler class>>evaluate:for:logged: (in category 'evaluating') -----
evaluate: textOrString for: anObject logged: logFlag 
	"See Compiler|evaluate:for:notifying:logged:. If a compilation error occurs, 
	a Syntax Error view is created rather than notifying any requestor."

	^self evaluate: textOrString for: anObject notifying: nil logged: logFlag!

----- Method: Compiler class>>evaluate:for:notifying:logged: (in category 'evaluating') -----
evaluate: textOrString for: anObject notifying: aController logged: logFlag
	"Compile and execute the argument, textOrString with respect to the class 
	of anObject. If a compilation error occurs, notify aController. If both 
	compilation and execution are successful then, if logFlag is true, log 
	(write) the text onto a system changes file so that it can be replayed if 
	necessary."

	^ self new
				evaluate: textOrString
				in: nil
				to: anObject
				notifying: aController
				ifFail: [^nil]
				logged: logFlag.!

----- Method: Compiler class>>evaluate:logged: (in category 'evaluating') -----
evaluate: textOrString logged: logFlag 
	"See Compiler|evaluate:for:notifying:logged:. If a compilation error occurs, 
	a Syntax Error view is created rather than notifying any requestor. 
	Compilation is carried out with respect to nil, i.e., no object."

	^self evaluate: textOrString for: nil logged: logFlag!

----- Method: Compiler class>>evaluate:notifying:logged: (in category 'evaluating') -----
evaluate: textOrString notifying: aController logged: logFlag 
	"See Compiler|evaluate:for:notifying:logged:. Compilation is carried out 
	with respect to nil, i.e., no object."

	^self evaluate: textOrString for: nil notifying: aController logged: logFlag!

----- Method: Compiler class>>format:in:notifying:contentsSymbol: (in category 'evaluating') -----
format: textOrStream in: aClass notifying: aRequestor contentsSymbol: aSymbol
	^self new format: textOrStream in: aClass notifying: aRequestor contentsSymbol: aSymbol!

----- Method: Compiler class>>format:in:notifying:decorated: (in category 'evaluating') -----
format: textOrStream in: aClass notifying: aRequestor decorated: aBoolean
	^self new format: textOrStream in: aClass notifying: aRequestor decorated: aBoolean!

----- Method: Compiler class>>new (in category 'accessing') -----
new

	^ super new parserClass: self parserClass!

----- Method: Compiler class>>parserClass (in category 'accessing') -----
parserClass
	"Return a parser class to use for parsing method headers."

	^Parser!

----- Method: Compiler class>>recompileAll (in category 'utilities') -----
recompileAll
	"Recompile all classes, starting with given name."

	Smalltalk forgetDoIts.
	Smalltalk allClassesAndTraits do: [:classOrTrait | classOrTrait compileAll] displayingProgress: 'Recompiling all classes and traits'. 


!

----- Method: Compiler class>>recompileAllFrom: (in category 'utilities') -----
recompileAllFrom: firstName 
	"Recompile all classes, starting with given name."

	Smalltalk forgetDoIts.
	Smalltalk allClassesDo: 
		[:class | class name >= firstName
			ifTrue: 
				[Transcript show: class name; cr.
				class compileAll]]

	"Compiler recompileAllFrom: 'AAABodyShop'."
!

----- Method: Compiler>>compile:in:classified:notifying:ifFail: (in category 'public access') -----
compile: textOrStream in: aClass classified: aCategory notifying: aRequestor ifFail: failBlock 
	"Answer a MethodNode for the argument, textOrStream. If the 
	MethodNode can not be created, notify the argument, aRequestor; if 
	aRequestor is nil, evaluate failBlock instead. The MethodNode is the root 
	of a parse tree. It can be told to generate a CompiledMethod to be 
	installed in the method dictionary of the argument, aClass."
	
	| methodNode |
	self from: textOrStream
		class: aClass
		classified: aCategory 
		context: nil
		notifying: aRequestor.
	methodNode := self translate: sourceStream noPattern: false ifFail: failBlock.
	methodNode encoder requestor: requestor.
	^methodNode.
!

----- Method: Compiler>>compile:in:notifying:ifFail: (in category 'public access') -----
compile: textOrStream in: aClass notifying: aRequestor ifFail: failBlock 
	^self compile: textOrStream in: aClass classified: nil notifying: aRequestor ifFail: failBlock !

----- Method: Compiler>>compileNoPattern:in:context:notifying:ifFail: (in category 'public access') -----
compileNoPattern: textOrStream in: aClass context: aContext notifying: aRequestor ifFail: failBlock
	"Similar to #compile:in:notifying:ifFail:, but the compiled code is
	expected to be a do-it expression, with no message pattern."

	self from: textOrStream
		class: aClass
		context: aContext
		notifying: aRequestor.
	^self
		translate: sourceStream
		noPattern: true
		ifFail: failBlock!

----- Method: Compiler>>compiledMethodFor:in:to: (in category 'public access') -----
compiledMethodFor: aString in: aContext to: aReceiver
	"evaluate aString in the given context, and return the result.  2/2/96 sw"
	| result |
	result _ self
				compiledMethodFor: aString 
				in: aContext 
				to: aReceiver 
				notifying: nil
				ifFail: [^#Failed] 
				logged: false.
	^ result!

----- Method: Compiler>>compiledMethodFor:in:to:notifying:ifFail:logged: (in category 'public access') -----
compiledMethodFor: textOrStream in: aContext to: receiver notifying: aRequestor ifFail: failBlock logged: logFlag
	"Compiles the sourceStream into a parse tree, then generates code into a 
	method. This method is then installed in the receiver's class so that it 
	can be invoked. In other words, if receiver is not nil, then the text can 
	refer to instance variables of that receiver (the Inspector uses this). If 
	aContext is not nil, the text can refer to temporaries in that context (the 
	Debugger uses this). If aRequestor is not nil, then it will receive a 
	notify:at: message before the attempt to evaluate is aborted. Finally, the 
	compiled method is invoked from here as DoIt or (in the case of 
	evaluation in aContext) DoItIn:. The method is subsequently removed 
	from the class, but this will not get done if the invocation causes an 
	error which is terminated. Such garbage can be removed by executing: 
	Smalltalk allBehaviorsDo: [:cl | cl removeSelector: #DoIt; removeSelector: 
	#DoItIn:]."

	| methodNode method |
	class _ (aContext == nil ifTrue: [receiver] ifFalse: [aContext receiver]) class.
	self from: textOrStream class: class context: aContext notifying: aRequestor.
	methodNode _ self translate: sourceStream noPattern: true ifFail:
		[^failBlock value].
	method _ methodNode generate: #(0 0 0 0).
	self interactive ifTrue:
		[method _ method copyWithTempNames: methodNode tempNames].
	
	logFlag ifTrue: [SystemChangeNotifier uniqueInstance evaluated: sourceStream contents context: aContext].
	^ method.!

----- Method: Compiler>>evaluate:in:to: (in category 'public access') -----
evaluate: aString in: aContext to: aReceiver
	"evaluate aString in the given context, and return the result.  2/2/96 sw"
	| result |
	result _ self
				evaluate: aString
				in: aContext
				to: aReceiver
				notifying: nil
				ifFail: [^ #failedDoit].
	^ result!

----- Method: Compiler>>evaluate:in:to:notifying:ifFail: (in category 'public access') -----
evaluate: textOrStream in: aContext to: receiver notifying: aRequestor ifFail: failBlock
	^ self evaluate: textOrStream in: aContext to: receiver notifying: aRequestor ifFail: failBlock logged: false.!

----- Method: Compiler>>evaluate:in:to:notifying:ifFail:logged: (in category 'public access') -----
evaluate: textOrStream in: aContext to: receiver notifying: aRequestor ifFail: failBlock logged: logFlag
	"Compiles the sourceStream into a parse tree, then generates code into a method. In other words, if receiver is not nil, then the text can refer to instance variables of that receiver (the Inspector uses this). If aContext is not nil, the text can refer to temporaries in that context (the Debugger uses this). If aRequestor is not nil, then it will receive a notify:at: message before the attempt to evaluate is aborted. Finally, the compiled method is directly invoked without modifying the receiving-class."

	| methodNode method value toLog itsSelectionString itsSelection |
	class := (aContext isNil 
		ifTrue: [ receiver ] 
		ifFalse: [ aContext receiver ])
			class.
	self from: textOrStream class: class context: aContext notifying: aRequestor.
	methodNode := self 
		translate: sourceStream
		noPattern: true 
		ifFail: [ ^ failBlock value ].
	method := methodNode generate.
	method selector ifNil: [method selector: #DoIt].
	self interactive
		ifTrue: [ method := method copyWithTempNames: methodNode tempNames ].
	value := receiver 
		withArgs: (context isNil
			ifTrue: [ #() ]
			ifFalse: [ Array with: aContext ])
		executeMethod: method.
	logFlag ifTrue:
		[toLog := ((requestor respondsTo: #selection)  and:
			[(itsSelection := requestor selection) notNil] and:
			[(itsSelectionString := itsSelection asString) isEmptyOrNil not] )
			ifTrue: 
				[itsSelectionString]
			ifFalse:
				[sourceStream contents].
		SystemChangeNotifier uniqueInstance evaluated: toLog context: aContext ].
	^ value.!

----- Method: Compiler>>format:in:notifying:contentsSymbol: (in category 'public access') -----
format: textOrStream in: aClass notifying: aRequestor contentsSymbol: aSymbol
	"Compile a parse tree from the argument, textOrStream. Answer a string containing the original code, formatted nicely.  If aSymbol is #colorPrint, then decorate the resulting text with color and hypertext actions"

	^self format: textOrStream in: aClass notifying: aRequestor decorated: (aSymbol == #colorPrint)!

----- Method: Compiler>>format:in:notifying:decorated: (in category 'public access') -----
format: textOrStream in: aClass notifying: aRequestor decorated: aBoolean
	"Compile a parse tree from the argument, textOrStream. Answer a string containing the original code, formatted nicely.  If aBoolean is true, then decorate the resulting text with color and hypertext actions"
	| aNode |
	self from: textOrStream
		class: aClass
		context: nil
		notifying: aRequestor.
	aNode _ self format: sourceStream noPattern: false ifFail: [^ nil].
	^ aBoolean
		ifTrue: [aNode decompileText]
		ifFalse: [aNode decompileString]!

----- Method: Compiler>>format:noPattern:ifFail: (in category 'private') -----
format: aStream noPattern: noPattern ifFail: failBlock
	| tree |
	tree _ 
		self parserClass new
			parse: aStream
			class: class
			noPattern: noPattern
			context: context
			notifying: requestor
			ifFail: [^ failBlock value].
	^ tree!

----- Method: Compiler>>from:class:classified:context:notifying: (in category 'private') -----
from: textOrStream class: aClass classified: aCategory context: aContext notifying: req

	(textOrStream isKindOf: PositionableStream)
		ifTrue: [sourceStream  := textOrStream]
		ifFalse: [sourceStream  := ReadStream on: textOrStream asString].
	class  := aClass.
	context  := aContext.
	requestor  := req.
	category  := aCategory
!

----- Method: Compiler>>from:class:context:notifying: (in category 'private') -----
from: textOrStream class: aClass context: aContext notifying: req

	(textOrStream isKindOf: PositionableStream)
		ifTrue: [sourceStream _ textOrStream]
		ifFalse: [sourceStream _ ReadStream on: textOrStream asString].
	class _ aClass.
	context _ aContext.
	requestor _ req!

----- Method: Compiler>>interactive (in category 'error handling') -----
interactive
	"this version of the method is necessary to load code from MC else the interactive mode is one. 
	This method is really bad since it links the compiler package with the Tools
	one. The solution would be to have a real SyntaxError exception belonging to the 
	compiler package and not a subclass of StringHolder - sd Nov 2005"
	"the code submitted by PlusTools is ideally the one that should be used
	interactive

	      ^requestor ~~ nil "
	
	^ (requestor == nil or: [requestor isKindOf: SyntaxError]) not!

----- Method: Compiler>>notify: (in category 'error handling') -----
notify: aString 
	"Refer to the comment in Object|notify:."

	^self notify: aString at: sourceStream position + 1!

----- Method: Compiler>>notify:at: (in category 'error handling') -----
notify: aString at: location
	"Refer to the comment in Object|notify:."

	requestor == nil
		ifTrue: [^SyntaxErrorNotification
					inClass: class
					category: category
					withCode: 
						(sourceStream contents
							copyReplaceFrom: location
							to: location - 1
							with: aString)
					doitFlag: false]
		ifFalse: [^requestor
					notify: aString
					at: location
					in: sourceStream]!

----- Method: Compiler>>parse:in:notifying: (in category 'public access') -----
parse: textOrStream in: aClass notifying: req
	"Compile the argument, textOrStream, with respect to the class, aClass, 
	and answer the MethodNode that is the root of the resulting parse tree. 
	Notify the argument, req, if an error occurs. The failBlock is defaulted to 
	an empty block."

	  self from: textOrStream class: aClass context: nil notifying: req.
       ^self parserClass new
                        parse: sourceStream
                        class: class
                        noPattern: false
                        context: context
                        notifying: requestor
                        ifFail: []!

----- Method: Compiler>>parserClass (in category 'private') -----
parserClass

	^ parserClass!

----- Method: Compiler>>parserClass: (in category 'private') -----
parserClass: aParserClass

	parserClass _ aParserClass.
!

----- Method: Compiler>>translate:noPattern:ifFail: (in category 'private') -----
translate: aStream noPattern: noPattern ifFail: failBlock
	| tree |
	tree  := 
		self parserClass new
			parse: aStream
			class: class
			category: category
			noPattern: noPattern
			context: context
			notifying: requestor
			ifFail: [^ failBlock value].
	^ tree
!

----- Method: Compiler>>translate:noPattern:ifFail:parser: (in category 'public access') -----
translate: aStream noPattern: noPattern ifFail: failBlock parser: parser
	| tree |
	tree := parser
			parse: aStream
			class: class
			noPattern: noPattern
			context: context
			notifying: requestor
			ifFail: [^ failBlock value].
	^ tree!

Object subclass: #ParseNode
	instanceVariableNames: 'comment pc'
	classVariableNames: 'Jmp SendType Store Dup JmpLong EndRemote Send NodeSelf LdTrue BtpLong LdInstLong SendLong StdLiterals StdVariables LdThisContext StorePop SendLimit JmpLimit SendPlus Pop NodeNil LdLitIndType SendLong2 DblExtDoAll LoadLong LdLitType NodeTrue LdNil EndMethod LongLongDoAll LdSelf LdSuper NodeSuper CodeLimits LdMinus1 LdTempType StdSelectors LdFalse CodeBases ShortStoP NodeFalse NodeThisContext LdInstType Bfp'
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

!ParseNode commentStamp: '<historical>' prior: 0!
This superclass of most compiler/decompiler classes declares common class variables, default messages, and the code emitters for jumps. Some of the class variables are initialized here; the rest are initialized in class VariableNode.!

ParseNode subclass: #AssignmentNode
	instanceVariableNames: 'variable value'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

!AssignmentNode commentStamp: '<historical>' prior: 0!
AssignmentNode comment: 'I represent a (var_expr) construct.'!

----- Method: AssignmentNode>>emitForEffect:on: (in category 'code generation') -----
emitForEffect: stack on: aStream

	variable emitLoad: stack on: aStream.
	value emitForValue: stack on: aStream.
	variable emitStorePop: stack on: aStream.
	pc _ aStream position!

----- Method: AssignmentNode>>emitForValue:on: (in category 'code generation') -----
emitForValue: stack on: aStream

	variable emitLoad: stack on: aStream.
	value emitForValue: stack on: aStream.
	variable emitStore: stack on: aStream.
	pc _ aStream position!

----- Method: AssignmentNode>>printOn:indent: (in category 'printing') -----
printOn: aStream indent: level 
	variable printOn: aStream indent: level.
	aStream nextPutAll: ' := '.
	value printOn: aStream indent: level + 2.!

----- Method: AssignmentNode>>printOn:indent:precedence: (in category 'printing') -----
printOn: aStream indent: level precedence: p

	p < 4
		ifTrue: [aStream nextPutAll: '('.
				self printOn: aStream indent: level.
				aStream nextPutAll: ')']
		ifFalse: [self printOn: aStream indent: level]!

----- Method: AssignmentNode>>sizeForEffect: (in category 'code generation') -----
sizeForEffect: encoder

	^(value sizeForValue: encoder)
		+ (variable sizeForStorePop: encoder)!

----- Method: AssignmentNode>>sizeForValue: (in category 'code generation') -----
sizeForValue: encoder

	^(value sizeForValue: encoder)
		+ (variable sizeForStore: encoder)!

----- Method: AssignmentNode>>toDoIncrement: (in category 'initialize-release') -----
toDoIncrement: var
	var = variable ifFalse: [^ nil].
	(value isMemberOf: MessageNode) 
		ifTrue: [^ value toDoIncrement: var]
		ifFalse: [^ nil]!

----- Method: AssignmentNode>>value (in category 'initialize-release') -----
value
	^ value!

----- Method: AssignmentNode>>variable (in category 'equation translation') -----
variable
	^variable!

----- Method: AssignmentNode>>variable:value: (in category 'initialize-release') -----
variable: aVariable value: expression

	variable _ aVariable.
	value _ expression!

----- Method: AssignmentNode>>variable:value:from: (in category 'initialize-release') -----
variable: aVariable value: expression from: encoder

	(aVariable isMemberOf: MessageAsTempNode)
		ifTrue: ["Case of remote temp vars"
				^ aVariable store: expression from: encoder].
	variable _ aVariable.
	value _ expression!

----- Method: AssignmentNode>>variable:value:from:sourceRange: (in category 'initialize-release') -----
variable: aVariable value: expression from: encoder sourceRange: range

	encoder noteSourceRange: range forNode: self.
	^self
		variable: aVariable
		value: expression
		from: encoder!

ParseNode subclass: #BlockArgsNode
	instanceVariableNames: 'temporaries'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

ParseNode subclass: #BlockNode
	instanceVariableNames: 'arguments statements returns nArgsNode size remoteCopyNode temporaries'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

!BlockNode commentStamp: '<historical>' prior: 0!
I represent a bracketed block with 0 or more arguments and 1 or more statements. If I am initialized with no statements, I create one. I have a flag to tell whether my last statement returns a value from the enclosing method. My last three fields remember data needed for code generation. I can emit for value in the usual way, in which case I create a literal method (actually a context remotely copied) to be evaluated by sending it value: at run time. Or I can emit code to be evaluated in line; this only happens at the top level of a method and in conditionals and while-loops, none of which have arguments.!

----- Method: BlockNode class>>statements:returns: (in category 'instance creation') -----
statements: statements returns: returns
	^ self new statements: statements returns: returns!

----- Method: BlockNode class>>withJust: (in category 'instance creation') -----
withJust: aNode
	^ self statements: (OrderedCollection with: aNode) returns: false!

----- Method: BlockNode>>arguments: (in category 'accessing') -----
arguments: argNodes 
	"Decompile."

	arguments _ argNodes!

----- Method: BlockNode>>arguments:statements:returns:from: (in category 'initialize-release') -----
arguments: argNodes statements: statementsCollection returns: returnBool from: encoder
	"Compile."

	arguments _ argNodes.
	statements _ statementsCollection size > 0
				ifTrue: [statementsCollection]
				ifFalse: [argNodes size > 0
						ifTrue: [statementsCollection copyWith: arguments last]
						ifFalse: [Array with: NodeNil]].
	returns _ returnBool!

----- Method: BlockNode>>arguments:statements:returns:from:sourceRange: (in category 'initialize-release') -----
arguments: argNodes statements: statementsCollection returns: returnBool from: encoder sourceRange: range
	"Compile."

	encoder noteSourceRange: range forNode: self.
	^self
		arguments: argNodes
		statements: statementsCollection
		returns: returnBool
		from: encoder!

----- Method: BlockNode>>block (in category 'accessing') -----
block
	^ self!

----- Method: BlockNode>>canBeSpecialArgument (in category 'testing') -----
canBeSpecialArgument
	"Can I be an argument of (e.g.) ifTrue:?"

	^arguments size = 0!

----- Method: BlockNode>>code (in category 'code generation') -----
code

	^statements first code!

----- Method: BlockNode>>decompileString (in category 'printing') -----
decompileString 
	"Answer a string description of the parse tree whose root is the receiver."

	^ self decompileText asString
!

----- Method: BlockNode>>decompileText (in category 'printing') -----
decompileText
	"Answer a text description of the parse tree whose root is the receiver."

	^ ColoredCodeStream contents: [:strm | self printOn: strm indent: 0]
!

----- Method: BlockNode>>emitExceptLast:on: (in category 'code generation') -----
emitExceptLast: stack on: aStream
	| nextToLast |
	nextToLast _ statements size - 1.
	nextToLast < 1 ifTrue: [^ self].  "Only one statement"
	1 to: nextToLast do:
		[:i | (statements at: i) emitForEffect: stack on: aStream].
!

----- Method: BlockNode>>emitForEvaluatedEffect:on: (in category 'code generation') -----
emitForEvaluatedEffect: stack on: aStream

	self returns
		ifTrue: 
			[self emitForEvaluatedValue: stack on: aStream.
			stack pop: 1]
		ifFalse: 
			[self emitExceptLast: stack on: aStream.
			statements last emitForEffect: stack on: aStream]!

----- Method: BlockNode>>emitForEvaluatedValue:on: (in category 'code generation') -----
emitForEvaluatedValue: stack on: aStream
	self emitExceptLast: stack on: aStream.
	statements last emitForValue: stack on: aStream.
!

----- Method: BlockNode>>emitForValue:on: (in category 'code generation') -----
emitForValue: stack on: aStream

	aStream nextPut: LdThisContext.
	stack push: 1.
	nArgsNode emitForValue: stack on: aStream.
	remoteCopyNode
		emit: stack
		args: 1
		on: aStream.
	"Force a two byte jump."
	self emitLong: size code: JmpLong on: aStream.
	stack push: arguments size.
	arguments reverseDo: [:arg | arg emitStorePop: stack on: aStream].
	self emitForEvaluatedValue: stack on: aStream.
	self returns ifFalse: [
		aStream nextPut: EndRemote.
		pc _ aStream position.
	].
	stack pop: 1!

----- Method: BlockNode>>firstArgument (in category 'accessing') -----
firstArgument
	^ arguments first!

----- Method: BlockNode>>isComplex (in category 'testing') -----
isComplex

	^statements size > 1 or: [statements size = 1 and: [statements first isComplex]]!

----- Method: BlockNode>>isJust: (in category 'testing') -----
isJust: node

	returns ifTrue: [^false].
	^statements size = 1 and: [statements first == node]!

----- Method: BlockNode>>isJustCaseError (in category 'testing') -----
isJustCaseError

	^ statements size = 1 and:
		[statements first
			isMessage: #caseError
			receiver: [:r | r==NodeSelf]
			arguments: nil]!

----- Method: BlockNode>>isQuick (in category 'testing') -----
isQuick
	^ statements size = 1
		and: [statements first isVariableReference
				or: [statements first isSpecialConstant]]!

----- Method: BlockNode>>numberOfArguments (in category 'accessing') -----
numberOfArguments

	^arguments size!

----- Method: BlockNode>>printArgumentsOn:indent: (in category 'printing') -----
printArgumentsOn: aStream indent: level
	arguments size = 0
		ifTrue: [^ self].
	arguments do: [:arg | aStream
						withStyleFor: #blockArgument
						do: [aStream nextPutAll: ':';
								 nextPutAll: arg key;
								 space
							]
					].
	aStream nextPutAll: '| '.

	"If >0 args and >1 statement, put all statements on separate lines"
	statements size > 1
		ifTrue: [aStream crtab: level]!

----- Method: BlockNode>>printOn:indent: (in category 'printing') -----
printOn: aStream indent: level

	aStream nextPut: $[.
	self printArgumentsOn: aStream indent: level.
	self printTemporariesOn: aStream indent: level.
	self printStatementsOn: aStream indent: level.
	aStream nextPut: $]!

----- Method: BlockNode>>printStatementsOn:indent: (in category 'printing') -----
printStatementsOn: aStream indent: levelOrZero
	| len shown thisStatement level |
	level _ 1 max: levelOrZero.
	comment == nil
		ifFalse: 
			[self printCommentOn: aStream indent: level.
			aStream crtab: level].
	len _ shown _ statements size.
	(levelOrZero = 0 "top level" and: [statements last isReturnSelf])
		ifTrue: [shown _ 1 max: shown - 1]
		ifFalse: [(len = 1 and: [((statements at: 1) == NodeNil) & (arguments size = 0)])
					ifTrue: [shown _ shown - 1]].
	1 to: shown do: 
		[:i | 
		thisStatement _ statements at: i.
		thisStatement printOn: aStream indent: level.
		i < shown ifTrue: [aStream nextPut: $.; crtab: level].
		(thisStatement comment ~~ nil and: [thisStatement comment size > 0])
			ifTrue: 
				[i = shown ifTrue: [aStream crtab: level].
				thisStatement printCommentOn: aStream indent: level.
				i < shown ifTrue: [aStream crtab: level]]]!

----- Method: BlockNode>>printTemporariesOn:indent: (in category 'printing') -----
printTemporariesOn: aStream indent: level

	(temporaries == nil or: [temporaries size = 0])
		ifFalse: 
			[aStream nextPut: $|.
			temporaries do: 
				[:arg | 
				aStream
					space;
					withStyleFor: #temporaryVariable
						do: [aStream nextPutAll: arg key]].
			aStream nextPutAll: ' | '.
			"If >0 args and >1 statement, put all statements on separate lines"
			statements size > 1 ifTrue: [aStream crtab: level]]!

----- Method: BlockNode>>returnLast (in category 'accessing') -----
returnLast

	self returns
		ifFalse: 
			[returns _ true.
			statements at: statements size put: statements last asReturnNode]!

----- Method: BlockNode>>returnSelfIfNoOther (in category 'accessing') -----
returnSelfIfNoOther

	self returns
		ifFalse: 
			[statements last == NodeSelf ifFalse: [statements add: NodeSelf].
			self returnLast]!

----- Method: BlockNode>>returns (in category 'testing') -----
returns

	^returns or: [statements last isReturningIf]!

----- Method: BlockNode>>sizeExceptLast: (in category 'code generation') -----
sizeExceptLast: encoder
	| codeSize nextToLast |
	nextToLast _ statements size - 1.
	nextToLast < 1 ifTrue: [^ 0]. "Only one statement"
	codeSize _ 0.
	1 to: nextToLast do: 
		[:i | codeSize _ codeSize + ((statements at: i) sizeForEffect: encoder)].
	^ codeSize!

----- Method: BlockNode>>sizeForEvaluatedEffect: (in category 'code generation') -----
sizeForEvaluatedEffect: encoder

	self returns ifTrue: [^self sizeForEvaluatedValue: encoder].
	^(self sizeExceptLast: encoder)
		+ (statements last sizeForEffect: encoder)!

----- Method: BlockNode>>sizeForEvaluatedValue: (in category 'code generation') -----
sizeForEvaluatedValue: encoder

	^(self sizeExceptLast: encoder)
		+ (statements last sizeForValue: encoder)!

----- Method: BlockNode>>sizeForValue: (in category 'code generation') -----
sizeForValue: encoder
	nArgsNode _ encoder encodeLiteral: arguments size.
	remoteCopyNode _ encoder encodeSelector: #blockCopy:.
	size _ (self sizeForEvaluatedValue: encoder)
				+ (self returns ifTrue: [0] ifFalse: [1]). "endBlock"
	arguments _ arguments collect:  "Chance to prepare debugger remote temps"
				[:arg | arg asStorableNode: encoder].
	arguments do: [:arg | size _ size + (arg sizeForStorePop: encoder)].
	^1 + (nArgsNode sizeForValue: encoder) 
		+ (remoteCopyNode size: encoder args: 1 super: false) + 2 + size!

----- Method: BlockNode>>statements (in category 'equation translation') -----
statements
	^statements!

----- Method: BlockNode>>statements: (in category 'equation translation') -----
statements: val
	statements _ val!

----- Method: BlockNode>>statements:returns: (in category 'initialize-release') -----
statements: statementsCollection returns: returnBool 
	"Decompile."

	| returnLast |
	returnLast _ returnBool.
	returns _ false.
	statements _ 
		(statementsCollection size > 1 
			and: [(statementsCollection at: statementsCollection size - 1) 
					isReturningIf])
				ifTrue: 
					[returnLast _ false.
					statementsCollection allButLast]
				ifFalse: [statementsCollection size = 0
						ifTrue: [Array with: NodeNil]
						ifFalse: [statementsCollection]].
	arguments _ #().
	temporaries _ #().
	returnLast ifTrue: [self returnLast]!

----- Method: BlockNode>>temporaries: (in category 'accessing') -----
temporaries: aCollection
	temporaries _ aCollection!

ParseNode subclass: #BraceNode
	instanceVariableNames: 'elements sourceLocations emitNode'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

!BraceNode commentStamp: '<historical>' prior: 0!
Used for compiling and decompiling brace constructs.

These now compile into either a fast short form for 4 elements or less:
	Array braceWith: a with: b ... 
or a long form of indefinfite length:
	(Array braceStream: N) nextPut: a; nextPut: b; ...; braceArray.

The erstwhile brace assignment form is no longer supported.!

----- Method: BraceNode class>>example (in category 'examples') -----
example
	"Test the {a. b. c} syntax."

	| x |
	x _ {1. {2. 3}. 4}.
	^ {x first. x second first. x second last. x last. 5} as: Set

"BraceNode example Set (0 1 2 3 4 5 )"
!

----- Method: BraceNode>>blockAssociationCheck: (in category 'testing') -----
blockAssociationCheck: encoder
	"If all elements are MessageNodes of the form [block]->[block], and there is at
	 least one element, answer true.
	 Otherwise, notify encoder of an error."

	elements size = 0
		ifTrue: [^encoder notify: 'At least one case required'].
	elements with: sourceLocations do:
			[:x :loc |
			(x 	isMessage: #->
				receiver:
					[:rcvr |
					(rcvr isKindOf: BlockNode) and: [rcvr numberOfArguments = 0]]
				arguments:
					[:arg |
					(arg isKindOf: BlockNode) and: [arg numberOfArguments = 0]])
			  ifFalse:
				[^encoder notify: 'Association between 0-argument blocks required' at: loc]].
	^true!

----- Method: BraceNode>>casesForwardDo: (in category 'enumerating') -----
casesForwardDo: aBlock
	"For each case in forward order, evaluate aBlock with three arguments:
	 the key block, the value block, and whether it is the last case."

	| numCases case |
	1 to: (numCases _ elements size) do:
		[:i |
		case _ elements at: i.
		aBlock value: case receiver value: case arguments first value: i=numCases]!

----- Method: BraceNode>>casesReverseDo: (in category 'enumerating') -----
casesReverseDo: aBlock
	"For each case in reverse order, evaluate aBlock with three arguments:
	 the key block, the value block, and whether it is the last case."

	| numCases case |
	(numCases _ elements size) to: 1 by: -1 do:
		[:i |
		case _ elements at: i.
		aBlock value: case receiver value: case arguments first value: i=numCases]!

----- Method: BraceNode>>elements: (in category 'initialize-release') -----
elements: collection
	"Decompile."

	elements _ collection!

----- Method: BraceNode>>elements:sourceLocations: (in category 'initialize-release') -----
elements: collection sourceLocations: locations
	"Compile."

	elements _ collection.
	sourceLocations _ locations!

----- Method: BraceNode>>emitForValue:on: (in category 'code generation') -----
emitForValue: stack on: aStream

	^ emitNode emitForValue: stack on: aStream!

----- Method: BraceNode>>matchBraceStreamReceiver:messages: (in category 'initialize-release') -----
matchBraceStreamReceiver: receiver messages: messages

	((receiver isMessage: #braceStream: receiver: nil arguments: [:arg | arg isConstantNumber])
		and: [messages last isMessage: #braceArray receiver: nil arguments: nil])
		ifFalse: [^ nil "no match"].

	"Appears to be a long form brace construct"
	self elements: (messages allButLast collect:
		[:msg | (msg isMessage: #nextPut: receiver: nil arguments: nil)
					ifFalse: [^ nil "not a brace element"].
		msg arguments first])!

----- Method: BraceNode>>matchBraceWithReceiver:selector:arguments: (in category 'initialize-release') -----
matchBraceWithReceiver: receiver selector: selector arguments: arguments

	selector = (self selectorForShortForm: arguments size)
		ifFalse: [^ nil "no match"].

	"Appears to be a short form brace construct"
	self elements: arguments!

----- Method: BraceNode>>numElements (in category 'testing') -----
numElements

	^ elements size!

----- Method: BraceNode>>printOn:indent: (in category 'printing') -----
printOn: aStream indent: level

	aStream nextPut: ${.
	1 to: elements size do: 
		[:i | (elements at: i) printOn: aStream indent: level.
		i < elements size ifTrue: [aStream nextPutAll: '. ']].
	aStream nextPut: $}!

----- Method: BraceNode>>selectorForShortForm: (in category 'code generation') -----
selectorForShortForm: nElements

	nElements > 4 ifTrue: [^ nil].
	^ #(braceWithNone braceWith: braceWith:with:
			braceWith:with:with: braceWith:with:with:with:) at: nElements + 1!

----- Method: BraceNode>>sizeForValue: (in category 'code generation') -----
sizeForValue: encoder

	emitNode _ elements size <= 4
		ifTrue: ["Short form: Array braceWith: a with: b ... "
				MessageNode new
					receiver: (encoder encodeVariable: #Array)
					selector: (self selectorForShortForm: elements size)
					arguments: elements precedence: 3 from: encoder]
		ifFalse: ["Long form: (Array braceStream: N) nextPut: a; nextPut: b; ...; braceArray"
				CascadeNode new
					receiver: (MessageNode new
								receiver: (encoder encodeVariable: #Array)
								selector: #braceStream:
								arguments: (Array with: (encoder encodeLiteral: elements size))
								precedence: 3 from: encoder)
					messages: ((elements collect: [:elt | MessageNode new receiver: nil
														selector: #nextPut:
														arguments: (Array with: elt)
														precedence: 3 from: encoder])
								copyWith: (MessageNode new receiver: nil
														selector: #braceArray
														arguments: (Array new)
														precedence: 1 from: encoder))].
	^ emitNode sizeForValue: encoder!

ParseNode subclass: #CascadeNode
	instanceVariableNames: 'receiver messages'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

!CascadeNode commentStamp: '<historical>' prior: 0!
The first message has the common receiver, the rest have receiver == nil, which signifies cascading.!

----- Method: CascadeNode>>emitForValue:on: (in category 'code generation') -----
emitForValue: stack on: aStream

	receiver emitForValue: stack on: aStream.
	1 to: messages size - 1 do: 
		[:i | 
		aStream nextPut: Dup.
		stack push: 1.
		(messages at: i) emitForValue: stack on: aStream.
		aStream nextPut: Pop.
		stack pop: 1].
	messages last emitForValue: stack on: aStream!

----- Method: CascadeNode>>printOn:indent: (in category 'printing') -----
printOn: aStream indent: level
	self printOn: aStream indent: level precedence: 0!

----- Method: CascadeNode>>printOn:indent:precedence: (in category 'printing') -----
printOn: aStream indent: level precedence: p 

	p > 0 ifTrue: [aStream nextPut: $(].
	messages first printReceiver: receiver on: aStream indent: level.
	1 to: messages size do: 
		[:i | (messages at: i) printOn: aStream indent: level.
		i < messages size ifTrue: 
				[aStream nextPut: $;.
				messages first precedence >= 2 ifTrue: [aStream crtab: level + 1]]].
	p > 0 ifTrue: [aStream nextPut: $)]!

----- Method: CascadeNode>>receiver (in category 'accessing') -----
receiver
	^receiver!

----- Method: CascadeNode>>receiver:messages: (in category 'initialize-release') -----
receiver: receivingObject messages: msgs
	" Transcript show: 'abc'; cr; show: 'def' "

	receiver _ receivingObject.
	messages _ msgs!

----- Method: CascadeNode>>sizeForValue: (in category 'code generation') -----
sizeForValue: encoder

	| size |
	size _ (receiver sizeForValue: encoder) + (messages size - 1 * 2).
	messages do: [:aMessage | size _ size + (aMessage sizeForValue: encoder)].
	^size!

ParseNode subclass: #CommentNode
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

ParseNode subclass: #DecompilerConstructor
	instanceVariableNames: 'method instVars nArgs literalValues tempVars'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-Support'!

!DecompilerConstructor commentStamp: '<historical>' prior: 0!
I construct the node tree for a Decompiler.!

----- Method: DecompilerConstructor>>codeAnyLitInd: (in category 'constructor') -----
codeAnyLitInd: association

	^VariableNode new
		name: association key
		key: association
		index: 0
		type: LdLitIndType!

----- Method: DecompilerConstructor>>codeAnyLiteral: (in category 'constructor') -----
codeAnyLiteral: value

	^LiteralNode new
		key: value
		index: 0
		type: LdLitType!

----- Method: DecompilerConstructor>>codeAnySelector: (in category 'constructor') -----
codeAnySelector: selector

	^SelectorNode new
		key: selector
		index: 0
		type: SendType!

----- Method: DecompilerConstructor>>codeArguments:block: (in category 'constructor') -----
codeArguments: args block: block

	^block arguments: args!

----- Method: DecompilerConstructor>>codeAssignTo:value: (in category 'constructor') -----
codeAssignTo: variable value: expression

	^AssignmentNode new variable: variable value: expression!

----- Method: DecompilerConstructor>>codeBlock:returns: (in category 'constructor') -----
codeBlock: statements returns: returns
	^ BlockNode statements: statements returns: returns!

----- Method: DecompilerConstructor>>codeBrace: (in category 'constructor') -----
codeBrace: elements

	^BraceNode new elements: elements!

----- Method: DecompilerConstructor>>codeCascade:messages: (in category 'constructor') -----
codeCascade: receiver messages: messages

	^ (BraceNode new matchBraceStreamReceiver: receiver messages: messages)
		ifNil: [CascadeNode new receiver: receiver messages: messages]!

----- Method: DecompilerConstructor>>codeCascadedMessage:arguments: (in category 'constructor') -----
codeCascadedMessage: selector arguments: arguments

	^self
		codeMessage: nil
		selector: selector
		arguments: arguments!

----- Method: DecompilerConstructor>>codeConstants (in category 'constructor') -----
codeConstants
	"Answer with an array of the objects representing self, true, false, nil,
	-1, 0, 1, 2."

	^(Array with: NodeSelf with: NodeTrue with: NodeFalse with: NodeNil)
		, ((-1 to: 2) collect: [:i | LiteralNode new key: i code: LdMinus1 + i + 1])!

----- Method: DecompilerConstructor>>codeEmptyBlock (in category 'constructor') -----
codeEmptyBlock
	^ BlockNode withJust: NodeNil!

----- Method: DecompilerConstructor>>codeInst: (in category 'constructor') -----
codeInst: index

	^VariableNode new
		name: (instVars at: index + 1 ifAbsent: ['unknown', index asString])
		index: index
		type: LdInstType!

----- Method: DecompilerConstructor>>codeMessage:selector:arguments: (in category 'constructor') -----
codeMessage: receiver selector: selector arguments: arguments
	| symbol node |
	symbol _ selector key.
	(node _ BraceNode new
			matchBraceWithReceiver: receiver
			selector: symbol
			arguments: arguments) ifNotNil: [^ node].
	(node _ self decodeIfNilWithReceiver: receiver
			selector: symbol
			arguments: arguments) ifNotNil: [^ node].
	^ MessageNode new
			receiver: receiver selector: selector
			arguments: arguments
			precedence: symbol precedence!

----- Method: DecompilerConstructor>>codeMethod:block:tempVars:primitive:class: (in category 'constructor') -----
codeMethod: selector block: block tempVars: vars primitive: primitive class: class
	| node methodTemps |
	node _ self codeSelector: selector code: nil.
	tempVars _ vars.
	methodTemps _ tempVars select: [:t | t scope >= 0].
	^MethodNode new
		selector: node
		arguments: (methodTemps copyFrom: 1 to: nArgs)
		precedence: selector precedence
		temporaries: (methodTemps copyFrom: nArgs + 1 to: methodTemps size)
		block: block
		encoder: (Encoder new initScopeAndLiteralTables
					temps: tempVars
					literals: literalValues
					class: class)
		primitive: primitive
		properties: method properties.!

----- Method: DecompilerConstructor>>codeSelector:code: (in category 'constructor') -----
codeSelector: sel code: code

	^SelectorNode new key: sel code: code!

----- Method: DecompilerConstructor>>codeSuper (in category 'constructor') -----
codeSuper

	^NodeSuper!

----- Method: DecompilerConstructor>>codeTemp: (in category 'constructor') -----
codeTemp: index

	^ TempVariableNode new
		name: 't' , (index + 1) printString
		index: index
		type: LdTempType
		scope: 0!

----- Method: DecompilerConstructor>>codeTemp:named: (in category 'constructor') -----
codeTemp: index named: tempName

	^ TempVariableNode new
		name: tempName
		index: index
		type: LdTempType
		scope: 0!

----- Method: DecompilerConstructor>>codeThisContext (in category 'constructor') -----
codeThisContext

	^NodeThisContext!

----- Method: DecompilerConstructor>>decodeIfNilWithReceiver:selector:arguments: (in category 'constructor') -----
decodeIfNilWithReceiver: receiver selector: selector arguments: arguments

	selector == #ifTrue:ifFalse:
		ifFalse: [^ nil].
	(receiver isMessage: #==
				receiver: nil
				arguments: [:argNode | argNode == NodeNil])
		ifFalse: [^ nil].
	^ (MessageNode new
			receiver: receiver
			selector: (SelectorNode new key: #ifTrue:ifFalse: code: #macro)
			arguments: arguments
			precedence: 3)
		noteSpecialSelector: #ifNil:ifNotNil:!

----- Method: DecompilerConstructor>>method:class:literals: (in category 'initialize-release') -----
method: aMethod class: aClass literals: literals

	method _ aMethod.
	instVars _ aClass allInstVarNames.
	nArgs _ method numArgs.
	literalValues _ literals!

ParseNode subclass: #Encoder
	instanceVariableNames: 'scopeTable nTemps supered requestor class literalStream selectorSet litIndSet litSet sourceRanges globalSourceRanges'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-Kernel'!

!Encoder commentStamp: '<historical>' prior: 0!
I encode names and literals into tree nodes with byte codes for the compiler. Byte codes for literals are not assigned until the tree-sizing pass of the compiler, because only then is it known which literals are actually needed. I also keep track of sourceCode ranges during parsing and code generation so I can provide an inverse map for the debugger.!

----- Method: Encoder>>allLiterals (in category 'results') -----
allLiterals
	(literalStream isKindOf: WriteStream) ifTrue: [
		self litIndex: nil.
		self litIndex: class binding .
	].
	^ literalStream contents!

----- Method: Encoder>>autoBind: (in category 'temps') -----
autoBind: name 
	"Declare a block argument as a temp if not already declared."
	| node |
	node _ scopeTable 
			at: name
			ifAbsent: 
				[(self lookupInPools: name ifFound: [:assoc | assoc])
					ifTrue: [self notify: 'Name already used in a Pool or Global'].
				^ (self reallyBind: name) nowHasDef nowHasRef scope: 1].
	node isTemp
		ifTrue: [node scope >= 0 ifTrue:
					[^ self notify: 'Name already used in this method'].
				node nowHasDef nowHasRef scope: 1]
		ifFalse: [^ self notify: 'Name already used in this class'].
	^node!

----- Method: Encoder>>bindAndJuggle: (in category 'temps') -----
bindAndJuggle: name

	| node nodes first thisCode |
	node _ self reallyBind: name.

	"Declared temps must precede block temps for decompiler and debugger to work right"
	nodes _ self tempNodes.
	(first _ nodes findFirst: [:n | n scope > 0]) > 0 ifTrue:
		[node == nodes last ifFalse: [self error: 'logic error'].
		thisCode _ (nodes at: first) code.
		first to: nodes size - 1 do:
			[:i | (nodes at: i) key: (nodes at: i) key
							code: (nodes at: i+1) code].
		nodes last key: nodes last key code: thisCode].
	
	^ node!

----- Method: Encoder>>bindArg: (in category 'temps') -----
bindArg: name 
	"Declare an argument."
	| node |
	nTemps >= 15
		ifTrue: [^self notify: 'Too many arguments'].
	node _ self bindTemp: name.
	^ node nowHasDef nowHasRef!

----- Method: Encoder>>bindBlockTemp: (in category 'temps') -----
bindBlockTemp: name 
	"Declare a temporary block variable; complain if it's not a field or class variable."

	| node |

	node _ scopeTable at: name ifAbsent: [^self reallyBind: name].
	node isTemp
		ifTrue: [
			node scope >= 0 ifTrue: [^ self notify: 'Name already used in this method'].
			node scope: 0]
		ifFalse: [^self notify: 'Name already used in this class'].
	^node
!

----- Method: Encoder>>bindTemp: (in category 'temps') -----
bindTemp: name 
	"Declare a temporary; error not if a field or class variable."
	scopeTable at: name ifPresent:[:node|
		"When non-interactive raise the error only if its a duplicate"
		(node isTemp or:[requestor interactive])
			ifTrue:[^self notify:'Name is already defined']
			ifFalse:[Transcript 
				show: '(', name, ' is shadowed in "' , class printString, '")']].
	^self reallyBind: name!

----- Method: Encoder>>bindTemp:in: (in category 'temps') -----
bindTemp: name in: methodSelector
	"Declare a temporary; error not if a field or class variable."
	scopeTable at: name ifPresent:[:node|
		"When non-interactive raise the error only if its a duplicate"
		(node isTemp or:[requestor interactive])
			ifTrue:[^self notify:'Name is already defined']
			ifFalse:[Transcript 
				show: '(', name, ' is shadowed in "' , class printString , '>>' , methodSelector printString , '")']].
	^self reallyBind: name!

----- Method: Encoder>>cantStoreInto: (in category 'encoding') -----
cantStoreInto: varName

	^StdVariables includesKey: varName!

----- Method: Encoder>>classEncoding (in category 'private') -----
classEncoding
	"This is a hack so that the parser may findout what class it was parsing for when it wants to create a syntax error view."
	^ class!

----- Method: Encoder>>encodeLiteral: (in category 'encoding') -----
encodeLiteral: object

	^self
		name: object
		key: (class literalScannedAs: object notifying: self)
		class: LiteralNode
		type: LdLitType
		set: litSet!

----- Method: Encoder>>encodeSelector: (in category 'encoding') -----
encodeSelector: selector

	^self
		name: selector
		key: selector
		class: SelectorNode
		type: SendType
		set: selectorSet!

----- Method: Encoder>>encodeVariable: (in category 'encoding') -----
encodeVariable: name
	^ self encodeVariable: name sourceRange: nil ifUnknown: [ self undeclared: name ]!

----- Method: Encoder>>encodeVariable:ifUnknown: (in category 'encoding') -----
encodeVariable: name ifUnknown: action
	^self encodeVariable: name sourceRange: nil ifUnknown: action!

----- Method: Encoder>>encodeVariable:sourceRange:ifUnknown: (in category 'encoding') -----
encodeVariable: name sourceRange: range ifUnknown: action
	| varNode |
	varNode _ scopeTable at: name
			ifAbsent: 
				[(self lookupInPools: name 
					ifFound: [:assoc | varNode _ self global: assoc name: name])
					ifTrue: [varNode]
					ifFalse: [action value]].
	range ifNotNil: [
		name first canBeGlobalVarInitial ifTrue:
			[globalSourceRanges addLast: { name. range. false }]. ].

	(varNode isTemp and: [varNode scope < 0]) ifTrue: [
		OutOfScopeNotification signal ifFalse: [ ^self notify: 'out of scope'].
	].
	^ varNode!

----- Method: Encoder>>fillDict:with:mapping:to: (in category 'initialize-release') -----
fillDict: dict with: nodeClass mapping: keys to: codeArray
	| codeStream |
	codeStream _ ReadStream on: codeArray.
	keys do: 
		[:key | dict 
				at: key
				put:  (nodeClass new name: key key: key code: codeStream next)]!

----- Method: Encoder>>global:name: (in category 'private') -----
global: ref name: name

	^self
		name: name
		key: ref
		class: LiteralVariableNode
		type: LdLitIndType
		set: litIndSet!

----- Method: Encoder>>globalSourceRanges (in category 'source mapping') -----
globalSourceRanges

	^ globalSourceRanges!

----- Method: Encoder>>init:context:notifying: (in category 'initialize-release') -----
init: aClass context: aContext notifying: req
	| node n homeNode indexNode |
	requestor _ req.
	class _ aClass.
	nTemps _ 0.
	supered _ false.
	self initScopeAndLiteralTables.
	n _ -1.
	class allInstVarNames do: 
		[:variable | 
		node _ VariableNode new
					name: variable
					index: (n _ n + 1)
					type: LdInstType.
		scopeTable at: variable put: node].
	aContext == nil
		ifFalse: 
			[homeNode _ self bindTemp: 'homeContext'.
			"first temp = aContext passed as arg"
			n _ 0.
			aContext tempNames do: 
				[:variable | 
				indexNode _ self encodeLiteral: (n _ n + 1).
				node _ MessageAsTempNode new
							receiver: homeNode
							selector: #tempAt:
							arguments: (Array with: indexNode)
							precedence: 3
							from: self.
				scopeTable at: variable put: node]].
	sourceRanges _ Dictionary new: 32.
	globalSourceRanges _ OrderedCollection new: 32.
!

----- Method: Encoder>>initScopeAndLiteralTables (in category 'initialize-release') -----
initScopeAndLiteralTables

	scopeTable _ StdVariables copy.
	litSet _ StdLiterals copy.
	selectorSet _ StdSelectors copy.
	litIndSet _ Dictionary new: 16.
	literalStream _ WriteStream on: (Array new: 32)!

----- Method: Encoder>>litIndex: (in category 'encoding') -----
litIndex: literal
	| p |
	p _ literalStream position.
	p = 256 ifTrue:
		[self notify: 'More than 256 literals referenced. 
You must split or otherwise simplify this method.
The 257th literal is: ', literal printString. ^nil].
		"Would like to show where it is in the source code, 
		 but that info is hard to get."
	literalStream nextPut: literal.
	^ p!

----- Method: Encoder>>literals (in category 'results') -----
literals
	"Should only be used for decompiling primitives"
	^ literalStream contents!

----- Method: Encoder>>lookupInPools:ifFound: (in category 'private') -----
lookupInPools: varName ifFound: assocBlock

	Symbol hasInterned: varName ifTrue:[:sym|
		(class bindingOf: sym) ifNotNilDo:[:assoc| 
			assocBlock value: assoc.
			^true].
		(Preferences valueOfFlag: #lenientScopeForGlobals)  "**Temporary**"
			ifTrue: [^ Smalltalk lenientScopeHas: sym ifTrue: assocBlock]
			ifFalse: [^ false]].
	(class bindingOf: varName) ifNotNilDo:[:assoc|
		assocBlock value: assoc.
		^true].
	^false!

----- Method: Encoder>>maxTemp (in category 'temps') -----
maxTemp

	^nTemps!

----- Method: Encoder>>nTemps:literals:class: (in category 'initialize-release') -----
nTemps: n literals: lits class: cl 
	"Decompile."

	supered _ false.
	class _ cl.
	nTemps _ n.
	literalStream _ ReadStream on: lits.
	literalStream position: lits size.
	sourceRanges _ Dictionary new: 32.
	globalSourceRanges _ OrderedCollection new: 32.
!

----- Method: Encoder>>name:key:class:type:set: (in category 'private') -----
name: name key: key class: leafNodeClass type: type set: dict

	| node |
	^dict 
		at: key
		ifAbsent: 
			[node _ leafNodeClass new
						name: name
						key: key
						index: nil
						type: type.
			dict at: key put: node.
			^node]!

----- Method: Encoder>>newTemp: (in category 'temps') -----
newTemp: name

	nTemps _ nTemps + 1.
	^ TempVariableNode new
		name: name
		index: nTemps - 1
		type: LdTempType
		scope: 0!

----- Method: Encoder>>noteSourceRange:forNode: (in category 'source mapping') -----
noteSourceRange: range forNode: node

	sourceRanges at: node put: range!

----- Method: Encoder>>noteSuper (in category 'initialize-release') -----
noteSuper

	supered _ true!

----- Method: Encoder>>notify: (in category 'error handling') -----
notify: string
	"Put a separate notifier on top of the requestor's window"
	| req |
	requestor == nil
		ifFalse: 
			[req _ requestor.
			self release.
			req notify: string].
	^false!

----- Method: Encoder>>notify:at: (in category 'error handling') -----
notify: string at: location

	| req |
	requestor == nil
		ifFalse: 
			[req _ requestor.
			self release.
			req notify: string at: location].
	^false!

----- Method: Encoder>>possibleNamesFor: (in category 'private') -----
possibleNamesFor: proposedName
	| results |
	results _ class possibleVariablesFor: proposedName continuedFrom: nil.
	^ proposedName correctAgainst: nil continuedFrom: results.
!

----- Method: Encoder>>possibleVariablesFor: (in category 'private') -----
possibleVariablesFor: proposedVariable

	| results |
	results _ proposedVariable correctAgainstDictionary: scopeTable
								continuedFrom: nil.
	proposedVariable first canBeGlobalVarInitial ifTrue:
		[ results _ class possibleVariablesFor: proposedVariable
						continuedFrom: results ].
	^ proposedVariable correctAgainst: nil continuedFrom: results.
!

----- Method: Encoder>>rawSourceRanges (in category 'source mapping') -----
rawSourceRanges

	^ sourceRanges !

----- Method: Encoder>>reallyBind: (in category 'private') -----
reallyBind: name

	| node |
	node _ self newTemp: name.
	scopeTable at: name put: node.
	^node!

----- Method: Encoder>>release (in category 'initialize-release') -----
release

	requestor _ nil!

----- Method: Encoder>>requestor: (in category 'error handling') -----
requestor: req
	"Often the requestor is a BrowserCodeController"
	requestor _ req!

----- Method: Encoder>>sharableLitIndex: (in category 'encoding') -----
sharableLitIndex: literal
	"Special access prevents multiple entries for post-allocated super send special selectors"
	| p |
	p _ literalStream originalContents indexOf: literal.
	p = 0 ifFalse: [^ p-1].
	^ self litIndex: literal
!

----- Method: Encoder>>sourceMap (in category 'source mapping') -----
sourceMap
	"Answer with a sorted set of associations (pc range)."

	^ (sourceRanges keys collect: 
		[:key |  Association key: key pc value: (sourceRanges at: key)])
			asSortedCollection!

----- Method: Encoder>>tempNames (in category 'results') -----
tempNames 

	^ self tempNodes collect:
		[:node | (node isMemberOf: MessageAsTempNode)
					ifTrue: [scopeTable keyAtValue: node]
					ifFalse: [node key]]!

----- Method: Encoder>>tempNodes (in category 'results') -----
tempNodes 
	| tempNodes |
	tempNodes _ SortedCollection sortBlock: [:n1 :n2 | n1 code <= n2 code].
	scopeTable associationsDo:
		[:assn | assn value isTemp ifTrue: [tempNodes add: assn value]].
	^ tempNodes!

----- Method: Encoder>>temps:literals:class: (in category 'initialize-release') -----
temps: tempVars literals: lits class: cl 
	"Decompile."

	supered _ false.
	class _ cl.
	nTemps _ tempVars size.
	tempVars do: [:node | scopeTable at: node name put: node].
	literalStream _ ReadStream on: lits.
	literalStream position: lits size.
	sourceRanges _ Dictionary new: 32.
	globalSourceRanges _ OrderedCollection new: 32.
!

----- Method: Encoder>>tempsAndBlockArgs (in category 'results') -----
tempsAndBlockArgs
	| tempNodes var |
	tempNodes _ OrderedCollection new.
	scopeTable associationsDo:
		[:assn | var _ assn value.
		((var isTemp and: [var isArg not])
					and: [var scope = 0 or: [var scope = -1]])
			ifTrue: [tempNodes add: var]].
	^ tempNodes!

----- Method: Encoder>>undeclared: (in category 'encoding') -----
undeclared: name

	| sym |
	requestor interactive ifTrue: [
		requestor requestor == #error: ifTrue: [requestor error: 'Undeclared'].
		^ self notify: 'Undeclared'].
	Transcript show: ' (' , name , ' is Undeclared) '.
	sym _ name asSymbol.
	Undeclared at: sym put: nil.
	^self global: (Undeclared associationAt: sym) name: sym!

----- Method: Encoder>>unusedTempNames (in category 'results') -----
unusedTempNames 
	| unused name |
	unused _ OrderedCollection new.
	scopeTable associationsDo:
		[:assn | (assn value isUnusedTemp)
			ifTrue: [name _ assn value key.
					name ~= 'homeContext' ifTrue: [unused add: name]]].
	^ unused!

ParseNode subclass: #LeafNode
	instanceVariableNames: 'key code'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

!LeafNode commentStamp: '<historical>' prior: 0!
I represent a leaf node of the compiler parse tree. I am abstract.
	
Types (defined in class ParseNode):
	1 LdInstType (which uses class VariableNode)
	2 LdTempType (which uses class VariableNode)
	3 LdLitType (which uses class LiteralNode)
	4 LdLitIndType (which uses class VariableNode)
	5 SendType (which uses class SelectorNode).

Note that Squeak departs slightly from the Blue Book bytecode spec.

In order to allow access to more than 63 literals and instance variables,
bytecode 132 has been redefined as DoubleExtendedDoAnything:
		byte2				byte3			Operation
(hi 3 bits)  (lo 5 bits)
	0		nargs			lit index			Send Literal Message 0-255
	1		nargs			lit index			Super-Send Lit Msg 0-255
	2		ignored			rcvr index		Push Receiver Variable 0-255
	3		ignored			lit index			Push Literal Constant 0-255
	4		ignored			lit index			Push Literal Variable 0-255
	5		ignored			rcvr index		Store Receiver Variable 0-255
	6		ignored			rcvr index		Store-pop Receiver Variable 0-255
	7		ignored			lit index			Store Literal Variable 0-255

	This has allowed bytecode 134 also to be redefined as a second extended send
	that can access literals up to 64 for nargs up to 3 without needing three bytes.
	It is just like 131, except that the extension byte is aallllll instead of aaalllll,
	where aaa are bits of argument count, and lll are bits of literal index.!

----- Method: LeafNode>>code (in category 'code generation') -----
code

	^ code!

----- Method: LeafNode>>code: (in category 'code generation') -----
code: aValue

	code := aValue!

----- Method: LeafNode>>code:type: (in category 'private') -----
code: index type: type

	index isNil 
		ifTrue: [^type negated].
	(CodeLimits at: type) > index 
		ifTrue: [^(CodeBases at: type) + index].
	^type * 256 + index!

----- Method: LeafNode>>emitForEffect:on: (in category 'code generation') -----
emitForEffect: stack on: strm

	^self!

----- Method: LeafNode>>emitLong:on: (in category 'code generation') -----
emitLong: mode on: aStream 
	"Emit extended variable access."
	| type index |
	self code < 256
		ifTrue:
			[self code < 16
			ifTrue: [type _ 0.
					index _ self code]
			ifFalse: [self code < 32
					ifTrue: [type _ 1.
							index _ self code - 16]
					ifFalse: [self code < 96
							ifTrue: [type _ self code // 32 + 1.
									index _ self code \\ 32]
							ifFalse: [self error: 
									'Sends should be handled in SelectorNode']]]]
		ifFalse: 
			[index _ self code \\ 256.
			type _ self code // 256 - 1].
	index <= 63 ifTrue:
		[aStream nextPut: mode.
		^ aStream nextPut: type * 64 + index].
	"Compile for Double-exetended Do-anything instruction..."
	mode = LoadLong ifTrue:
		[aStream nextPut: DblExtDoAll.
		aStream nextPut: (#(64 0 96 128) at: type+1).  "Cant be temp (type=1)"
		^ aStream nextPut: index].
	mode = Store ifTrue:
		[aStream nextPut: DblExtDoAll.
		aStream nextPut: (#(160 0 0 224) at: type+1).  "Cant be temp or const (type=1 or 2)"
		^ aStream nextPut: index].
	mode = StorePop ifTrue:
		[aStream nextPut: DblExtDoAll.
		aStream nextPut: (#(192 0 0 0) at: type+1).  "Can only be inst"
		^ aStream nextPut: index].
!

----- Method: LeafNode>>key (in category 'accessing') -----
key

	^key!

----- Method: LeafNode>>key: (in category 'initialize-release') -----
key: object

	key _ object.
!

----- Method: LeafNode>>key:code: (in category 'initialize-release') -----
key: object code: byte

	self key: object.
	self code: byte!

----- Method: LeafNode>>key:index:type: (in category 'initialize-release') -----
key: object index: i type: type

	self key: object code: (self code: i type: type)!

----- Method: LeafNode>>name:key:code: (in category 'initialize-release') -----
name: ignored key: object code: byte

	self key: object.
	self code: byte!

----- Method: LeafNode>>name:key:index:type: (in category 'initialize-release') -----
name: literal key: object index: i type: type

	self key: object
		index: i
		type: type!

----- Method: LeafNode>>reserve: (in category 'code generation') -----
reserve: encoder 
	"If this is a yet unused literal of type -code, reserve it."

	self code < 0 ifTrue: [self code: (self code: (encoder litIndex: self key) type: 0 - self code)]!

----- Method: LeafNode>>sizeForEffect: (in category 'code generation') -----
sizeForEffect: encoder

	^0!

----- Method: LeafNode>>sizeForValue: (in category 'code generation') -----
sizeForValue: encoder
	self reserve: encoder.
	self code < 256 ifTrue: [^ 1].
	(self code \\ 256) <= 63 ifTrue: [^ 2].
	^ 3!

----- Method: LeafNode>>veryDeepFixupWith: (in category 'copying') -----
veryDeepFixupWith: deepCopier
	"If fields were weakly copied, fix them here.  If they were in the tree being copied, fix them up, otherwise point to the originals!!!!"

super veryDeepFixupWith: deepCopier.
self key: (deepCopier references at: self key ifAbsent: [self key]).
!

----- Method: LeafNode>>veryDeepInner: (in category 'copying') -----
veryDeepInner: deepCopier
	"Copy all of my instance variables.  Some need to be not copied at all, but shared.  	Warning!!!!  Every instance variable defined in this class must be handled.  We must also implement veryDeepFixupWith:.  See DeepCopier class comment."

super veryDeepInner: deepCopier.
"key _ key.		Weakly copied"
self code: (self code veryDeepCopyWith: deepCopier).
!

LeafNode subclass: #LiteralNode
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

!LiteralNode commentStamp: '<historical>' prior: 0!
I am a parse tree leaf representing a literal string or number.!

----- Method: LiteralNode>>emitForValue:on: (in category 'code generation') -----
emitForValue: stack on: strm

	code < 256
		ifTrue: [strm nextPut: code]
		ifFalse: [self emitLong: LoadLong on: strm].
	stack push: 1!

----- Method: LiteralNode>>eval (in category 'evaluation') -----
eval
	"When everything in me is a constant, I can produce a value.  This is only used by the Scripting system (TilePadMorph tilesFrom:in:)"

	^ key!

----- Method: LiteralNode>>isConstantNumber (in category 'testing') -----
isConstantNumber
	^ key isNumber!

----- Method: LiteralNode>>isLiteral (in category 'testing') -----
isLiteral

	^ true!

----- Method: LiteralNode>>isSpecialConstant (in category 'testing') -----
isSpecialConstant
	^ code between: LdTrue and: LdMinus1+3!

----- Method: LiteralNode>>literalValue (in category 'testing') -----
literalValue

	^key!

----- Method: LiteralNode>>printOn:indent: (in category 'printing') -----
printOn: aStream indent: level

	(key isVariableBinding)
		ifTrue:
			[key key isNil
				ifTrue:
					[aStream nextPutAll: '###';
					 	nextPutAll: key value soleInstance name]
				ifFalse:
					[aStream nextPutAll: '##';
						nextPutAll: key key]]
		ifFalse:
			[aStream withStyleFor: #literal
					do: [key storeOn: aStream]]!

LeafNode subclass: #SelectorNode
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

!SelectorNode commentStamp: '<historical>' prior: 0!
I am a parse tree leaf representing a selector.!

SelectorNode subclass: #KeyWordNode
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

!KeyWordNode commentStamp: '<historical>' prior: 0!
I am a part of a selector.   #at:put: is owned by a SelectorNode, and #put: within it is owned by a KeyWordNode.!

----- Method: SelectorNode>>emit:args:on: (in category 'code generation') -----
emit: stack args: nArgs on: strm

	self emit: stack
		args: nArgs
		on: strm
		super: false!

----- Method: SelectorNode>>emit:args:on:super: (in category 'code generation') -----
emit: stack args: nArgs on: aStream super: supered
	| index |
	stack pop: nArgs.
	(supered not and: [code - Send < SendLimit and: [nArgs < 3]]) ifTrue:
		["short send"
		code < Send
			ifTrue: [^ aStream nextPut: code "special"]
			ifFalse: [^ aStream nextPut: nArgs * 16 + code]].
	index _ code < 256 ifTrue: [code - Send] ifFalse: [code \\ 256].
	(index <= 31 and: [nArgs <= 7]) ifTrue: 
		["extended (2-byte) send [131 and 133]"
		aStream nextPut: SendLong + (supered ifTrue: [2] ifFalse: [0]).
		^ aStream nextPut: nArgs * 32 + index].
	(supered not and: [index <= 63 and: [nArgs <= 3]]) ifTrue:
		["new extended (2-byte) send [134]"
		aStream nextPut: SendLong2.
		^ aStream nextPut: nArgs * 64 + index].
	"long (3-byte) send"
	aStream nextPut: DblExtDoAll.
	aStream nextPut: nArgs + (supered ifTrue: [32] ifFalse: [0]).
	aStream nextPut: index!

----- Method: SelectorNode>>emitForEffect:on: (in category 'inappropriate') -----
emitForEffect: stack on: strm

	self shouldNotImplement!

----- Method: SelectorNode>>emitForValue:on: (in category 'inappropriate') -----
emitForValue: stack on: strm

	self shouldNotImplement!

----- Method: SelectorNode>>isPvtSelector (in category 'testing') -----
isPvtSelector
	"Answer if this selector node is a private message selector."

	^key isPvtSelector!

----- Method: SelectorNode>>printOn:indent: (in category 'printing') -----
printOn: aStream indent: level 
	aStream withStyleFor: #keyword
		do: [key == nil
				ifTrue: [aStream nextPutAll: '<key==nil>']
				ifFalse: [aStream nextPutAll: key]]!

----- Method: SelectorNode>>size:args:super: (in category 'code generation') -----
size: encoder args: nArgs super: supered
	| index |
	self reserve: encoder.
	(supered not and: [code - Send < SendLimit and: [nArgs < 3]])
		ifTrue: [^1]. "short send"
	(supered and: [code < Send]) ifTrue: 
		["super special:"
		code _ self code: (encoder sharableLitIndex: key) type: 5].
	index _ code < 256 ifTrue: [code - Send] ifFalse: [code \\ 256].
	(index <= 31 and: [nArgs <= 7])
		ifTrue: [^ 2]. "medium send"
	(supered not and: [index <= 63 and: [nArgs <= 3]])
		ifTrue: [^ 2]. "new medium send"
	^ 3 "long send"!

----- Method: SelectorNode>>sizeForEffect: (in category 'inappropriate') -----
sizeForEffect: encoder

	self shouldNotImplement!

----- Method: SelectorNode>>sizeForValue: (in category 'inappropriate') -----
sizeForValue: encoder

	self shouldNotImplement!

LeafNode subclass: #VariableNode
	instanceVariableNames: 'name'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

!VariableNode commentStamp: '<historical>' prior: 0!
I am a parse tree leaf representing a variable. Note that my name and key are different for pool variables: the key is the Object Reference.!

VariableNode subclass: #LiteralVariableNode
	instanceVariableNames: 'splNode'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

----- Method: LiteralVariableNode>>emitLoad:on: (in category 'code generation') -----
emitLoad: stack on: strm
	splNode ifNil:[^super emitLoad: stack on: strm].
	self code < 256
		ifTrue: [strm nextPut: self code]
		ifFalse: [self emitLong: LoadLong on: strm].
	stack push: 1.!

----- Method: LiteralVariableNode>>emitStore:on: (in category 'code generation') -----
emitStore: stack on: strm
	splNode ifNil:[^super emitStore: stack on: strm].
	splNode
			emit: stack
			args: 1
			on: strm
			super: false.!

----- Method: LiteralVariableNode>>emitStorePop:on: (in category 'code generation') -----
emitStorePop: stack on: strm
	splNode ifNil:[^super emitStorePop: stack on: strm].
	self emitStore: stack on: strm.
	strm nextPut: Pop.
	stack pop: 1.!

----- Method: LiteralVariableNode>>sizeForStore: (in category 'code generation') -----
sizeForStore: encoder
	| index |
	(self key isVariableBinding and:[self key isSpecialWriteBinding]) 
		ifFalse:[^super sizeForStore: encoder].
	self code < 0 ifTrue:[
		index _ self index.
		self code: (self code: index type: LdLitType)].
	splNode _ encoder encodeSelector: #value:.
	^ (splNode size: encoder args: 1 super: false) + (super sizeForValue: encoder)!

----- Method: LiteralVariableNode>>sizeForStorePop: (in category 'code generation') -----
sizeForStorePop: encoder
	| index |
	(self key isVariableBinding and:[self key isSpecialWriteBinding]) 
		ifFalse:[^super sizeForStorePop: encoder].
	self code < 0 ifTrue:[
		index _ self index.
		self code: (self code: index type: LdLitType)].
	splNode _ encoder encodeSelector: #value:.
	^ (splNode size: encoder args: 1 super: false) + (super sizeForValue: encoder) + 1!

VariableNode subclass: #TempVariableNode
	instanceVariableNames: 'isAnArg hasRefs hasDefs scope'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

!TempVariableNode commentStamp: '<historical>' prior: 0!
I am a parse tree leaf representing a temporary variable!

----- Method: TempVariableNode>>assignmentCheck:at: (in category 'testing') -----
assignmentCheck: encoder at: location

	self isArg ifTrue: [^ location]
			ifFalse: [^ -1]!

----- Method: TempVariableNode>>isArg (in category 'testing') -----
isArg
	^ isAnArg!

----- Method: TempVariableNode>>isArg: (in category 'initialize-release') -----
isArg: aBoolean

	isAnArg _ aBoolean.
	isAnArg ifTrue: [hasDefs _ true]!

----- Method: TempVariableNode>>isTemp (in category 'testing') -----
isTemp
	^ true!

----- Method: TempVariableNode>>isUndefTemp (in category 'testing') -----
isUndefTemp
	^ hasDefs not!

----- Method: TempVariableNode>>isUnusedTemp (in category 'testing') -----
isUnusedTemp
	^ hasRefs not!

----- Method: TempVariableNode>>name:index:type:scope: (in category 'initialize-release') -----
name: varName index: i type: type scope: level
	"Only used for initting temporary variables"
	self name: varName.
	self key: varName
		index: i
		type: type.
	self isArg: (hasDefs _ hasRefs _ false).
	self scope: level!

----- Method: TempVariableNode>>nowHasDef (in category 'initialize-release') -----
nowHasDef
	hasDefs _ true!

----- Method: TempVariableNode>>nowHasRef (in category 'initialize-release') -----
nowHasRef
	hasRefs _ true!

----- Method: TempVariableNode>>printOn:indent: (in category 'printing') -----
printOn: aStream indent: level 
	aStream withStyleFor: #temporaryVariable
			do: [aStream nextPutAll: self name]!

----- Method: TempVariableNode>>scope (in category 'testing') -----
scope
	^ scope!

----- Method: TempVariableNode>>scope: (in category 'initialize-release') -----
scope: level
	"Note scope of temporary variables.
	Currently only the following distinctions are made:
		0	outer level: args and user-declared temps
		1	block args and doLimiT temps
		-1	a block temp that is no longer active
		-2	a block temp that held limit of to:do:"
	scope _ level!

----- Method: VariableNode class>>initialize (in category 'class initialization') -----
initialize    "VariableNode initialize.  Decompiler initialize"
	| encoder |
	encoder _ Encoder new.
	StdVariables _ Dictionary new: 16.
	encoder
		fillDict: StdVariables
		with: VariableNode
		mapping: #('self' 'thisContext' 'super' 'nil' 'false' 'true' )
		to: (Array with: LdSelf with: LdThisContext with: LdSuper)
				, (Array with: LdNil with: LdFalse with: LdTrue).
	StdSelectors _ Dictionary new: 64.
	encoder
		fillDict: StdSelectors
		with: SelectorNode
		mapping: ((1 to: Smalltalk specialSelectorSize) collect: 
							[:i | Smalltalk specialSelectorAt: i])
		to: (SendPlus to: SendPlus + 31).
	StdLiterals _ LiteralDictionary new: 16.
	encoder
		fillDict: StdLiterals
		with: LiteralNode
		mapping: #(-1 0 1 2 )
		to: (LdMinus1 to: LdMinus1 + 3).
	encoder initScopeAndLiteralTables.

	NodeNil _ encoder encodeVariable: 'nil'.
	NodeTrue _ encoder encodeVariable: 'true'.
	NodeFalse _ encoder encodeVariable: 'false'.
	NodeSelf _ encoder encodeVariable: 'self'.
	NodeThisContext _ encoder encodeVariable: 'thisContext'.
	NodeSuper _ encoder encodeVariable: 'super'!

----- Method: VariableNode>>asStorableNode: (in category 'initialize-release') -----
asStorableNode: encoder
	^ self!

----- Method: VariableNode>>assignmentCheck:at: (in category 'testing') -----
assignmentCheck: encoder at: location

	(encoder cantStoreInto: self name)
		ifTrue: [^ location]
		ifFalse: [^ -1]
!

----- Method: VariableNode>>canBeSpecialArgument (in category 'testing') -----
canBeSpecialArgument
	"Can I be an argument of (e.g.) ifTrue:?"

	^ self code < LdNil!

----- Method: VariableNode>>emitForReturn:on: (in category 'code generation') -----
emitForReturn: stack on: strm

	(self code >= LdSelf and: [self code <= LdNil])
		ifTrue: 
			["short returns"
			strm nextPut: EndMethod - 4 + (self code - LdSelf).
			stack push: 1 "doesnt seem right"]
		ifFalse: 
			[super emitForReturn: stack on: strm]!

----- Method: VariableNode>>emitForValue:on: (in category 'code generation') -----
emitForValue: stack on: strm

	self code < 256
		ifTrue: 
			[strm nextPut: (self code = LdSuper ifTrue: [LdSelf] ifFalse: [self code]).
			stack push: 1]
		ifFalse: 
			[self emitLong: LoadLong on: strm.
			stack push: 1]!

----- Method: VariableNode>>emitLoad:on: (in category 'code generation') -----
emitLoad: stack on: strm
	"Do nothing"!

----- Method: VariableNode>>emitStore:on: (in category 'code generation') -----
emitStore: stack on: strm

	self emitLong: Store on: strm!

----- Method: VariableNode>>emitStorePop:on: (in category 'code generation') -----
emitStorePop: stack on: strm
	(self code between: 0 and: 7)
		ifTrue: 
			[strm nextPut: ShortStoP + self code "short stopop inst"]
		ifFalse:
			[(self code between: 16 and: 23)
				ifTrue: [strm nextPut: ShortStoP + 8 + self code - 16 "short stopop temp"]
				ifFalse: [(self code >= 256 and: [self code \\ 256 > 63 and: [self code // 256 = 4]])
						ifTrue: [self emitLong: Store on: strm. strm nextPut: Pop]
						ifFalse: [self emitLong: StorePop on: strm]]].
	stack pop: 1!

----- Method: VariableNode>>fieldOffset (in category 'code generation') -----
fieldOffset  "Return temp or instVar offset for this variable"

	self code < 256
		ifTrue: 
			[^ self code \\ 16]
		ifFalse: 
			[^ self code \\ 256]!

----- Method: VariableNode>>index (in category 'testing') -----
index
	"This code attempts to reconstruct the index from its encoding in code."
	self code < 0 ifTrue:[^ nil].
	self code > 256 ifTrue:[^ self code \\ 256].
	^self code - self type!

----- Method: VariableNode>>isSelfPseudoVariable (in category 'testing') -----
isSelfPseudoVariable
	"Answer if this ParseNode represents the 'self' pseudo-variable."

	^ (self key = 'self') | (self name = '{{self}}')!

----- Method: VariableNode>>isVariableReference (in category 'testing') -----
isVariableReference

	^true!

----- Method: VariableNode>>name (in category 'accessing') -----
name
	^ name!

----- Method: VariableNode>>name: (in category 'initialize-release') -----
name: string
	"Change name"

	name _ string.
!

----- Method: VariableNode>>name:index:type: (in category 'initialize-release') -----
name: varName index: i type: type
	"Only used for initting instVar refs"
	self name: varName.
	self key: varName
		index: i
		type: type!

----- Method: VariableNode>>name:key:code: (in category 'initialize-release') -----
name: string key: object code: byte
	"Only used for initting std variables, nil, true, false, self, etc."
	self name: string.
	self key: object.
	self code: byte!

----- Method: VariableNode>>name:key:index:type: (in category 'initialize-release') -----
name: varName key: objRef index: i type: type
	"Only used for initting global (litInd) variables"
	self name: varName.
	self key: objRef
		index: i
		type: type!

----- Method: VariableNode>>printOn:indent: (in category 'printing') -----
printOn: aStream indent: level 
	aStream withStyleFor: #variable
		do: [aStream nextPutAll: self name].
!

----- Method: VariableNode>>sizeForReturn: (in category 'code generation') -----
sizeForReturn: encoder

	(self code >= LdSelf and: [self code <= LdNil])
		ifTrue: ["short returns" ^1].
	^super sizeForReturn: encoder!

----- Method: VariableNode>>sizeForStore: (in category 'code generation') -----
sizeForStore: encoder
	self reserve: encoder.
	self code < 256 ifTrue: [^ 2].
	(self code \\ 256) <= 63 ifTrue: [^ 2].
	^ 3!

----- Method: VariableNode>>sizeForStorePop: (in category 'code generation') -----
sizeForStorePop: encoder
	self reserve: encoder.
	(self code < 24 and: [self code noMask: 8]) ifTrue: [^ 1].
	self code < 256 ifTrue: [^ 2].
	self code \\ 256 <= 63 ifTrue: [^ 2].  "extended StorePop"
	self code // 256 = 1 ifTrue: [^ 3].  "dbl extended StorePopInst"
	self code // 256 = 4 ifTrue: [^ 4].  "dbl extended StoreLitVar , Pop"
	self halt.  "Shouldn't get here"!

----- Method: VariableNode>>type (in category 'testing') -----
type
	"This code attempts to reconstruct the type from its encoding in code.
		This allows one to test, for instance, (aNode type = LdInstType)."
	| type |
	self code < 0 ifTrue: [^ self code negated].
	self code < 256 ifFalse: [^ self code // 256].
	type _ CodeBases findFirst: [:one | self code < one].
	type = 0
		ifTrue: [^ 5]
		ifFalse: [^ type - 1]!

ParseNode subclass: #MessageNode
	instanceVariableNames: 'receiver selector precedence special arguments sizes equalNode caseErrorNode'
	classVariableNames: 'MacroEmitters ThenFlag MacroPrinters MacroSelectors MacroTransformers MacroSizers StdTypers'
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

!MessageNode commentStamp: '<historical>' prior: 0!
I represent a receiver and its message.
	
Precedence codes:
	1 unary
	2 binary
	3 keyword
	4 other
	
If special>0, I compile special code in-line instead of sending messages with literal methods as remotely copied contexts.!

MessageNode subclass: #MessageAsTempNode
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

!MessageAsTempNode commentStamp: '<historical>' prior: 0!
This node represents accesses to temporary variables for do-its in the debugger.  Since they execute in another context, they must send a message to the original context to access the value of the temporary variable in that context.!

----- Method: MessageAsTempNode>>asStorableNode: (in category 'access to remote temps') -----
asStorableNode: encoder
	"This node is a message masquerading as a temporary variable.
	It currently has the form {homeContext tempAt: offset}.
	We need to generate code for {expr storeAt: offset inTempFrame: homeContext},
	where the expr, the block argument, is already on the stack.
	This, in turn will get turned into {homeContext tempAt: offset put: expr}
	at runtime if nobody disturbs storeAt:inTempFrame: in Object (not clean)"
	^ MessageAsTempNode new
		receiver: nil  "suppress code generation for reciever already on stack"
		selector: #storeAt:inTempFrame:
		arguments: (arguments copyWith: receiver)
		precedence: precedence
		from: encoder!

----- Method: MessageAsTempNode>>code (in category 'access to remote temps') -----
code
	"Allow synthetic temp nodes to be sorted by code"
	^ arguments first literalValue!

----- Method: MessageAsTempNode>>emitStorePop:on: (in category 'access to remote temps') -----
emitStorePop: stack on: codeStream
	"This node has the form {expr storeAt: offset inTempFrame: homeContext},
	where the expr, the block argument, is already on the stack."
	^ self emitForEffect: stack on: codeStream!

----- Method: MessageAsTempNode>>isTemp (in category 'access to remote temps') -----
isTemp
	"Masquerading for debugger access to temps."
	^ true!

----- Method: MessageAsTempNode>>nowHasDef (in category 'access to remote temps') -----
nowHasDef
	"For compatibility with temp scope protocol"
!

----- Method: MessageAsTempNode>>nowHasRef (in category 'access to remote temps') -----
nowHasRef
	"For compatibility with temp scope protocol"
!

----- Method: MessageAsTempNode>>scope (in category 'access to remote temps') -----
scope
	"For compatibility with temp scope protocol"
	^ -1!

----- Method: MessageAsTempNode>>scope: (in category 'access to remote temps') -----
scope: ignored
	"For compatibility with temp scope protocol"
!

----- Method: MessageAsTempNode>>sizeForStorePop: (in category 'access to remote temps') -----
sizeForStorePop: encoder
	"This node has the form {expr storeAt: offset inTempFrame: homeContext},
	where the expr, the block argument, is already on the stack."
	^ self sizeForEffect: encoder!

----- Method: MessageAsTempNode>>store:from: (in category 'access to remote temps') -----
store: expr from: encoder 
	"ctxt tempAt: n -> ctxt tempAt: n put: expr (see Assignment).
	For assigning into temps of a context being debugged."

	selector key ~= #tempAt: 
		ifTrue: [^self error: 'cant transform this message'].
	^ MessageAsTempNode new
		receiver: receiver
		selector: #tempAt:put:
		arguments: (arguments copyWith: expr)
		precedence: precedence
		from: encoder!

----- Method: MessageNode class>>initialize (in category 'class initialization') -----
initialize		"MessageNode initialize"
	MacroSelectors _ 
		#(ifTrue: ifFalse: ifTrue:ifFalse: ifFalse:ifTrue:
			and: or:
			whileFalse: whileTrue: whileFalse whileTrue
			to:do: to:by:do:
			caseOf: caseOf:otherwise:
			ifNil: ifNotNil:  ifNil:ifNotNil: ifNotNil:ifNil:).
	MacroTransformers _ 
		#(transformIfTrue: transformIfFalse: transformIfTrueIfFalse: transformIfFalseIfTrue:
			transformAnd: transformOr:
			transformWhile: transformWhile: transformWhile: transformWhile:
			transformToDo: transformToDo:
			transformCase: transformCase:
			transformIfNil: transformIfNil:  transformIfNilIfNotNil: transformIfNotNilIfNil:).
	MacroEmitters _ 
		#(emitIf:on:value: emitIf:on:value: emitIf:on:value: emitIf:on:value:
			emitIf:on:value: emitIf:on:value:
			emitWhile:on:value: emitWhile:on:value: emitWhile:on:value: emitWhile:on:value:
			emitToDo:on:value: emitToDo:on:value:
			emitCase:on:value: emitCase:on:value:
			emitIfNil:on:value: emitIfNil:on:value: emitIf:on:value: emitIf:on:value:).
	MacroSizers _ 
		#(sizeIf:value: sizeIf:value: sizeIf:value: sizeIf:value:
			sizeIf:value: sizeIf:value:
			sizeWhile:value: sizeWhile:value: sizeWhile:value: sizeWhile:value:
			sizeToDo:value: sizeToDo:value:
			sizeCase:value: sizeCase:value:
			sizeIfNil:value: sizeIfNil:value: sizeIf:value: sizeIf:value: ).
	MacroPrinters _ 
		#(printIfOn:indent: printIfOn:indent: printIfOn:indent: printIfOn:indent:
			printIfOn:indent: printIfOn:indent:
			printWhileOn:indent: printWhileOn:indent: printWhileOn:indent: printWhileOn:indent:
			printToDoOn:indent: printToDoOn:indent:
			printCaseOn:indent: printCaseOn:indent:
			printIfNil:indent: printIfNil:indent: printIfNilNotNil:indent: printIfNilNotNil:indent:)!

----- Method: MessageNode>>arguments (in category 'equation translation') -----
arguments
	^arguments!

----- Method: MessageNode>>arguments: (in category 'equation translation') -----
arguments: list
	arguments _ list!

----- Method: MessageNode>>asMorphicCaseOn:indent: (in category 'printing') -----
asMorphicCaseOn: parent indent: ignored
	"receiver caseOf: {[key]->[value]. ...} otherwise: [otherwise]"

	| braceNode otherwise |

	braceNode _ arguments first.
	otherwise _ arguments last.
	((arguments size = 1) or: [otherwise isJustCaseError]) ifTrue: [
		self morphFromKeywords: #caseOf: arguments: {braceNode} on: parent indent: nil.
		^parent
	].
	self morphFromKeywords: #caseOf:otherwise: arguments: arguments on: parent indent: nil.
	^parent
!

----- Method: MessageNode>>canCascade (in category 'testing') -----
canCascade

	^(receiver == NodeSuper or: [special > 0]) not!

----- Method: MessageNode>>cascadeReceiver (in category 'cascading') -----
cascadeReceiver
	"Nil out rcvr (to indicate cascade) and return what it had been."

	| rcvr |
	rcvr _ receiver.
	receiver _ nil.
	^rcvr!

----- Method: MessageNode>>checkBlock:as:from: (in category 'private') -----
checkBlock: node as: nodeName from: encoder

	node canBeSpecialArgument ifTrue: [^node isMemberOf: BlockNode].
	((node isKindOf: BlockNode) and: [node numberOfArguments > 0])
		ifTrue:	[^encoder notify: '<- ', nodeName , ' of ' ,
					(MacroSelectors at: special) , ' must be a 0-argument block']
		ifFalse: [^encoder notify: '<- ', nodeName , ' of ' ,
					(MacroSelectors at: special) , ' must be a block or variable']!

----- Method: MessageNode>>emitCase:on:value: (in category 'code generation') -----
emitCase: stack on: strm value: forValue

	| braceNode sizeStream thenSize elseSize |
	forValue not
		ifTrue: [^super emitForEffect: stack on: strm].
	braceNode _ arguments first.
	sizeStream _ ReadStream on: sizes.
	receiver emitForValue: stack on: strm.
	braceNode casesForwardDo:
		[:keyNode :valueNode :last |
		thenSize _ sizeStream next.
		elseSize _ sizeStream next.
		last ifFalse: [strm nextPut: Dup. stack push: 1].
		keyNode emitForEvaluatedValue: stack on: strm.
		equalNode emit: stack args: 1 on: strm.
		self emitBranchOn: false dist: thenSize pop: stack on: strm.
		last ifFalse: [strm nextPut: Pop. stack pop: 1].
		valueNode emitForEvaluatedValue: stack on: strm.
		last ifTrue: [stack pop: 1].
		valueNode returns ifFalse: [self emitJump: elseSize on: strm]].
	arguments size = 2
		ifTrue:
			[arguments last emitForEvaluatedValue: stack on: strm] "otherwise: [...]"
		ifFalse:
			[NodeSelf emitForValue: stack on: strm.
			caseErrorNode emit: stack args: 0 on: strm]!

----- Method: MessageNode>>emitForEffect:on: (in category 'code generation') -----
emitForEffect: stack on: strm
	"For #ifTrue:ifFalse: and #whileTrue: / #whileFalse: style messages, the pc is set to the jump instruction, so that mustBeBoolean exceptions can be shown correctly."
	special > 0
		ifTrue: 
			[pc _ 0.
			self perform: (MacroEmitters at: special) with: stack with: strm with: false]
		ifFalse: 
			[super emitForEffect: stack on: strm]!

----- Method: MessageNode>>emitForValue:on: (in category 'code generation') -----
emitForValue: stack on: strm
	"For #ifTrue:ifFalse: and #whileTrue: / #whileFalse: style messages, the pc is set to the jump instruction, so that mustBeBoolean exceptions can be shown correctly."
	special > 0
		ifTrue: 
			[pc _ 0.
			self perform: (MacroEmitters at: special) with: stack with: strm with: true]
		ifFalse: 
			[receiver ~~ nil ifTrue: [receiver emitForValue: stack on: strm].
			arguments do: [:argument | argument emitForValue: stack on: strm].
			selector
				emit: stack
				args: arguments size
				on: strm
				super: receiver == NodeSuper.
			pc _ strm position]!

----- Method: MessageNode>>emitIf:on:value: (in category 'code generation') -----
emitIf: stack on: strm value: forValue
	| thenExpr thenSize elseExpr elseSize |
	thenSize _ sizes at: 1.
	elseSize _ sizes at: 2.
	(forValue not and: [(elseSize*thenSize) > 0])
		ifTrue:  "Two-armed IFs forEffect share a single pop"
			[^ super emitForEffect: stack on: strm].
	thenExpr _ arguments at: 1.
	elseExpr _ arguments at: 2.
	receiver emitForValue: stack on: strm.
	forValue
		ifTrue:  "Code all forValue as two-armed"
			[self emitBranchOn: false dist: thenSize pop: stack on: strm.
			pc _ strm position.
			thenExpr emitForEvaluatedValue: stack on: strm.
			stack pop: 1.  "then and else alternate; they don't accumulate"
			thenExpr returns not
				ifTrue:  "Elide jump over else after a return"
					[self emitJump: elseSize on: strm].
			elseExpr emitForEvaluatedValue: stack on: strm]
		ifFalse:  "One arm is empty here (two-arms code forValue)"
			[thenSize > 0
				ifTrue:
					[self emitBranchOn: false dist: thenSize pop: stack on: strm.
					pc _ strm position.
					thenExpr emitForEvaluatedEffect: stack on: strm]
				ifFalse:
					[self emitBranchOn: true dist: elseSize pop: stack on: strm.
					pc _ strm position.
					elseExpr emitForEvaluatedEffect: stack on: strm]]!

----- Method: MessageNode>>emitIfNil:on:value: (in category 'code generation') -----
emitIfNil: stack on: strm value: forValue

	| theNode theSize theSelector |
	theNode _ arguments first.
	theSize _ sizes at: 1.
	theSelector _ #ifNotNil:.
	receiver emitForValue: stack on: strm.
	forValue ifTrue: [strm nextPut: Dup. stack push: 1].
	strm nextPut: LdNil. stack push: 1.
	equalNode emit: stack args: 1 on: strm.
	self 
		emitBranchOn: (selector key == theSelector)
		dist: theSize 
		pop: stack 
		on: strm.
	pc _ strm position.
	forValue 
		ifTrue: 
			[strm nextPut: Pop. stack pop: 1.
			theNode emitForEvaluatedValue: stack on: strm]	
		ifFalse: [theNode emitForEvaluatedEffect: stack on: strm].!

----- Method: MessageNode>>emitToDo:on:value: (in category 'code generation') -----
emitToDo: stack on: strm value: forValue 
	" var _ rcvr. L1: [var <= arg1] Bfp(L2) [block body. var _ var + inc] Jmp(L1) L2: "
	| loopSize initStmt limitInit test block incStmt blockSize |
	initStmt _ arguments at: 4.
	limitInit _ arguments at: 7.
	test _ arguments at: 5.
	block _ arguments at: 3.
	incStmt _ arguments at: 6.
	blockSize _ sizes at: 1.
	loopSize _ sizes at: 2.
	limitInit == nil
		ifFalse: [limitInit emitForEffect: stack on: strm].
	initStmt emitForEffect: stack on: strm.
	test emitForValue: stack on: strm.
	self emitBranchOn: false dist: blockSize pop: stack on: strm.
	pc _ strm position.
	block emitForEvaluatedEffect: stack on: strm.
	incStmt emitForEffect: stack on: strm.
	self emitJump: 0 - loopSize on: strm.
	forValue ifTrue: [strm nextPut: LdNil. stack push: 1]!

----- Method: MessageNode>>emitWhile:on:value: (in category 'code generation') -----
emitWhile: stack on: strm value: forValue 
	" L1: ... Bfp(L2)|Btp(L2) ... Jmp(L1) L2: "
	| cond stmt stmtSize loopSize |
	cond _ receiver.
	stmt _ arguments at: 1.
	stmtSize _ sizes at: 1.
	loopSize _ sizes at: 2.
	cond emitForEvaluatedValue: stack on: strm.
	self emitBranchOn: (selector key == #whileFalse:)  "Bfp for whileTrue"
					dist: stmtSize pop: stack on: strm.   "Btp for whileFalse"
	pc _ strm position.
	stmt emitForEvaluatedEffect: stack on: strm.
	self emitJump: 0 - loopSize on: strm.
	forValue ifTrue: [strm nextPut: LdNil. stack push: 1]!

----- Method: MessageNode>>eval (in category 'equation translation') -----
eval
	"When everything in me is a constant, I can produce a value.  This is only used by the Scripting system (TilePadMorph tilesFrom:in:)"

	| rec args |
	(receiver isKindOf: VariableNode) ifFalse: [^ #illegal].
	rec _ receiver key value.
	args _ arguments collect: [:each | each eval].
	^ rec perform: selector key withArguments: args!

----- Method: MessageNode>>ifNilReceiver (in category 'private') -----
ifNilReceiver

	^receiver!

----- Method: MessageNode>>isComplex (in category 'testing') -----
isComplex
	
	^(special between: 1 and: 10) or: [arguments size > 2 or: [receiver isComplex]]!

----- Method: MessageNode>>isMessage (in category 'testing') -----
isMessage
	^true!

----- Method: MessageNode>>isMessage:receiver:arguments: (in category 'testing') -----
isMessage: selSymbol receiver: rcvrPred arguments: argsPred
	"Answer whether selector is selSymbol, and the predicates rcvrPred and argsPred
	 evaluate to true with respect to receiver and the list of arguments.  If selSymbol or
	 either predicate is nil, it means 'don't care'.  Note that argsPred takes numArgs
	 arguments.  All block arguments are ParseNodes."

	^(selSymbol isNil or: [selSymbol==selector key]) and:
		[(rcvrPred isNil or: [rcvrPred value: receiver]) and:
			[(argsPred isNil or: [argsPred valueWithArguments: arguments])]]!

----- Method: MessageNode>>isReturningIf (in category 'testing') -----
isReturningIf

	^(special between: 3 and: 4)
		and: [arguments first returns and: [arguments last returns]]!

----- Method: MessageNode>>macroPrinter (in category 'printing') -----
macroPrinter

	special > 0 ifTrue: [^MacroPrinters at: special].
	^nil
!

----- Method: MessageNode>>morphFromKeywords:arguments:on:indent: (in category 'tiles') -----
morphFromKeywords: key arguments: args on: parent indent: ignored

	^parent
		messageNode: self 
		receiver: receiver 
		selector: selector 
		keywords: key 
		arguments: args
!

----- Method: MessageNode>>noteSpecialSelector: (in category 'macro transformations') -----
noteSpecialSelector: selectorSymbol
	" special > 0 denotes specially treated messages. "

	"Deconvert initial keywords from SQ2K"
	special _ #(:Test:Yes: :Test:No: :Test:Yes:No: :Test:No:Yes:
				and: or:
				:Until:do: :While:do: whileFalse whileTrue
				:Repeat:to:do: :Repeat:to:by:do:
				) indexOf: selectorSymbol.
	special > 0 ifTrue: [^ self].

	special _ MacroSelectors indexOf: selectorSymbol.
!

----- Method: MessageNode>>precedence (in category 'printing') -----
precedence

	^precedence!

----- Method: MessageNode>>printCaseOn:indent: (in category 'printing') -----
printCaseOn: aStream indent: level 
	"receiver caseOf: {[key]->[value]. ...} otherwise: [otherwise]"
	| braceNode otherwise extra |
	braceNode _ arguments first.
	otherwise _ arguments last.
	(arguments size = 1 or: [otherwise isJustCaseError])
		ifTrue: [otherwise _ nil].
	receiver
		printOn: aStream
		indent: level
		precedence: 3.
	aStream nextPutAll: ' caseOf: '.
	braceNode isVariableReference ifTrue: [braceNode printOn: aStream indent: level]
		ifFalse: 
			[aStream nextPutAll: '{';
				 crtab: level + 1.
			braceNode
				casesForwardDo: 
					[:keyNode :valueNode :last | 
					keyNode printOn: aStream indent: level + 1.
					aStream nextPutAll: ' -> '.
					valueNode isComplex
						ifTrue: 
							[aStream crtab: level + 2.
							extra _ 1]
						ifFalse: [extra _ 0].
					valueNode printOn: aStream indent: level + 1 + extra.
					last ifTrue: [aStream nextPut: $}]
						ifFalse: [aStream nextPut: $.;
								 crtab: level + 1]]].
	otherwise isNil
		ifFalse: 
			[aStream crtab: level + 1;
			nextPutAll: ' otherwise: '.
			otherwise isComplex
				ifTrue: 
					[aStream crtab: level + 2.
					extra _ 1]
				ifFalse: [extra _ 0].
			otherwise printOn: aStream indent: level + 1 + extra.]!

----- Method: MessageNode>>printIfNil:indent: (in category 'printing') -----
printIfNil: aStream indent: level

	self printReceiver: receiver on: aStream indent: level.

	^self printKeywords: selector key
		arguments: (Array with: arguments first)
		on: aStream indent: level!

----- Method: MessageNode>>printIfNilNotNil:indent: (in category 'printing') -----
printIfNilNotNil: aStream indent: level

	self printReceiver: receiver ifNilReceiver on: aStream indent: level.

	(arguments first isJust: NodeNil) ifTrue:
		[^ self printKeywords: #ifNotNil:
				arguments: { arguments second }
				on: aStream indent: level].
	(arguments second isJust: NodeNil) ifTrue:
		[^ self printKeywords: #ifNil:
				arguments: { arguments first }
				on: aStream indent: level].
	^ self printKeywords: #ifNil:ifNotNil:
			arguments: arguments
			on: aStream indent: level!

----- Method: MessageNode>>printIfOn:indent: (in category 'printing') -----
printIfOn: aStream indent: level

	receiver ifNotNil: [
		receiver printOn: aStream indent: level + 1 precedence: precedence.
	].
	(arguments last isJust: NodeNil) ifTrue:
		[^ self printKeywords: #ifTrue: arguments: (Array with: arguments first)
					on: aStream indent: level].
	(arguments last isJust: NodeFalse) ifTrue:
		[^ self printKeywords: #and: arguments: (Array with: arguments first)
					on: aStream indent: level].
	(arguments first isJust: NodeNil) ifTrue:
		[^ self printKeywords: #ifFalse: arguments: (Array with: arguments last)
					on: aStream indent: level].
	(arguments first isJust: NodeTrue) ifTrue:
		[^ self printKeywords: #or: arguments: (Array with: arguments last)
					on: aStream indent: level].
	self printKeywords: #ifTrue:ifFalse: arguments: arguments
					on: aStream indent: level!

----- Method: MessageNode>>printKeywords:arguments:on:indent: (in category 'printing') -----
printKeywords: key arguments: args on: aStream indent: level

	^ self printKeywords: key arguments: args on: aStream indent: level prefix: false
!

----- Method: MessageNode>>printKeywords:arguments:on:indent:prefix: (in category 'printing') -----
printKeywords: key arguments: args on: aStream indent: level prefix: isPrefix
	| keywords indent arg kwd doCrTab |
	args size = 0 ifTrue: [aStream space; nextPutAll: key. ^ self].
	keywords _ key keywords.
	doCrTab _ args size > 2 or:
		[{receiver} , args
			inject: false
			into: [:was :thisArg |
				was or: [(thisArg isKindOf: BlockNode)
					or: [(thisArg isKindOf: MessageNode) and: [thisArg precedence >= 3]]]]].
	1 to: (args size min: keywords size) do:
		[:i | arg _ args at: i.  kwd _ keywords at: i.
		doCrTab
			ifTrue: [aStream crtab: level+1. indent _ 1] "newline after big args"
			ifFalse: [aStream space. indent _ 0].
		aStream nextPutAll: kwd; space.
		arg printOn: aStream indent: level + 1 + indent
			 	precedence: (precedence = 2 ifTrue: [1] ifFalse: [precedence]).
		]!

----- Method: MessageNode>>printOn:indent: (in category 'printing') -----
printOn: aStream indent: level

	| leadingKeyword |

"may not need this check anymore - may be fixed by the #receiver: change"
	special ifNil: [^aStream nextPutAll: '** MessageNode with nil special **'].


	(special > 0)
		ifTrue: [self perform: self macroPrinter with: aStream with: level]
		ifFalse: [selector key first = $:
				ifTrue: [leadingKeyword _ selector key keywords first.
						aStream nextPutAll: leadingKeyword; space.
						self printReceiver: receiver on: aStream indent: level.
						self printKeywords: (selector key allButFirst: leadingKeyword size + 1) arguments: arguments
							on: aStream indent: level]
				ifFalse: [self printReceiver: receiver on: aStream indent: level.
						self printKeywords: selector key arguments: arguments
						on: aStream indent: level]]!

----- Method: MessageNode>>printOn:indent:precedence: (in category 'printing') -----
printOn: strm indent: level precedence: outerPrecedence

	| parenthesize |
	parenthesize _ precedence > outerPrecedence
		or: [outerPrecedence = 3 and: [precedence = 3 "both keywords"]].
	parenthesize
		ifTrue: [strm nextPutAll: '('.
				self printOn: strm indent: level.
				strm nextPutAll: ')']
		ifFalse: [self printOn: strm indent: level]!

----- Method: MessageNode>>printParenReceiver:on:indent: (in category 'printing') -----
printParenReceiver: rcvr on: aStream indent: level
					
	(rcvr isKindOf: BlockNode) ifTrue:
		[^ rcvr printOn: aStream indent: level].
	aStream nextPutAll: '('.
	rcvr printOn: aStream indent: level.
	aStream nextPutAll: ')'
!

----- Method: MessageNode>>printReceiver:on:indent: (in category 'printing') -----
printReceiver: rcvr on: aStream indent: level
					
	rcvr ifNil: [^ self].

	"Force parens around keyword receiver of kwd message"
	 rcvr printOn: aStream indent: level precedence: precedence.
!

----- Method: MessageNode>>printToDoOn:indent: (in category 'printing') -----
printToDoOn: aStream indent: level

	| limitNode |
	
	self printReceiver: receiver on: aStream indent: level.

	(arguments last == nil or: [(arguments last isMemberOf: AssignmentNode) not])
		ifTrue: [limitNode _ arguments first]
		ifFalse: [limitNode _ arguments last value].
	(selector key = #to:by:do:
			and: [(arguments at: 2) isConstantNumber
				and: [(arguments at: 2) key = 1]])
		ifTrue: [self printKeywords: #to:do:
					arguments: (Array with: limitNode with: (arguments at: 3))
					on: aStream indent: level prefix: true]
		ifFalse: [self printKeywords: selector key
					arguments: (Array with: limitNode) , arguments allButFirst
					on: aStream indent: level prefix: true]!

----- Method: MessageNode>>printWhileOn:indent: (in category 'printing') -----
printWhileOn: aStream indent: level

	self printReceiver: receiver on: aStream indent: level.
			(arguments isEmpty not and: [ arguments first isJust: NodeNil]) ifTrue:
					[selector _ SelectorNode new
								key: (selector key == #whileTrue:
									ifTrue: [#whileTrue] ifFalse: [#whileFalse])
								code: #macro.
						arguments _ Array new].
				self printKeywords: selector key arguments: arguments
					on: aStream indent: level.!

----- Method: MessageNode>>pvtCheckForPvtSelector: (in category 'private') -----
pvtCheckForPvtSelector: encoder
	"If the code being compiled is trying to send a private message (e.g. 'pvtCheckForPvtSelector:') to anyone other than self, then complain to encoder."

	selector isPvtSelector ifTrue:
		[receiver isSelfPseudoVariable ifFalse:
			[encoder notify: 'Private messages may only be sent to self']].!

----- Method: MessageNode>>receiver (in category 'equation translation') -----
receiver
	^receiver!

----- Method: MessageNode>>receiver: (in category 'equation translation') -----
receiver: val
	"14 feb 2001 - removed return arrow"

	receiver _ val!

----- Method: MessageNode>>receiver:arguments:precedence: (in category 'private') -----
receiver: rcvr arguments: args precedence: p

	receiver _ rcvr.
	arguments _ args.
	sizes _ Array new: arguments size.
	precedence _ p!

----- Method: MessageNode>>receiver:selector:arguments:precedence: (in category 'initialize-release') -----
receiver: rcvr selector: selNode arguments: args precedence: p 
	"Decompile."

	self receiver: rcvr
		arguments: args
		precedence: p.
	self noteSpecialSelector: selNode key.
	selector _ selNode.
	"self pvtCheckForPvtSelector: encoder"
	"We could test code being decompiled, but the compiler should've checked already. And where to send the complaint?"!

----- Method: MessageNode>>receiver:selector:arguments:precedence:from: (in category 'initialize-release') -----
receiver: rcvr selector: aSelector arguments: args precedence: p from: encoder 
	"Compile."

	| theSelector |
	self receiver: rcvr
		arguments: args
		precedence: p.
	aSelector = #':Repeat:do:'
		ifTrue: [theSelector _ #do:]
		ifFalse: [theSelector _ aSelector].
	self noteSpecialSelector: theSelector.
	(self transform: encoder)
		ifTrue: 
			[selector isNil
				ifTrue: [selector _ SelectorNode new 
							key: (MacroSelectors at: special)
							code: #macro]]
		ifFalse: 
			[selector _ encoder encodeSelector: theSelector.
			rcvr == NodeSuper ifTrue: [encoder noteSuper]].
	self pvtCheckForPvtSelector: encoder!

----- Method: MessageNode>>receiver:selector:arguments:precedence:from:sourceRange: (in category 'initialize-release') -----
receiver: rcvr selector: selName arguments: args precedence: p from: encoder sourceRange: range 
	"Compile."

	encoder noteSourceRange: range forNode: self.
	^self
		receiver: rcvr
		selector: selName
		arguments: args
		precedence: p
		from: encoder!

----- Method: MessageNode>>selector (in category 'equation translation') -----
selector
	^selector!

----- Method: MessageNode>>selector: (in category 'initialize-release') -----
selector: sel
	selector _ sel!

----- Method: MessageNode>>sizeCase:value: (in category 'code generation') -----
sizeCase: encoder value: forValue

	| braceNode sizeIndex thenSize elseSize |
	forValue not
		ifTrue: [^super sizeForEffect: encoder].
	equalNode _ encoder encodeSelector: #=.
	braceNode _ arguments first.
	sizes _ Array new: 2 * braceNode numElements.
	sizeIndex _ sizes size.
	elseSize _ arguments size = 2
		ifTrue:
			[arguments last sizeForEvaluatedValue: encoder] "otherwise: [...]"
		ifFalse:
			[caseErrorNode _ encoder encodeSelector: #caseError.
			 1 + (caseErrorNode size: encoder args: 0 super: false)]. "self caseError"
	braceNode casesReverseDo:
		[:keyNode :valueNode :last |
		sizes at: sizeIndex put: elseSize.
		thenSize _ valueNode sizeForEvaluatedValue: encoder.
		last ifFalse: [thenSize _ thenSize + 1]. "Pop"
		valueNode returns ifFalse: [thenSize _ thenSize + (self sizeJump: elseSize)].
		sizes at: sizeIndex-1 put: thenSize.
		last ifFalse: [elseSize _ elseSize + 1]. "Dup"
		elseSize _ elseSize + (keyNode sizeForEvaluatedValue: encoder) +
			(equalNode size: encoder args: 1 super: false) +
			(self sizeBranchOn: false dist: thenSize) + thenSize.
		sizeIndex _ sizeIndex - 2].
	^(receiver sizeForValue: encoder) + elseSize
!

----- Method: MessageNode>>sizeForEffect: (in category 'code generation') -----
sizeForEffect: encoder

	special > 0 
		ifTrue: [^self perform: (MacroSizers at: special) with: encoder with: false].
	^super sizeForEffect: encoder!

----- Method: MessageNode>>sizeForValue: (in category 'code generation') -----
sizeForValue: encoder
	| total argSize |
	special > 0 
		ifTrue: [^self perform: (MacroSizers at: special) with: encoder with: true].
	receiver == NodeSuper
		ifTrue: [selector _ selector copy "only necess for splOops"].
	total _ selector size: encoder args: arguments size super: receiver == NodeSuper.
	receiver == nil 
		ifFalse: [total _ total + (receiver sizeForValue: encoder)].
	sizes _ arguments collect: 
					[:arg | 
					argSize _ arg sizeForValue: encoder.
					total _ total + argSize.
					argSize].
	^total!

----- Method: MessageNode>>sizeIf:value: (in category 'code generation') -----
sizeIf: encoder value: forValue
	| thenExpr elseExpr branchSize thenSize elseSize |
	thenExpr _ arguments at: 1.
	elseExpr _ arguments at: 2.
	(forValue
		or: [(thenExpr isJust: NodeNil)
		or: [elseExpr isJust: NodeNil]]) not
			"(...not ifTrue: avoids using ifFalse: alone during this compile)"
		ifTrue:  "Two-armed IFs forEffect share a single pop"
			[^ super sizeForEffect: encoder].
	forValue
		ifTrue:  "Code all forValue as two-armed"
			[elseSize _ elseExpr sizeForEvaluatedValue: encoder.
			thenSize _ (thenExpr sizeForEvaluatedValue: encoder)
					+ (thenExpr returns
						ifTrue: [0]  "Elide jump over else after a return"
						ifFalse: [self sizeJump: elseSize]).
			branchSize _ self sizeBranchOn: false dist: thenSize]
		ifFalse:  "One arm is empty here (two-arms code forValue)"
			[(elseExpr isJust: NodeNil)
				ifTrue:
					[elseSize _ 0.
					thenSize _ thenExpr sizeForEvaluatedEffect: encoder.
					branchSize _ self sizeBranchOn: false dist: thenSize]
				ifFalse:
					[thenSize _ 0.
					elseSize _ elseExpr sizeForEvaluatedEffect: encoder.
					branchSize _ self sizeBranchOn: true dist: elseSize]].
	sizes _ Array with: thenSize with: elseSize.
	^ (receiver sizeForValue: encoder) + branchSize
			+ thenSize + elseSize!

----- Method: MessageNode>>sizeIfNil:value: (in category 'code generation') -----
sizeIfNil: encoder value: forValue

	| theNode theSize theSelector |
	equalNode _ encoder encodeSelector: #==.
	sizes _ Array new: 1.
	theNode _ arguments first.
	theSelector _ #ifNotNil:.
	forValue
		ifTrue:
			[sizes at: 1 put: (theSize _ (1 "pop" + (theNode sizeForEvaluatedValue: encoder))).
			 ^(receiver sizeForValue: encoder) +
				2 "Dup. LdNil" +
				(equalNode size: encoder args: 1 super: false) +
				(self 
					sizeBranchOn: (selector key == theSelector) 
					dist: theSize) +
				theSize]
		ifFalse:
			[sizes at: 1 put: (theSize _ (theNode sizeForEvaluatedEffect: encoder)).
			 ^(receiver sizeForValue: encoder) +
				1 "LdNil" +
				(equalNode size: encoder args: 1 super: false) +
				(self 
					sizeBranchOn: (selector key == theSelector) 
					dist: theSize) +
				theSize]

!

----- Method: MessageNode>>sizeToDo:value: (in category 'code generation') -----
sizeToDo: encoder value: forValue 
	" var _ rcvr. L1: [var <= arg1] Bfp(L2) [block body. var _ var + inc] Jmp(L1) L2: "
	| loopSize initStmt test block incStmt blockSize blockVar initSize limitInit |
	block _ arguments at: 3.
	blockVar _ block firstArgument.
	initStmt _ arguments at: 4.
	test _ arguments at: 5.
	incStmt _ arguments at: 6.
	limitInit _ arguments at: 7.
	initSize _ initStmt sizeForEffect: encoder.
	limitInit == nil
		ifFalse: [initSize _ initSize + (limitInit sizeForEffect: encoder)].
	blockSize _ (block sizeForEvaluatedEffect: encoder)
			+ (incStmt sizeForEffect: encoder) + 2.  "+2 for Jmp backward"
	loopSize _ (test sizeForValue: encoder)
			+ (self sizeBranchOn: false dist: blockSize)
			+ blockSize.
	sizes _ Array with: blockSize with: loopSize.
	^ initSize + loopSize
			+ (forValue ifTrue: [1] ifFalse: [0])    " +1 for value (push nil) "!

----- Method: MessageNode>>sizeWhile:value: (in category 'code generation') -----
sizeWhile: encoder value: forValue 
	"L1: ... Bfp(L2) ... Jmp(L1) L2: nil (nil for value only);
	justStmt, wholeLoop, justJump."
	| cond stmt stmtSize loopSize branchSize |
	cond _ receiver.
	stmt _ arguments at: 1.
	stmtSize _ (stmt sizeForEvaluatedEffect: encoder) + 2.
	branchSize _ self sizeBranchOn: (selector key == #whileFalse:)  "Btp for whileFalse"
					dist: stmtSize.
	loopSize _ (cond sizeForEvaluatedValue: encoder)
			+ branchSize + stmtSize.
	sizes _ Array with: stmtSize with: loopSize.
	^ loopSize    " +1 for value (push nil) "
		+ (forValue ifTrue: [1] ifFalse: [0])!

----- Method: MessageNode>>test (in category 'printing') -----
test

	3 > 4 ifTrue: [4+5 between: 6 and: 7]
			ifFalse: [4 between: 6+5 and: 7-2]!

----- Method: MessageNode>>toDoFromWhileWithInit: (in category 'macro transformations') -----
toDoFromWhileWithInit: initStmt
	"Return nil, or a to:do: expression equivalent to this whileTrue:"
	| variable increment limit toDoBlock body test |
	(selector key == #whileTrue:
		and: [(initStmt isMemberOf: AssignmentNode) and:
				[initStmt variable isTemp]])
		ifFalse: [^ nil].
	body _ arguments last statements.
	variable _ initStmt variable.
	increment _ body last toDoIncrement: variable.
	(increment == nil or: [receiver statements size ~= 1])
		ifTrue: [^ nil].
	test _ receiver statements first.
	"Note: test chould really be checked that <= or >= comparison
	jibes with the sign of the (constant) increment"
	((test isMemberOf: MessageNode)
		and: [(limit _ test toDoLimit: variable) notNil])
		ifFalse: [^ nil].
	toDoBlock _ BlockNode statements: body allButLast returns: false.
	toDoBlock arguments: (Array with: variable).
	^ MessageNode new
		receiver: initStmt value
		selector: (SelectorNode new key: #to:by:do: code: #macro)
		arguments: (Array with: limit with: increment with: toDoBlock)
		precedence: precedence!

----- Method: MessageNode>>toDoIncrement: (in category 'testing') -----
toDoIncrement: variable
	(receiver = variable and: [selector key = #+]) 
		ifFalse: [^ nil].
	arguments first isConstantNumber
		ifTrue: [^ arguments first]
		ifFalse: [^ nil]!

----- Method: MessageNode>>toDoLimit: (in category 'testing') -----
toDoLimit: variable
	(receiver = variable and: [selector key = #<= or: [selector key = #>=]]) 
		ifTrue: [^ arguments first]
		ifFalse: [^ nil]!

----- Method: MessageNode>>transform: (in category 'macro transformations') -----
transform: encoder
	special = 0 ifTrue: [^false].
	(self perform: (MacroTransformers at: special) with: encoder)
		ifTrue: 
			[^true]
		ifFalse: 
			[special _ 0. ^false]!

----- Method: MessageNode>>transformAnd: (in category 'macro transformations') -----
transformAnd: encoder
	(self transformBoolean: encoder)
		ifTrue: 
			[arguments _ 
				Array 
					with: (arguments at: 1)
					with: (BlockNode withJust: NodeFalse).
			^true]
		ifFalse: 
			[^false]!

----- Method: MessageNode>>transformBoolean: (in category 'macro transformations') -----
transformBoolean: encoder
	^self
		checkBlock: (arguments at: 1)
		as: 'argument'
		from: encoder!

----- Method: MessageNode>>transformCase: (in category 'private') -----
transformCase: encoder

	| caseNode |
	caseNode _ arguments first.
	(caseNode isKindOf: BraceNode)
		ifTrue:
			[^(caseNode blockAssociationCheck: encoder) and:
			 	[arguments size = 1 or:
					[self checkBlock: arguments last as: 'otherwise arg' from: encoder]]].
	(caseNode canBeSpecialArgument and: [(caseNode isMemberOf: BlockNode) not])
		ifTrue:
			[^false]. "caseOf: variable"
	^encoder notify: 'caseOf: argument must be a brace construct or a variable'!

----- Method: MessageNode>>transformIfFalse: (in category 'macro transformations') -----
transformIfFalse: encoder
	(self transformBoolean: encoder)
		ifTrue: 
			[arguments _ 
				Array 
					with: (BlockNode withJust: NodeNil)
					with: (arguments at: 1).
			^true]
		ifFalse:
			[^false]!

----- Method: MessageNode>>transformIfFalseIfTrue: (in category 'macro transformations') -----
transformIfFalseIfTrue: encoder
	((self checkBlock: (arguments at: 1) as: 'False arg' from: encoder)
		and: [self checkBlock: (arguments at: 2) as: 'True arg' from: encoder])
		ifTrue: 
			[selector _ #ifTrue:ifFalse:.
			arguments swap: 1 with: 2.
			^true]
		ifFalse: 
			[^false]!

----- Method: MessageNode>>transformIfNil: (in category 'macro transformations') -----
transformIfNil: encoder

	(self transformBoolean: encoder) ifFalse: [^ false].
	(MacroSelectors at: special) = #ifNotNil:
	ifTrue:
		[(self checkBlock: arguments first as: 'ifNotNil arg' from: encoder) ifFalse: [^ false].

		"Transform 'ifNotNil: [stuff]' to 'ifNil: [nil] ifNotNil: [stuff]'.
		Slightly better code and more consistent with decompilation."
		self noteSpecialSelector: #ifNil:ifNotNil:.
		selector _ SelectorNode new key: (MacroSelectors at: special) code: #macro.
		arguments _ {BlockNode withJust: NodeNil. arguments first}.
		(self transform: encoder) ifFalse: [self error: 'compiler logic error'].
		^ true]
	ifFalse:
		[^ self checkBlock: arguments first as: 'ifNil arg' from: encoder]
!

----- Method: MessageNode>>transformIfNilIfNotNil: (in category 'macro transformations') -----
transformIfNilIfNotNil: encoder
	((self checkBlock: (arguments at: 1) as: 'Nil arg' from: encoder)
		and: [self checkBlock: (arguments at: 2) as: 'NotNil arg' from: encoder])
		ifTrue: 
			[selector _ SelectorNode new key: #ifTrue:ifFalse: code: #macro.
			receiver _ MessageNode new
				receiver: receiver
				selector: #==
				arguments: (Array with: NodeNil)
				precedence: 2
				from: encoder.
			^true]
		ifFalse: 
			[^false]!

----- Method: MessageNode>>transformIfNotNilIfNil: (in category 'macro transformations') -----
transformIfNotNilIfNil: encoder
	((self checkBlock: (arguments at: 1) as: 'NotNil arg' from: encoder)
		and: [self checkBlock: (arguments at: 2) as: 'Nil arg' from: encoder])
		ifTrue: 
			[selector _ SelectorNode new key: #ifTrue:ifFalse: code: #macro.
			receiver _ MessageNode new
				receiver: receiver
				selector: #==
				arguments: (Array with: NodeNil)
				precedence: 2
				from: encoder.
			arguments swap: 1 with: 2.
			^true]
		ifFalse: 
			[^false]!

----- Method: MessageNode>>transformIfTrue: (in category 'macro transformations') -----
transformIfTrue: encoder
	(self transformBoolean: encoder)
		ifTrue: 
			[arguments _ 
				Array 
					with: (arguments at: 1)
					with: (BlockNode withJust: NodeNil).
			^true]
		ifFalse: 
			[^false]!

----- Method: MessageNode>>transformIfTrueIfFalse: (in category 'macro transformations') -----
transformIfTrueIfFalse: encoder
	^(self checkBlock: (arguments at: 1) as: 'True arg' from: encoder)
		and: [self checkBlock: (arguments at: 2) as: 'False arg' from: encoder]!

----- Method: MessageNode>>transformOr: (in category 'macro transformations') -----
transformOr: encoder
	(self transformBoolean: encoder)
		ifTrue: 
			[arguments _ 
				Array 
					with: (BlockNode withJust: NodeTrue)
					with: (arguments at: 1).
			^true]
		ifFalse: 
			[^false]!

----- Method: MessageNode>>transformToDo: (in category 'macro transformations') -----
transformToDo: encoder
	" var _ rcvr. L1: [var <= arg1] Bfp(L2) [block body. var _ var + inc] 
Jmp(L1) L2: "
	| limit increment block initStmt test incStmt limitInit blockVar myRange blockRange |
	"First check for valid arguments"
	((arguments last isMemberOf: BlockNode)
			and: [arguments last numberOfArguments = 1])
		ifFalse: [^ false].
	arguments last firstArgument isVariableReference
		ifFalse: [^ false]. "As with debugger remote vars"
	arguments size = 3
		ifTrue: [increment _ arguments at: 2.
				(increment isConstantNumber and:
					[increment literalValue ~= 0]) ifFalse: [^ false]]
		ifFalse: [increment _ encoder encodeLiteral: 1].
	arguments size < 3 ifTrue:   "transform to full form"
		[selector _ SelectorNode new key: #to:by:do: code: #macro].

	"Now generate auxiliary structures"
	myRange _ encoder rawSourceRanges at: self ifAbsent: [1 to: 0].
	block _ arguments last.
	blockRange _ encoder rawSourceRanges at: block ifAbsent: [1 to: 0].
	blockVar _ block firstArgument.
	initStmt _ AssignmentNode new variable: blockVar value: receiver.
	limit _ arguments at: 1.
	limit isVariableReference | limit isConstantNumber
		ifTrue: [limitInit _ nil]
		ifFalse:  "Need to store limit in a var"
			[limit _ encoder autoBind: blockVar key , 'LimiT'.
			limit scope: -2.  "Already done parsing block"
			limitInit _ AssignmentNode new
					variable: limit
					value: (arguments at: 1)].
	test _ MessageNode new receiver: blockVar
			selector: (increment key > 0 ifTrue: [#<=] ifFalse: [#>=])
			arguments: (Array with: limit)
			precedence: precedence from: encoder
			sourceRange: (myRange first to: blockRange first).
	incStmt _ AssignmentNode new
			variable: blockVar
			value: (MessageNode new
				receiver: blockVar selector: #+
				arguments: (Array with: increment)
				precedence: precedence from: encoder)
			from: encoder
			sourceRange: (myRange last to: myRange last).
	arguments _ (Array with: limit with: increment with: block)
		, (Array with: initStmt with: test with: incStmt with: limitInit).
	^ true!

----- Method: MessageNode>>transformWhile: (in category 'macro transformations') -----
transformWhile: encoder
	(self checkBlock: receiver as: 'receiver' from: encoder)
		ifFalse: [^ false].
	arguments size = 0   "transform bodyless form to body form"
		ifTrue: [selector _ SelectorNode new
					key: (special = 10 ifTrue: [#whileTrue:] ifFalse: [#whileFalse:])
					code: #macro.
				arguments _ Array with: (BlockNode withJust: NodeNil).
				^ true]
		ifFalse: [^ self transformBoolean: encoder]!

ParseNode subclass: #MethodNode
	instanceVariableNames: 'selectorOrFalse precedence arguments block literals primitive encoder temporaries properties sourceText'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

!MethodNode commentStamp: '<historical>' prior: 0!
I am the root of the parse tree.!

----- Method: MethodNode>>block (in category 'initialize-release') -----
block
	^ block!

----- Method: MethodNode>>body (in category 'accessing') -----
body
	^block!

----- Method: MethodNode>>decompileString (in category 'printing') -----
decompileString 
	"Answer a string description of the parse tree whose root is the receiver."

	^ self decompileText asString
!

----- Method: MethodNode>>decompileText (in category 'printing') -----
decompileText 
	"Answer a text description of the parse tree whose root is the receiver."

	^ ColoredCodeStream contents: [:strm | self printOn: strm]!

----- Method: MethodNode>>encoder (in category 'code generation') -----
encoder
	^ encoder!

----- Method: MethodNode>>generate (in category 'code generation') -----
generate
	"The receiver is the root of a parse tree. Answer a CompiledMethod. The
	argument, trailer, is the references to the source code that is stored with 
	every CompiledMethod."

	^self generate: #(0 0 0 0)!

----- Method: MethodNode>>generate: (in category 'code generation') -----
generate: trailer 
	^self generateWith: trailer using: CompiledMethod!

----- Method: MethodNode>>generate:ifQuick: (in category 'code generation') -----
generate: trailer ifQuick: methodBlock
	| v |
	(primitive = 0 and: [arguments size = 0 and: [block isQuick]])
		ifFalse: [^ self].
	v _ block code.
	v < 0
		ifTrue: [^ self].
	v = LdSelf
		ifTrue: [^ methodBlock value: (CompiledMethod toReturnSelfTrailerBytes: trailer)].
	(v between: LdTrue and: LdMinus1 + 3)
		ifTrue: [^ methodBlock value: (CompiledMethod toReturnConstant: v - LdSelf trailerBytes: trailer)].
	v < ((CodeBases at: LdInstType) + (CodeLimits at: LdInstType))
		ifTrue: [^ methodBlock value: (CompiledMethod toReturnField: v trailerBytes: trailer)].
	v // 256 = 1
		ifTrue: [^ methodBlock value: (CompiledMethod toReturnField: v \\ 256 trailerBytes: trailer)]!

----- Method: MethodNode>>generateWith:using: (in category 'code generation') -----
generateWith: trailer using: aCompiledMethodClass
	"The receiver is the root of a parse tree. Answer a CompiledMethod. The
	argument, trailer, is the references to the source code that is stored with 
	every CompiledMethod."

	| blkSize nLits stack strm nArgs method |
	self generate: trailer ifQuick: 
		[:m |  method _ m.
		method properties: properties.
		^ method].
	nArgs _ arguments size.
	blkSize _ block sizeForEvaluatedValue: encoder.
	literals _ encoder allLiterals.
	nLits _ literals size.
	method _ aCompiledMethodClass	"Dummy to allocate right size"
				newBytes: blkSize
				trailerBytes: trailer 
				nArgs: nArgs
				nTemps: encoder maxTemp
				nStack: 0
				nLits: nLits
				primitive: primitive.
	strm _ ReadWriteStream with: method.
	strm position: method initialPC - 1.
	stack _ ParseStack new init.
	block emitForEvaluatedValue: stack on: strm.
	stack position ~= 1 ifTrue: [^self error: 'Compiler stack
discrepancy'].
	strm position ~= (method size - trailer size) 
		ifTrue: [^self error: 'Compiler code size discrepancy'].
	method needsFrameSize: stack size.
	1 to: nLits do: [:lit | method literalAt: lit put: (literals at: lit)].
	method properties: properties.
	^ method!

----- Method: MethodNode>>methodClass (in category 'printing') -----
methodClass

	^ encoder classEncoding!

----- Method: MethodNode>>parserClass (in category 'code generation') -----
parserClass
	"Which parser produces this class of parse node"

	^ Parser!

----- Method: MethodNode>>printOn: (in category 'printing') -----
printOn: aStream 
	precedence = 1
		ifTrue: 
			[aStream nextPutAll: self selector]
		ifFalse: 
			[self selector keywords with: arguments do: 
				[:kwd :arg | 
				aStream nextPutAll: kwd; space.
				aStream withStyleFor: #methodArgument
					do: [aStream nextPutAll: arg key].
				aStream space]].
	comment == nil ifFalse: 
			[aStream crtab: 1.
			self printCommentOn: aStream indent: 1].
	temporaries size > 0 ifTrue: 
			[aStream crtab: 1.
			aStream nextPutAll: '|'.
			aStream withStyleFor: #temporaryVariable
				do: [temporaries do: 
						[:temp | aStream space; nextPutAll: temp key]].
				aStream nextPutAll: ' |'].
	properties ifNotNil: [ 
		properties pragmas do: [ :each |
			"Don't decompile basic primitives that return self, i-vars, etc."
			each keyword = #primitive:
				ifFalse: [ aStream crtab: 1. each printOn: aStream ]
				ifTrue: [
					( (each argumentAt: 1) isNumber and: [(each argumentAt: 1) between: 255 and: 519])
						ifFalse: [ aStream crtab: 1. self printPrimitiveOn: aStream ] ] ] ].
	aStream crtab: 1.
	^ block printStatementsOn: aStream indent: 0!

----- Method: MethodNode>>printPrimitiveOn: (in category 'printing') -----
printPrimitiveOn: aStream 
	"Print the primitive on aStream"
	| primIndex primDecl |
	primIndex _ primitive.
	primIndex = 0
		ifTrue: [^ self].
	primIndex = 120
		ifTrue: ["External call spec"
			^ aStream print: encoder literals first].
	aStream nextPutAll: '<primitive: '.
	primIndex = 117
		ifTrue: [primDecl _ encoder literals at: 1.
			aStream nextPut: $';
				
				nextPutAll: (primDecl at: 2);
				 nextPut: $'.
			(primDecl at: 1) notNil
				ifTrue: [aStream nextPutAll: ' module:';
						 nextPut: $';
						
						nextPutAll: (primDecl at: 1);
						 nextPut: $']]
		ifFalse: [aStream print: primIndex].
	aStream nextPut: $>.
	Smalltalk at: #Interpreter ifPresent:[:cls|
		aStream nextPutAll: ' "'
				, ((cls classPool at: #PrimitiveTable)
						at: primIndex + 1) , '" '].!

----- Method: MethodNode>>properties (in category 'code generation') -----
properties
	^ properties!

----- Method: MethodNode>>rawSourceRanges (in category 'tiles') -----
rawSourceRanges

	self generate.
	^encoder rawSourceRanges!

----- Method: MethodNode>>selector (in category 'code generation') -----
selector 
	"Answer the message selector for the method represented by the receiver."

	(selectorOrFalse isSymbol)
		ifTrue: [^selectorOrFalse].
	^selectorOrFalse key.
!

----- Method: MethodNode>>selector: (in category 'initialize-release') -----
selector: symbol

	selectorOrFalse _ symbol!

----- Method: MethodNode>>selector:arguments:precedence:temporaries:block:encoder:primitive: (in category 'initialize-release') -----
selector: selOrFalse arguments: args precedence: p temporaries: temps block: blk encoder: anEncoder primitive: prim 
	
	self 
		selector: selOrFalse
		arguments: args
		precedence: p
		temporaries: temps
		block: blk encoder:
		anEncoder 
		primitive: prim 
		properties: MethodProperties new.!

----- Method: MethodNode>>selector:arguments:precedence:temporaries:block:encoder:primitive:properties: (in category 'initialize-release') -----
selector: selOrFalse arguments: args precedence: p temporaries: temps block: blk encoder: anEncoder primitive: prim properties: propDict
	"Initialize the receiver with respect to the arguments given."

	encoder _ anEncoder.
	selectorOrFalse _ selOrFalse.
	precedence _ p.
	arguments _ args.
	temporaries _ temps.
	block _ blk.
	primitive _ prim.
	properties _ propDict.!

----- Method: MethodNode>>sourceMap (in category 'code generation') -----
sourceMap
	"Answer a SortedCollection of associations of the form: pc (byte offset in me) -> sourceRange (an Interval) in source text."

	| methNode |
	methNode _ self.
	sourceText ifNil: [
		"No source, use decompile string as source to map from"
		methNode _ self parserClass new
			parse: self decompileString
			class: self methodClass
	].
	methNode generate.  "set bytecodes to map to"
	^ methNode encoder sourceMap!

----- Method: MethodNode>>sourceText (in category 'printing') -----
sourceText

	^ sourceText ifNil: [self printString]!

----- Method: MethodNode>>sourceText: (in category 'initialize-release') -----
sourceText: stringOrText

	sourceText _ stringOrText!

----- Method: MethodNode>>tempNames (in category 'printing') -----
tempNames
	^ encoder tempNames!

ParseNode subclass: #MethodTempsNode
	instanceVariableNames: 'temporaries'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

----- Method: ParseNode class>>blockReturnCode (in category 'class initialization') -----
blockReturnCode

	^ EndRemote!

----- Method: ParseNode class>>initialize (in category 'class initialization') -----
initialize
	"ParseNode initialize. VariableNode initialize"
	LdInstType _ 1.
	LdTempType _ 2.
	LdLitType _ 3.
	LdLitIndType _ 4.
	SendType _ 5.
	CodeBases _ #(0 16 32 64 208 ).
	CodeLimits _ #(16 16 32 32 16 ).
	LdSelf _ 112.
	LdTrue _ 113.
	LdFalse _ 114.
	LdNil _ 115.
	LdMinus1 _ 116.
	LoadLong _ 128.
	Store _ 129.
	StorePop _ 130.
	ShortStoP _ 96.
	SendLong _ 131.
	DblExtDoAll _ 132.
	SendLong2 _ 134.
	LdSuper _ 133.
	Pop _ 135.
	Dup _ 136.
	LdThisContext _ 137.
	EndMethod _ 124.
	EndRemote _ 125.
	Jmp _ 144.
	Bfp _ 152.
	JmpLimit _ 8.
	JmpLong _ 164.  "code for jmp 0"
	BtpLong _ 168.
	SendPlus _ 176.
	Send _ 208.
	SendLimit _ 16!

----- Method: ParseNode class>>popCode (in category 'class initialization') -----
popCode

	^ Pop!

----- Method: ParseNode>>asReturnNode (in category 'converting') -----
asReturnNode

	^ReturnNode new expr: self!

----- Method: ParseNode>>assignmentCheck:at: (in category 'testing') -----
assignmentCheck: encoder at: location
	"For messageNodes masquerading as variables for the debugger.
	For now we let this through - ie we allow stores ev
	into args.  Should check against numArgs, though."
	^ -1!

----- Method: ParseNode>>canBeSpecialArgument (in category 'testing') -----
canBeSpecialArgument
	"Can I be an argument of (e.g.) ifTrue:?"

	^false!

----- Method: ParseNode>>canCascade (in category 'testing') -----
canCascade

	^false!

----- Method: ParseNode>>comment (in category 'comment') -----
comment

	^comment!

----- Method: ParseNode>>comment: (in category 'comment') -----
comment: newComment

	comment _ newComment!

----- Method: ParseNode>>emitBranchOn:dist:pop:on: (in category 'code generation') -----
emitBranchOn:
condition dist: dist pop: stack on: strm
	stack pop: 1.
	dist = 0 ifTrue: [^ strm nextPut: Pop].
	condition
		ifTrue: [self emitLong: dist code: BtpLong on: strm]
		ifFalse: [self emitShortOrLong: dist code: Bfp on: strm]!

----- Method: ParseNode>>emitForEffect:on: (in category 'code generation') -----
emitForEffect: stack on: strm

	self emitForValue: stack on: strm.
	strm nextPut: Pop.
	stack pop: 1!

----- Method: ParseNode>>emitForReturn:on: (in category 'code generation') -----
emitForReturn: stack on: strm

	self emitForValue: stack on: strm.
	strm nextPut: EndMethod!

----- Method: ParseNode>>emitJump:on: (in category 'code generation') -----
emitJump: dist on: strm

	dist = 0 ifFalse: [self emitShortOrLong: dist code: Jmp on: strm]!

----- Method: ParseNode>>emitLong:code:on: (in category 'code generation') -----
emitLong: dist code: longCode on: aStream 
	"Force a two-byte jump."
	| code distance |
	code _ longCode.
	distance _ dist.
	distance < 0
		ifTrue: 
			[distance _ distance + 1024.
			code _ code - 4]
		ifFalse: 
			[distance > 1023 ifTrue: [distance _ -1]].
	distance < 0
		ifTrue: 
			[self error: 'A block compiles more than 1K bytes of code']
		ifFalse: 
			[aStream nextPut: distance // 256 + code.
			aStream nextPut: distance \\ 256]!

----- Method: ParseNode>>emitShortOrLong:code:on: (in category 'code generation') -----
emitShortOrLong: dist code: shortCode on: strm
	(1 <= dist and: [dist <= JmpLimit])
		ifTrue: [strm nextPut: shortCode + dist - 1]
		ifFalse: [self emitLong: dist code: shortCode + (JmpLong-Jmp) on: strm]!

----- Method: ParseNode>>encodeSelector: (in category 'encoding') -----
encodeSelector: selector

	^nil!

----- Method: ParseNode>>ifNilReceiver (in category 'private') -----
ifNilReceiver
	"assuming this object is the receiver of an ifNil:, what object is being asked about?"
	^self!

----- Method: ParseNode>>isArg (in category 'testing') -----
isArg

	^false!

----- Method: ParseNode>>isComplex (in category 'testing') -----
isComplex
	"Used for pretty printing to determine whether to start a new line"

	^false!

----- Method: ParseNode>>isConstantNumber (in category 'testing') -----
isConstantNumber  "Overridden in LiteralNode"
	^false!

----- Method: ParseNode>>isDoIt (in category 'testing') -----
isDoIt
	"polymorphic with RBNodes; called by debugger"

	^ false!

----- Method: ParseNode>>isJust: (in category 'testing') -----
isJust: node
	^false!

----- Method: ParseNode>>isLiteral (in category 'testing') -----
isLiteral

	^ false!

----- Method: ParseNode>>isMessage (in category 'testing') -----
isMessage
	^false!

----- Method: ParseNode>>isMessage:receiver:arguments: (in category 'testing') -----
isMessage: selSymbol receiver: rcvrPred arguments: argsPred
	"See comment in MessageNode."

	^false!

----- Method: ParseNode>>isReturnSelf (in category 'testing') -----
isReturnSelf

	^false!

----- Method: ParseNode>>isReturningIf (in category 'testing') -----
isReturningIf

	^false!

----- Method: ParseNode>>isSelfPseudoVariable (in category 'testing') -----
isSelfPseudoVariable	
	"Overridden in VariableNode."
	^false!

----- Method: ParseNode>>isSpecialConstant (in category 'testing') -----
isSpecialConstant
	^ false!

----- Method: ParseNode>>isTemp (in category 'testing') -----
isTemp
	^ false!

----- Method: ParseNode>>isUndefTemp (in category 'testing') -----
isUndefTemp
	^ false!

----- Method: ParseNode>>isUnusedTemp (in category 'testing') -----
isUnusedTemp
	^ false!

----- Method: ParseNode>>isVariableReference (in category 'testing') -----
isVariableReference

	^false!

----- Method: ParseNode>>nextWordFrom:setCharacter: (in category 'private') -----
nextWordFrom: aStream setCharacter: aBlock
	| outStream char |
	outStream _ WriteStream on: (String new: 16).
	[(aStream peekFor: Character space) 
		or: [aStream peekFor: Character tab]] whileTrue.
	[aStream atEnd
		or:
			[char _ aStream next.
			char = Character cr or: [char = Character space]]]
		whileFalse: [outStream nextPut: char].
	aBlock value: char.
	^ outStream contents!

----- Method: ParseNode>>nodePrintOn:indent: (in category 'printing') -----
nodePrintOn: aStrm indent: nn
	| var aaStrm myLine |
	"Show just the sub nodes and the code."

	(aaStrm _ aStrm) ifNil: [aaStrm _ WriteStream on: (String new: 500)].
	nn timesRepeat: [aaStrm tab].
	aaStrm nextPutAll: self class name; space.
	myLine _ self printString copyWithout: Character cr.
	myLine _ myLine copyFrom: 1 to: (myLine size min: 70).
	aaStrm nextPutAll: myLine; cr.
	1 to: self class instSize do: [:ii | 
		var _ self instVarAt: ii.
		(var respondsTo: #asReturnNode) ifTrue: [var nodePrintOn: aaStrm indent: nn+1]].
	1 to: self class instSize do: [:ii | 
		var _ self instVarAt: ii.
		(var isKindOf: SequenceableCollection) ifTrue: [
				var do: [:aNode | 
					(aNode respondsTo: #asReturnNode) ifTrue: [
						aNode nodePrintOn: aaStrm indent: nn+1]]]].
	^ aaStrm
!

----- Method: ParseNode>>nowHasDef (in category 'testing') -----
nowHasDef  "Ignored in all but VariableNode"!

----- Method: ParseNode>>nowHasRef (in category 'testing') -----
nowHasRef  "Ignored in all but VariableNode"!

----- Method: ParseNode>>pc (in category 'code generation') -----
pc
	"Used by encoder source mapping."

	^pc ifNil: [ 0 ]
!

----- Method: ParseNode>>printCommentOn:indent: (in category 'printing') -----
printCommentOn: aStream indent: indent 
	| thisComment |
	self comment == nil ifTrue: [^ self].
	aStream withStyleFor: #comment
		do: [1 to: self comment size do: 
				[:index | 
				index > 1 ifTrue: [aStream crtab: indent].
				aStream nextPut: $".
				thisComment _ self comment at: index.
				self printSingleComment: thisComment
					on: aStream
					indent: indent.
				aStream nextPut: $"]].
	self comment: nil!

----- Method: ParseNode>>printOn: (in category 'printing') -----
printOn: aStream 
	"Refer to the comment in Object|printOn:."

	aStream nextPutAll: '{'.
	aStream nextPutAll: ((ColoredCodeStream contents: [:strm | self printOn: strm indent: 0])
							asString).
	aStream nextPutAll: '}'!

----- Method: ParseNode>>printOn:indent: (in category 'printing') -----
printOn: aStream indent: anInteger 
	"If control gets here, avoid recursion loop."

	super printOn: aStream!

----- Method: ParseNode>>printOn:indent:precedence: (in category 'printing') -----
printOn: aStream indent: level precedence: p

	self printOn: aStream indent: level!

----- Method: ParseNode>>printSingleComment:on:indent: (in category 'private') -----
printSingleComment: aString on: aStream indent: indent 
	"Print the comment string, assuming it has been indented indent tabs.
	Break the string at word breaks, given the widths in the default
	font, at 450 points."

	| readStream word position lineBreak font wordWidth tabWidth spaceWidth lastChar |
	readStream _ ReadStream on: aString.
	font _ TextStyle default defaultFont.
	tabWidth _ TextConstants at: #DefaultTab.
	spaceWidth _ font widthOf: Character space.
	position _ indent * tabWidth.
	lineBreak _ 450.
	[readStream atEnd]
		whileFalse: 
			[word _ self nextWordFrom: readStream setCharacter: [:lc | lastChar _ lc].
			wordWidth _ word inject: 0 into: [:width :char | width + (font widthOf: char)].
			position _ position + wordWidth.
			position > lineBreak
				ifTrue: 
					[aStream skip: -1; crtab: indent.
					position _ indent * tabWidth + wordWidth + spaceWidth.
					lastChar = Character cr
						ifTrue: [[readStream peekFor: Character tab] whileTrue].
					word isEmpty ifFalse: [aStream nextPutAll: word; space]]
				ifFalse: 
					[aStream nextPutAll: word.
					readStream atEnd
						ifFalse: 
							[position _ position + spaceWidth.
							aStream space].
					lastChar = Character cr
						ifTrue: 
							[aStream skip: -1; crtab: indent.
							position _ indent * tabWidth.
							[readStream peekFor: Character tab] whileTrue]]]!

----- Method: ParseNode>>shortPrintOn: (in category 'printing') -----
shortPrintOn: aStream 
	self printOn: aStream indent: 0!

----- Method: ParseNode>>sizeBranchOn:dist: (in category 'code generation') -----
sizeBranchOn: condition dist: dist
	dist = 0 ifTrue: [^1].
	^ condition
		ifTrue: [2]  "Branch on true is always 2 bytes"
		ifFalse: [self sizeShortOrLong: dist]!

----- Method: ParseNode>>sizeForEffect: (in category 'code generation') -----
sizeForEffect: encoder

	^(self sizeForValue: encoder) + 1!

----- Method: ParseNode>>sizeForReturn: (in category 'code generation') -----
sizeForReturn: encoder

	^(self sizeForValue: encoder) + 1!

----- Method: ParseNode>>sizeJump: (in category 'code generation') -----
sizeJump: dist

	dist = 0 ifTrue: [^0].
	^self sizeShortOrLong: dist!

----- Method: ParseNode>>sizeShortOrLong: (in category 'code generation') -----
sizeShortOrLong: dist

	(1 <= dist and: [dist <= JmpLimit])
		ifTrue: [^1].
	^2!

----- Method: ParseNode>>toDoIncrement: (in category 'testing') -----
toDoIncrement: ignored
	"Only meant for Messages or Assignments - else return nil"
	^ nil!

ParseNode subclass: #ReturnNode
	instanceVariableNames: 'expr'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

!ReturnNode commentStamp: '<historical>' prior: 0!
I represent an expression of the form ^expr.!

----- Method: ReturnNode>>asReturnNode (in category 'converting') -----
asReturnNode!

----- Method: ReturnNode>>code (in category 'code generation') -----
code

	^expr code!

----- Method: ReturnNode>>emitForReturn:on: (in category 'code generation') -----
emitForReturn: stack on: strm

	expr emitForReturn: stack on: strm.
	pc _ strm position!

----- Method: ReturnNode>>emitForValue:on: (in category 'code generation') -----
emitForValue: stack on: strm

	expr emitForReturn: stack on: strm.
	pc _ strm position!

----- Method: ReturnNode>>expr (in category 'printing') -----
expr

	^ expr.
!

----- Method: ReturnNode>>expr: (in category 'initialize-release') -----
expr: e

	expr _ e!

----- Method: ReturnNode>>expr:encoder:sourceRange: (in category 'initialize-release') -----
expr: e encoder: encoder sourceRange: range

	expr _ e.
	encoder noteSourceRange: range forNode: self!

----- Method: ReturnNode>>isReturnSelf (in category 'testing') -----
isReturnSelf

	^expr == NodeSelf!

----- Method: ReturnNode>>isSpecialConstant (in category 'testing') -----
isSpecialConstant

	^expr isSpecialConstant!

----- Method: ReturnNode>>isVariableReference (in category 'testing') -----
isVariableReference

	^expr isVariableReference!

----- Method: ReturnNode>>printOn:indent: (in category 'printing') -----
printOn: aStream indent: level

	aStream nextPutAll: '^ '.
	expr printOn: aStream indent: level.
	expr printCommentOn: aStream indent: level.
!

----- Method: ReturnNode>>sizeForReturn: (in category 'code generation') -----
sizeForReturn: encoder

	^expr sizeForReturn: encoder!

----- Method: ReturnNode>>sizeForValue: (in category 'code generation') -----
sizeForValue: encoder

	^expr sizeForReturn: encoder!

Object subclass: #ParseStack
	instanceVariableNames: 'position length'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-Support'!

!ParseStack commentStamp: '<historical>' prior: 0!
I keep track of the current and high position of the stack that will be needed by code being compiled.!

----- Method: ParseStack>>init (in category 'initialize-release') -----
init

	length _ position _ 0!

----- Method: ParseStack>>pop: (in category 'accessing') -----
pop: n

	(position _ position - n) < 0 
		ifTrue: [self error: 'Parse stack underflow']!

----- Method: ParseStack>>position (in category 'results') -----
position

	^position!

----- Method: ParseStack>>printOn: (in category 'printing') -----
printOn: aStream
	
	super printOn: aStream.
	aStream nextPutAll: ' at '; print: position; nextPutAll: ' of '; print: length!

----- Method: ParseStack>>push: (in category 'accessing') -----
push: n

	(position _ position + n) > length 
		ifTrue: [length _ position]!

----- Method: ParseStack>>size (in category 'accessing') -----
size

	^length!

Object subclass: #PrimitiveNode
	instanceVariableNames: 'primitiveNum spec'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-ParseNodes'!

!PrimitiveNode commentStamp: 'ajh 3/24/2003 21:35' prior: 0!
I represent a primitive.  I am more than just a number if I am a named primitive.

Structure:

 num	<Integer>	Primitive number.
 spec	<Object>		Stored in first literal when num is 117 or 120.
!

----- Method: PrimitiveNode class>>null (in category 'as yet unclassified') -----
null

	^ self new num: 0!

----- Method: PrimitiveNode>>num (in category 'as yet unclassified') -----
num

	^ primitiveNum!

----- Method: PrimitiveNode>>num: (in category 'as yet unclassified') -----
num: n

	primitiveNum _ n!

----- Method: PrimitiveNode>>printOn: (in category 'as yet unclassified') -----
printOn: aStream

	aStream nextPutAll: 'primitive '; print: primitiveNum!

----- Method: PrimitiveNode>>printPrimitiveOn: (in category 'as yet unclassified') -----
printPrimitiveOn: aStream 
	"Print the primitive on aStream"

	| primIndex primDecl |
	primIndex _ primitiveNum.
	primIndex = 0 ifTrue: [^ self].
	primIndex = 120 ifTrue: [
		"External call spec"
		^ aStream print: spec].
	aStream nextPutAll: '<primitive: '.
	primIndex = 117 ifTrue: [
		primDecl _ spec.
		aStream nextPut: $';
			nextPutAll: (primDecl at: 2);
			nextPut: $'.
		(primDecl at: 1) ifNotNil: [
			aStream nextPutAll: ' module: ';
				nextPut: $';
				nextPutAll: (primDecl at: 1);
				nextPut: $'].
	] ifFalse: [aStream print: primIndex].
	aStream nextPut: $>.
	(primIndex ~= 117 and: [primIndex ~= 120]) ifTrue: [
		Smalltalk at: #Interpreter ifPresent: [:cls |
			aStream nextPutAll: ' "', 
				((cls classPool at: #PrimitiveTable) at: primIndex + 1) , '" '
		].
	].
!

----- Method: PrimitiveNode>>sourceText (in category 'as yet unclassified') -----
sourceText

	^ String streamContents: [:stream |
		self printPrimitiveOn: stream]!

----- Method: PrimitiveNode>>spec (in category 'as yet unclassified') -----
spec

	^ spec!

----- Method: PrimitiveNode>>spec: (in category 'as yet unclassified') -----
spec: literal

	spec _ literal!

Object subclass: #Scanner
	instanceVariableNames: 'source mark hereChar aheadChar token tokenType currentComment buffer typeTable'
	classVariableNames: 'TypeTable'
	poolDictionaries: ''
	category: 'Compiler-Kernel'!

!Scanner commentStamp: '<historical>' prior: 0!
I scan a string or text, picking out Smalltalk syntactic tokens. I look one character ahead. I put each token found into the instance variable, token, and its type (a Symbol) into the variable, tokenType. At the end of the input stream, I pretend to see an endless sequence of special characters called doits.!

Scanner subclass: #Parser
	instanceVariableNames: 'here hereType hereMark hereEnd prevMark prevEnd encoder requestor parseNode failBlock requestorOffset tempsMark doitFlag properties category'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-Kernel'!

!Parser commentStamp: '<historical>' prior: 0!
I parse Smalltalk syntax and create a MethodNode that is the root of the parse tree. I look one token ahead.!

----- Method: Parser>>addComment (in category 'private') -----
addComment

	parseNode ~~ nil
		ifTrue: 
			[parseNode comment: currentComment.
			currentComment _ nil]!

----- Method: Parser>>addPragma: (in category 'pragmas') -----
addPragma: aPragma
	self properties addPragma: aPragma!

----- Method: Parser>>advance (in category 'scanning') -----
advance
	| this |
	prevMark _ hereMark.
	prevEnd _ hereEnd.
	this _ here.
	here _ token.
	hereType _ tokenType.
	hereMark _ mark.
	hereEnd _ source position - (source atEnd ifTrue: [hereChar == 30 asCharacter ifTrue: [0] ifFalse: [1]] ifFalse: [2]).
	self scanToken.
	"Transcript show: 'here: ', here printString, ' mark: ', hereMark printString, ' end: ', hereEnd printString; cr."
	^this!

----- Method: Parser>>allocateLiteral: (in category 'primitives') -----
allocateLiteral: lit
	encoder litIndex: lit!

----- Method: Parser>>argumentName (in category 'expression types') -----
argumentName

	hereType == #word
		ifFalse: [^self expected: 'Argument name'].
	^self advance!

----- Method: Parser>>assignment: (in category 'expression types') -----
assignment: varNode
	" var '_' expression => AssignmentNode."
	| loc start |
	(loc _ varNode assignmentCheck: encoder at: prevMark + requestorOffset) >= 0
		ifTrue: [^self notify: 'Cannot store into' at: loc].
	start _ self startOfNextToken.
	varNode nowHasDef.
	self advance.
	self expression ifFalse: [^self expected: 'Expression'].
	parseNode _ AssignmentNode new
				variable: varNode
				value: parseNode
				from: encoder
				sourceRange: (start to: self endOfLastToken).
	^true!

----- Method: Parser>>bindArg: (in category 'temps') -----
bindArg: name

	^ self bindTemp: name!

----- Method: Parser>>bindTemp: (in category 'temps') -----
bindTemp: name

	^name!

----- Method: Parser>>bindTemp:in: (in category 'temps') -----
bindTemp: name in: methodSelector

	^self bindTemp: name!

----- Method: Parser>>blockExpression (in category 'expression types') -----
blockExpression
	"[ ({:var} |) (| {temps} |) (statements) ] => BlockNode."

	| variableNodes temporaryBlockVariables start |

	variableNodes _ OrderedCollection new.
	start _ prevMark + requestorOffset.
	"Gather parameters."
	[self match: #colon] whileTrue: [variableNodes addLast: (encoder autoBind: self argumentName)].
	(variableNodes size > 0 & (hereType ~~ #rightBracket) and: [(self match: #verticalBar) not]) ifTrue: [^self expected: 'Vertical bar'].

	temporaryBlockVariables _ self temporaryBlockVariables.
	self statements: variableNodes innerBlock: true.
	parseNode temporaries: temporaryBlockVariables.

	(self match: #rightBracket) ifFalse: [^self expected: 'Period or right bracket'].

	encoder noteSourceRange: (self endOfLastToken to: self endOfLastToken) forNode: parseNode.

	"The scope of the parameters and temporary block variables is no longer active."
	temporaryBlockVariables do: [:variable | variable scope: -1].
	variableNodes do: [:variable | variable scope: -1]!

----- Method: Parser>>braceExpression (in category 'expression types') -----
braceExpression
	" { elements } => BraceNode."

	| elements locations loc more |
	elements _ OrderedCollection new.
	locations _ OrderedCollection new.
	self advance.
	more _ hereType ~~ #rightBrace.
	[more]
		whileTrue: 
			[loc _ hereMark + requestorOffset.
			self expression
				ifTrue: 
					[elements addLast: parseNode.
					locations addLast: loc]
				ifFalse:
					[^self expected: 'Variable or expression'].
			(self match: #period)
				ifTrue: [more _ hereType ~~ #rightBrace]
				ifFalse: [more _ false]].
	parseNode _ BraceNode new elements: elements sourceLocations: locations.
	(self match: #rightBrace)
		ifFalse: [^self expected: 'Period or right brace'].
	^true!

----- Method: Parser>>cascade (in category 'expression types') -----
cascade
	" {; message} => CascadeNode."

	| rcvr msgs |
	parseNode canCascade
		ifFalse: [^self expected: 'Cascading not'].
	rcvr _ parseNode cascadeReceiver.
	msgs _ OrderedCollection with: parseNode.
	[self match: #semicolon]
		whileTrue: 
			[parseNode _ rcvr.
			(self messagePart: 3 repeat: false)
				ifFalse: [^self expected: 'Cascade'].
			parseNode canCascade
				ifFalse: [^self expected: '<- No special messages'].
			parseNode cascadeReceiver.
			msgs addLast: parseNode].
	parseNode _ CascadeNode new receiver: rcvr messages: msgs!

----- Method: Parser>>correctSelector:wordIntervals:exprInterval:ifAbort: (in category 'error correction') -----
correctSelector: proposedKeyword wordIntervals: spots exprInterval: expInt ifAbort: abortAction
	"Correct the proposedKeyword to some selector symbol, correcting the original text if such action is indicated.  abortAction is invoked if the proposedKeyword couldn't be converted into a valid selector.  Spots is an ordered collection of intervals within the test stream of the for each of the keyword parts."

	| alternatives aStream choice correctSelector userSelection lines firstLine |
	"If we can't ask the user, assume that the keyword will be defined later"
	self interactive ifFalse: [ ^ proposedKeyword asSymbol ].

	userSelection _ requestor selectionInterval.
	requestor selectFrom: spots first first to: spots last last.
	requestor select.
	alternatives _ Symbol possibleSelectorsFor: proposedKeyword.
	self flag: #toBeFixed.
	"alternatives addAll: (MultiSymbol possibleSelectorsFor: proposedKeyword)."

	aStream _ WriteStream on: (String new: 200).
	aStream nextPutAll: (proposedKeyword contractTo: 35); cr.
	firstLine _ 1.
 	alternatives do:
		[:sel | aStream nextPutAll: (sel contractTo: 35); nextPut: Character cr].
	aStream nextPutAll: 'cancel'.
	lines _ Array with: firstLine with: (alternatives size + firstLine).
	
	choice _ (UIManager default 
			chooseFrom: (aStream contents substrings)
			lines: lines
			title: 'Unknown selector, please\confirm, correct, or cancel' withCRs).

	(choice = 0) | (choice > (lines at: 2))
		ifTrue: [ ^ abortAction value ].

	requestor deselect.
	requestor selectInvisiblyFrom: userSelection first to: userSelection last.

	choice = 1 ifTrue: [ ^ proposedKeyword asSymbol ].
	correctSelector _ alternatives at: choice - 1.
	self substituteSelector: correctSelector keywords wordIntervals: spots.
	((proposedKeyword last ~= $:) and: [correctSelector last == $:]) ifTrue: [
		^ abortAction value].
	^ correctSelector.
!

----- Method: Parser>>correctVariable:interval: (in category 'error correction') -----
correctVariable: proposedVariable interval: spot
	"Correct the proposedVariable to a known variable, or declare it as a new
	variable if such action is requested.  We support declaring lowercase
	variables as temps or inst-vars, and uppercase variables as Globals or 
	ClassVars, depending on whether the context is nil (class=UndefinedObject).
	Spot is the interval within the test stream of the variable.
	rr 3/4/2004 10:26 : adds the option to define a new class. "

	| tempIvar labels actions lines alternatives binding userSelection choice action |

	"Check if this is an i-var, that has been corrected already (ugly)"
	(encoder classEncoding allInstVarNames includes: proposedVariable) ifTrue: [
		^LiteralVariableNode new 
			name: proposedVariable index: (encoder classEncoding allInstVarNames indexOf: proposedVariable) - 1 type: 1;
			yourself ].

	"If we can't ask the user for correction, make it undeclared"
	self interactive 
		ifFalse: [ ^encoder undeclared: proposedVariable ].

	"First check to see if the requestor knows anything about the variable"
	tempIvar _ proposedVariable first canBeNonGlobalVarInitial.
	(tempIvar and: [ (binding _ requestor bindingOf: proposedVariable) notNil ])
		ifTrue: [ ^encoder global: binding name: proposedVariable ].
	userSelection _ requestor selectionInterval.
	requestor selectFrom: spot first to: spot last.
	requestor select.

	"Build the menu with alternatives"
	labels _ OrderedCollection new. actions _ OrderedCollection new. lines _ OrderedCollection new.
	alternatives _ encoder possibleVariablesFor: proposedVariable.
	tempIvar 
		ifTrue: [ 
			labels add: 'declare temp'. 
			actions add: [ self declareTempAndPaste: proposedVariable ].
			labels add: 'declare instance'.
			actions add: [ self declareInstVar: proposedVariable ] ]
		ifFalse: [ 
			labels add: 'define new class'.
			actions add: [self defineClass: proposedVariable].
			labels add: 'declare global'.
			actions add: [ self declareGlobal: proposedVariable ].
			encoder classEncoding == UndefinedObject ifFalse: [ 
				labels add: 'declare class variable'.
				actions add: [ self declareClassVar: proposedVariable ] ] ].
	lines add: labels size.
	alternatives do: [ :each | 
		labels add: each.
		actions add: [ 
			self substituteWord: each wordInterval: spot offset: 0.
			encoder encodeVariable: each ] fixTemps ].
	lines add: labels size.
	labels add: 'cancel'.

	"Display the pop-up menu"
	choice _ (UIManager default chooseFrom: labels asArray lines: lines asArray
		title:  'Unknown variable: ', proposedVariable, ' please correct, or cancel:').
	action _ actions at: choice ifAbsent: [ ^self fail ].

	"Execute the selected action"
	requestor deselect.
	requestor selectInvisiblyFrom: userSelection first to: userSelection last.
	^action value!

----- Method: Parser>>declareClassVar: (in category 'error correction') -----
declareClassVar: name
	| sym class |
	sym _ name asSymbol.
	class _ encoder classEncoding.
	class _ class theNonMetaClass.		"not the metaclass"
	class addClassVarName: name.
	^ encoder global: (class classPool associationAt: sym)
			name: sym!

----- Method: Parser>>declareGlobal: (in category 'error correction') -----
declareGlobal: name
	| sym |
	sym _ name asSymbol.
	Smalltalk at: sym put: nil.
	^ encoder global: (Smalltalk associationAt: sym) name: sym!

----- Method: Parser>>declareInstVar: (in category 'error correction') -----
declareInstVar: name
	" rr 3/6/2004 16:06 : adds the line to correctly compute the index. uncommented the option in 
	the caller."
	| index |
	encoder classEncoding addInstVarName: name.
	index _ encoder classEncoding instVarNames indexOf: name.
	encoder classEncoding allSuperclassesDo: [:cls | index := index + cls instVarNames size].
	^LiteralVariableNode new
		name: name index: index - 1 type: 1;
		yourself
		!

----- Method: Parser>>declareTempAndPaste: (in category 'error correction') -----
declareTempAndPaste: name
	| insertion delta theTextString characterBeforeMark |

	theTextString _ requestor text string.
	characterBeforeMark _ theTextString at: tempsMark-1 ifAbsent: [$ ].
	(theTextString at: tempsMark) = $| ifTrue: [
  		"Paste it before the second vertical bar"
		insertion _ name, ' '.
		characterBeforeMark isSeparator ifFalse: [insertion _ ' ', insertion].
		delta _ 0.
	] ifFalse: [
		"No bars - insert some with CR, tab"
		insertion _ '| ' , name , ' |',String cr.
		delta _ 2.	"the bar and CR"
		characterBeforeMark = Character tab ifTrue: [
			insertion _ insertion , String tab.
			delta _ delta + 1.	"the tab"
		].
	].
	tempsMark _ tempsMark +
		(self substituteWord: insertion
			wordInterval: (tempsMark to: tempsMark-1)
			offset: 0) - delta.
	^ encoder bindAndJuggle: name!

----- Method: Parser>>defineClass: (in category 'error correction') -----
defineClass: className 
	"prompts the user to define a new class,  
	asks for it's category, and lets the users edit further  
	the definition"
	| sym cat def d2 |
	sym := className asSymbol.
	cat := UIManager default request: 'Enter class category : ' initialAnswer: self encoder classEncoding theNonMetaClass category.
	cat
		ifEmpty: [cat := 'Unknown'].
	def := 'Object subclass: #' , sym , '
		instanceVariableNames: '''' 
		classVariableNames: ''''
		poolDictionaries: ''''
		category: ''' , cat , ''''.
	d2 := UIManager default request: 'Edit class definition : ' initialAnswer: def.
	d2
		ifEmpty: [d2 := def].
	Compiler evaluate: d2.
	^ encoder
		global: (Smalltalk associationAt: sym)
		name: sym!

----- Method: Parser>>encoder (in category 'public access') -----
encoder
	^ encoder!

----- Method: Parser>>endOfLastToken (in category 'scanning') -----
endOfLastToken

	^ prevEnd ifNil: [mark]!

----- Method: Parser>>expected: (in category 'error handling') -----
expected: aString 
	"Notify a problem at token 'here'."

	tokenType == #doIt ifTrue: [hereMark _ hereMark + 1].
	hereType == #doIt ifTrue: [hereMark _ hereMark + 1].
	^ self notify: aString , ' expected' at: hereMark + requestorOffset!

----- Method: Parser>>expression (in category 'expression types') -----
expression

	(hereType == #word and: [tokenType == #leftArrow])
		ifTrue: [^ self assignment: self variable].
	hereType == #leftBrace
		ifTrue: [self braceExpression]
		ifFalse: [self primaryExpression ifFalse: [^ false]].
	(self messagePart: 3 repeat: true)
		ifTrue: [hereType == #semicolon ifTrue: [self cascade]].
	^ true!

----- Method: Parser>>externalFunctionDeclaration (in category 'primitives') -----
externalFunctionDeclaration
	"Parse the function declaration for a call to an external library."
	| descriptorClass callType retType externalName args argType module fn |
	descriptorClass _ Smalltalk at: #ExternalFunction ifAbsent:[nil].
	descriptorClass == nil ifTrue:[^false].
	callType _ descriptorClass callingConventionFor: here.
	callType == nil ifTrue:[^false].
	"Parse return type"
	self advance.
	retType _ self externalType: descriptorClass.
	retType == nil ifTrue:[^self expected:'return type'].
	"Parse function name or index"
	externalName _ here.
	(self match: #string) 
		ifTrue:[externalName _ externalName asSymbol]
		ifFalse:[(self match:#number) ifFalse:[^self expected:'function name or index']].
	(self matchToken:'(' asSymbol) ifFalse:[^self expected:'argument list'].
	args _ WriteStream on: Array new.
	[here == ')' asSymbol] whileFalse:[
		argType _ self externalType: descriptorClass.
		argType == nil ifTrue:[^self expected:'argument'].
		argType isVoid & argType isPointerType not ifFalse:[args nextPut: argType].
	].
	(self matchToken:')' asSymbol) ifFalse:[^self expected:')'].
	(self matchToken: 'module:') ifTrue:[
		module _ here.
		(self match: #string) ifFalse:[^self expected: 'String'].
		module _ module asSymbol].
	Smalltalk at: #ExternalLibraryFunction ifPresent:[:xfn|
		fn _ xfn name: externalName 
				module: module 
				callType: callType
				returnType: retType
				argumentTypes: args contents.
		self allocateLiteral: fn.
	].
	self addPragma: (Pragma keyword: #primitive: arguments: #(120)).
	^true!

----- Method: Parser>>externalType: (in category 'primitives') -----
externalType: descriptorClass
	"Parse an return an external type"
	| xType |
	xType _ descriptorClass atomicTypeNamed: here.
	xType == nil ifTrue:["Look up from class scope"
		Symbol hasInterned: here ifTrue:[:sym|
			xType _ descriptorClass structTypeNamed: sym]].
	xType == nil ifTrue:[
		"Raise an error if user is there"
		self interactive ifTrue:[^nil].
		"otherwise go over it silently"
		xType _ descriptorClass forceTypeNamed: here].
	self advance.
	(self matchToken:#*)
		ifTrue:[^xType asPointerType]
		ifFalse:[^xType]!

----- Method: Parser>>fail (in category 'error handling') -----
fail

	| exitBlock |
	encoder == nil
		ifFalse: [encoder release. encoder _ nil]. "break cycle"
	exitBlock _ failBlock.
	failBlock _ nil.
	^exitBlock value!

----- Method: Parser>>init:notifying:failBlock: (in category 'private') -----
init: sourceStream notifying: req failBlock: aBlock

	requestor _ req.
	failBlock _ aBlock.
	super scan: sourceStream.
	prevMark _ hereMark _ mark.
	requestorOffset _ 0.
	self advance!

----- Method: Parser>>initPattern:notifying:return: (in category 'private') -----
initPattern: aString notifying: req return: aBlock

	| result |
	self
		init: (ReadStream on: aString asString)
		notifying: req
		failBlock: [^nil].
	encoder _ self.
	result _ aBlock value: (self pattern: false inContext: nil).
	encoder _ failBlock _ nil.  "break cycles"
	^result!

----- Method: Parser>>interactive (in category 'error handling') -----
interactive
	"this version of the method is necessary to load code from MC else the interactive mode is one. 
	This method is really bad since it links the compiler package with the Tools
	one. The solution would be to have a real SyntaxError exception belonging to the 
	compiler package and not a subclass of StringHolder - sd Nov 2005"
	"the code submitted by PlusTools is ideally the one that should be used
	interactive

	      ^requestor ~~ nil "
	
	^ (requestor == nil or: [requestor isKindOf: SyntaxError]) not!

----- Method: Parser>>keylessMessagePartTest:repeat: (in category 'expression types') -----
keylessMessagePartTest: level repeat: repeat
!

----- Method: Parser>>match: (in category 'scanning') -----
match: type 
	"Answer with true if next tokens type matches."

	hereType == type
		ifTrue: 
			[self advance.
			^true].
	^false!

----- Method: Parser>>matchReturn (in category 'scanning') -----
matchReturn

	^ self match: #upArrow!

----- Method: Parser>>matchToken: (in category 'scanning') -----
matchToken: thing 
	"Matches the token, not its type."

	here = thing ifTrue: [self advance. ^true].
	^false!

----- Method: Parser>>messagePart:repeat: (in category 'expression types') -----
messagePart: level repeat: repeat

	| start receiver selector args precedence words keywordStart |
	[receiver _ parseNode.
	(hereType == #keyword and: [level >= 3])
		ifTrue: 
			[start _ self startOfNextToken.
			selector _ WriteStream on: (String new: 32).
			args _ OrderedCollection new.
			words _ OrderedCollection new.
			[hereType == #keyword]
				whileTrue: 
					[keywordStart _ self startOfNextToken + requestorOffset.
					selector nextPutAll: self advance.
					words addLast: (keywordStart to: self endOfLastToken + requestorOffset).
					self primaryExpression ifFalse: [^self expected: 'Argument'].
					self messagePart: 2 repeat: true.
					args addLast: parseNode].
			(Symbol hasInterned: selector contents ifTrue: [ :sym | selector _ sym])
				ifFalse: [ selector _ self correctSelector: selector contents
										wordIntervals: words
										exprInterval: (start to: self endOfLastToken)
										ifAbort: [ ^ self fail ] ].
			precedence _ 3]
		ifFalse: [((hereType == #binary or: [hereType == #verticalBar])
				and: [level >= 2])
				ifTrue: 
					[start _ self startOfNextToken.
					selector _ self advance asOctetString asSymbol.
					self primaryExpression ifFalse: [^self expected: 'Argument'].
					self messagePart: 1 repeat: true.
					args _ Array with: parseNode.
					precedence _ 2]
				ifFalse: [hereType == #word
						ifTrue: 
							[start _ self startOfNextToken.
							selector _ self advance.
							args _ #().
							words _ OrderedCollection with: (start  + requestorOffset to: self endOfLastToken + requestorOffset).
							(Symbol hasInterned: selector ifTrue: [ :sym | selector _ sym])
								ifFalse: [ selector _ self correctSelector: selector
													wordIntervals: words
													exprInterval: (start to: self endOfLastToken)
													ifAbort: [ ^ self fail ] ].
							precedence _ 1]
						ifFalse: [^args notNil]]].
	parseNode _ MessageNode new
				receiver: receiver
				selector: selector
				arguments: args
				precedence: precedence
				from: encoder
				sourceRange: (start to: self endOfLastToken).
	repeat]
		whileTrue: [].
	^true!

----- Method: Parser>>method:context:encoder: (in category 'expression types') -----
method: doit context: ctxt encoder: encoderToUse
	" pattern [ | temporaries ] block => MethodNode."

	| sap blk prim temps messageComment methodNode |
	encoder _ encoderToUse.
	sap _ self pattern: doit inContext: ctxt.
	"sap={selector, arguments, precedence}"
	(sap at: 2) do: [:argNode | argNode isArg: true].
	doit ifFalse: [ self pragmaSequence ].
	temps _ self temporariesIn: (sap at: 1)..
	messageComment _ currentComment.
	currentComment _ nil.
	doit ifFalse: [ self pragmaSequence ].
	prim := self pragmaPrimitives.
	self statements: #() innerBlock: doit.
	blk _ parseNode.
	doit ifTrue: [blk returnLast]
		ifFalse: [blk returnSelfIfNoOther].
	hereType == #doIt ifFalse: [^self expected: 'Nothing more'].
	self interactive ifTrue: [self removeUnusedTemps].
	methodNode _ self newMethodNode comment: messageComment.
	^ methodNode
		selector: (sap at: 1)
		arguments: (sap at: 2)
		precedence: (sap at: 3)
		temporaries: temps
		block: blk
		encoder: encoder
		primitive: prim
		properties: properties!

----- Method: Parser>>newMethodNode (in category 'expression types') -----
newMethodNode

	^ MethodNode new!

----- Method: Parser>>notify: (in category 'error handling') -----
notify: aString 
	"Notify problem at token before 'here'."

	^self notify: aString at: prevMark + requestorOffset!

----- Method: Parser>>notify:at: (in category 'error handling') -----
notify: string at: location
	requestor isNil
		ifTrue: [(encoder == self or: [encoder isNil]) ifTrue: [^ self fail  "failure setting up syntax error"].
				SyntaxErrorNotification
					inClass: encoder classEncoding
					category: encoder classEncoding category
					withCode: 
						(source contents
							copyReplaceFrom: location
							to: location - 1
							with: string , ' ->')
					doitFlag: doitFlag]
		ifFalse: [requestor
					notify: string , ' ->'
					at: location
					in: source].
	^self fail!

----- Method: Parser>>offEnd: (in category 'error handling') -----
offEnd: aString 
	"Notify a problem beyond 'here' (in lookAhead token). Don't be offEnded!!"

	requestorOffset == nil
		ifTrue: [^ self notify: aString at: mark]
		ifFalse: [^ self notify: aString at: mark + requestorOffset]
!

----- Method: Parser>>parse:class: (in category 'public access') -----
parse: sourceStreamOrString class: behavior

	^ self parse: sourceStreamOrString readStream class: behavior
		noPattern: false context: nil notifying: nil ifFail: [self parseError]!

----- Method: Parser>>parse:class:category:noPattern:context:notifying:ifFail: (in category 'public access') -----
parse: sourceStream class: class category: aCategory noPattern: noPattern context: ctxt notifying: req ifFail: aBlock 
        "Answer a MethodNode for the argument, sourceStream, that is the root of 
        a parse tree. Parsing is done with respect to the argument, class, to find 
        instance, class, and pool variables; and with respect to the argument, 
        ctxt, to find temporary variables. Errors in parsing are reported to the 
        argument, req, if not nil; otherwise aBlock is evaluated. The argument 
        noPattern is a Boolean that is true if the the sourceStream does not 
        contain a method header (i.e., for DoIts)."

		category := aCategory.
        	^ self parse: sourceStream class: class  noPattern: noPattern context: ctxt notifying: req ifFail: aBlock 
!

----- Method: Parser>>parse:class:noPattern:context:notifying:ifFail: (in category 'public access') -----
parse: sourceStream class: class noPattern: noPattern context: ctxt notifying: req ifFail: aBlock 
        "Answer a MethodNode for the argument, sourceStream, that is the root of 
        a parse tree. Parsing is done with respect to the argument, class, to find 
        instance, class, and pool variables; and with respect to the argument, 
        ctxt, to find temporary variables. Errors in parsing are reported to the 
        argument, req, if not nil; otherwise aBlock is evaluated. The argument 
        noPattern is a Boolean that is true if the the sourceStream does not 
        contain a method header (i.e., for DoIts)."

         | methNode repeatNeeded myStream s p |
 
        myStream _ sourceStream.
        [repeatNeeded _ false.
	   p _ myStream position.
	   s _ myStream upToEnd.
	   myStream position: p.
        self init: myStream notifying: req failBlock: [^ aBlock value].
        doitFlag _ noPattern.
        failBlock_ aBlock.
        [methNode _ self method: noPattern context: ctxt
                                encoder: (Encoder new init: class context: ctxt notifying: self)] 
                on: ParserRemovedUnusedTemps 
                do: 
                        [ :ex | repeatNeeded _ (requestor isKindOf: TextMorphEditor) not.
                        myStream _ ReadStream on: requestor text string.
                        ex resume].
        repeatNeeded] whileTrue.
        encoder _ failBlock _ requestor _ parseNode _ nil. "break cycles & mitigate refct overflow"
	   methNode sourceText: s.
        ^ methNode!

----- Method: Parser>>parse:class:noPattern:notifying:ifFail: (in category 'public access') -----
parse: sourceStream class: class noPattern: noPattern notifying: req ifFail: aBlock

	^ self parse: sourceStream class: class noPattern: noPattern context: nil notifying: req ifFail: aBlock!

----- Method: Parser>>parseArgsAndTemps:notifying: (in category 'public access') -----
parseArgsAndTemps: aString notifying: req 
        "Parse the argument, aString, notifying req if an error occurs. Otherwise, 
        answer a two-element Array containing Arrays of strings (the argument 
        names and temporary variable names)."

        aString == nil ifTrue: [^#()].
        doitFlag _ false.               "Don't really know if a doit or not!!"
        ^self initPattern: aString
                notifying: req
                return: [:pattern | (pattern at: 2) , (self temporariesIn: (pattern at: 1))]!

----- Method: Parser>>parseMethodComment:setPattern: (in category 'public access') -----
parseMethodComment: aString setPattern: aBlock
	"Answer the method comment for the argument, aString. Evaluate aBlock 
	with the message pattern in the form #(selector, arguments, precedence)."

	self
		initPattern: aString
		notifying: nil
		return: aBlock.
	currentComment==nil
		ifTrue:	[^OrderedCollection new]
		ifFalse:	[^currentComment]!

----- Method: Parser>>parseSelector: (in category 'public access') -----
parseSelector: aString 
	"Answer the message selector for the argument, aString, which should 
	parse successfully up to the temporary declaration or the end of the 
	method header."

	^self
		initPattern: aString
		notifying: nil
		return: [:pattern | pattern at: 1]!

----- Method: Parser>>pattern:inContext: (in category 'expression types') -----
pattern: fromDoit inContext: ctxt 
	" unarySelector | binarySelector arg | keyword arg {keyword arg} =>  
	{selector, arguments, precedence}."
	| args selector |
	doitFlag _ fromDoit.
	fromDoit ifTrue:
			[ctxt == nil
				ifTrue: [^ {#DoIt. {}. 1}]
				ifFalse: [^ {#DoItIn:. {encoder encodeVariable: 'homeContext'}. 3}]].

	hereType == #word ifTrue: [^ {self advance asSymbol. {}. 1}].

	(hereType == #binary or: [hereType == #verticalBar])
		ifTrue: 
			[selector _ self advance asSymbol.
			args _ Array with: (encoder bindArg: self argumentName).
			^ {selector. args. 2}].

	hereType == #keyword
		ifTrue: 
			[selector _ WriteStream on: (String new: 32).
			args _ OrderedCollection new.
			[hereType == #keyword] whileTrue:[
				selector nextPutAll: self advance.
				args addLast: (encoder bindArg: self argumentName).
			].
			^ {selector contents asSymbol. args. 3}].

	^ self expected: 'Message pattern'!

----- Method: Parser>>pragmaLiteral (in category 'pragmas') -----
pragmaLiteral
	"Read a pragma literal."

	(hereType == #string or: [ hereType == #literal or: [ hereType == #number ] ])
		ifTrue: [ ^ self advance ].
	(here == $# and: [ tokenType == #word ])
		ifTrue: [ ^ self advance ].
	(here == #- and: [ tokenType == #number ])
		ifTrue: [ ^ (self advance; advance) negated ].
	(here = 'true' or: [ here = 'false' or: [ here = 'nil' ] ])
		ifTrue: [ ^ Compiler evaluate: self advance ].
	^ self expected: 'Literal constant'!

----- Method: Parser>>pragmaPrimitives (in category 'pragmas') -----
pragmaPrimitives
	| pragmas primitives |
	self properties pragmas isEmpty
		ifTrue: [ ^ 0 ].
	pragmas := Pragma allNamed: #primitive from: self class to: Parser.
	primitives := self properties pragmas select: [ :prim |
		pragmas anySatisfy: [ :prag | 
			prag selector = prim keyword ] ].
	primitives isEmpty 
		ifTrue: [ ^ 0 ].
	primitives size = 1 
		ifFalse: [ ^ self notify: 'Ambigous primitives' ].
	^ primitives first message sendTo: self!

----- Method: Parser>>pragmaSequence (in category 'pragmas') -----
pragmaSequence
	"Parse a sequence of method pragmas."
	
	[ true ] whileTrue: [
		(self matchToken: #<)
			ifFalse: [ ^ self ].
		self pragmaStatement.
		(self matchToken: #>)
			ifFalse: [ ^ self expected: '>' ] ]!

----- Method: Parser>>pragmaStatement (in category 'pragmas') -----
pragmaStatement
	"Read a single pragma statement. Parse all generic pragmas in the form of: <key1: val1 key2: val2 ...> and remember them, including primitives."
	
	| selector arguments words index keyword |
	(hereType = #keyword or: [ hereType = #word or: [ hereType = #binary ] ])
		ifFalse: [  ^ self expected: 'pragma declaration' ].

	" This is a ugly hack into the compiler of the FFI package. FFI should be changed to use propre pragmas that can be parsed with the code here. "
	(here = #apicall: or: [ here = #cdecl: ])
		ifTrue: [ ^ self externalFunctionDeclaration ].

	selector := String new.
	arguments := OrderedCollection new.
	words := OrderedCollection new.
	[ hereType = #keyword or: [ (hereType = #word or: [ hereType = #binary ]) and: [ selector isEmpty ] ] ] whileTrue: [
		index := self startOfNextToken + requestorOffset.
		selector := selector , self advance.
		words add: (index to: self endOfLastToken + requestorOffset).
		(selector last = $: or: [ selector first isLetter not ])
			ifTrue: [ arguments add: self pragmaLiteral ] ].
	selector numArgs ~= arguments size
		ifTrue: [ ^ self expected: 'pragma argument' ].
	(Symbol hasInterned: selector 
		ifTrue: [ :value | keyword := value]) 
		ifFalse: [ 
			keyword := self 
				correctSelector: selector wordIntervals: words
				exprInterval: (words first first to: words last last)
				ifAbort: [ ^ self fail ] ].
	self addPragma: (Pragma keyword: keyword arguments: arguments asArray).
	^ true!

----- Method: Parser>>primaryExpression (in category 'expression types') -----
primaryExpression 
	hereType == #word 
		ifTrue: 
			[parseNode _ self variable.
			(parseNode isUndefTemp and: [self interactive])
				ifTrue: [self queryUndefined].
			parseNode nowHasRef.
			^ true].
	hereType == #leftBracket
		ifTrue: 
			[self advance.
			self blockExpression.
			^true].
	hereType == #leftBrace
		ifTrue: 
			[self braceExpression.
			^true].
	hereType == #leftParenthesis
		ifTrue: 
			[self advance.
			self expression ifFalse: [^self expected: 'expression'].
			(self match: #rightParenthesis)
				ifFalse: [^self expected: 'right parenthesis'].
			^true].
	(hereType == #string or: [hereType == #number or: [hereType == #literal]])
		ifTrue: 
			[parseNode _ encoder encodeLiteral: self advance.
			^true].
	(here == #- and: [tokenType == #number])
		ifTrue: 
			[self advance.
			parseNode _ encoder encodeLiteral: self advance negated.
			^true].
	^false!

----- Method: Parser>>primitive: (in category 'primitives') -----
primitive: anIntegerOrString
	"Create indexed primitive."
	
	<primitive>
	^ anIntegerOrString isInteger
		ifTrue: [ anIntegerOrString ]
		ifFalse: [ 
			anIntegerOrString isString
				ifTrue: [ self primitive: anIntegerOrString module: nil ]
				ifFalse: [ self expected: 'Indexed primitive' ] ]!

----- Method: Parser>>primitive:module: (in category 'primitives') -----
primitive: aNameString module: aModuleStringOrNil
	"Create named primitive."
	
	<primitive>
	(aNameString isString and: [ aModuleStringOrNil isNil or: [ aModuleStringOrNil isString ] ])
		ifFalse: [ ^ self expected: 'Named primitive' ].
	self allocateLiteral: (Array 
		with: (aModuleStringOrNil isNil 
			ifFalse: [ aModuleStringOrNil asSymbol ])
		with: aNameString asSymbol
		with: 0 with: 0).
	^ 117!

----- Method: Parser>>properties (in category 'pragmas') -----
properties
	^ properties ifNil: [ properties := MethodProperties new ]!

----- Method: Parser>>queryUndefined (in category 'error correction') -----
queryUndefined
	| varStart varName | 
	varName _ parseNode key.
	varStart _ self endOfLastToken + requestorOffset - varName size + 1.
	requestor selectFrom: varStart to: varStart + varName size - 1; select.
	((UIManager default 
		chooseFrom: #('yes' 'no') 
		title: ((varName , ' appears to be\undefined at this point.Proceed anyway?') 
				withCRs asText makeBoldFrom: 1 to: varName size))
		= 1) ifFalse: [^ self fail]!

----- Method: Parser>>removeUnusedTemps (in category 'error correction') -----
removeUnusedTemps
	"Scan for unused temp names, and prompt the user about the prospect of removing each one found"

	| str end start madeChanges | 
	madeChanges _ false.
	str _ requestor text string.
	((tempsMark between: 1 and: str size)
		and: [(str at: tempsMark) = $|]) ifFalse: [^ self].
	encoder unusedTempNames do:
		[:temp |
		(encoder encodeVariable: temp) isUndefTemp
			ifTrue:
			[(UIManager default 
				confirm: (temp , ' appears to be\unused in this method.\OK to remove it?') withCRs)
				ifTrue: [end _ tempsMark.
						["Beginning at right temp marker..."
						start _ end - temp size + 1.
						end < temp size or: [temp = (str copyFrom: start to: end)
							and: [(str at: start-1) isAlphaNumeric not & (str at: end+1) isAlphaNumeric not]]]
						whileFalse: ["Search left for the unused temp"
									end _ requestor nextTokenFrom: end direction: -1].
						end < temp size ifFalse:
						[(str at: start-1) = $  ifTrue: [start _ start-1].
						requestor correctFrom: start to: end with: ''.
						str _ str copyReplaceFrom: start to: end with: ''. 
						madeChanges _ true.
						tempsMark _ tempsMark - (end-start+1)]]]].
	madeChanges ifTrue: [ParserRemovedUnusedTemps signal]!

----- Method: Parser>>startOfNextToken (in category 'scanning') -----
startOfNextToken
	"Return starting position in source of next token."

	hereType == #doIt ifTrue: [^source position + 1].
	^hereMark!

----- Method: Parser>>statements:innerBlock: (in category 'expression types') -----
statements: argNodes innerBlock: inner

	| stmts returns start more blockComment |
	stmts _ OrderedCollection new.
	"give initial comment to block, since others trail statements"
	blockComment _ currentComment.
	currentComment _ nil.
	returns _ false.
	more _ hereType ~~ #rightBracket.
	[more]
		whileTrue: 
		[start _ self startOfNextToken.
		(returns _ self matchReturn)
			ifTrue: 
				[self expression
					ifFalse: [^self expected: 'Expression to return'].
				self addComment.
				stmts addLast: (parseNode isReturningIf
					ifTrue: [parseNode]
					ifFalse: [ReturnNode new
							expr: parseNode
							encoder: encoder
							sourceRange: (start to: self endOfLastToken)])]
			ifFalse: 
				[self expression
					ifTrue: 
						[self addComment.
						stmts addLast: parseNode]
					ifFalse: 
						[self addComment.
						stmts size = 0
							ifTrue: 
								[stmts addLast: 
									(encoder encodeVariable:
										(inner ifTrue: ['nil'] ifFalse: ['self']))]]].
		returns 
			ifTrue: 
				[self match: #period.
				(hereType == #rightBracket or: [hereType == #doIt])
					ifFalse: [^self expected: 'End of block']].
		more _ returns not and: [self match: #period]].
	parseNode _ BlockNode new
				arguments: argNodes
				statements: stmts
				returns: returns
				from: encoder.
	parseNode comment: blockComment.
	^ true!

----- Method: Parser>>substituteSelector:wordIntervals: (in category 'error correction') -----
substituteSelector: selectorParts wordIntervals: spots
	"Substitute the correctSelector into the (presuamed interactive) receiver."
	| offset |
	offset _ 0.
	selectorParts with: spots do:
		[ :word :interval |
		offset _ self substituteWord: word wordInterval: interval offset: offset ]
!

----- Method: Parser>>substituteWord:wordInterval:offset: (in category 'error correction') -----
substituteWord: correctWord wordInterval: spot offset: o
	"Substitute the correctSelector into the (presuamed interactive) receiver."

	requestor correctFrom: (spot first + o)
					to: (spot last + o)
					with: correctWord.

	requestorOffset _ requestorOffset + correctWord size - spot size.
	^ o + correctWord size - spot size!

----- Method: Parser>>temporaries (in category 'expression types') -----
temporaries
	" [ '|' (variable)* '|' ]"
	| vars theActualText |
	(self match: #verticalBar) ifFalse: 
		["no temps"
		doitFlag ifTrue: [self interactive
				ifFalse: [tempsMark _ 1]
				ifTrue: [tempsMark _ requestor selectionInterval first].
			^ #()].
		tempsMark _ (prevEnd ifNil: [0]) + 1.
		tempsMark _ hereMark	"formerly --> prevMark + prevToken".

		tempsMark > 0 ifTrue:
			[theActualText _ source contents.
			[tempsMark < theActualText size and: [(theActualText at: tempsMark) isSeparator]]
				whileTrue: [tempsMark _ tempsMark + 1]].
			^ #()].
	vars _ OrderedCollection new.
	[hereType == #word]
		whileTrue: [vars addLast: (encoder bindTemp: self advance)].
	(self match: #verticalBar) ifTrue: 
		[tempsMark _ prevMark.
		^ vars].
	^ self expected: 'Vertical bar'
!

----- Method: Parser>>temporariesIn: (in category 'expression types') -----
temporariesIn: methodSelector
	" [ '|' (variable)* '|' ]"
	| vars theActualText |
	(self match: #verticalBar) ifFalse: 
		["no temps"
		doitFlag ifTrue: [requestor
				ifNil: [tempsMark _ 1]
				ifNotNil: [tempsMark _ requestor selectionInterval first].
			^ #()].
		tempsMark _ (prevEnd ifNil: [0]) + 1.
		tempsMark _ hereMark	"formerly --> prevMark + prevToken".

		tempsMark > 0 ifTrue:
			[theActualText _ source contents.
			[tempsMark < theActualText size and: [(theActualText at: tempsMark) isSeparator]]
				whileTrue: [tempsMark _ tempsMark + 1]].
			^ #()].
	vars _ OrderedCollection new.
	[hereType == #word]
		whileTrue: [vars addLast: (encoder bindTemp: self advance in: methodSelector)].
	(self match: #verticalBar) ifTrue: 
		[tempsMark _ prevMark.
		^ vars].
	^ self expected: 'Vertical bar'!

----- Method: Parser>>temporaryBlockVariables (in category 'expression types') -----
temporaryBlockVariables
	"Scan and answer temporary block variables."

	| variables |

	(self match: #verticalBar) ifFalse: [
		"There are't any temporary variables."
		^#()].

	variables _ OrderedCollection new.
	[hereType == #word] whileTrue: [variables addLast: (encoder bindBlockTemp: self advance)].
	(self match: #verticalBar) ifTrue: [^variables].
	^self expected: 'Vertical bar'!

----- Method: Parser>>variable (in category 'expression types') -----
variable

	| varName varStart varEnd |
	varStart _ self startOfNextToken + requestorOffset.
	varName _ self advance.
	varEnd _ self endOfLastToken + requestorOffset.
	^ encoder encodeVariable: varName
		sourceRange: (varStart to: varEnd)
		ifUnknown: [self correctVariable: varName interval: (varStart to: varEnd)]!

----- Method: Scanner class>>initialize (in category 'class initialization') -----
initialize
	| newTable |
	newTable _ Array new: 256 withAll: #xBinary. "default"
	newTable atAll: #(9 10 12 13 32 ) put: #xDelimiter. "tab lf ff cr space"
	newTable atAll: ($0 asciiValue to: $9 asciiValue) put: #xDigit.

	1 to: 255
		do: [:index |
			(Character value: index) isLetter
				ifTrue: [newTable at: index put: #xLetter]].

	newTable at: 30 put: #doIt.
	newTable at: $" asciiValue put: #xDoubleQuote.
	newTable at: $# asciiValue put: #xLitQuote.
	newTable at: $$ asciiValue put: #xDollar.
	newTable at: $' asciiValue put: #xSingleQuote.
	newTable at: $: asciiValue put: #xColon.
	newTable at: $( asciiValue put: #leftParenthesis.
	newTable at: $) asciiValue put: #rightParenthesis.
	newTable at: $. asciiValue put: #period.
	newTable at: $; asciiValue put: #semicolon.
	newTable at: $[ asciiValue put: #leftBracket.
	newTable at: $] asciiValue put: #rightBracket.
	newTable at: ${ asciiValue put: #leftBrace.
	newTable at: $} asciiValue put: #rightBrace.
	newTable at: $^ asciiValue put: #upArrow.
	newTable at: $_ asciiValue put: #leftArrow.
	newTable at: $| asciiValue put: #verticalBar.
	TypeTable _ newTable "bon voyage!!"

	"Scanner initialize"!

----- Method: Scanner class>>inviolateInstanceVariableNames (in category 'testing') -----
inviolateInstanceVariableNames
	"Answer a list of instance variable names not to be used.  (Place holder for real list)"
	^ #('thisContext' 'self')!

----- Method: Scanner class>>isLegalInstVarName: (in category 'testing') -----
isLegalInstVarName: aString
	"Answer whether aString is a legal instance variable name."

	^ ((self isLiteralSymbol: aString) and: [(aString includes: $:) not]) and:
		[(self inviolateInstanceVariableNames includes:  aString) not]!

----- Method: Scanner class>>isLiteralSymbol: (in category 'testing') -----
isLiteralSymbol: aSymbol 
	"Test whether a symbol can be stored as # followed by its characters.  
	Symbols created internally with asSymbol may not have this property, 
	e.g. '3' asSymbol."
	| i ascii type |
	i _ aSymbol size.
	i = 0 ifTrue: [^ false].
	i = 1 ifTrue: [('$''"()#0123456789' includes: (aSymbol at: 1))
		ifTrue: [^ false] ifFalse: [^ true]].
	ascii _ (aSymbol at: 1) asciiValue.
	"TypeTable should have been origined at 0 rather than 1 ..."
	ascii = 0 ifTrue: [^ false].
	type _ TypeTable at: ascii ifAbsent:[#xLetter].
	(type == #xColon or: [type == #verticalBar or: [type == #xBinary]]) ifTrue: [
		i = 1 ifTrue: [^ true] ifFalse: [^ false]
	].
	type == #xLetter ifTrue: 
			[[i > 1]
				whileTrue: 
					[ascii _ (aSymbol at: i) asciiValue.
					ascii = 0 ifTrue: [^ false].
					type _ TypeTable at: ascii ifAbsent:[#xLetter].
					(type == #xLetter or: [type == #xDigit or: [type == #xColon]])
						ifFalse: [^ false].
					i _ i - 1].
			^ true].
	^ false!

----- Method: Scanner class>>new (in category 'instance creation') -----
new

	^super new initScanner!

----- Method: Scanner class>>wellFormedInstanceVariableNameFrom: (in category 'testing') -----
wellFormedInstanceVariableNameFrom: aString
	"Answer a legal instance variable name, derived from aString"

	| cleansedString |
	cleansedString _ aString select: [:ch | ch isDigit or: [ch isLetter]].
	(cleansedString isEmpty or: [cleansedString first isDigit])
		ifTrue: [cleansedString _ 'a', cleansedString]
		ifFalse:	[cleansedString _ cleansedString withFirstCharacterDownshifted].

	[self isLegalInstVarName: cleansedString] whileFalse:
		[cleansedString _ cleansedString, 'x'].
	^ cleansedString

"Scanner wellFormedInstanceVariableNameFrom:  '234 xx\ Uml /ler42342380-4'"!

----- Method: Scanner>>advance (in category 'expression types') -----
advance

	| prevToken |
	prevToken _ token.
	self scanToken.
	^prevToken!

----- Method: Scanner>>checkpoint (in category 'expression types') -----
checkpoint
	"Return a copy of all changeable state.  See revertToCheckpoint:"

	^ {self clone. source clone. currentComment copy}!

----- Method: Scanner>>errorMultibyteCharacter (in category 'error handling') -----
errorMultibyteCharacter

	self error: 'multi-byte character is found at unexpected place'.
!

----- Method: Scanner>>initScanner (in category 'initialize-release') -----
initScanner

	buffer _ WriteStream on: (String new: 40).
	typeTable _ TypeTable!

----- Method: Scanner>>nextLiteral (in category 'expression types') -----
nextLiteral
	"Same as advance, but -4 comes back as a number instead of two tokens"

	| prevToken |
	prevToken _ self advance.
	(prevToken == #- and: [token isKindOf: Number])
		ifTrue: 
			[^self advance negated].
	^prevToken!

----- Method: Scanner>>notify: (in category 'error handling') -----
notify: string 
	"Refer to the comment in Object|notify:." 
	self error: string!

----- Method: Scanner>>offEnd: (in category 'error handling') -----
offEnd: aString 
	"Parser overrides this"

	^self notify: aString!

----- Method: Scanner>>revertToCheckpoint: (in category 'expression types') -----
revertToCheckpoint: checkpoint
	"Revert to the state when checkpoint was made."

	| myCopy |
	myCopy _ checkpoint first.
	1 to: self class instSize do:
		[:i | self instVarAt: i put: (myCopy instVarAt: i)].
	source _ checkpoint second.
	currentComment _ checkpoint third!

----- Method: Scanner>>scan: (in category 'initialize-release') -----
scan: inputStream 
	"Bind the input stream, fill the character buffers and first token buffer."

	source _ inputStream.
	self step.
	self step.
	self scanToken!

----- Method: Scanner>>scanFieldNames: (in category 'public access') -----
scanFieldNames: stringOrArray
	"Answer an Array of Strings that are the identifiers in the input string, 
	stringOrArray. If passed an Array, just answer with that Array, i.e., 
	assume it has already been scanned."

	| strm |
	(stringOrArray isMemberOf: Array)
		ifTrue: [^stringOrArray].
	self scan: (ReadStream on: stringOrArray asString).
	strm _ WriteStream on: (Array new: 10).
	[tokenType = #doIt]
		whileFalse: 
			[tokenType = #word ifTrue: [strm nextPut: token].
			self scanToken].
	^strm contents

	"Scanner new scanFieldNames: 'abc  def ghi' ('abc' 'def' 'ghi' )"!

----- Method: Scanner>>scanLitVec (in category 'expression types') -----
scanLitVec

	| s |
	s _ WriteStream on: (Array new: 16).
	[tokenType = #rightParenthesis or: [tokenType = #doIt]]
		whileFalse: 
			[tokenType = #leftParenthesis
				ifTrue: 
					[self scanToken; scanLitVec]
				ifFalse: 
					[tokenType = #word | (tokenType = #keyword) | (tokenType = #colon)
						ifTrue: 
							[self scanLitWord.
							token = #true ifTrue: [token _ true].
							token = #false ifTrue: [token _ false].
							token = #nil ifTrue: [token _ nil]]
						ifFalse:
							[(token == #- 
									and: [((typeTable at: hereChar charCode ifAbsent: [#xLetter])) = #xDigit])
								ifTrue: 
									[self scanToken.
									token _ token negated]]].
			s nextPut: token.
			self scanToken].
	token _ s contents!

----- Method: Scanner>>scanLitWord (in category 'expression types') -----
scanLitWord
	"Accumulate keywords and asSymbol the result."

	| t |
	[(typeTable at: hereChar asciiValue ifAbsent: [#xLetter]) = #xLetter] whileTrue: [
		t _ token.
		self xLetter.
		token _ t , token
	].
	token _ token asSymbol.
!

----- Method: Scanner>>scanMessageParts: (in category 'public access') -----
scanMessageParts: sourceString
	"Return an array of the form (comment keyword comment arg comment keyword comment arg comment) for the message pattern of this method.  Courtesy of Ted Kaehler, June 1999"

	| coll nonKeywords |
	coll _ OrderedCollection new.
	self scan: (ReadStream on: sourceString asString).
	nonKeywords _ 0.
	[tokenType = #doIt] whileFalse:
		[(currentComment == nil or: [currentComment isEmpty])
			ifTrue: [coll addLast: nil]
			ifFalse: [coll addLast: currentComment removeFirst.
				[currentComment isEmpty] whileFalse:
					[coll at: coll size put: (coll last, ' ', currentComment removeFirst)]].
		(token numArgs < 1 or: [(token = #|) & (coll size > 1)])
			ifTrue: [(nonKeywords _ nonKeywords + 1) > 1 ifTrue: [^ coll]]
						"done with header"
			ifFalse: [nonKeywords _ 0].
		coll addLast: token.
		self scanToken].
	(currentComment == nil or: [currentComment isEmpty])
		ifTrue: [coll addLast: nil]
		ifFalse: [coll addLast: currentComment removeFirst.
			[currentComment isEmpty] whileFalse: [
				coll at: coll size put: (coll last, ' ', currentComment removeFirst)]].
	^ coll!

----- Method: Scanner>>scanStringStruct (in category 'expression types') -----
scanStringStruct

	| s |
	s _ WriteStream on: (Array new: 16).
	[tokenType = #rightParenthesis or: [tokenType = #doIt]]
		whileFalse: 
			[tokenType = #leftParenthesis
				ifTrue: 
					[self scanToken; scanStringStruct]
				ifFalse: 
					[tokenType = #word ifFalse:
						[^self error: 'only words and parens allowed']].
			s nextPut: token.
			self scanToken].
	token _ s contents!

----- Method: Scanner>>scanStringStruct: (in category 'public access') -----
scanStringStruct: textOrString 
	"The input is a string whose elements are identifiers and parenthesized
	 groups of identifiers.  Answer an array reflecting that structure, representing
	 each identifier by an uninterned string."

	self scan: (ReadStream on: textOrString asString).
	self scanStringStruct.
	^token

	"Scanner new scanStringStruct: 'a b (c d) (e f g)'"!

----- Method: Scanner>>scanToken (in category 'expression types') -----
scanToken

	[(tokenType _ typeTable at: hereChar asciiValue ifAbsent: [#xLetter]) == #xDelimiter]
		whileTrue: [self step].  "Skip delimiters fast, there almost always is one."
	mark _ source position - 1.
	(tokenType at: 1) = $x "x as first letter"
		ifTrue: [self perform: tokenType "means perform to compute token & type"]
		ifFalse: [token _ self step asSymbol "else just unique the first char"].
	^ token.
!

----- Method: Scanner>>scanTokens: (in category 'public access') -----
scanTokens: textOrString 
	"Answer an Array that has been tokenized as though the input text, 
	textOrString, had appeared between the array delimitors #( and ) in a 
	Smalltalk literal expression."

	self scan: (ReadStream on: textOrString asString).
	self scanLitVec.
	^token

	"Scanner new scanTokens: 'identifier keyword: 8r31 ''string'' .'"!

----- Method: Scanner>>step (in category 'expression types') -----
step

	| c |
	c _ hereChar.
	hereChar _ aheadChar.
	source atEnd
		ifTrue: [aheadChar _ 30 asCharacter "doit"]
		ifFalse: [aheadChar _ source next].
	^c!

----- Method: Scanner>>xBinary (in category 'multi-character scans') -----
xBinary

	tokenType _ #binary.
	token _ self step asSymbol.
	[| type | 
	type _ typeTable at: hereChar asciiValue ifAbsent: [#xLetter].
	type == #xBinary and: [hereChar ~= $-]] whileTrue: [
		token _ (token, (String with: self step)) asSymbol].
!

----- Method: Scanner>>xColon (in category 'multi-character scans') -----
xColon		"Allow := for assignment by converting to #_ "
	aheadChar = $= ifTrue:
		[self step.
		tokenType _ #leftArrow.
		self step.
		^ token _ #'_'].
	"Otherwise, just do what normal scan of colon would do"
	tokenType _ #colon.
	^ token _ self step asSymbol!

----- Method: Scanner>>xDelimiter (in category 'multi-character scans') -----
xDelimiter
	"Ignore blanks, etc."

	self scanToken!

----- Method: Scanner>>xDigit (in category 'multi-character scans') -----
xDigit
	"Form a number."

	tokenType _ #number.
	(aheadChar = 30 asCharacter and: [source atEnd
			and:  [source skip: -1. source next ~= 30 asCharacter]])
		ifTrue: [source skip: -1 "Read off the end last time"]
		ifFalse: [source skip: -2].
	token _ [Number readFrom: source] ifError: [:err :rcvr | self offEnd: err].
	self step; step!

----- Method: Scanner>>xDollar (in category 'multi-character scans') -----
xDollar
	"Form a Character literal."

	self step. "pass over $"
	token _ self step.
	tokenType _ #number "really should be Char, but rest of compiler doesn't know"!

----- Method: Scanner>>xDoubleQuote (in category 'multi-character scans') -----
xDoubleQuote

    "Collect a comment."
    "wod 1/10/98: Allow 'empty' comments by testing the first character
for $"" rather than blindly adding it to the comment being collected."
    | aStream stopChar |
    stopChar _ 30 asCharacter.
    aStream _ WriteStream on: (String new: 200).
    self step.
    [hereChar = $"]
        whileFalse:
            [(hereChar = stopChar and: [source atEnd])
                ifTrue: [^self offEnd: 'Unmatched comment quote'].
            aStream nextPut: self step.].
    self step.
    currentComment == nil
        ifTrue: [currentComment _ OrderedCollection with: aStream
contents]
        ifFalse: [currentComment add: aStream contents].
    self scanToken.
!

----- Method: Scanner>>xLetter (in category 'multi-character scans') -----
xLetter
	"Form a word or keyword."

	| type c |
	buffer reset.
	[c _ hereChar asciiValue.
	(type _ typeTable at: c ifAbsent: [#xLetter]) == #xLetter or: [type == #xDigit]]
		whileTrue: ["open code step for speed"
			buffer nextPut: hereChar.
			hereChar _ aheadChar.
			source atEnd
				ifTrue: [aheadChar _ 30 asCharacter
					"doit"]
				ifFalse: [aheadChar _ source next]].
	(type == #colon or: [type == #xColon and: [aheadChar ~= $=]])
		ifTrue: [buffer nextPut: self step.
			["Allow any number of embedded colons in literal symbols"
			(typeTable at: hereChar asciiValue ifAbsent: [#xLetter])
				== #xColon]
				whileTrue: [buffer nextPut: self step].
			tokenType _ #keyword]
		ifFalse: [tokenType _ #word].
	token _ buffer contents.
	token isOctetString ifTrue: [token _ token asOctetString].
!

----- Method: Scanner>>xLitQuote (in category 'multi-character scans') -----
xLitQuote
	"Symbols and vectors: #(1 (4 5) 2 3) #ifTrue:ifFalse: #'abc'."

	| start |
	start _ mark.
	self step. "litQuote"
	self scanToken.
	tokenType = #leftParenthesis
		ifTrue: 
			[self scanToken; scanLitVec.
			mark _ start+1.
			tokenType == #doIt
				ifTrue: [self offEnd: 'Unmatched parenthesis']]
		ifFalse: 
			[(#(word keyword colon ) includes: tokenType) 
				ifTrue:
					[self scanLitWord]
				ifFalse:
					[(tokenType==#literal)
						ifTrue:
							[(token isSymbol)
								ifTrue: "##word"
									[token _ token "May want to move toward ANSI here"]]
						ifFalse:
							[tokenType==#string ifTrue: [token _ token asSymbol]]]].
	mark _ start.
	tokenType _ #literal

"	#(Pen)
	#Pen
	#'Pen'
	##Pen
	###Pen
"!

----- Method: Scanner>>xSingleQuote (in category 'multi-character scans') -----
xSingleQuote
	"String."

	self step.
	buffer reset.
	[hereChar = $' and: [aheadChar = $' ifTrue: [self step. false] ifFalse: [true]]] whileFalse: [
		buffer nextPut: self step.
		(hereChar = 30 asCharacter and: [source atEnd])
			ifTrue: [^self offEnd: 'Unmatched string quote']].
	self step.
	token _ buffer contents.
	token isOctetString ifTrue: [token _ token asOctetString].
	tokenType _ #string.
!

Object subclass: #SyntaxAttribute
	instanceVariableNames: 'color emphasis attributeList'
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-Support'!

!SyntaxAttribute commentStamp: '<historical>' prior: 0!
Represents a color and possibly a style attribute to be applied to a syntactic element for pretty-printing.  The attributeList inst var is a cache.!

----- Method: SyntaxAttribute class>>color:emphasis: (in category 'as yet unclassified') -----
color: aColor emphasis: anEmphasis
	^ self new color: aColor; emphasis: anEmphasis; yourself!

----- Method: SyntaxAttribute>>attributeList (in category 'accessing') -----
attributeList
	"Answer a list of text attributes that characterize the receiver"
	attributeList ifNil:
		[attributeList _ OrderedCollection new: 2.
		color ifNotNil: [attributeList add: (TextColor color: color)].
		emphasis ifNotNil: [attributeList add: (TextEmphasis perform: emphasis)]].
	^ attributeList!

----- Method: SyntaxAttribute>>color (in category 'accessing') -----
color

	^ color!

----- Method: SyntaxAttribute>>color: (in category 'accessing') -----
color: aTextColor
	color _ aTextColor.
	attributeList _ nil!

----- Method: SyntaxAttribute>>emphasis (in category 'accessing') -----
emphasis

	^ emphasis!

----- Method: SyntaxAttribute>>emphasis: (in category 'accessing') -----
emphasis: aTextEmphasis
	emphasis _ aTextEmphasis.
	attributeList _ nil!

TextStream subclass: #ColoredCodeStream
	instanceVariableNames: 'dialect colorTable'
	classVariableNames: 'ST80ColorTable'
	poolDictionaries: ''
	category: 'Compiler-Kernel'!

----- Method: ColoredCodeStream class>>contents: (in category 'instance creation') -----
contents: blockWithArg 
	"Evaluate blockWithArg on a DialectStream of the given description"

	| stream |
	stream _ self on: (Text new: 400).
	blockWithArg value: stream.
	^ stream contents!

----- Method: ColoredCodeStream class>>initialize (in category 'class initialization') -----
initialize
	"Initialize the colors that characterize the ST80 dialect"

	ST80ColorTable _ IdentityDictionary new.
	#(	(temporaryVariable blue italic)
		(methodArgument blue normal)
		(methodSelector black bold)
		(blockArgument red normal)
		(comment brown normal)
		(variable magenta normal)
		(literal	orange normal)
		(keyword darkGray bold)
		(prefixKeyword veryDarkGray bold)
		(setOrReturn black bold)) do:
			[:aTriplet |
				ST80ColorTable at: aTriplet first put: aTriplet allButFirst]

"ColoredCodeStream initialize"!

----- Method: ColoredCodeStream>>colorTable (in category 'color/style') -----
colorTable
	"Answer the table to use to determine colors"

	^ colorTable ifNil: [colorTable _ ST80ColorTable]!

----- Method: ColoredCodeStream>>withColor:emphasis:do: (in category 'color/style') -----
withColor: colorSymbol emphasis: emphasisSymbol do: aBlock
	"Evaluate the given block with the given color and style text attribute"

	^ self withAttributes: {TextColor color: (Color perform: colorSymbol).
							TextEmphasis perform: emphasisSymbol}
		do: aBlock!

----- Method: ColoredCodeStream>>withStyleFor:do: (in category 'color/style') -----
withStyleFor: elementType do: aBlock
	"Evaluate aBlock with appropriate emphasis and color for the given elementType"

	| colorAndStyle |
	colorAndStyle _ self colorTable at: elementType.
	^ self withColor: colorAndStyle first emphasis: colorAndStyle second do: aBlock!

TestCase subclass: #ArrayLiteralTest
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-Tests'!

----- Method: ArrayLiteralTest>>tearDown (in category 'initialize-release') -----
tearDown
	self class removeSelector: #array!

----- Method: ArrayLiteralTest>>testReservedIdentifiers (in category 'tests') -----
testReservedIdentifiers
	self class compile: 'array ^ #(nil true false)'.
	self assert: self array = {nil. true. false}.!

----- Method: ArrayLiteralTest>>testSymbols (in category 'tests') -----
testSymbols
	self class compile: 'array ^ #(#nil #true #false #''nil'' #''true'' #''false'')'.
	self assert: self array = {#nil. #true. #false. #nil. #true. #false}.!

----- Method: Dictionary>>bindingOf: (in category '*Compiler') -----
bindingOf: varName
	^self associationAt: varName ifAbsent:[nil]!

----- Method: Dictionary>>bindingsDo: (in category '*Compiler') -----
bindingsDo: aBlock
	^self associationsDo: aBlock!

Dictionary subclass: #LiteralDictionary
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Compiler-Support'!

!LiteralDictionary commentStamp: '<historical>' prior: 0!
A LiteralDictionary, like an IdentityDictionary, has a special test for equality.  In this case it is simple equality between objects of like class.  This allows equal Float or String literals to be shared without the possibility of erroneously sharing, say, 1 and 1.0!

----- Method: LiteralDictionary>>arrayEquality:and: (in category 'as yet unclassified') -----
arrayEquality: x and: y

	x size = y size ifFalse: [^ false].
	x with: y do: [:e1 :e2 | 
		(self literalEquality: e1 and: e2) ifFalse: [^ false]
	].
	^true.
!

----- Method: LiteralDictionary>>literalEquality:and: (in category 'as yet unclassified') -----
literalEquality: x and: y

	^ (x class = Array and: [y class = Array]) ifTrue: [
		self arrayEquality: x and: y.
	] ifFalse: [
		(x class == y class) and: [x = y]
	].
!

----- Method: LiteralDictionary>>scanFor: (in category 'as yet unclassified') -----
scanFor: anObject
	"Scan the key array for the first slot containing either a nil (indicating an empty slot) or an element that matches anObject. Answer the index of that slot or zero if no slot is found. This method will be overridden in various subclasses that have different interpretations for matching elements."
	| element start finish |
	finish _ array size.
	start _ (anObject hash \\ finish) + 1.

	"Search from (hash mod size) to the end."
	start to: finish do:
		[:index | ((element _ array at: index) == nil
					or: [self literalEquality: element key and: anObject])
					ifTrue: [^ index ]].

	"Search from 1 to where we started."
	1 to: start-1 do:
		[:index | ((element _ array at: index) == nil
					or: [self literalEquality: element key and: anObject])
					ifTrue: [^ index ]].

	^ 0  "No match AND no empty slot"!

InstructionStream subclass: #Decompiler
	instanceVariableNames: 'constructor method instVars tempVars constTable stack statements lastPc exit caseExits lastJumpPc lastReturnPc limit hasValue blockStackBase'
	classVariableNames: 'ArgumentFlag CascadeFlag IfNilFlag CaseFlag'
	poolDictionaries: ''
	category: 'Compiler-Kernel'!

!Decompiler commentStamp: 'ls 1/28/2004 13:31' prior: 0!
I decompile a method in three phases:
	Reverser: postfix byte codes -> prefix symbolic codes (nodes and atoms)
	Parser: prefix symbolic codes -> node tree (same as the compiler)
	Printer: node tree -> text (done by the nodes)
	

instance vars:

	constructor
	method
	instVars
	tempVars
	constTable
	stack
	statements
	lastPc
	exit
	caseExits	- stack of exit addresses that have been seen in the branches of caseOf:'s
	lastJumpPc
	lastReturnPc
	limit
	hasValue
	blockStackBase!

----- Method: Decompiler class>>initialize (in category 'class initialization') -----
initialize

	CascadeFlag _ 'cascade'.  "A unique object"
	CaseFlag _ 'case'. "Ditto"
	ArgumentFlag _ 'argument'.  "Ditto"
	IfNilFlag _ 'ifNil'.  "Ditto"

	"Decompiler initialize"!

----- Method: Decompiler class>>recompileAllTest (in category 'testing') -----
recompileAllTest
	"[self recompileAllTest]"
	"decompile every method and compile it back; if the decompiler is correct then the system should keep running.  :)"
	
	| decompiled ast compiled |
	SystemNavigation default allBehaviorsDo: [ :behavior |
		Utilities informUser: (behavior printString) during: [
			behavior selectors do: [ :sel |
				decompiled := Decompiler new decompile: sel in: behavior.
				ast := Compiler new compile: decompiled in: behavior notifying: nil ifFail: [ self error: 'failed' ].
				compiled := ast generate: (behavior compiledMethodAt: sel) trailer.
				behavior addSelector: sel withMethod: compiled. ] ] ]!

----- Method: Decompiler>>blockForCaseTo: (in category 'control') -----
blockForCaseTo: end
	"Decompile a range of code as in statementsForCaseTo:, but return a block node."
	| exprs block oldBase |
	oldBase _ blockStackBase.
	blockStackBase _ stack size.
	exprs _ self statementsForCaseTo: end.
	block _ constructor codeBlock: exprs returns: lastReturnPc = lastPc.
	blockStackBase _ oldBase.
	lastReturnPc _ -1.  "So as not to mislead outer calls"
	^block!

----- Method: Decompiler>>blockReturnTop (in category 'instruction decoding') -----
blockReturnTop
	"No action needed"!

----- Method: Decompiler>>blockTo: (in category 'control') -----
blockTo: end
	"Decompile a range of code as in statementsTo:, but return a block node."
	| exprs block oldBase |
	oldBase _ blockStackBase.
	blockStackBase _ stack size.
	exprs _ self statementsTo: end.
	block _ constructor codeBlock: exprs returns: lastReturnPc = lastPc.
	blockStackBase _ oldBase.
	lastReturnPc _ -1.  "So as not to mislead outer calls"
	^block!

----- Method: Decompiler>>case: (in category 'instruction decoding') -----
case: dist
	"statements = keyStmts CascadeFlag keyValueBlock ... keyStmts"

	| nextCase thenJump stmtStream elements b node cases otherBlock myExits |
	nextCase _ pc + dist.

	"Now add CascadeFlag & keyValueBlock to statements"
	statements addLast: stack removeLast.
	stack addLast: CaseFlag. "set for next pop"
	statements addLast: (self blockForCaseTo: nextCase).

	stack last == CaseFlag
		ifTrue: "Last case"
			["ensure jump is within block (in case thenExpr returns wierdly I guess)"
			stack removeLast. "get rid of CaseFlag"
			stmtStream _ ReadStream on: (self popTo: stack removeLast).
			
			elements _ OrderedCollection new.
			b _ OrderedCollection new.
			[stmtStream atEnd] whileFalse:
				[(node _ stmtStream next) == CascadeFlag
					ifTrue:
						[elements addLast: (constructor
							codeMessage: (constructor codeBlock: b returns: false)
							selector: (constructor codeSelector: #-> code: #macro)
							arguments: (Array with: stmtStream next)).
						 b _ OrderedCollection new]
					ifFalse: [b addLast: node]].
			b size > 0 ifTrue: [self error: 'Bad cases'].
			cases _ constructor codeBrace: elements.
			
			"try find the end of the case"
			myExits := caseExits removeLast: elements size.
			myExits := myExits reject: [ :e | e isNil or: [ e < 0 or: [ e > method size ] ] ].
			myExits isEmpty
				ifTrue: [ thenJump := nextCase ]
				ifFalse: [ thenJump := myExits min ].
			
			otherBlock _ self blockTo: thenJump.
			stack addLast:
				(constructor
					codeMessage: stack removeLast
					selector: (constructor codeSelector: #caseOf:otherwise: code: #macro)
					arguments: (Array with: cases with: otherBlock)).
					
			myExits isEmpty ifTrue:[
				"all branches returned; pop off the statement"
				statements addLast: stack removeLast. ] ].!

----- Method: Decompiler>>checkForBlock: (in category 'control') -----
checkForBlock: receiver
	"We just saw a blockCopy: message. Check for a following block."

	| savePc jump args argPos block |
	receiver == constructor codeThisContext ifFalse: [^false].
	savePc _ pc.
	(jump _ self interpretJump) notNil
		ifFalse:
			[pc _ savePc.  ^nil].
	"Definitely a block"
	jump _ jump + pc.
	argPos _ statements size.
	[self willStorePop]
		whileTrue:
			[stack addLast: ArgumentFlag.  "Flag for doStore:"
			self interpretNextInstructionFor: self].
	args _ Array new: statements size - argPos.
	1 to: args size do:  "Retrieve args"
		[:i | args at: i put: statements removeLast.
		(args at: i) scope: -1  "flag args as block temps"].
	block _ self blockTo: jump.
	stack addLast: (constructor codeArguments: args block: block).
	^true!

----- Method: Decompiler>>convertToDoLoop (in category 'private') -----
convertToDoLoop
	"If statements contains the pattern
		var _ startExpr.
		[var <= limit] whileTrue: [...statements... var _ var + incConst]
	then replace this by
		startExpr to: limit by: incConst do: [:var | ...statements...]"
	| initStmt toDoStmt limitStmt |
	statements size < 2 ifTrue: [^ self].
	initStmt _ statements at: statements size-1.
	(toDoStmt _ statements last toDoFromWhileWithInit: initStmt)
		== nil ifTrue: [^ self].
	initStmt variable scope: -1.  "Flag arg as block temp"
	statements removeLast; removeLast; addLast: toDoStmt.

	"Attempt further conversion of the pattern
		limitVar _ limitExpr.
		startExpr to: limitVar by: incConst do: [:var | ...statements...]
	to
		startExpr to: limitExpr by: incConst do: [:var | ...statements...]"
	statements size < 2 ifTrue: [^ self].
	limitStmt _ statements at: statements size-1.
	((limitStmt isMemberOf: AssignmentNode)
		and: [limitStmt variable isTemp
		and: [limitStmt variable == toDoStmt arguments first
		and: [self methodRefersOnlyOnceToTemp: limitStmt variable fieldOffset]]])
		ifFalse: [^ self].
	toDoStmt arguments at: 1 put: limitStmt value.
	limitStmt variable scope: -2.  "Flag limit var so it won't print"
	statements removeLast; removeLast; addLast: toDoStmt.

!

----- Method: Decompiler>>decompile:in: (in category 'public access') -----
decompile: aSelector in: aClass 
	"See Decompiler|decompile:in:method:. The method is found by looking up 
	the message, aSelector, in the method dictionary of the class, aClass."

	^self
		decompile: aSelector
		in: aClass
		method: (aClass compiledMethodAt: aSelector)!

----- Method: Decompiler>>decompile:in:method: (in category 'public access') -----
decompile: aSelector in: aClass method: aMethod
	"Answer a MethodNode that is the root of the parse tree for the 
	argument, aMethod, which is the CompiledMethod associated with the 
	message, aSelector. Variables are determined with respect to the 
	argument, aClass."

	^self
		decompile: aSelector
		in: aClass
		method: aMethod
		using: DecompilerConstructor new!

----- Method: Decompiler>>decompile:in:method:using: (in category 'private') -----
decompile: aSelector in: aClass method: aMethod using: aConstructor

	| block |
	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.
			super method: method pc: method initialPC.
			block _ self blockTo: method endPC + 1.
			stack isEmpty ifFalse: [self error: 'stack not empty']].
	^constructor
		codeMethod: aSelector
		block: block
		tempVars: tempVars
		primitive: method primitive
		class: aClass!

----- Method: Decompiler>>decompileBlock: (in category 'public access') -----
decompileBlock: aBlock 
	"Decompile aBlock, returning the result as a BlockNode.  
	Show temp names from source if available."

	| startpc end homeClass blockNode home |

	(home := aBlock home) ifNil: [^ nil].
	(homeClass := home methodClass) ifNil: [^ nil].
	method := home method.
	constructor := DecompilerConstructor new.
	
	self withTempNames: method methodNode tempNames.
	
	self initSymbols: homeClass.
	startpc _ aBlock startpc.
	end _ aBlock endPC.
	stack _ OrderedCollection new: method frameSize.
	caseExits _ OrderedCollection new.
	statements _ OrderedCollection new: 20.
	super method: method pc: startpc - 5.
	blockNode _ self blockTo: end.
	stack isEmpty ifFalse: [self error: 'stack not empty'].
	^ blockNode statements first
	
	"Decompiler new decompileBlock: [3 + 4]"!

----- Method: Decompiler>>doDup (in category 'instruction decoding') -----
doDup

	stack last == CascadeFlag
		ifFalse:
			["Save position and mark cascade"
			stack addLast: statements size.
			stack addLast: CascadeFlag].
	stack addLast: CascadeFlag!

----- Method: Decompiler>>doPop (in category 'instruction decoding') -----
doPop

	stack isEmpty ifTrue:
		["Ignore pop in first leg of ifNil for value"
		^ self].
	stack last == CaseFlag
		ifTrue: [stack removeLast]
		ifFalse: [statements addLast: stack removeLast].!

----- Method: Decompiler>>doStore: (in category 'instruction decoding') -----
doStore: stackOrBlock
	"Only called internally, not from InstructionStream. StackOrBlock is stack
	for store, statements for storePop."

	| var expr |
	var _ stack removeLast.
	expr _ stack removeLast.
	stackOrBlock addLast: (expr == ArgumentFlag
		ifTrue: [var]
		ifFalse: [constructor codeAssignTo: var value: expr])!

----- Method: Decompiler>>initSymbols: (in category 'initialize-release') -----
initSymbols: aClass
	| nTemps namedTemps |
	constructor method: method class: aClass literals: method literals.
	constTable _ constructor codeConstants.
	instVars _ Array new: aClass instSize.
	nTemps _ method numTemps.
	namedTemps _ tempVars ifNil: [method tempNames].
	tempVars _ (1 to: nTemps) collect:
				[:i | i <= namedTemps size
					ifTrue: [constructor codeTemp: i - 1 named: (namedTemps at: i)]
					ifFalse: [constructor codeTemp: i - 1]]!

----- Method: Decompiler>>interpretNextInstructionFor: (in category 'private') -----
interpretNextInstructionFor: client

	| code varNames |

"Change false here will trace all state in Transcript."
true ifTrue: [^ super interpretNextInstructionFor: client].

	varNames _ self class allInstVarNames.
	code _ (self method at: pc) radix: 16.
	Transcript cr; cr; print: pc; space;
		nextPutAll: '<' , code, '>'.
	8 to: varNames size do:
		[:i | i <= 10 ifTrue: [Transcript cr]
				ifFalse: [Transcript space; space].
		Transcript nextPutAll: (varNames at: i);
				nextPutAll: ': '; print: (self instVarAt: i)].
	Transcript endEntry.
	^ super interpretNextInstructionFor: client!

----- Method: Decompiler>>jump: (in category 'instruction decoding') -----
jump: dist

	exit _ pc + dist.
	lastJumpPc _ lastPc!

----- Method: Decompiler>>jump:if: (in category 'instruction decoding') -----
jump: dist if: condition

	| savePc elseDist sign elsePc elseStart end cond ifExpr thenBlock elseBlock thenJump
		elseJump condHasValue b isIfNil saveStack |
	stack last == CascadeFlag ifTrue: [^ self case: dist].
	elsePc _ lastPc.
	elseStart _ pc + dist.
	end _ limit.
	"Check for bfp-jmp to invert condition.
	Don't be fooled by a loop with a null body."
	sign _ condition.
	savePc _ pc.
	((elseDist _ self interpretJump) notNil and: [elseDist >= 0 and: [elseStart = pc]])
		ifTrue: [sign _ sign not.  elseStart _ pc + elseDist].
	pc _ savePc.
	ifExpr _ stack removeLast.
	(stack size > 0 and: [stack last == IfNilFlag])
		ifTrue: [stack removeLast.  isIfNil _ true]
		ifFalse: [isIfNil _ false].
	saveStack _ stack.
	stack _ OrderedCollection new.
	thenBlock _ self blockTo: elseStart.
	condHasValue _ hasValue or: [isIfNil].
	"ensure jump is within block (in case thenExpr returns)"
	thenJump _ exit <= end ifTrue: [exit] ifFalse: [elseStart].
	"if jump goes back, then it's a loop"
	thenJump < elseStart
		ifTrue:
			["Must be a while loop...
			thenJump will jump to the beginning of the while expr.  In the case of
			while's with a block in the condition, the while expr
			should include more than just the last expression: find all the
			statements needed by re-decompiling."
			stack _ saveStack.
			pc _ thenJump.
			b _ self statementsTo: elsePc.
			"discard unwanted statements from block"
			b size - 1 timesRepeat: [statements removeLast].
			statements addLast: (constructor
					codeMessage: (constructor codeBlock: b returns: false)
					selector: (constructor codeSelector: (sign ifTrue: [#whileFalse:] ifFalse: [#whileTrue:]) code: #macro)
					arguments: (Array with: thenBlock)).
			pc _ elseStart.
			self convertToDoLoop]
		ifFalse:
			["Must be a conditional..."
			elseBlock _ self blockTo: thenJump.
			elseJump _ exit.
			"if elseJump is backwards, it is not part of the elseExpr"
			elseJump < elsePc
				ifTrue: [pc _ lastPc].
			isIfNil
			ifTrue: [cond _ constructor
						codeMessage: ifExpr ifNilReceiver
						selector: (sign
							ifTrue: [constructor codeSelector: #ifNotNil: code: #macro]
							ifFalse: [constructor codeSelector: #ifNil: code: #macro])
						arguments: (Array with: thenBlock)]
			ifFalse: [cond _ constructor
						codeMessage: ifExpr
						selector: (constructor codeSelector: #ifTrue:ifFalse: code: #macro)
						arguments:
							(sign
								ifTrue: [Array with: elseBlock with: thenBlock]
								ifFalse: [Array with: thenBlock with: elseBlock])].
			stack _ saveStack.
			condHasValue
				ifTrue: [stack addLast: cond]
				ifFalse: [statements addLast: cond]]!

----- Method: Decompiler>>methodRefersOnlyOnceToTemp: (in category 'private') -----
methodRefersOnlyOnceToTemp: offset
	| nRefs byteCode extension scanner |
	nRefs _ 0.
	offset <= 15
		ifTrue:
			[byteCode _ 16 + offset.
			(InstructionStream on: method) scanFor:
				[:instr | instr = byteCode ifTrue: [nRefs _ nRefs + 1].
				nRefs > 1]]
		ifFalse:
			[extension _ 64 + offset.
			scanner _ InstructionStream on: method.
			scanner scanFor:
				[:instr | (instr = 128 and: [scanner followingByte = extension])
							ifTrue: [nRefs _ nRefs + 1].
				nRefs > 1]].
	^ nRefs = 1
!

----- Method: Decompiler>>methodReturnConstant: (in category 'instruction decoding') -----
methodReturnConstant: value

	self pushConstant: value; methodReturnTop!

----- Method: Decompiler>>methodReturnReceiver (in category 'instruction decoding') -----
methodReturnReceiver

	self pushReceiver; methodReturnTop!

----- Method: Decompiler>>methodReturnTop (in category 'instruction decoding') -----
methodReturnTop
	| last |
	last _ stack removeLast "test test" asReturnNode.
	stack size > blockStackBase  "get effect of elided pop before return"
		ifTrue: [statements addLast: stack removeLast].
	exit _ method size + 1.
	lastJumpPc _ lastReturnPc _ lastPc.
	statements addLast: last!

----- Method: Decompiler>>popIntoLiteralVariable: (in category 'instruction decoding') -----
popIntoLiteralVariable: value

	self pushLiteralVariable: value; doStore: statements!

----- Method: Decompiler>>popIntoReceiverVariable: (in category 'instruction decoding') -----
popIntoReceiverVariable: offset

	self pushReceiverVariable: offset; doStore: statements!

----- Method: Decompiler>>popIntoTemporaryVariable: (in category 'instruction decoding') -----
popIntoTemporaryVariable: offset

	self pushTemporaryVariable: offset; doStore: statements!

----- Method: Decompiler>>popTo: (in category 'private') -----
popTo: oldPos

	| t |
	t _ Array new: statements size - oldPos.
	(t size to: 1 by: -1) do:
		[:i | t at: i put: statements removeLast].
	^t!

----- Method: Decompiler>>pushActiveContext (in category 'instruction decoding') -----
pushActiveContext

	stack addLast: constructor codeThisContext!

----- Method: Decompiler>>pushConstant: (in category 'instruction decoding') -----
pushConstant: value

	| node |
	node _ value == true ifTrue: [constTable at: 2]
		ifFalse: [value == false ifTrue: [constTable at: 3]
		ifFalse: [value == nil ifTrue: [constTable at: 4]
		ifFalse: [constructor codeAnyLiteral: value]]].
	stack addLast: node!

----- Method: Decompiler>>pushLiteralVariable: (in category 'instruction decoding') -----
pushLiteralVariable: assoc

	stack addLast: (constructor codeAnyLitInd: assoc)!

----- Method: Decompiler>>pushReceiver (in category 'instruction decoding') -----
pushReceiver

	stack addLast: (constTable at: 1)!

----- Method: Decompiler>>pushReceiverVariable: (in category 'instruction decoding') -----
pushReceiverVariable: offset

	| var |
	(var _ instVars at: offset + 1 ifAbsent: []) == nil
		ifTrue:
			["Not set up yet"
			var _ constructor codeInst: offset.
			instVars size < (offset + 1) ifTrue: [
				instVars _ (Array new: offset + 1)
					replaceFrom: 1 to: instVars size with: instVars; yourself ].
			instVars at: offset + 1 put: var].
	stack addLast: var!

----- Method: Decompiler>>pushTemporaryVariable: (in category 'instruction decoding') -----
pushTemporaryVariable: offset

	stack addLast: (tempVars at: offset + 1)!

----- Method: Decompiler>>quickMethod (in category 'private') -----
quickMethod
	| |
	method isReturnSpecial
		ifTrue: [^ constructor codeBlock:
				(Array with: (constTable at: method primitive - 255)) returns: true].
	method isReturnField
		ifTrue: [^ constructor codeBlock:
				(Array with: (constructor codeInst: method returnField)) returns: true].
	self error: 'improper short method'!

----- Method: Decompiler>>send:super:numArgs: (in category 'instruction decoding') -----
send: selector super: superFlag numArgs: numArgs

	| args rcvr selNode msgNode messages |
	args _ Array new: numArgs.
	(numArgs to: 1 by: -1) do:
		[:i | args at: i put: stack removeLast].
	rcvr _ stack removeLast.
	superFlag ifTrue: [rcvr _ constructor codeSuper].
	(selector == #blockCopy: and: [self checkForBlock: rcvr])
		ifFalse:
			[selNode _ constructor codeAnySelector: selector.
			rcvr == CascadeFlag
				ifTrue:
					["May actually be a cascade or an ifNil: for value."
					self willJumpIfFalse
						ifTrue: "= generated by a case macro"
							[selector == #= ifTrue:
								[" = signals a case statement..."
								statements addLast: args first.
								stack addLast: rcvr. "restore CascadeFlag"
								^ self].
							selector == #== ifTrue:
								[" == signals an ifNil: for value..."
								stack removeLast; removeLast.
								rcvr _ stack removeLast.
								stack addLast: IfNilFlag;
									addLast: (constructor
										codeMessage: rcvr
										selector: selNode
										arguments: args).
								^ self].
							self error: 'bad case: ', selector]
						ifFalse:
							[(self willJumpIfTrue and: [selector == #==]) ifTrue:
								[" == signals an ifNotNil: for value..."
								stack removeLast; removeLast.
								rcvr _ stack removeLast.
								stack addLast: IfNilFlag;
									addLast: (constructor
										codeMessage: rcvr
										selector: selNode
										arguments: args).
								^ self].
							msgNode _ constructor codeCascadedMessage: selNode
											arguments: args].
					stack last == CascadeFlag
						ifFalse:
							["Last message of a cascade"
							statements addLast: msgNode.
							messages _ self popTo: stack removeLast.  "Depth saved by first dup"
							msgNode _ constructor
								codeCascade: stack removeLast
								messages: messages]]
				ifFalse:
					[msgNode _ constructor
								codeMessage: rcvr
								selector: selNode
								arguments: args].
			stack addLast: msgNode]!

----- Method: Decompiler>>statementsForCaseTo: (in category 'control') -----
statementsForCaseTo: end
	"Decompile the method from pc up to end and return an array of
	expressions. If at run time this block will leave a value on the stack,
	set hasValue to true. If the block ends with a jump or return, set exit
	to the destination of the jump, or the end of the method; otherwise, set
	exit = end. Leave pc = end.
	Note that stack initially contains a CaseFlag which will be removed by
	a subsequent Pop instruction, so adjust the StackPos accordingly."

	| blockPos stackPos |
	blockPos _ statements size.
	stackPos _ stack size - 1. "Adjust for CaseFlag"
	[pc < end]
		whileTrue:
			[lastPc _ pc.  limit _ end.  "for performs"
			self interpretNextInstructionFor: self].
	"If there is an additional item on the stack, it will be the value
	of this block."
	(hasValue _ stack size > stackPos)
		ifTrue:
			[stack last == CaseFlag
				ifFalse: [ statements addLast: stack removeLast] ].
	lastJumpPc = lastPc ifFalse: [exit _ pc].
	caseExits add: exit.
	^self popTo: blockPos!

----- Method: Decompiler>>statementsTo: (in category 'control') -----
statementsTo: end
	"Decompile the method from pc up to end and return an array of
	expressions. If at run time this block will leave a value on the stack,
	set hasValue to true. If the block ends with a jump or return, set exit
	to the destination of the jump, or the end of the method; otherwise, set
	exit = end. Leave pc = end."

	| blockPos stackPos t |
	blockPos _ statements size.
	stackPos _ stack size.
	[pc < end]
		whileTrue:
			[lastPc _ pc.  limit _ end.  "for performs"
			self interpretNextInstructionFor: self].
	"If there is an additional item on the stack, it will be the value
	of this block."
	(hasValue _ stack size > stackPos)
		ifTrue:
			[statements addLast: stack removeLast].
	lastJumpPc = lastPc ifFalse: [exit _ pc].
	^self popTo: blockPos!

----- Method: Decompiler>>storeIntoLiteralVariable: (in category 'instruction decoding') -----
storeIntoLiteralVariable: assoc

	self pushLiteralVariable: assoc; doStore: stack!

----- Method: Decompiler>>storeIntoReceiverVariable: (in category 'instruction decoding') -----
storeIntoReceiverVariable: offset

	self pushReceiverVariable: offset; doStore: stack!

----- Method: Decompiler>>storeIntoTemporaryVariable: (in category 'instruction decoding') -----
storeIntoTemporaryVariable: offset

	self pushTemporaryVariable: offset; doStore: stack!

----- Method: Decompiler>>tempAt: (in category 'public access') -----
tempAt: offset
	"Needed by BraceConstructor<PopIntoTemporaryVariable"

	^tempVars at: offset + 1!

----- Method: Decompiler>>withTempNames: (in category 'initialize-release') -----
withTempNames: tempNameArray
	tempVars _ tempNameArray!



More information about the Packages mailing list