SystemOrganization addCategory: #'AST-Core-Matching'! SystemOrganization addCategory: #'AST-Core-Nodes'! SystemOrganization addCategory: #'AST-Core-Parser'! SystemOrganization addCategory: #'AST-Core-Tokens'! SystemOrganization addCategory: #'AST-Core-Visitors'! !Behavior methodsFor: '*ast-core' stamp: 'lr 10/20/2009 19:21'! parseTreeFor: aSymbol ^ RBParser parseMethod: (self sourceCodeAt: aSymbol) onError: [ :msg :pos | ^ nil ]! ! Dictionary subclass: #RBSmallDictionary instanceVariableNames: 'values' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Matching'! !RBSmallDictionary commentStamp: 'md 4/1/2007 12:34' prior: 0! RBSmallDictionary is a special dictionary optimized for small collections. In addition to the normal dictionary protocol, it also supports an #empty message which "empties" the collection but may hang on to the original elements (so it could collect garbage). Without #empty we would either need to create a new dictionary or explicitly remove everything from the dictionary. Both of these take more time and #empty. Instance Variables: array array of keys (we don't use Associations for our key value pairs) tally the size of the dictionary values array of our values ! !RBSmallDictionary class methodsFor: 'instance creation' stamp: 'md 3/31/2007 11:19'! new ^self basicNew initialize: 2! ! !RBSmallDictionary class methodsFor: 'instance creation' stamp: 'md 4/3/2007 12:09'! new: aSize ^self basicNew initialize: aSize.! ! !RBSmallDictionary methodsFor: 'adding' stamp: ''! add: anAssociation self at: anAssociation key put: anAssociation value. ^anAssociation! ! !RBSmallDictionary methodsFor: 'accessing' stamp: 'md 4/13/2007 11:47'! associationAt: key ifAbsent: aBlock | index | index := self findIndexFor: key. ^index == 0 ifTrue: [aBlock value] ifFalse: [ key -> (values at: index)].! ! !RBSmallDictionary methodsFor: 'enumerating' stamp: ''! associationsDo: aBlock self keysAndValuesDo: [:key :value | aBlock value: key -> value]! ! !RBSmallDictionary methodsFor: 'accessing' stamp: ''! at: key ifAbsent: aBlock | index | index := self findIndexFor: key. ^index == 0 ifTrue: [aBlock value] ifFalse: [values at: index]! ! !RBSmallDictionary methodsFor: 'accessing' stamp: ''! at: key ifAbsentPut: aBlock | index | index := self findIndexFor: key. ^index == 0 ifTrue: [self privateAt: key put: aBlock value] ifFalse: [values at: index]! ! !RBSmallDictionary methodsFor: 'adding' stamp: ''! at: key put: value | index | index := self findIndexFor: key. ^index == 0 ifTrue: [self privateAt: key put: value] ifFalse: [values at: index put: value]! ! !RBSmallDictionary methodsFor: 'copying' stamp: 'md 3/29/2007 23:26'! copy ^self shallowCopy postCopy! ! !RBSmallDictionary methodsFor: 'enumerating' stamp: 'md 3/30/2007 16:03'! do: aBlock 1 to: tally do: [:i | aBlock value: (values at: i)]! ! !RBSmallDictionary methodsFor: 'accessing' stamp: 'md 3/30/2007 16:05'! empty tally := 0! ! !RBSmallDictionary methodsFor: 'private' stamp: 'md 3/30/2007 16:09'! findIndexFor: aKey 1 to: tally do: [:i | (array at: i) = aKey ifTrue: [^i]]. ^0! ! !RBSmallDictionary methodsFor: 'private' stamp: 'md 3/30/2007 16:04'! growKeysAndValues self growTo: tally * 2! ! !RBSmallDictionary methodsFor: 'private' stamp: 'md 3/30/2007 16:08'! growTo: aSize | newKeys newValues | newKeys := Array new: aSize. newValues := Array new: aSize. 1 to: tally do: [:i | newKeys at: i put: (array at: i). newValues at: i put: (values at: i)]. array := newKeys. values := newValues! ! !RBSmallDictionary methodsFor: 'testing' stamp: ''! includesKey: aKey ^(self findIndexFor: aKey) ~~ 0! ! !RBSmallDictionary methodsFor: 'initialization' stamp: 'md 4/3/2007 12:10'! initialize: size array := Array new: size. values := Array new: size. tally := 0! ! !RBSmallDictionary methodsFor: 'enumerating' stamp: 'md 3/30/2007 16:09'! keysAndValuesDo: aBlock 1 to: tally do: [:i | aBlock value: (array at: i) value: (values at: i)]! ! !RBSmallDictionary methodsFor: 'enumerating' stamp: 'md 3/30/2007 16:08'! keysDo: aBlock 1 to: tally do: [:i | aBlock value: (array at: i)]! ! !RBSmallDictionary methodsFor: 'adding' stamp: 'md 4/13/2007 11:49'! noCheckAdd: anObject ^self add: anObject! ! !RBSmallDictionary methodsFor: 'copying' stamp: 'md 3/30/2007 16:09'! postCopy array := array copy. values := values copy! ! !RBSmallDictionary methodsFor: 'private' stamp: 'md 3/30/2007 16:08'! privateAt: key put: value tally == array size ifTrue: [self growKeysAndValues]. tally := tally + 1. array at: tally put: key. ^values at: tally put: value! ! !RBSmallDictionary methodsFor: 'private' stamp: 'md 4/13/2007 16:45'! rehash "do nothing for now"! ! !RBSmallDictionary methodsFor: 'removing' stamp: 'md 3/29/2007 23:24'! remove:anAssociation self removeKey: anAssociation key.! ! !RBSmallDictionary methodsFor: 'removing' stamp: ''! remove: oldObject ifAbsent: anExceptionBlock self removeKey: oldObject key ifAbsent: anExceptionBlock. ^oldObject! ! !RBSmallDictionary methodsFor: 'removing' stamp: 'md 3/30/2007 16:09'! removeKey: key ifAbsent: aBlock | index value | index := self findIndexFor: key. index == 0 ifTrue: [^aBlock value]. value := values at: index. index to: tally - 1 do: [:i | array at: i put: (array at: i + 1). values at: i put: (values at: i + 1)]. array at: tally put: nil. values at: tally put: nil. tally := tally - 1. ^value! ! !RBSmallDictionary methodsFor: 'accessing' stamp: 'md 3/30/2007 16:04'! size ^tally! ! RBSmallDictionary subclass: #RBSmallIdentityDictionary instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Matching'! !RBSmallIdentityDictionary methodsFor: 'private' stamp: 'md 4/2/2007 08:21'! findIndexFor: aKey 1 to: tally do: [:i | (array at: i) == aKey ifTrue: [^i]]. ^0! ! !RBSmallIdentityDictionary methodsFor: 'accessing' stamp: 'md 4/2/2007 08:27'! keys "Answer a Set containing the receiver's keys." | aSet | aSet := IdentitySet new: self size. self keysDo: [:key | aSet add: key]. ^ aSet! ! Object subclass: #RBParseTreeRule instanceVariableNames: 'searchTree owner' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Matching'! !RBParseTreeRule commentStamp: 'md 8/9/2005 14:55' prior: 0! RBParseTreeRule is the abstract superclass of all of the parse tree searching rules. A parse tree rule is the first class representation of a particular rule to search for. The owner of a rule is the algorithm that actually executes the search. This arrangement allows multiple searches to be conducted by a single Searcher. Instance Variables: owner The searcher that is actually performing the search. searchTree The parse tree to be searched. ! !RBParseTreeRule class methodsFor: 'instance creation' stamp: ''! methodSearch: aString ^(self new) methodSearchString: aString; yourself! ! !RBParseTreeRule class methodsFor: 'instance creation' stamp: ''! new ^(super new) initialize; yourself! ! !RBParseTreeRule class methodsFor: 'instance creation' stamp: ''! search: aString ^(self new) searchString: aString; yourself! ! !RBParseTreeRule methodsFor: 'matching' stamp: ''! canMatch: aProgramNode ^true! ! !RBParseTreeRule methodsFor: 'private' stamp: ''! context ^owner context! ! !RBParseTreeRule methodsFor: 'matching' stamp: ''! foundMatchFor: aProgramNode ^aProgramNode! ! !RBParseTreeRule methodsFor: 'initialize-release' stamp: ''! initialize! ! !RBParseTreeRule methodsFor: 'initialize-release' stamp: ''! methodSearchString: aString searchTree := RBParser parseRewriteMethod: aString! ! !RBParseTreeRule methodsFor: 'initialize-release' stamp: ''! owner: aParseTreeSearcher owner := aParseTreeSearcher! ! !RBParseTreeRule methodsFor: 'matching' stamp: ''! performOn: aProgramNode self context empty. ^((searchTree match: aProgramNode inContext: self context) and: [self canMatch: aProgramNode]) ifTrue: [owner recusivelySearchInContext. self foundMatchFor: aProgramNode] ifFalse: [nil]! ! !RBParseTreeRule methodsFor: 'initialize-release' stamp: ''! searchString: aString searchTree := RBParser parseRewriteExpression: aString! ! !RBParseTreeRule methodsFor: 'accessing' stamp: ''! sentMessages ^searchTree sentMessages! ! RBParseTreeRule subclass: #RBReplaceRule instanceVariableNames: 'verificationBlock' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Matching'! !RBReplaceRule commentStamp: 'md 8/9/2005 14:56' prior: 0! RBReplaceRule is the abstract superclass of all of the transforming rules. The rules change the source code by replacing the node that matches the rule. Subclasses implement different strategies for this replacement. Subclasses must implement the following messages: matching foundMatchFor: Instance Variables: verificationBlock Is evaluated with the matching node. This allows for further verification of a match beyond simple tree matching. ! RBReplaceRule subclass: #RBBlockReplaceRule instanceVariableNames: 'replaceBlock' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Matching'! !RBBlockReplaceRule commentStamp: 'md 8/9/2005 14:55' prior: 0! RBBlockReplaceRule replaces the matching node by the result of evaluating replaceBlock. This allows arbitrary computation to come up with a replacement. Instance Variables: replaceBlock The block that returns the node to replace to matching node with. ! !RBBlockReplaceRule class methodsFor: 'instance creation' stamp: ''! searchFor: searchString replaceWith: replaceBlock ^self new searchFor: searchString replaceWith: replaceBlock! ! !RBBlockReplaceRule class methodsFor: 'instance creation' stamp: ''! searchFor: searchString replaceWith: replaceBlock when: aBlock ^self new searchFor: searchString replaceWith: replaceBlock when: aBlock! ! !RBBlockReplaceRule class methodsFor: 'instance creation' stamp: ''! searchForMethod: searchString replaceWith: replaceBlock ^self new searchForMethod: searchString replaceWith: replaceBlock! ! !RBBlockReplaceRule class methodsFor: 'instance creation' stamp: ''! searchForMethod: searchString replaceWith: replaceBlock when: aBlock ^self new searchForMethod: searchString replaceWith: replaceBlock when: aBlock! ! !RBBlockReplaceRule class methodsFor: 'instance creation' stamp: ''! searchForTree: aRBProgramNode replaceWith: replaceBlock ^self new searchForTree: aRBProgramNode replaceWith: replaceBlock! ! !RBBlockReplaceRule class methodsFor: 'instance creation' stamp: ''! searchForTree: aRBProgramNode replaceWith: replaceBlock when: aBlock ^self new searchForTree: aRBProgramNode replaceWith: replaceBlock when: aBlock! ! !RBBlockReplaceRule methodsFor: 'matching' stamp: ''! foundMatchFor: aProgramNode | newNode | newNode := replaceBlock value: aProgramNode. aProgramNode replaceMethodSource: newNode. ^newNode! ! !RBBlockReplaceRule methodsFor: 'initialize-release' stamp: ''! initialize super initialize. replaceBlock := [:aNode | aNode]! ! !RBBlockReplaceRule methodsFor: 'initialize-release' stamp: ''! searchFor: searchString replaceWith: aBlock self searchString: searchString. replaceBlock := aBlock! ! !RBBlockReplaceRule methodsFor: 'initialize-release' stamp: ''! searchFor: searchString replaceWith: replBlock when: verifyBlock self searchFor: searchString replaceWith: replBlock. verificationBlock := verifyBlock! ! !RBBlockReplaceRule methodsFor: 'initialize-release' stamp: ''! searchForMethod: searchString replaceWith: aBlock self methodSearchString: searchString. replaceBlock := aBlock! ! !RBBlockReplaceRule methodsFor: 'initialize-release' stamp: ''! searchForMethod: searchString replaceWith: replBlock when: verifyBlock self searchForMethod: searchString replaceWith: replBlock. verificationBlock := verifyBlock! ! !RBBlockReplaceRule methodsFor: 'initialize-release' stamp: ''! searchForTree: aBRProgramNode replaceWith: aBlock searchTree := aBRProgramNode. replaceBlock := aBlock! ! !RBBlockReplaceRule methodsFor: 'initialize-release' stamp: ''! searchForTree: aBRProgramNode replaceWith: replBlock when: verifyBlock self searchForTree: aBRProgramNode replaceWith: replBlock. verificationBlock := verifyBlock! ! !RBReplaceRule methodsFor: 'matching' stamp: ''! canMatch: aProgramNode ^verificationBlock value: aProgramNode! ! !RBReplaceRule methodsFor: 'matching' stamp: ''! foundMatchFor: aProgramNode self subclassResponsibility! ! !RBReplaceRule methodsFor: 'initialize-release' stamp: ''! initialize super initialize. verificationBlock := [:aNode | true]! ! !RBReplaceRule methodsFor: 'matching' stamp: ''! replace: aProgramNode with: newNode aProgramNode replaceMethodSource: newNode! ! RBReplaceRule subclass: #RBStringReplaceRule instanceVariableNames: 'replaceTree' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Matching'! !RBStringReplaceRule commentStamp: 'md 8/9/2005 14:56' prior: 0! RBStringReplaceRule replaces a matched tree with another tree (which may include metavariable from the matching tree). This is a very succint syntax for specifying most rewrites. Instance Variables: replaceTree The tree to replace the matched tree with. ! !RBStringReplaceRule class methodsFor: 'instance creation' stamp: ''! searchFor: searchString replaceWith: replaceString ^self new searchFor: searchString replaceWith: replaceString! ! !RBStringReplaceRule class methodsFor: 'instance creation' stamp: ''! searchFor: searchString replaceWith: replaceString when: aBlock ^self new searchFor: searchString replaceWith: replaceString when: aBlock! ! !RBStringReplaceRule class methodsFor: 'instance creation' stamp: ''! searchForMethod: searchString replaceWith: replaceString ^(self new) searchForMethod: searchString replaceWith: replaceString; yourself! ! !RBStringReplaceRule class methodsFor: 'instance creation' stamp: ''! searchForMethod: searchString replaceWith: replaceString when: aBlock ^self new searchForMethod: searchString replaceWith: replaceString when: aBlock! ! !RBStringReplaceRule class methodsFor: 'instance creation' stamp: ''! searchForTree: aRBProgramNode replaceWith: replaceString ^self new searchForTree: aRBProgramNode replaceWith: replaceString! ! !RBStringReplaceRule class methodsFor: 'instance creation' stamp: ''! searchForTree: aRBProgramNode replaceWith: replaceString when: aBlock ^self new searchForTree: aRBProgramNode replaceWith: replaceString when: aBlock! ! !RBStringReplaceRule methodsFor: 'matching' stamp: ''! foundMatchFor: aProgramNode | newNode | newNode := replaceTree copyInContext: self context. aProgramNode replaceMethodSource: newNode. newNode copyCommentsFrom: aProgramNode. ^newNode! ! !RBStringReplaceRule methodsFor: 'initialize-release' stamp: ''! methodReplaceString: replaceString replaceTree := RBParser parseRewriteMethod: replaceString! ! !RBStringReplaceRule methodsFor: 'initialize-release' stamp: ''! replaceString: replaceString replaceTree := RBParser parseRewriteExpression: replaceString. searchTree isSequence = replaceTree isSequence ifFalse: [searchTree isSequence ifTrue: [replaceTree := RBSequenceNode statements: (Array with: replaceTree)] ifFalse: [searchTree := RBSequenceNode statements: (Array with: searchTree)]]! ! !RBStringReplaceRule methodsFor: 'initialize-release' stamp: ''! searchFor: searchString replaceWith: replaceString self searchString: searchString. self replaceString: replaceString! ! !RBStringReplaceRule methodsFor: 'initialize-release' stamp: ''! searchFor: searchString replaceWith: replaceString when: aBlock self searchFor: searchString replaceWith: replaceString. verificationBlock := aBlock! ! !RBStringReplaceRule methodsFor: 'initialize-release' stamp: ''! searchForMethod: searchString replaceWith: replaceString self methodSearchString: searchString. self methodReplaceString: replaceString! ! !RBStringReplaceRule methodsFor: 'initialize-release' stamp: ''! searchForMethod: searchString replaceWith: replaceString when: aBlock self searchForMethod: searchString replaceWith: replaceString. verificationBlock := aBlock! ! !RBStringReplaceRule methodsFor: 'initialize-release' stamp: ''! searchForTree: aBRProgramNode replaceWith: replaceNode searchTree := aBRProgramNode. replaceTree := replaceNode! ! !RBStringReplaceRule methodsFor: 'initialize-release' stamp: ''! searchForTree: aBRProgramNode replaceWith: replaceString when: aBlock self searchForTree: aBRProgramNode replaceWith: replaceString. verificationBlock := aBlock! ! RBParseTreeRule subclass: #RBSearchRule instanceVariableNames: 'answerBlock' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Matching'! !RBSearchRule commentStamp: 'md 8/9/2005 14:56' prior: 0! RBSearchRule is a parse tree rule that simply searches for matches to the rule. Every time a match is found, answerBlock is evaluated with the node that matches and the cureent answer. This two-argument approach allows a collection to be formed from all of the matches (Think inject:into:). Instance Variables: answerBlock Block to evaluate with the matching node and the current answer. ! !RBSearchRule class methodsFor: 'instance creation' stamp: ''! searchFor: aString thenDo: aBlock ^self new searchFor: aString thenDo: aBlock! ! !RBSearchRule class methodsFor: 'instance creation' stamp: ''! searchForMethod: aString thenDo: aBlock ^self new searchForMethod: aString thenDo: aBlock! ! !RBSearchRule class methodsFor: 'instance creation' stamp: ''! searchForTree: aBRProgramNode thenDo: aBlock ^self new searchForTree: aBRProgramNode thenDo: aBlock! ! !RBSearchRule methodsFor: 'testing' stamp: ''! canMatch: aProgramNode owner answer: (answerBlock value: aProgramNode value: owner answer). ^true! ! !RBSearchRule methodsFor: 'initialize-release' stamp: ''! searchFor: aString thenDo: aBlock self searchString: aString. answerBlock := aBlock! ! !RBSearchRule methodsFor: 'initialize-release' stamp: ''! searchForMethod: aString thenDo: aBlock self methodSearchString: aString. answerBlock := aBlock! ! !RBSearchRule methodsFor: 'initialize-release' stamp: ''! searchForTree: aBRProgramNode thenDo: aBlock searchTree := aBRProgramNode. answerBlock := aBlock! ! Object subclass: #RBParser instanceVariableNames: 'scanner currentToken nextToken errorBlock source comments pragmas' classVariableNames: 'ParserType' poolDictionaries: '' category: 'AST-Core-Parser'! !RBParser commentStamp: '' prior: 0! RBParser takes a source code string and generates an AST for it. This is a hand-written, recursive descent parser and has been optimized for speed. The simplest way to call this is either 'RBParser parseExpression: aString' if you want the AST for an expression, or 'RBParser parseMethod: aString' if you want to parse an entire method. Instance Variables: currentToken The current token being processed. emptyStatements True if empty statements are allowed. In IBM, they are, in VW they aren't. errorBlock The block to evaluate on a syntax error. nextToken The next token that will be processed. This allows one-token lookahead. scanner The scanner that generates a stream of tokens to parse. source The source code to parse tags The source intervals of the tags appearing at the top of a method (e.g. Primitive calls) Shared Variables: ParserType the type code we are parsing! !RBParser class methodsFor: 'accessing' stamp: ''! parseExpression: aString ^self parseExpression: aString onError: nil! ! !RBParser class methodsFor: 'accessing' stamp: ''! parseExpression: aString onError: aBlock | node parser | parser := self new. parser errorBlock: aBlock. parser initializeParserWith: aString. node := parser parseExpression: aString. ^(node statements size == 1 and: [node temporaries isEmpty]) ifTrue: [node statements first] ifFalse: [node]! ! !RBParser class methodsFor: 'accessing' stamp: ''! parseMethod: aString ^self parseMethod: aString onError: nil! ! !RBParser class methodsFor: 'accessing' stamp: ''! parseMethod: aString onError: aBlock | parser | parser := self new. parser errorBlock: aBlock. parser initializeParserWith: aString. ^parser parseMethod: aString! ! !RBParser class methodsFor: 'parsing' stamp: ''! parseMethodPattern: aString | parser | parser := self new. parser errorBlock: [:error :position | ^nil]. parser initializeParserWith: aString. ^parser parseMessagePattern selector! ! !RBParser class methodsFor: 'accessing' stamp: ''! parseRewriteExpression: aString ^self parseRewriteExpression: aString onError: nil! ! !RBParser class methodsFor: 'accessing' stamp: ''! parseRewriteExpression: aString onError: aBlock ^RBPatternParser parseExpression: aString onError: aBlock! ! !RBParser class methodsFor: 'accessing' stamp: ''! parseRewriteMethod: aString ^self parseRewriteMethod: aString onError: nil! ! !RBParser class methodsFor: 'accessing' stamp: ''! parseRewriteMethod: aString onError: aBlock ^RBPatternParser parseMethod: aString onError: aBlock! ! !RBParser methodsFor: 'private' stamp: ''! addCommentsTo: aNode aNode comments: aNode comments , comments. comments := OrderedCollection new! ! !RBParser methodsFor: 'private-classes' stamp: 'lr 11/1/2009 19:49'! arrayNodeClass ^ RBArrayNode! ! !RBParser methodsFor: 'private-classes' stamp: 'lr 11/1/2009 19:49'! assignmentNodeClass ^ RBAssignmentNode! ! !RBParser methodsFor: 'testing' stamp: ''! atEnd ^currentToken class == RBToken! ! !RBParser methodsFor: 'private-classes' stamp: 'lr 11/1/2009 19:49'! blockNodeClass ^ RBBlockNode! ! !RBParser methodsFor: 'private-classes' stamp: 'lr 11/1/2009 19:49'! cascadeNodeClass ^ RBCascadeNode! ! !RBParser methodsFor: 'error handling' stamp: ''! errorBlock ^errorBlock isNil ifTrue: [[:message :position | ]] ifFalse: [errorBlock]! ! !RBParser methodsFor: 'accessing' stamp: ''! errorBlock: aBlock errorBlock := aBlock. scanner notNil ifTrue: [scanner errorBlock: aBlock]! ! !RBParser methodsFor: 'error handling' stamp: ''! errorPosition ^currentToken start! ! !RBParser methodsFor: 'initialize-release' stamp: 'lr 11/1/2009 19:17'! initialize comments := OrderedCollection new! ! !RBParser methodsFor: 'accessing' stamp: ''! initializeParserWith: aString source := aString. self scanner: (self scannerClass on: (ReadStream on: aString) errorBlock: self errorBlock)! ! !RBParser methodsFor: 'private-classes' stamp: 'lr 11/1/2009 19:50'! literalArrayNodeClass ^ RBLiteralArrayNode! ! !RBParser methodsFor: 'private-classes' stamp: 'lr 11/1/2009 19:50'! literalNodeClass ^ RBLiteralNode! ! !RBParser methodsFor: 'private-classes' stamp: 'lr 11/1/2009 19:50'! messageNodeClass ^ RBMessageNode! ! !RBParser methodsFor: 'private-classes' stamp: 'lr 11/1/2009 19:50'! methodNodeClass ^ RBMethodNode! ! !RBParser methodsFor: 'private' stamp: ''! nextToken ^nextToken isNil ifTrue: [nextToken := scanner next] ifFalse: [nextToken]! ! !RBParser methodsFor: 'private-parsing' stamp: ''! parseArgs | args | args := OrderedCollection new. [currentToken isIdentifier] whileTrue: [args add: self parseVariableNode]. ^args! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 11/2/2009 23:37'! parseArray | position node | position := currentToken start. self step. node := self arrayNodeClass new. node left: position. self parseStatementList: false into: node. (currentToken isSpecial and: [currentToken value = $}]) ifFalse: [self parserError: 'expected }']. node right: currentToken start. self step. ^ node! ! !RBParser methodsFor: 'private-parsing' stamp: ''! parseAssignment "Need one token lookahead to see if we have a ':='. This method could make it possible to assign the literals true, false and nil." | node position | (currentToken isIdentifier and: [self nextToken isAssignment]) ifFalse: [^self parseCascadeMessage]. node := self parseVariableNode. position := currentToken start. self step. ^self assignmentNodeClass variable: node value: self parseAssignment position: position! ! !RBParser methodsFor: 'private-parsing' stamp: ''! parseBinaryMessage | node | node := self parseUnaryMessage. [currentToken isLiteralToken ifTrue: [self patchNegativeLiteral]. currentToken isBinary] whileTrue: [node := self parseBinaryMessageWith: node]. ^node! ! !RBParser methodsFor: 'private-parsing' stamp: ''! parseBinaryMessageWith: aNode | binaryToken | binaryToken := currentToken. self step. ^self messageNodeClass receiver: aNode selectorParts: (Array with: binaryToken) arguments: (Array with: self parseUnaryMessage)! ! !RBParser methodsFor: 'private-parsing' stamp: ''! parseBinaryPattern | binaryToken node args | currentToken isBinary ifFalse: [self parserError: 'Message pattern expected']. binaryToken := currentToken. self step. args := Array with: self parseVariableNode. node := self methodNodeClass selectorParts: (Array with: binaryToken) arguments: args. node comments: node comments , args last comments. args last comments: nil. ^node! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 11/1/2009 19:55'! parseBinaryPragma | binaryToken | currentToken isBinary ifFalse: [ self parserError: 'Message pattern expected' ]. binaryToken := currentToken. self step. ^ RBPragmaNode selectorParts: (Array with: binaryToken) arguments: (Array with: self parseLiteralArrayObject)! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 11/2/2009 23:37'! parseBlock | position node | position := currentToken start. self step. node := self blockNodeClass new. self parseBlockArgsInto: node. node left: position. node body: (self parseStatements: false). (currentToken isSpecial and: [currentToken value = $]]) ifFalse: [self parserError: ''']'' expected']. node right: currentToken start. self step. ^node! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 11/2/2009 23:37'! parseBlockArgsInto: node | verticalBar args colons | args := OrderedCollection new: 2. colons := OrderedCollection new: 2. verticalBar := false. [currentToken isSpecial and: [currentToken value = $:]] whileTrue: [colons add: currentToken start. self step. ":" verticalBar := true. args add: self parseVariableNode]. verticalBar ifTrue: [currentToken isBinary ifTrue: [node bar: currentToken start. currentToken value = #| ifTrue: [self step] ifFalse: [currentToken value = #'||' ifTrue: ["Hack the current token to be the start of temps bar" currentToken value: #|; start: currentToken start + 1] ifFalse: [self parserError: '''|'' expected']]] ifFalse: [(currentToken isSpecial and: [currentToken value = $]]) ifFalse: [self parserError: '''|'' expected']]]. node arguments: args; colons: colons. ^node! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 11/2/2009 23:37'! parseCascadeMessage | node receiver messages semicolons | node := self parseKeywordMessage. (currentToken isSpecial and: [currentToken value = $; and: [node isMessage]]) ifFalse: [^node]. receiver := node receiver. messages := OrderedCollection new: 3. semicolons := OrderedCollection new: 3. messages add: node. [currentToken isSpecial and: [currentToken value = $;]] whileTrue: [semicolons add: currentToken start. self step. messages add: (currentToken isIdentifier ifTrue: [self parseUnaryMessageWith: receiver] ifFalse: [currentToken isKeyword ifTrue: [self parseKeywordMessageWith: receiver] ifFalse: [| temp | currentToken isLiteralToken ifTrue: [self patchNegativeLiteral]. currentToken isBinary ifFalse: [self parserError: 'Message expected']. temp := self parseBinaryMessageWith: receiver. temp == receiver ifTrue: [self parserError: 'Message expected']. temp]])]. ^self cascadeNodeClass messages: messages semicolons: semicolons! ! !RBParser methodsFor: 'accessing' stamp: ''! parseExpression: aString | node | node := self parseStatements: false. (RBMethodNode selector: #noMethod body: node) source: aString. "Make the sequence node have a method node as its parent" self atEnd ifFalse: [self parserError: 'Unknown input at end']. ^node! ! !RBParser methodsFor: 'private-parsing' stamp: ''! parseKeywordMessage ^self parseKeywordMessageWith: self parseBinaryMessage! ! !RBParser methodsFor: 'private-parsing' stamp: ''! parseKeywordMessageWith: node | args isKeyword keywords | args := OrderedCollection new: 3. keywords := OrderedCollection new: 3. isKeyword := false. [currentToken isKeyword] whileTrue: [keywords add: currentToken. self step. args add: self parseBinaryMessage. isKeyword := true]. ^isKeyword ifTrue: [self messageNodeClass receiver: node selectorParts: keywords arguments: args] ifFalse: [node]! ! !RBParser methodsFor: 'private-parsing' stamp: ''! parseKeywordPattern | keywords args node | keywords := OrderedCollection new: 2. args := OrderedCollection new: 2. [currentToken isKeyword] whileTrue: [keywords add: currentToken. self step. args add: self parseVariableNode]. node := self methodNodeClass selectorParts: keywords arguments: args. node comments: (node comments, args last comments). args last comments: nil. ^node! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 11/1/2009 19:55'! parseKeywordPragma | keywords arguments | keywords := OrderedCollection new: 2. arguments := OrderedCollection new: 2. [ currentToken isKeyword ] whileTrue: [ keywords addLast: currentToken. self step. arguments addLast: self parseLiteralArrayObject ]. ^ RBPragmaNode selectorParts: keywords arguments: arguments! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 11/2/2009 23:37'! parseLiteralArray | stream start stop | start := currentToken start. stream := WriteStream on: (Array new: 5). self step. [self atEnd or: [currentToken isSpecial and: [currentToken value = $)]]] whileFalse: [stream nextPut: self parseLiteralArrayObject]. (currentToken isSpecial and: [currentToken value = $)]) ifFalse: [self parserError: ''')'' expected']. stop := currentToken stop. self step. ^self literalArrayNodeClass startPosition: start contents: stream contents stopPosition: stop isByteArray: false! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 11/2/2009 23:37'! parseLiteralArrayObject currentToken isSpecial ifTrue: [currentToken value = $( ifTrue: [^self parseLiteralArray]. "currentToken value == $[ ifTrue: [^self parseLiteralByteArray]"]. currentToken isLiteralArrayToken ifTrue: [^currentToken isForByteArray ifTrue: [self parseLiteralByteArray] ifFalse: [self parseLiteralArray]]. currentToken isLiteralToken ifFalse: [self patchLiteralArrayToken]. ^self parsePrimitiveLiteral! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 11/2/2009 23:37'! parseLiteralByteArray | stream start stop | start := currentToken start. stream := WriteStream on: (Array new: 5). self step. [self atEnd or: [currentToken isSpecial and: [currentToken value = $]]]] whileFalse: [stream nextPut: self parseLiteralByteArrayObject]. (currentToken isSpecial and: [currentToken value = $]]) ifFalse: [self parserError: ''']'' expected']. stop := currentToken stop. self step. ^self literalArrayNodeClass startPosition: start contents: stream contents stopPosition: stop isByteArray: true! ! !RBParser methodsFor: 'private-parsing' stamp: ''! parseLiteralByteArrayObject (currentToken isLiteralToken and: [currentToken value isInteger and: [currentToken value between: 0 and: 255]]) ifFalse: [self parserError: 'Expecting 8-bit integer']. ^self parsePrimitiveLiteral! ! !RBParser methodsFor: 'private-parsing' stamp: ''! parseMessagePattern currentToken isLiteralToken ifTrue: [self patchLiteralMessage]. ^currentToken isIdentifier ifTrue: [self parseUnaryPattern] ifFalse: [currentToken isKeyword ifTrue: [self parseKeywordPattern] ifFalse: [self parseBinaryPattern]]! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 11/1/2009 19:36'! parseMethod | methodNode | methodNode := self parseMessagePattern. self parsePragmas. self addCommentsTo: methodNode. methodNode body: (self parseStatements: true). pragmas isNil ifFalse: [ methodNode pragmas: pragmas ]. ^methodNode! ! !RBParser methodsFor: 'accessing' stamp: ''! parseMethod: aString | node | node := self parseMethod. self atEnd ifFalse: [self parserError: 'Unknown input at end']. node source: aString. ^node! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 11/1/2009 22:19'! parseNegatedNumber | token | (self nextToken isLiteral not or: [ self nextToken realValue isNumber not ]) ifTrue: [ self parserError: 'only numbers may be negated' ]. token := RBLiteralToken value: self nextToken realValue negated start: currentToken start stop: nextToken stop. self step; step. ^ self literalNodeClass literalToken: token ! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 11/2/2009 23:37'! parseParenthesizedExpression | leftParen node | leftParen := currentToken start. self step. node := self parseAssignment. ^(currentToken isSpecial and: [currentToken value = $)]) ifTrue: [node addParenthesis: (leftParen to: currentToken start). self step. node] ifFalse: [self parserError: ''')'' expected']! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 10/18/2009 12:17'! parsePragma ^ currentToken isIdentifier ifTrue: [ self parseUnaryPragma ] ifFalse: [ currentToken isKeyword ifTrue: [ self parseKeywordPragma ] ifFalse: [ self parseBinaryPragma ] ]! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 11/2/2009 23:37'! parsePragmas | pragma start | [ currentToken isBinary and: [ currentToken value = #< ] ] whileTrue: [ start := currentToken start. self step. pragma := self parsePragma. (currentToken isBinary and: [ currentToken value = #> ]) ifFalse: [ self parserError: '''>'' expected' ]. pragma brackets: (start to: currentToken start). pragmas isNil ifTrue: [ pragmas := OrderedCollection new ]. pragmas addLast: pragma. self step ]! ! !RBParser methodsFor: 'private-parsing' stamp: ''! parsePrimitiveIdentifier | token node | token := currentToken. self step. node := self variableNodeClass identifierToken: token. self addCommentsTo: node. ^node! ! !RBParser methodsFor: 'private-parsing' stamp: ''! parsePrimitiveLiteral | token | token := currentToken. self step. ^self literalNodeClass literalToken: token! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 11/2/2009 23:37'! parsePrimitiveObject currentToken isIdentifier ifTrue: [^self parsePrimitiveIdentifier]. (currentToken isLiteralToken and: [currentToken isMultiKeyword not]) ifTrue: [^self parsePrimitiveLiteral]. currentToken isLiteralArrayToken ifTrue: [^currentToken isForByteArray ifTrue: [self parseLiteralByteArray] ifFalse: [self parseLiteralArray]]. currentToken isSpecial ifTrue: [currentToken value = $[ ifTrue: [^self parseBlock]. currentToken value = $( ifTrue: [^self parseParenthesizedExpression]. currentToken value = ${ ifTrue: [^self parseArray]]. (currentToken isBinary and: [ currentToken value = #- ]) ifTrue: [ ^self parseNegatedNumber ]. self parserError: 'Variable expected'! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 11/2/2009 23:37'! parseStatementList: pragmaBoolean into: sequenceNode | statements return periods returnPosition node | return := false. statements := OrderedCollection new. periods := OrderedCollection new. self addCommentsTo: sequenceNode. pragmaBoolean ifTrue: [self parsePragmas]. [currentToken isSpecial and: [currentToken value = $.]] whileTrue: [periods add: currentToken start. self step]. [self atEnd or: [currentToken isSpecial and: ['])}' includes: currentToken value]]] whileFalse: [return ifTrue: [self parserError: 'End of statement list encounted']. (currentToken isSpecial and: [currentToken value = $^]) ifTrue: [returnPosition := currentToken start. self step. node := self returnNodeClass return: returnPosition value: self parseAssignment. statements add: node. return := true] ifFalse: [node := self parseAssignment. statements add: node]. (currentToken isSpecial and: [currentToken value = $.]) ifTrue: [periods add: currentToken start. self step. self addCommentsTo: node] ifFalse: [return := true]. [currentToken isSpecial and: [currentToken value = $.]] whileTrue: [periods add: currentToken start. self step]]. statements notEmpty ifTrue: [self addCommentsTo: statements last]. sequenceNode statements: statements; periods: periods. ^sequenceNode! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 11/2/2009 23:37'! parseStatements: pragmaBoolean | args leftBar rightBar | args := #(). leftBar := rightBar := nil. currentToken isBinary ifTrue: [currentToken value = #| ifTrue: [leftBar := currentToken start. self step. args := self parseArgs. (currentToken isBinary and: [currentToken value = #|]) ifFalse: [self parserError: '''|'' expected']. rightBar := currentToken start. self step] ifFalse: [currentToken value = #'||' ifTrue: [rightBar := (leftBar := currentToken start) + 1. self step]]]. ^self parseStatementList: pragmaBoolean into: (self sequenceNodeClass leftBar: leftBar temporaries: args rightBar: rightBar)! ! !RBParser methodsFor: 'private-parsing' stamp: ''! parseUnaryMessage | node | node := self parsePrimitiveObject. self addCommentsTo: node. [currentToken isLiteralToken ifTrue: [self patchLiteralMessage]. currentToken isIdentifier] whileTrue: [node := self parseUnaryMessageWith: node]. self addCommentsTo: node. ^node! ! !RBParser methodsFor: 'private-parsing' stamp: ''! parseUnaryMessageWith: aNode | selector | selector := currentToken. self step. ^self messageNodeClass receiver: aNode selectorParts: (Array with: selector) arguments: #()! ! !RBParser methodsFor: 'private-parsing' stamp: ''! parseUnaryPattern | selector | selector := currentToken. self step. ^self methodNodeClass selectorParts: (Array with: selector) arguments: #()! ! !RBParser methodsFor: 'private-parsing' stamp: 'lr 10/18/2009 12:17'! parseUnaryPragma | selector | selector := currentToken. self step. ^ RBPragmaNode selectorParts: (Array with: selector) arguments: #()! ! !RBParser methodsFor: 'private-parsing' stamp: ''! parseVariableNode currentToken isIdentifier ifFalse: [self parserError: 'Variable name expected']. ^self parsePrimitiveIdentifier! ! !RBParser methodsFor: 'error handling' stamp: ''! parserError: aString "Evaluate the block. If it returns raise an error" self errorBlock value: aString value: self errorPosition. self error: aString! ! !RBParser methodsFor: 'private' stamp: 'lr 11/1/2009 20:03'! patchLiteralArrayToken (currentToken isIdentifier and: [self nextToken isAssignment and: [currentToken stop + 1 = self nextToken start]]) ifTrue: [currentToken := RBLiteralToken value: (currentToken value , ':') asSymbol start: currentToken start stop: self nextToken start. nextToken := RBLiteralToken value: #= start: nextToken stop stop: nextToken stop. ^self]. currentToken isAssignment ifTrue: [currentToken := RBLiteralToken value: #':' start: currentToken start stop: currentToken start. nextToken := RBLiteralToken value: #= start: currentToken stop stop: currentToken stop. ^self]. currentToken isSpecial ifTrue: [currentToken := RBLiteralToken value: (String with: currentToken value) asSymbol start: currentToken start stop: currentToken stop. ^self]. (currentToken isIdentifier and: [currentToken value includes: $.]) ifTrue: [currentToken := RBLiteralToken value: currentToken value start: currentToken start stop: currentToken stop. ^self]. (currentToken isIdentifier or: [currentToken isBinary or: [currentToken isKeyword]]) ifFalse: [^self parserError: 'Invalid token']. currentToken := RBLiteralToken value: currentToken value asSymbol start: currentToken start stop: currentToken stop! ! !RBParser methodsFor: 'private' stamp: ''! patchLiteralMessage currentToken value == true ifTrue: [^currentToken := RBIdentifierToken value: 'true' start: currentToken start]. currentToken value == false ifTrue: [^currentToken := RBIdentifierToken value: 'false' start: currentToken start]. currentToken value == nil ifTrue: [^currentToken := RBIdentifierToken value: 'nil' start: currentToken start]! ! !RBParser methodsFor: 'private' stamp: 'lr 11/2/2009 23:37'! patchNegativeLiteral "Handle the special negative number case for binary message sends." currentToken value isNumber ifFalse: [^self]. currentToken value <= 0 ifFalse: [^self]. currentToken value = 0 ifTrue: [(source notNil and: [source notEmpty and: [(source at: (currentToken start min: source size)) = $-]]) ifFalse: [^self]]. nextToken := currentToken. currentToken := RBBinarySelectorToken value: #- start: nextToken start. nextToken value: nextToken value negated. (nextToken isKindOf: RBNumberLiteralToken) ifTrue: [nextToken source: (nextToken source copyFrom: 2 to: nextToken source size)]. nextToken start: nextToken start + 1! ! !RBParser methodsFor: 'private-classes' stamp: 'lr 11/1/2009 19:50'! returnNodeClass ^ RBReturnNode! ! !RBParser methodsFor: 'initialize-release' stamp: 'lr 11/1/2009 19:35'! scanner: aScanner scanner := aScanner. pragmas := nil. self initialize. self step! ! !RBParser methodsFor: 'accessing' stamp: ''! scannerClass ^RBScanner! ! !RBParser methodsFor: 'private-classes' stamp: 'lr 11/1/2009 19:50'! sequenceNodeClass ^ RBSequenceNode! ! !RBParser methodsFor: 'private' stamp: ''! step (currentToken notNil and: [currentToken comments notNil]) ifTrue: [comments addAll: currentToken comments]. nextToken notNil ifTrue: [currentToken := nextToken. nextToken := nil] ifFalse: [currentToken := scanner next]! ! !RBParser methodsFor: 'private-classes' stamp: 'lr 11/1/2009 19:50'! variableNodeClass ^ RBVariableNode! ! RBParser subclass: #RBPatternParser instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Parser'! !RBPatternParser commentStamp: '' prior: 0! RBPatternParser is a subclass of RBParser that allows the extended syntax that creates matching trees. These trees can be used by the ParseTreeMatcher to search and transform source code. ! !RBPatternParser methodsFor: 'private-classes' stamp: ''! messageNodeClass ^RBPatternMessageNode! ! !RBPatternParser methodsFor: 'private-classes' stamp: ''! methodNodeClass ^RBPatternMethodNode! ! !RBPatternParser methodsFor: 'private-parsing' stamp: ''! parseLiteralByteArrayObject | node | (currentToken isIdentifier and: [currentToken isPatternVariable]) ifTrue: [node := self variableNodeClass identifierToken: currentToken. node isLiteralNode ifTrue: [self step. ^node]]. ^super parseLiteralByteArrayObject! ! !RBPatternParser methodsFor: 'private-parsing' stamp: 'lr 11/2/2009 23:37'! parsePatternBlock: aClass | position node | position := currentToken start. self step. node := self parseBlockArgsInto: aClass new. node left: position. node body: (self parseStatements: false). (currentToken isSpecial and: [currentToken value = $}]) ifFalse: [self parserError: '''}'' expected']. node right: currentToken start. self step. ^node! ! !RBPatternParser methodsFor: 'private-parsing' stamp: ''! parsePrimitiveLiteral | node | (currentToken isIdentifier and: [currentToken isPatternVariable]) ifTrue: [node := self variableNodeClass identifierToken: currentToken. node isLiteralNode ifTrue: [self step. ^node]. currentToken := RBLiteralToken value: currentToken value asSymbol start: currentToken start stop: currentToken stop]. ^super parsePrimitiveLiteral! ! !RBPatternParser methodsFor: 'private-parsing' stamp: ''! parsePrimitiveObject currentToken isPatternBlock ifTrue: [^self parsePatternBlock: RBPatternBlockNode]. ^super parsePrimitiveObject! ! !RBPatternParser methodsFor: 'private-parsing' stamp: ''! parseUnaryMessage | node | node := self parsePrimitiveObject. self addCommentsTo: node. [currentToken isLiteralToken ifTrue: [self patchLiteralMessage]. currentToken isPatternBlock ifTrue: [node := (self parsePatternBlock: RBPatternWrapperBlockNode) wrappedNode: node; yourself]. currentToken isIdentifier] whileTrue: [node := self parseUnaryMessageWith: node]. self addCommentsTo: node. ^node! ! !RBPatternParser methodsFor: 'private' stamp: ''! patchLiteralArrayToken (currentToken isIdentifier and: [currentToken isPatternVariable]) ifTrue: [^self]. super patchLiteralArrayToken! ! !RBPatternParser methodsFor: 'accessing' stamp: ''! scannerClass ^RBPatternScanner! ! !RBPatternParser methodsFor: 'private-classes' stamp: ''! variableNodeClass ^RBPatternVariableNode! ! Object subclass: #RBProgramNode instanceVariableNames: 'parent comments properties' classVariableNames: 'FormatterClass' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBProgramNode commentStamp: '' prior: 0! RBProgramNode is an abstract class that represents an abstract syntax tree node in a Smalltalk program. Subclasses must implement the following messages: accessing start stop visitor acceptVisitor: The #start and #stop methods are used to find the source that corresponds to this node. "source copyFrom: self start to: self stop" should return the source for this node. The #acceptVisitor: method is used by RBProgramNodeVisitors (the visitor pattern). This will also require updating all the RBProgramNodeVisitors so that they know of the new node. Subclasses might also want to redefine match:inContext: and copyInContext: to do parse tree searching and replacing. Subclasses that contain other nodes should override equalTo:withMapping: to compare nodes while ignoring renaming temporary variables, and children that returns a collection of our children nodes. Instance Variables: comments the intervals in the source that have comments for this node parent the node we're contained in Shared Variables: FormatterClass the formatter class that is used when we are formatted! RBProgramNode subclass: #RBMethodNode instanceVariableNames: 'selector selectorParts body source arguments pragmas replacements nodeReplacements' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBMethodNode commentStamp: '' prior: 0! RBMethodNode is the AST that represents a Smalltalk method. Instance Variables: arguments the arguments to the method body the body/statements of the method nodeReplacements a dictionary of oldNode -> newNode replacements replacements the collection of string replacements for each node replacement in the parse tree selector the method name (cached) selectorParts the tokens for the selector keywords source the source we compiled tags the source location of any resource/primitive tags ! !RBMethodNode class methodsFor: 'instance creation' stamp: ''! new ^(super new) initialize; yourself! ! !RBMethodNode class methodsFor: 'instance creation' stamp: ''! selector: aSymbol arguments: variableNodes body: aSequenceNode ^(self new) arguments: variableNodes; selector: aSymbol; body: aSequenceNode; yourself! ! !RBMethodNode class methodsFor: 'instance creation' stamp: ''! selector: aSymbol body: aSequenceNode ^self selector: aSymbol arguments: #() body: aSequenceNode! ! !RBMethodNode class methodsFor: 'instance creation' stamp: ''! selectorParts: tokenCollection arguments: variableNodes ^(self new) selectorParts: tokenCollection arguments: variableNodes; yourself! ! !RBMethodNode methodsFor: 'comparing' stamp: 'lr 10/18/2009 16:03'! = anObject self == anObject ifTrue: [ ^ true ]. self class = anObject class ifFalse: [ ^ false ]. (self selector = anObject selector and: [ self pragmas size = anObject pragmas size and: [ self body = anObject body ] ]) ifFalse: [ ^ false ]. self arguments with: anObject arguments do: [ :first :second | first = second ifFalse: [ ^ false ] ]. self pragmas with: anObject pragmas do: [ :first :second | first = second ifFalse: [ ^ false ] ]. ^ true! ! !RBMethodNode methodsFor: 'visitor' stamp: ''! acceptVisitor: aProgramNodeVisitor ^aProgramNodeVisitor acceptMethodNode: self! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! addNode: aNode ^body addNode: aNode! ! !RBMethodNode methodsFor: 'replacing' stamp: ''! addReplacement: aStringReplacement replacements isNil ifTrue: [^self]. replacements add: aStringReplacement! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! addReturn body addReturn! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! addSelfReturn ^body addSelfReturn! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! allArgumentVariables ^(self argumentNames asOrderedCollection) addAll: super allArgumentVariables; yourself! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! allDefinedVariables ^(self argumentNames asOrderedCollection) addAll: super allDefinedVariables; yourself! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! argumentNames ^self arguments collect: [:each | each name]! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! arguments ^arguments! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! arguments: variableNodes arguments := variableNodes. arguments do: [:each | each parent: self]! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! body ^body! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! body: stmtsNode body := stmtsNode. body parent: self! ! !RBMethodNode methodsFor: 'private' stamp: ''! buildSelector | selectorStream | selectorStream := WriteStream on: (String new: 50). selectorParts do: [:each | selectorStream nextPutAll: each value]. ^selectorStream contents asSymbol! ! !RBMethodNode methodsFor: 'private-replacing' stamp: ''! changeSourceSelectors: oldSelectorParts arguments: oldArguments [oldSelectorParts size = selectorParts size ifFalse: [^self]. oldArguments size = arguments size ifFalse: [^self]. oldSelectorParts with: selectorParts do: [:old :new | self addReplacement: (RBStringReplacement replaceFrom: old start to: old stop with: new value)]. oldArguments with: arguments do: [:old :new | self addReplacement: (RBStringReplacement replaceFrom: old start to: old stop with: new value)]] on: Error do: [:ex | ex return]! ! !RBMethodNode methodsFor: 'accessing' stamp: 'lr 11/1/2009 19:57'! children ^ OrderedCollection new addAll: self arguments; addAll: self pragmas; add: self body; yourself! ! !RBMethodNode methodsFor: 'replacing' stamp: ''! clearReplacements replacements := nil! ! !RBMethodNode methodsFor: 'matching' stamp: 'lr 10/18/2009 13:50'! copyInContext: aDictionary ^ self class new selectorParts: (self selectorParts collect: [ :each | each removePositions ]); arguments: (self arguments collect: [ :each | each copyInContext: aDictionary ]); pragmas: (self pragmas collect: [ :each | each copyInContext: aDictionary ]); body: (self body copyInContext: aDictionary); source: (aDictionary at: '-source-'); yourself! ! !RBMethodNode methodsFor: 'testing' stamp: ''! defines: aName ^arguments anySatisfy: [:each | each name = aName]! ! !RBMethodNode methodsFor: 'comparing' stamp: 'lr 10/18/2009 15:44'! equalTo: anObject withMapping: aDictionary self class = anObject class ifFalse: [ ^ false ]. (self selector = anObject selector and: [ self pragmas size = anObject pragmas size and: [ self body equalTo: anObject body withMapping: aDictionary ] ]) ifFalse: [ ^ false ]. self arguments with: anObject arguments do: [ :first :second | (first equalTo: second withMapping: aDictionary) ifFalse: [ ^ false ]. aDictionary removeKey: first name ]. self pragmas with: anObject pragmas do: [ :first :second | (first equalTo: second withMapping: aDictionary) ifFalse: [ ^ false ] ]. ^ true! ! !RBMethodNode methodsFor: 'comparing' stamp: ''! hash ^(self selector hash bitXor: self body hash) bitXor: self arguments hash! ! !RBMethodNode methodsFor: 'initialize-release' stamp: ''! initialize replacements := SortedCollection sortBlock: [:a :b | a startPosition < b startPosition or: [a startPosition = b startPosition and: [a stopPosition < b stopPosition]]]. nodeReplacements := IdentityDictionary new! ! !RBMethodNode methodsFor: 'testing' stamp: ''! isLast: aNode ^body isLast: aNode! ! !RBMethodNode methodsFor: 'testing' stamp: ''! isMethod ^true! ! !RBMethodNode methodsFor: 'testing' stamp: 'lr 11/1/2009 19:37'! isPrimitive ^ self pragmas anySatisfy: [ :each | each isPrimitive ]! ! !RBMethodNode methodsFor: 'testing' stamp: ''! lastIsReturn ^body lastIsReturn! ! !RBMethodNode methodsFor: 'replacing' stamp: ''! map: oldNode to: newNode nodeReplacements at: oldNode put: newNode! ! !RBMethodNode methodsFor: 'replacing' stamp: ''! mappingFor: oldNode ^nodeReplacements at: oldNode ifAbsent: [oldNode]! ! !RBMethodNode methodsFor: 'matching' stamp: 'lr 10/18/2009 13:52'! match: aNode inContext: aDictionary self class == aNode class ifFalse: [ ^ false ]. aDictionary at: '-source-' put: aNode source. self selector == aNode selector ifFalse: [ ^ false ]. ^ (self matchList: arguments against: aNode arguments inContext: aDictionary) and: [ (self matchList: self pragmas against: aNode pragmas inContext: aDictionary) and: [ body match: aNode body inContext: aDictionary ] ]! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! methodComments | methodComments | methodComments := OrderedCollection withAll: self comments. arguments do: [:each | methodComments addAll: each comments]. ^methodComments asSortedCollection: [:a :b | a first < b first]! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! methodNode ^self! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! newSource replacements isNil ifTrue: [^self formattedCode]. ^[self reformatSource] on: Error do: [:ex | ex return: self formattedCode]! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! numArgs ^self selector numArgs! ! !RBMethodNode methodsFor: 'copying' stamp: 'lr 10/18/2009 15:40'! postCopy super postCopy. self body: self body copy. self pragmas: (self pragmas collect: [ :each | each copy ]). self arguments: (self arguments collect: [ :each | each copy ])! ! !RBMethodNode methodsFor: 'accessing' stamp: 'lr 11/1/2009 19:34'! pragmas ^ pragmas ifNil: [ #() ]! ! !RBMethodNode methodsFor: 'accessing' stamp: 'lr 11/1/2009 19:34'! pragmas: aCollection pragmas := aCollection. pragmas do: [ :each | each parent: self ]! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! primitiveSources ^self tags collect: [:each | self source copyFrom: each first to: each last]! ! !RBMethodNode methodsFor: 'printing' stamp: ''! printOn: aStream aStream nextPutAll: self formattedCode! ! !RBMethodNode methodsFor: 'testing' stamp: ''! references: aVariableName ^body references: aVariableName! ! !RBMethodNode methodsFor: 'private' stamp: ''! reformatSource | newSource stream | stream := WriteStream on: (String new: source size + 100). stream nextPutAll: (source copyFrom: (replacements inject: 1 into: [:sum :each | stream nextPutAll: (source copyFrom: sum to: each startPosition - 1); nextPutAll: each string. each stopPosition + 1]) to: source size). newSource := stream contents. self = (RBParser parseMethod: newSource onError: [:s :p | ^self formattedCode]) ifFalse: [^self formattedCode]. "Sanity check -- make sure the formatted code is = self" ^newSource! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! renameSelector: newSelector andArguments: varNodeCollection | oldSelectorParts oldArguments | oldSelectorParts := selectorParts. oldArguments := arguments. self arguments: varNodeCollection; selector: newSelector. self changeSourceSelectors: oldSelectorParts arguments: oldArguments! ! !RBMethodNode methodsFor: 'replacing' stamp: ''! replaceNode: aNode withNode: anotherNode aNode == body ifTrue: [self body: anotherNode]. self arguments: (arguments collect: [:each | each == aNode ifTrue: [anotherNode] ifFalse: [each]])! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! selector ^selector isNil ifTrue: [selector := self buildSelector] ifFalse: [selector]! ! !RBMethodNode methodsFor: 'accessing' stamp: 'lr 11/2/2009 23:37'! selector: aSelector | keywords numArgs | keywords := aSelector keywords. numArgs := aSelector numArgs. numArgs == arguments size ifFalse: [self error: 'Attempting to assign selector with wrong number of arguments.']. selectorParts := numArgs == 0 ifTrue: [Array with: (RBIdentifierToken value: keywords first start: nil)] ifFalse: [keywords first last = $: ifTrue: [keywords collect: [:each | RBKeywordToken value: each start: nil]] ifFalse: [Array with: (RBBinarySelectorToken value: aSelector start: nil)]]. selector := aSelector! ! !RBMethodNode methodsFor: 'private' stamp: ''! selectorParts ^selectorParts! ! !RBMethodNode methodsFor: 'private' stamp: ''! selectorParts: tokenCollection selectorParts := tokenCollection! ! !RBMethodNode methodsFor: 'initialize-release' stamp: ''! selectorParts: tokenCollection arguments: variableNodes selectorParts := tokenCollection. self arguments: variableNodes! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! source ^source! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! source: anObject source := anObject! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! start ^1! ! !RBMethodNode methodsFor: 'accessing' stamp: ''! stop ^source size! ! !RBMethodNode methodsFor: 'testing' stamp: ''! uses: aNode ^body == aNode and: [aNode lastIsReturn]! ! RBMethodNode subclass: #RBPatternMethodNode instanceVariableNames: 'isList' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBPatternMethodNode commentStamp: 'md 8/9/2005 14:59' prior: 0! RBPatternMethodNode is a RBMethodNode that will match other method nodes without their selectors being equal. Instance Variables: isList are we matching each keyword or matching all keywords together (e.g., `keyword1: would match a one argument method whereas `@keywords: would match 0 or more arguments) ! !RBPatternMethodNode class methodsFor: 'instance creation' stamp: ''! selectorParts: tokenCollection arguments: variableNodes ^(tokenCollection anySatisfy: [:each | each isPatternVariable]) ifTrue: [super selectorParts: tokenCollection arguments: variableNodes] ifFalse: [RBMethodNode selectorParts: tokenCollection arguments: variableNodes]! ! !RBPatternMethodNode methodsFor: 'matching' stamp: 'lr 11/2/2009 23:37'! copyInContext: aDictionary | selectors | selectors := self isSelectorList ifTrue: [(aDictionary at: selectorParts first value) keywords] ifFalse: [selectorParts collect: [:each | aDictionary at: each value]]. ^(RBMethodNode new) selectorParts: (selectors collect: [:each | (each last = $: ifTrue: [RBKeywordToken] ifFalse: [RBIdentifierToken]) value: each start: nil]); arguments: (self copyList: arguments inContext: aDictionary); body: (body copyInContext: aDictionary); source: (aDictionary at: '-source-'); yourself! ! !RBPatternMethodNode methodsFor: 'testing-matching' stamp: ''! isPatternNode ^true! ! !RBPatternMethodNode methodsFor: 'testing' stamp: ''! isSelectorList ^isList! ! !RBPatternMethodNode methodsFor: 'matching' stamp: ''! match: aNode inContext: aDictionary aNode class == self matchingClass ifFalse: [^false]. aDictionary at: '-source-' put: aNode source. self isSelectorList ifTrue: [^(aDictionary at: selectorParts first value ifAbsentPut: [aNode selector]) = aNode selector and: [(aDictionary at: arguments first ifAbsentPut: [aNode arguments]) = aNode arguments and: [body match: aNode body inContext: aDictionary]]]. ^(self matchArgumentsAgainst: aNode inContext: aDictionary) and: [body match: aNode body inContext: aDictionary]! ! !RBPatternMethodNode methodsFor: 'matching' stamp: ''! matchArgumentsAgainst: aNode inContext: aDictionary self arguments size == aNode arguments size ifFalse: [^false]. (self matchSelectorAgainst: aNode inContext: aDictionary) ifFalse: [^false]. 1 to: arguments size do: [:i | ((arguments at: i) match: (aNode arguments at: i) inContext: aDictionary) ifFalse: [^false]]. ^true! ! !RBPatternMethodNode methodsFor: 'matching' stamp: ''! matchSelectorAgainst: aNode inContext: aDictionary | keyword | 1 to: selectorParts size do: [:i | keyword := selectorParts at: i. (aDictionary at: keyword value ifAbsentPut: [keyword isPatternVariable ifTrue: [(aNode selectorParts at: i) value] ifFalse: [keyword value]]) = (aNode selectorParts at: i) value ifFalse: [^false]]. ^true! ! !RBPatternMethodNode methodsFor: 'private' stamp: ''! matchingClass ^RBMethodNode! ! !RBPatternMethodNode methodsFor: 'initialize-release' stamp: ''! selectorParts: tokenCollection arguments: variableNodes super selectorParts: tokenCollection arguments: variableNodes. isList := (tokenCollection first value at: 2) == self listCharacter! ! RBProgramNode subclass: #RBPragmaNode instanceVariableNames: 'selector selectorParts arguments brackets' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBPragmaNode class methodsFor: 'instance creation' stamp: 'lr 10/13/2009 14:21'! selectorParts: keywordTokens arguments: valueNodes ^ self new selectorParts: keywordTokens arguments: valueNodes ! ! !RBPragmaNode methodsFor: 'comparing' stamp: 'lr 10/18/2009 15:49'! = anObject self == anObject ifTrue: [ ^ true ]. self class = anObject class ifFalse: [ ^ false ]. self selector = anObject selector ifFalse: [ ^ false ]. self arguments with: anObject arguments do: [ :first :second | first = second ifFalse: [ ^ false ] ]. ^ true! ! !RBPragmaNode methodsFor: 'visitor' stamp: 'lr 10/13/2009 14:01'! acceptVisitor: aProgramNodeVisitor ^ aProgramNodeVisitor acceptPragmaNode: self! ! !RBPragmaNode methodsFor: 'accessing' stamp: 'lr 10/18/2009 12:14'! arguments ^ arguments ifNil: [ #() ]! ! !RBPragmaNode methodsFor: 'accessing' stamp: 'lr 10/13/2009 14:01'! arguments: aLiteralCollection arguments := aLiteralCollection. arguments do: [ :each | each parent: self ]! ! !RBPragmaNode methodsFor: 'accessing' stamp: 'lr 10/27/2009 11:56'! brackets ^ brackets! ! !RBPragmaNode methodsFor: 'accessing' stamp: 'lr 10/27/2009 11:56'! brackets: anInterval brackets := anInterval! ! !RBPragmaNode methodsFor: 'private' stamp: 'lr 10/13/2009 13:54'! buildSelector | selectorStream | selectorStream := WriteStream on: (String new: 50). selectorParts do: [:each | selectorStream nextPutAll: each value]. ^selectorStream contents asSymbol! ! !RBPragmaNode methodsFor: 'accessing' stamp: 'lr 10/27/2009 11:57'! children ^ self arguments! ! !RBPragmaNode methodsFor: 'matching' stamp: 'lr 10/13/2009 13:58'! copyInContext: aDictionary ^ self class new selectorParts: (selectorParts collect: [ :each | each removePositions ]); arguments: (arguments collect: [ :each | each copyInContext: aDictionary ]); yourself! ! !RBPragmaNode methodsFor: 'comparing' stamp: 'lr 10/18/2009 15:43'! equalTo: anObject withMapping: aDictionary self class = anObject class ifFalse: [ ^ false ]. self selector = anObject selector ifFalse: [ ^ false ]. self arguments with: anObject arguments do: [ :first :second | (first equalTo: second withMapping: aDictionary) ifFalse: [ ^ false ] ]. ^ true! ! !RBPragmaNode methodsFor: 'comparing' stamp: 'lr 10/13/2009 13:57'! hash ^ self selector hash bitXor: self arguments hash! ! !RBPragmaNode methodsFor: 'testing' stamp: 'lr 10/13/2009 14:00'! isBinary ^ (self isUnary or: [self isKeyword]) not! ! !RBPragmaNode methodsFor: 'testing' stamp: 'lr 11/2/2009 23:37'! isKeyword ^ selectorParts first value last = $:! ! !RBPragmaNode methodsFor: 'testing' stamp: 'lr 10/13/2009 14:00'! isPragma ^ true! ! !RBPragmaNode methodsFor: 'testing' stamp: 'lr 10/18/2009 12:26'! isPrimitive ^ #(primitive: primitive:module:) includes: self selector! ! !RBPragmaNode methodsFor: 'testing' stamp: 'lr 10/13/2009 14:01'! isUnary ^ arguments isEmpty! ! !RBPragmaNode methodsFor: 'matching' stamp: 'lr 10/18/2009 15:32'! match: aNode inContext: aDictionary aNode class = self class ifFalse: [ ^ false ]. self selector = aNode selector ifFalse: [ ^ false ]. 1 to: arguments size do: [ :index | ((arguments at: index) match: (aNode arguments at: index) inContext: aDictionary) ifFalse: [ ^ false ] ]. ^ true! ! !RBPragmaNode methodsFor: 'copying' stamp: 'lr 11/1/2009 22:37'! postCopy super postCopy. self arguments: (self arguments collect: [ :each | each copy ])! ! !RBPragmaNode methodsFor: 'replacing' stamp: 'lr 10/13/2009 14:00'! replaceNode: aNode withNode: anotherNode self arguments: (arguments collect: [ :each | each == aNode ifTrue: [ anotherNode ] ifFalse: [ each ] ])! ! !RBPragmaNode methodsFor: 'accessing' stamp: 'lr 10/13/2009 13:55'! selector ^ selector ifNil: [ selector := self buildSelector ]! ! !RBPragmaNode methodsFor: 'accessing' stamp: 'lr 11/2/2009 23:37'! selector: aSelector | keywords numArgs | keywords := aSelector keywords. numArgs := aSelector numArgs. numArgs == arguments size ifFalse: [self error: 'Attempting to assign selector with wrong number of arguments.']. selectorParts := numArgs == 0 ifTrue: [Array with: (RBIdentifierToken value: keywords first start: nil)] ifFalse: [keywords first last = $: ifTrue: [keywords collect: [:each | RBKeywordToken value: each start: nil]] ifFalse: [Array with: (RBBinarySelectorToken value: aSelector start: nil)]]. selector := aSelector! ! !RBPragmaNode methodsFor: 'private' stamp: 'lr 10/13/2009 13:54'! selectorParts ^selectorParts! ! !RBPragmaNode methodsFor: 'private' stamp: 'lr 10/13/2009 13:54'! selectorParts: tokenCollection selectorParts := tokenCollection! ! !RBPragmaNode methodsFor: 'initialization' stamp: 'lr 10/27/2009 11:58'! selectorParts: keywordTokens arguments: valueNodes self selectorParts: keywordTokens. self arguments: valueNodes! ! !RBPragmaNode methodsFor: 'querying' stamp: 'lr 10/27/2009 13:56'! sentMessages ^ OrderedCollection with: self selector! ! !RBPragmaNode methodsFor: 'accessing' stamp: 'lr 10/27/2009 12:01'! start ^ brackets first! ! !RBPragmaNode methodsFor: 'accessing' stamp: 'lr 10/27/2009 12:02'! stop ^ brackets last! ! !RBProgramNode class methodsFor: 'accessing' stamp: ''! formatterClass ^FormatterClass isNil ifTrue: [RBFormatter] ifFalse: [FormatterClass]! ! !RBProgramNode class methodsFor: 'accessing' stamp: ''! formatterClass: aClass FormatterClass := aClass! ! !RBProgramNode class methodsFor: 'accessing' stamp: 'lr 2/28/2009 21:57'! optimizedSelectors ^ #( and: caseOf: caseOf:otherwise: ifFalse: ifFalse:ifTrue: ifNil: ifNil:ifNotNil: ifNotNil: ifNotNil:ifNil: ifTrue: ifTrue:ifFalse: or: to:by:do: to:do: whileFalse whileFalse: whileTrue whileTrue: ) ! ! !RBProgramNode methodsFor: 'visitor' stamp: ''! acceptVisitor: aProgramNodeVisitor self subclassResponsibility! ! !RBProgramNode methodsFor: 'replacing' stamp: ''! addReplacement: aStringReplacement parent isNil ifTrue: [^self]. parent addReplacement: aStringReplacement! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! allArgumentVariables | children | children := self children. children isEmpty ifTrue: [^#()]. ^children inject: OrderedCollection new into: [:vars :each | vars addAll: each allArgumentVariables; yourself]! ! !RBProgramNode methodsFor: 'iterating' stamp: 'lr 11/1/2009 20:49'! allChildren | children | children := OrderedCollection new. self nodesDo: [ :each | children addLast: each ]. ^ children! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! allDefinedVariables | children | children := self children. children isEmpty ifTrue: [^#()]. ^children inject: OrderedCollection new into: [:vars :each | vars addAll: each allDefinedVariables; yourself]! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! allTemporaryVariables | children | children := self children. children isEmpty ifTrue: [^#()]. ^children inject: OrderedCollection new into: [:vars :each | vars addAll: each allTemporaryVariables; yourself]! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! asReturn "Change the current node to a return node." parent isNil ifTrue: [self error: 'Cannot change to a return without a parent node.']. parent isSequence ifFalse: [self error: 'Parent node must be a sequence node.']. (parent isLast: self) ifFalse: [self error: 'Return node must be last.']. ^parent addReturn! ! !RBProgramNode methodsFor: 'testing' stamp: ''! assigns: aVariableName ^self children anySatisfy: [:each | each assigns: aVariableName]! ! !RBProgramNode methodsFor: 'querying' stamp: ''! bestNodeFor: anInterval | selectedChildren | (self intersectsInterval: anInterval) ifFalse: [^nil]. (self containedBy: anInterval) ifTrue: [^self]. selectedChildren := self children select: [:each | each intersectsInterval: anInterval]. ^selectedChildren size == 1 ifTrue: [selectedChildren first bestNodeFor: anInterval] ifFalse: [self]! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! blockVariables ^parent isNil ifTrue: [#()] ifFalse: [parent blockVariables]! ! !RBProgramNode methodsFor: 'testing-matching' stamp: ''! canMatchMethod: aCompiledMethod ^self sentMessages allSatisfy: [:each | (self class optimizedSelectors includes: each) or: [aCompiledMethod refersToLiteral: each]]! ! !RBProgramNode methodsFor: 'meta variable-accessing' stamp: ''! cascadeListCharacter ^$;! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! children ^#()! ! !RBProgramNode methodsFor: 'replacing' stamp: ''! clearReplacements parent isNil ifTrue: [^self]. parent clearReplacements! ! !RBProgramNode methodsFor: 'enumeration' stamp: ''! collect: aBlock "Hacked to fit collection protocols" ^aBlock value: self! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! comments ^comments isNil ifTrue: [#()] ifFalse: [comments]! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! comments: aCollection comments := aCollection! ! !RBProgramNode methodsFor: 'testing' stamp: ''! containedBy: anInterval ^anInterval first <= self start and: [anInterval last >= self stop]! ! !RBProgramNode methodsFor: 'testing' stamp: ''! containsReturn ^self children anySatisfy: [:each | each containsReturn]! ! !RBProgramNode methodsFor: 'copying' stamp: ''! copyCommentsFrom: aNode "Add all comments from aNode to us. If we already have the comment, then don't add it." | newComments | newComments := OrderedCollection new. aNode nodesDo: [:each | newComments addAll: each comments]. self nodesDo: [:each | each comments do: [:comment | newComments remove: comment ifAbsent: []]]. newComments isEmpty ifTrue: [^self]. newComments := newComments asSortedCollection: [:a :b | a first < b first]. self comments: newComments! ! !RBProgramNode methodsFor: 'matching' stamp: ''! copyInContext: aDictionary ^self copy! ! !RBProgramNode methodsFor: 'matching' stamp: ''! copyList: matchNodes inContext: aDictionary | newNodes | newNodes := OrderedCollection new. matchNodes do: [:each | | object | object := each copyInContext: aDictionary. newNodes addAll: object]. ^newNodes! ! !RBProgramNode methodsFor: 'testing' stamp: ''! defines: aName ^false! ! !RBProgramNode methodsFor: 'testing' stamp: ''! directlyUses: aNode ^true! ! !RBProgramNode methodsFor: 'enumeration' stamp: ''! do: aBlock "Hacked to fit collection protocols" aBlock value: self! ! !RBProgramNode methodsFor: 'comparing' stamp: ''! equalTo: aNode exceptForVariables: variableNameCollection | dictionary | dictionary := Dictionary new. (self equalTo: aNode withMapping: dictionary) ifFalse: [^false]. dictionary keysAndValuesDo: [:key :value | (key = value or: [variableNameCollection includes: key]) ifFalse: [^false]]. ^true! ! !RBProgramNode methodsFor: 'comparing' stamp: ''! equalTo: aNode withMapping: aDictionary ^self = aNode! ! !RBProgramNode methodsFor: 'testing' stamp: ''! evaluatedFirst: aNode self children do: [:each | each == aNode ifTrue: [^true]. each isImmediateNode ifFalse: [^false]]. ^false! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! formattedCode ^self formatterClass new format: self! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! formatterClass ^self class formatterClass! ! !RBProgramNode methodsFor: 'testing' stamp: ''! hasMultipleReturns | count | count := 0. self nodesDo: [:each | each isReturn ifTrue: [count := count + 1]]. ^count > 1! ! !RBProgramNode methodsFor: 'properties' stamp: 'lr 10/18/2009 17:19'! hasProperty: aKey "Test if the property aKey is present." ^ properties notNil and: [ properties includesKey: aKey ]! ! !RBProgramNode methodsFor: 'testing' stamp: ''! intersectsInterval: anInterval ^(anInterval first between: self start and: self stop) or: [self start between: anInterval first and: anInterval last]! ! !RBProgramNode methodsFor: 'testing' stamp: 'lr 11/1/2009 18:39'! isArray ^ false! ! !RBProgramNode methodsFor: 'testing' stamp: ''! isAssignment ^false! ! !RBProgramNode methodsFor: 'testing' stamp: ''! isBlock ^false! ! !RBProgramNode methodsFor: 'testing' stamp: ''! isCascade ^false! ! !RBProgramNode methodsFor: 'testing' stamp: ''! isDirectlyUsed "This node is directly used as an argument, receiver, or part of an assignment." ^parent isNil ifTrue: [false] ifFalse: [parent directlyUses: self]! ! !RBProgramNode methodsFor: 'testing' stamp: ''! isEvaluatedFirst "Return true if we are the first thing evaluated in this statement." ^parent isNil or: [parent isSequence or: [parent evaluatedFirst: self]]! ! !RBProgramNode methodsFor: 'deprecated' stamp: ''! isImmediate ^self isImmediateNode! ! !RBProgramNode methodsFor: 'testing' stamp: ''! isImmediateNode ^false! ! !RBProgramNode methodsFor: 'testing' stamp: ''! isLast: aNode | children | children := self children. ^children notEmpty and: [children last == aNode]! ! !RBProgramNode methodsFor: 'testing-matching' stamp: ''! isList ^false! ! !RBProgramNode methodsFor: 'deprecated' stamp: ''! isLiteral ^self isLiteralNode! ! !RBProgramNode methodsFor: 'testing' stamp: ''! isLiteralArray ^false! ! !RBProgramNode methodsFor: 'testing' stamp: ''! isLiteralNode ^false! ! !RBProgramNode methodsFor: 'testing' stamp: ''! isMessage ^false! ! !RBProgramNode methodsFor: 'testing' stamp: ''! isMethod ^false! ! !RBProgramNode methodsFor: 'testing-matching' stamp: ''! isPatternNode ^false! ! !RBProgramNode methodsFor: 'testing' stamp: 'lr 10/27/2009 14:33'! isPragma ^false! ! !RBProgramNode methodsFor: 'testing' stamp: ''! isReturn ^false! ! !RBProgramNode methodsFor: 'testing' stamp: ''! isSequence ^false! ! !RBProgramNode methodsFor: 'testing' stamp: ''! isUsed "Answer true if this node could be used as part of another expression. For example, you could use the result of this node as a receiver of a message, an argument, the right part of an assignment, or the return value of a block. This differs from isDirectlyUsed in that it is conservative since it also includes return values of blocks." ^parent isNil ifTrue: [false] ifFalse: [parent uses: self]! ! !RBProgramNode methodsFor: 'testing' stamp: ''! isValue ^false! ! !RBProgramNode methodsFor: 'testing' stamp: ''! isVariable ^false! ! !RBProgramNode methodsFor: 'testing' stamp: ''! lastIsReturn ^self isReturn! ! !RBProgramNode methodsFor: 'meta variable-accessing' stamp: ''! listCharacter ^$@! ! !RBProgramNode methodsFor: 'meta variable-accessing' stamp: ''! literalCharacter ^$#! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! mappingFor: aNode | method | method := self methodNode. method isNil ifTrue: [^aNode]. ^method mappingFor: aNode! ! !RBProgramNode methodsFor: 'matching' stamp: ''! match: aNode inContext: aDictionary ^self = aNode! ! !RBProgramNode methodsFor: 'matching' stamp: ''! matchList: matchNodes against: programNodes inContext: aDictionary ^self matchList: matchNodes index: 1 against: programNodes index: 1 inContext: aDictionary! ! !RBProgramNode methodsFor: 'matching' stamp: ''! matchList: matchNodes index: matchIndex against: programNodes index: programIndex inContext: aDictionary | node currentIndex currentDictionary nodes | matchNodes size < matchIndex ifTrue: [^programNodes size < programIndex]. node := matchNodes at: matchIndex. node isList ifTrue: [currentIndex := programIndex - 1. [currentDictionary := aDictionary copy. programNodes size < currentIndex or: [nodes := programNodes copyFrom: programIndex to: currentIndex. (currentDictionary at: node ifAbsentPut: [nodes]) = nodes and: [(self matchList: matchNodes index: matchIndex + 1 against: programNodes index: currentIndex + 1 inContext: currentDictionary) ifTrue: [currentDictionary keysAndValuesDo: [:key :value | aDictionary at: key put: value]. ^true]. false]]] whileFalse: [currentIndex := currentIndex + 1]. ^false]. programNodes size < programIndex ifTrue: [^false]. (node match: (programNodes at: programIndex) inContext: aDictionary) ifFalse: [^false]. ^self matchList: matchNodes index: matchIndex + 1 against: programNodes index: programIndex + 1 inContext: aDictionary! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! methodComments ^self comments! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! methodNode ^parent isNil ifTrue: [nil] ifFalse: [parent methodNode]! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! newSource ^self formattedCode! ! !RBProgramNode methodsFor: 'iterating' stamp: 'lr 11/1/2009 20:49'! nodesDo: aBlock aBlock value: self. self children do: [ :each | each nodesDo: aBlock ]! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! parent ^parent! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! parent: aRBProgramNode parent := aRBProgramNode! ! !RBProgramNode methodsFor: 'copying' stamp: 'lr 11/1/2009 22:34'! postCopy! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! precedence ^6! ! !RBProgramNode methodsFor: 'printing' stamp: 'lr 11/1/2009 19:28'! printOn: aStream aStream nextPutAll: self class name; nextPut: $(; nextPutAll: self formattedCode; nextPut: $)! ! !RBProgramNode methodsFor: 'properties' stamp: 'md 3/29/2007 14:48'! propertyAt: aKey "Answer the property value associated with aKey." ^ self propertyAt: aKey ifAbsent: [ self error: 'Property not found' ].! ! !RBProgramNode methodsFor: 'properties' stamp: 'lr 10/18/2009 17:19'! propertyAt: aKey ifAbsent: aBlock "Answer the property value associated with aKey or, if aKey isn't found, answer the result of evaluating aBlock." properties isNil ifTrue: [ ^ aBlock value ]. ^ properties at: aKey ifAbsent: aBlock! ! !RBProgramNode methodsFor: 'properties' stamp: 'md 3/29/2007 14:48'! propertyAt: aKey ifAbsentPut: aBlock "Answer the property associated with aKey or, if aKey isn't found store the result of evaluating aBlock as new value." ^ self propertyAt: aKey ifAbsent: [ self propertyAt: aKey put: aBlock value ].! ! !RBProgramNode methodsFor: 'properties' stamp: 'lr 10/18/2009 17:18'! propertyAt: aKey put: anObject "Set the property at aKey to be anObject. If aKey is not found, create a new entry for aKey and set is value to anObject. Answer anObject." properties ifNil: [ properties := RBSmallIdentityDictionary new: 1 ]. ^ properties at: aKey put: anObject! ! !RBProgramNode methodsFor: 'testing-matching' stamp: ''! recurseInto ^false! ! !RBProgramNode methodsFor: 'meta variable-accessing' stamp: ''! recurseIntoCharacter ^$`! ! !RBProgramNode methodsFor: 'testing' stamp: ''! references: aVariableName ^self children anySatisfy: [:each | each references: aVariableName]! ! !RBProgramNode methodsFor: 'replacing' stamp: ''! removeDeadCode self children do: [:each | each removeDeadCode]! ! !RBProgramNode methodsFor: 'properties' stamp: 'md 3/29/2007 14:51'! removeProperty: aKey "Remove the property with aKey. Answer the property or raise an error if aKey isn't found." ^ self removeProperty: aKey ifAbsent: [ self error: 'Property not found' ].! ! !RBProgramNode methodsFor: 'properties' stamp: 'lr 10/18/2009 17:19'! removeProperty: aKey ifAbsent: aBlock "Remove the property with aKey. Answer the value or, if aKey isn't found, answer the result of evaluating aBlock." | answer | properties isNil ifTrue: [ ^ aBlock value ]. answer := properties removeKey: aKey ifAbsent: aBlock. properties isEmpty ifTrue: [ properties := nil ]. ^ answer! ! !RBProgramNode methodsFor: 'replacing' stamp: ''! replaceMethodSource: aNode "We are being replaced with aNode -- if possible try to perform an in place edit of the source." | method | method := self methodNode. method notNil ifTrue: [method map: self to: aNode]. aNode parent: self parent. [self replaceSourceWith: aNode] on: Error do: [:ex | self clearReplacements. ex return]! ! !RBProgramNode methodsFor: 'replacing' stamp: ''! replaceNode: aNode withNode: anotherNode self error: 'I don''t store other nodes'! ! !RBProgramNode methodsFor: 'private-replacing' stamp: ''! replaceSourceFrom: aNode self clearReplacements! ! !RBProgramNode methodsFor: 'private-replacing' stamp: ''! replaceSourceWith: aNode aNode replaceSourceFrom: self! ! !RBProgramNode methodsFor: 'replacing' stamp: ''! replaceWith: aNode parent isNil ifTrue: [self error: 'This node doesn''t have a parent']. self replaceMethodSource: aNode. parent replaceNode: self withNode: aNode! ! !RBProgramNode methodsFor: 'querying' stamp: 'lr 11/2/2009 00:14'! selfMessages | searcher | searcher := RBParseTreeSearcher new. searcher matches: 'self `@msg: ``@args' do: [:aNode :answer | answer add: aNode selector; yourself]. ^searcher executeTree: self initialAnswer: Set new! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! sentMessages | messages | messages := Set new. self children do: [:each | messages addAll: each sentMessages]. ^messages! ! !RBProgramNode methodsFor: 'enumeration' stamp: ''! size "Hacked to fit collection protocols" ^1! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! source ^parent notNil ifTrue: [parent source] ifFalse: [nil]! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! sourceInterval ^self start to: self stop! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! start self subclassResponsibility! ! !RBProgramNode methodsFor: 'meta variable-accessing' stamp: ''! statementCharacter ^$.! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! statementComments | statementComments | statementComments := OrderedCollection withAll: self comments. self children do: [:each | statementComments addAll: each statementComments]. ^statementComments asSortedCollection: [:a :b | a first < b first]! ! !RBProgramNode methodsFor: 'querying' stamp: ''! statementNode "Return your topmost node that is contained by a sequence node." (parent isNil or: [parent isSequence]) ifTrue: [^self]. ^parent statementNode! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! stop self subclassResponsibility! ! !RBProgramNode methodsFor: 'querying' stamp: 'lr 11/2/2009 00:14'! superMessages | searcher | searcher := RBParseTreeSearcher new. searcher matches: 'super `@msg: ``@args' do: [:aNode :answer | answer add: aNode selector; yourself]. ^searcher executeTree: self initialAnswer: Set new! ! !RBProgramNode methodsFor: 'accessing' stamp: ''! temporaryVariables ^parent isNil ifTrue: [#()] ifFalse: [parent temporaryVariables]! ! !RBProgramNode methodsFor: 'testing' stamp: ''! uses: aNode ^true! ! !RBProgramNode methodsFor: 'querying' stamp: ''! whichNodeIsContainedBy: anInterval | selectedChildren | (self intersectsInterval: anInterval) ifFalse: [^nil]. (self containedBy: anInterval) ifTrue: [^self]. selectedChildren := self children select: [:each | each intersectsInterval: anInterval]. ^selectedChildren size == 1 ifTrue: [selectedChildren first whichNodeIsContainedBy: anInterval] ifFalse: [nil]! ! !RBProgramNode methodsFor: 'querying' stamp: ''! whoDefines: aName ^(self defines: aName) ifTrue: [self] ifFalse: [parent notNil ifTrue: [parent whoDefines: aName] ifFalse: [nil]]! ! RBProgramNode subclass: #RBReturnNode instanceVariableNames: 'return value' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBReturnNode commentStamp: '' prior: 0! RBReturnNode is an AST node that represents a return expression. Instance Variables: return the position of the ^ character value the value that is being returned ! !RBReturnNode class methodsFor: 'instance creation' stamp: ''! return: returnInteger value: aValueNode ^self new return: returnInteger value: aValueNode! ! !RBReturnNode class methodsFor: 'instance creation' stamp: ''! value: aNode ^self return: nil value: aNode! ! !RBReturnNode methodsFor: 'comparing' stamp: ''! = anObject self == anObject ifTrue: [^true]. self class = anObject class ifFalse: [^false]. ^self value = anObject value! ! !RBReturnNode methodsFor: 'visitor' stamp: ''! acceptVisitor: aProgramNodeVisitor ^aProgramNodeVisitor acceptReturnNode: self! ! !RBReturnNode methodsFor: 'accessing' stamp: ''! children ^Array with: value! ! !RBReturnNode methodsFor: 'testing' stamp: ''! containsReturn ^true! ! !RBReturnNode methodsFor: 'matching' stamp: ''! copyInContext: aDictionary ^(self class new) value: (value copyInContext: aDictionary); yourself! ! !RBReturnNode methodsFor: 'comparing' stamp: ''! equalTo: anObject withMapping: aDictionary ^self class = anObject class and: [self value equalTo: anObject value withMapping: aDictionary]! ! !RBReturnNode methodsFor: 'comparing' stamp: ''! hash ^self value hash! ! !RBReturnNode methodsFor: 'testing' stamp: ''! isReturn ^true! ! !RBReturnNode methodsFor: 'matching' stamp: ''! match: aNode inContext: aDictionary aNode class == self class ifFalse: [^false]. ^value match: aNode value inContext: aDictionary! ! !RBReturnNode methodsFor: 'copying' stamp: 'lr 11/1/2009 22:34'! postCopy super postCopy. self value: self value copy! ! !RBReturnNode methodsFor: 'replacing' stamp: ''! replaceNode: aNode withNode: anotherNode value == aNode ifTrue: [self value: anotherNode]! ! !RBReturnNode methodsFor: 'initialize-release' stamp: ''! return: returnInteger value: aValueNode return := returnInteger. self value: aValueNode! ! !RBReturnNode methodsFor: 'accessing' stamp: ''! start ^return! ! !RBReturnNode methodsFor: 'accessing' stamp: ''! stop ^value stop! ! !RBReturnNode methodsFor: 'accessing' stamp: ''! value ^value! ! !RBReturnNode methodsFor: 'accessing' stamp: ''! value: valueNode value := valueNode. value parent: self! ! RBProgramNode subclass: #RBSequenceNode instanceVariableNames: 'leftBar rightBar statements periods temporaries' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBSequenceNode commentStamp: '' prior: 0! RBSequenceNode is an AST node that represents a sequence of statements. Both RBBlockNodes and RBMethodNodes contain these. Instance Variables: leftBar the position of the left | in the temporaries definition periods the positions of all the periods that separate the statements rightBar the position of the right | in the temporaries definition statements the statement nodes temporaries the temporaries defined ! !RBSequenceNode class methodsFor: 'instance creation' stamp: ''! leftBar: leftInteger temporaries: variableNodes rightBar: rightInteger ^(self new) leftBar: leftInteger temporaries: variableNodes rightBar: rightInteger; yourself! ! !RBSequenceNode class methodsFor: 'instance creation' stamp: ''! statements: statementNodes ^self temporaries: #() statements: statementNodes! ! !RBSequenceNode class methodsFor: 'instance creation' stamp: ''! temporaries: variableNodes statements: statementNodes ^(self new) temporaries: variableNodes; statements: statementNodes; yourself! ! !RBSequenceNode methodsFor: 'comparing' stamp: ''! = anObject "Can't send = to the temporaries and statements collection since they might change from arrays to OCs" self == anObject ifTrue: [^true]. self class = anObject class ifFalse: [^false]. self temporaries size = anObject temporaries size ifFalse: [^false]. 1 to: self temporaries size do: [:i | (self temporaries at: i) = (anObject temporaries at: i) ifFalse: [^false]]. self statements size = anObject statements size ifFalse: [^false]. 1 to: self statements size do: [:i | (self statements at: i) = (anObject statements at: i) ifFalse: [^false]]. ^true! ! !RBSequenceNode methodsFor: 'visitor' stamp: ''! acceptVisitor: aProgramNodeVisitor ^aProgramNodeVisitor acceptSequenceNode: self! ! !RBSequenceNode methodsFor: 'adding nodes' stamp: ''! addNode: aNode aNode parent: self. (statements notEmpty and: [statements last isReturn]) ifTrue: [self error: 'Cannot add statement after return node']. statements := (statements asOrderedCollection) add: aNode; yourself! ! !RBSequenceNode methodsFor: 'adding nodes' stamp: ''! addNode: aNode before: anotherNode | index | index := self indexOfNode: anotherNode. index = 0 ifTrue: [^self addNode: aNode]. statements := (statements asOrderedCollection) add: aNode beforeIndex: index; yourself. aNode parent: self! ! !RBSequenceNode methodsFor: 'adding nodes' stamp: ''! addNodeFirst: aNode aNode parent: self. statements := (statements asOrderedCollection) addFirst: aNode; yourself! ! !RBSequenceNode methodsFor: 'adding nodes' stamp: ''! addNodes: aCollection aCollection do: [:each | each parent: self]. (statements notEmpty and: [statements last isReturn]) ifTrue: [self error: 'Cannot add statement after return node']. statements := (statements asOrderedCollection) addAll: aCollection; yourself! ! !RBSequenceNode methodsFor: 'adding nodes' stamp: ''! addNodes: aCollection before: anotherNode aCollection do: [:each | self addNode: each before: anotherNode]! ! !RBSequenceNode methodsFor: 'adding nodes' stamp: ''! addNodesFirst: aCollection aCollection do: [:each | each parent: self]. statements := (statements asOrderedCollection) addAllFirst: aCollection; yourself! ! !RBSequenceNode methodsFor: 'accessing' stamp: ''! addReturn | node | statements isEmpty ifTrue: [^nil]. statements last isReturn ifTrue: [^statements last]. node := RBReturnNode value: statements last. statements at: statements size put: node. node parent: self. ^node! ! !RBSequenceNode methodsFor: 'adding nodes' stamp: ''! addSelfReturn | node | self lastIsReturn ifTrue: [^self]. node := RBReturnNode value: (RBVariableNode named: 'self'). self addNode: node! ! !RBSequenceNode methodsFor: 'adding nodes' stamp: ''! addTemporariesNamed: aCollection aCollection do: [:each | self addTemporaryNamed: each]! ! !RBSequenceNode methodsFor: 'adding nodes' stamp: ''! addTemporaryNamed: aString | variableNode | variableNode := RBVariableNode named: aString. variableNode parent: self. temporaries := temporaries copyWith: variableNode! ! !RBSequenceNode methodsFor: 'accessing' stamp: ''! allDefinedVariables ^(self temporaryNames asOrderedCollection) addAll: super allDefinedVariables; yourself! ! !RBSequenceNode methodsFor: 'accessing' stamp: ''! allTemporaryVariables ^(self temporaryNames asOrderedCollection) addAll: super allTemporaryVariables; yourself! ! !RBSequenceNode methodsFor: 'querying' stamp: ''! bestNodeFor: anInterval | node | node := super bestNodeFor: anInterval. node == self ifTrue: [(temporaries isEmpty and: [statements size == 1]) ifTrue: [^statements first]]. ^node! ! !RBSequenceNode methodsFor: 'accessing' stamp: ''! children ^(OrderedCollection new) addAll: self temporaries; addAll: self statements; yourself! ! !RBSequenceNode methodsFor: 'matching' stamp: ''! copyInContext: aDictionary ^(self class new) temporaries: (self copyList: temporaries inContext: aDictionary); statements: (self copyList: statements inContext: aDictionary); yourself! ! !RBSequenceNode methodsFor: 'testing' stamp: ''! defines: aName ^temporaries anySatisfy: [:each | each name = aName]! ! !RBSequenceNode methodsFor: 'testing' stamp: ''! directlyUses: aNode ^false! ! !RBSequenceNode methodsFor: 'comparing' stamp: ''! equalTo: anObject withMapping: aDictionary self class = anObject class ifFalse: [^false]. self statements size == anObject statements size ifFalse: [^false]. 1 to: self statements size do: [:i | ((self statements at: i) equalTo: (anObject statements at: i) withMapping: aDictionary) ifFalse: [^false]]. aDictionary values asSet size = aDictionary size ifFalse: [^false]. "Not a one-to-one mapping" self temporaries do: [:each | aDictionary removeKey: each name ifAbsent: []]. ^true! ! !RBSequenceNode methodsFor: 'comparing' stamp: 'lr 11/1/2009 18:37'! hash ^ self temporaries hash bitXor: (self statements isEmpty ifTrue: [ 0 ] ifFalse: [ self statements first hash ])! ! !RBSequenceNode methodsFor: 'private' stamp: ''! indexOfNode: aNode "Try to find the node by first looking for ==, and then for =" ^(1 to: statements size) detect: [:each | (statements at: each) == aNode] ifNone: [statements indexOf: aNode]! ! !RBSequenceNode methodsFor: 'testing' stamp: ''! isLast: aNode | last | statements isEmpty ifTrue: [^false]. last := statements last. ^last == aNode or: [last isMessage and: [(#(#ifTrue:ifFalse: #ifFalse:ifTrue:) includes: last selector) and: [last arguments anySatisfy: [:each | each isLast: aNode]]]]! ! !RBSequenceNode methodsFor: 'testing' stamp: ''! isSequence ^true! ! !RBSequenceNode methodsFor: 'testing' stamp: ''! lastIsReturn ^statements notEmpty and: [statements last lastIsReturn]! ! !RBSequenceNode methodsFor: 'initialize-release' stamp: ''! leftBar: leftInteger temporaries: variableNodes rightBar: rightInteger leftBar := leftInteger. self temporaries: variableNodes. rightBar := rightInteger! ! !RBSequenceNode methodsFor: 'matching' stamp: ''! match: aNode inContext: aDictionary self class == aNode class ifFalse: [^false]. ^(self matchList: temporaries against: aNode temporaries inContext: aDictionary) and: [self matchList: statements against: aNode statements inContext: aDictionary]! ! !RBSequenceNode methodsFor: 'accessing' stamp: ''! methodComments | methodComments | methodComments := OrderedCollection withAll: self comments. temporaries do: [:each | methodComments addAll: each comments]. (parent notNil and: [parent isBlock]) ifTrue: [parent arguments do: [:each | methodComments addAll: each comments]]. ^methodComments asSortedCollection: [:a :b | a first < b first]! ! !RBSequenceNode methodsFor: 'accessing' stamp: 'lr 11/1/2009 20:30'! periods ^ periods! ! !RBSequenceNode methodsFor: 'accessing' stamp: ''! periods: anObject periods := anObject! ! !RBSequenceNode methodsFor: 'copying' stamp: 'lr 11/1/2009 22:37'! postCopy super postCopy. self temporaries: (self temporaries collect: [ :each | each copy ]). self statements: (self statements collect: [ :each | each copy ])! ! !RBSequenceNode methodsFor: 'testing' stamp: ''! references: aVariableName ^statements anySatisfy: [:each | each references: aVariableName]! ! !RBSequenceNode methodsFor: 'replacing' stamp: 'lr 11/1/2009 20:34'! removeDeadCode (self isUsed ifTrue: [statements size - 1] ifFalse: [statements size]) to: 1 by: -1 do: [:i | (statements at: i) isImmediateNode ifTrue: [self clearReplacements. statements removeAt: i]]. super removeDeadCode! ! !RBSequenceNode methodsFor: 'replacing' stamp: ''! removeNode: aNode self replaceNode: aNode withNodes: #()! ! !RBSequenceNode methodsFor: 'accessing' stamp: ''! removeTemporaryNamed: aName temporaries := temporaries reject: [:each | each name = aName]! ! !RBSequenceNode methodsFor: 'replacing' stamp: ''! replaceNode: aNode withNode: anotherNode self statements: (statements collect: [:each | each == aNode ifTrue: [anotherNode] ifFalse: [each]]). self temporaries: (temporaries collect: [:each | each == aNode ifTrue: [anotherNode] ifFalse: [each]])! ! !RBSequenceNode methodsFor: 'replacing' stamp: ''! replaceNode: aNode withNodes: aCollection | index newStatements | self clearReplacements. index := self indexOfNode: aNode. newStatements := OrderedCollection new: statements size + aCollection size. 1 to: index - 1 do: [:i | newStatements add: (statements at: i)]. newStatements addAll: aCollection. index + 1 to: statements size do: [:i | newStatements add: (statements at: i)]. aCollection do: [:each | each parent: self]. statements := newStatements! ! !RBSequenceNode methodsFor: 'accessing' stamp: ''! start ^leftBar isNil ifTrue: [statements isEmpty ifTrue: [1] ifFalse: [statements first start]] ifFalse: [leftBar]! ! !RBSequenceNode methodsFor: 'accessing' stamp: ''! statements ^statements! ! !RBSequenceNode methodsFor: 'accessing' stamp: ''! statements: stmtCollection statements := stmtCollection. statements do: [:each | each parent: self]! ! !RBSequenceNode methodsFor: 'accessing' stamp: ''! stop ^(periods isEmpty ifTrue: [0] ifFalse: [periods last]) max: (statements isEmpty ifTrue: [0] ifFalse: [statements last stop])! ! !RBSequenceNode methodsFor: 'accessing' stamp: ''! temporaries ^temporaries! ! !RBSequenceNode methodsFor: 'accessing' stamp: ''! temporaries: tempCollection temporaries := tempCollection. temporaries do: [:each | each parent: self]! ! !RBSequenceNode methodsFor: 'accessing' stamp: ''! temporaryNames ^temporaries collect: [:each | each name]! ! !RBSequenceNode methodsFor: 'accessing' stamp: ''! temporaryVariables ^(super temporaryVariables asOrderedCollection) addAll: self temporaryNames; yourself! ! !RBSequenceNode methodsFor: 'testing' stamp: ''! uses: aNode statements isEmpty ifTrue: [^false]. aNode == statements last ifFalse: [^false]. ^self isUsed! ! !RBSequenceNode methodsFor: 'querying' stamp: ''! whichNodeIsContainedBy: anInterval | node | node := super whichNodeIsContainedBy: anInterval. node == self ifTrue: [(temporaries isEmpty and: [statements size == 1]) ifTrue: [^statements first]]. ^node! ! RBProgramNode subclass: #RBValueNode instanceVariableNames: 'parentheses' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBValueNode commentStamp: '' prior: 0! RBValueNode is an abstract class that represents a node that returns some value. Subclasses must implement the following messages: accessing startWithoutParentheses stopWithoutParentheses testing needsParenthesis Instance Variables: parentheses the positions of the parethesis around this node. We need a collection of intervals for stupid code such as "((3 + 4))" that has multiple parethesis around the same expression. ! RBValueNode subclass: #RBArrayNode instanceVariableNames: 'left right statements periods' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBArrayNode class methodsFor: 'instance creation' stamp: 'ajh 3/4/2003 02:03'! statements: statements ^ self new statements: statements! ! !RBArrayNode methodsFor: 'comparing' stamp: 'lr 11/1/2009 18:36'! = anObject self == anObject ifTrue: [ ^ true ]. self class = anObject class ifFalse: [ ^ false ]. self statements size = anObject statements size ifFalse: [ ^ false ]. 1 to: self statements size do: [ :i | (self statements at: i) = (anObject statements at: i) ifFalse: [ ^ false ] ]. ^ true! ! !RBArrayNode methodsFor: 'visitor' stamp: 'ajh 3/17/2003 00:25'! acceptVisitor: aProgramNodeVisitor ^ aProgramNodeVisitor acceptArrayNode: self! ! !RBArrayNode methodsFor: 'accessing' stamp: 'lr 11/1/2009 18:36'! children ^ self statements! ! !RBArrayNode methodsFor: 'matching' stamp: 'lr 10/18/2009 16:16'! copyInContext: aDictionary ^ self class statements: (self copyList: statements inContext: aDictionary)! ! !RBArrayNode methodsFor: 'comparing' stamp: 'lr 10/18/2009 16:15'! equalTo: anObject withMapping: aDictionary self class = anObject class ifFalse: [ ^ false ]. self statements size = anObject statements size ifFalse: [ ^ false ]. self statements with: anObject statements do: [ :first :second | (first equalTo: second withMapping: aDictionary) ifFalse: [ ^ false ] ]. aDictionary values asSet size = aDictionary size ifFalse: [ ^ false ]. ^ true! ! !RBArrayNode methodsFor: 'comparing' stamp: 'lr 10/18/2009 16:14'! hash ^ self statements isEmpty ifTrue: [ 0 ] ifFalse: [ self statements first hash ]! ! !RBArrayNode methodsFor: 'testing' stamp: 'lr 10/18/2009 16:11'! isArray ^ true! ! !RBArrayNode methodsFor: 'testing' stamp: 'ls 1/24/2000 00:28'! lastIsReturn statements isEmpty ifTrue:[ ^false ]. ^statements last lastIsReturn! ! !RBArrayNode methodsFor: 'accessing' stamp: 'lr 11/1/2009 19:52'! left ^ left! ! !RBArrayNode methodsFor: 'accessing' stamp: 'lr 11/1/2009 19:52'! left: anInteger left := anInteger! ! !RBArrayNode methodsFor: 'matching' stamp: 'lr 10/18/2009 16:16'! match: aNode inContext: aDictionary aNode class == self class ifFalse: [ ^ false ]. ^ self matchList: statements against: aNode statements inContext: aDictionary! ! !RBArrayNode methodsFor: 'testing' stamp: 'lr 11/1/2009 20:24'! needsParenthesis ^ false! ! !RBArrayNode methodsFor: 'accessing' stamp: 'lr 11/1/2009 20:44'! periods ^ periods! ! !RBArrayNode methodsFor: 'accessing' stamp: 'lr 10/27/2009 14:24'! periods: aCollection periods := aCollection! ! !RBArrayNode methodsFor: 'copying' stamp: 'lr 11/1/2009 22:36'! postCopy super postCopy. self statements: (self statements collect: [ :each | each copy ])! ! !RBArrayNode methodsFor: 'accessing' stamp: 'lr 11/1/2009 20:24'! precedence ^0! ! !RBArrayNode methodsFor: 'testing' stamp: 'lr 11/1/2009 18:39'! references: aVariableName ^ statements anySatisfy: [ :each | each references: aVariableName ]! ! !RBArrayNode methodsFor: 'replacing' stamp: 'lr 6/6/2008 16:15'! replaceNode: oldNode withNode: newNode self statements: (statements collect: [ :statement | statement == oldNode ifTrue: [ newNode ] ifFalse: [ statement ] ])! ! !RBArrayNode methodsFor: 'accessing' stamp: 'lr 11/1/2009 19:52'! right ^ right! ! !RBArrayNode methodsFor: 'accessing' stamp: 'lr 11/1/2009 19:52'! right: anInteger right := anInteger! ! !RBArrayNode methodsFor: 'accessing' stamp: 'lr 11/1/2009 19:52'! startWithoutParentheses ^ left! ! !RBArrayNode methodsFor: 'accessing' stamp: 'lr 11/1/2009 20:25'! statementComments ^self comments! ! !RBArrayNode methodsFor: 'accessing' stamp: 'ls 1/24/2000 00:32'! statements ^statements! ! !RBArrayNode methodsFor: 'accessing' stamp: 'lr 6/6/2008 16:16'! statements: statements0 statements := statements0. statements do: [:statement | statement parent: self]! ! !RBArrayNode methodsFor: 'accessing' stamp: 'lr 11/1/2009 19:53'! stopWithoutParentheses ^ right! ! !RBArrayNode methodsFor: 'testing' stamp: 'lr 11/1/2009 18:40'! uses: aNode ^ (statements anySatisfy: [ :each | each == aNode ]) or: [ self isUsed ]! ! RBValueNode subclass: #RBAssignmentNode instanceVariableNames: 'variable assignment value' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBAssignmentNode commentStamp: '' prior: 0! RBAssignmentNode is an AST node for assignment statements Instance Variables: assignment position of the := value the value that we're assigning variable the variable being assigned ! !RBAssignmentNode class methodsFor: 'instance creation' stamp: ''! variable: aVariableNode value: aValueNode ^self variable: aVariableNode value: aValueNode position: nil! ! !RBAssignmentNode class methodsFor: 'instance creation' stamp: ''! variable: aVariableNode value: aValueNode position: anInteger ^(self new) variable: aVariableNode value: aValueNode position: anInteger; yourself! ! !RBAssignmentNode methodsFor: 'comparing' stamp: ''! = anObject self == anObject ifTrue: [^true]. self class = anObject class ifFalse: [^false]. ^self variable = anObject variable and: [self value = anObject value]! ! !RBAssignmentNode methodsFor: 'visitor' stamp: ''! acceptVisitor: aProgramNodeVisitor ^aProgramNodeVisitor acceptAssignmentNode: self! ! !RBAssignmentNode methodsFor: 'accessing' stamp: 'lr 11/2/2009 20:50'! assignmentOperator ^ (self assignmentPosition notNil and: [ self source notNil and: [ (self source at: self assignmentPosition ifAbsent: [ nil ]) = $_ ] ]) ifTrue: [ '_' ] ifFalse: [ ':=' ]! ! !RBAssignmentNode methodsFor: 'accessing' stamp: 'lr 11/2/2009 20:50'! assignmentPosition ^ assignment! ! !RBAssignmentNode methodsFor: 'testing' stamp: ''! assigns: aVariableName ^variable name = aVariableName or: [value assigns: aVariableName]! ! !RBAssignmentNode methodsFor: 'querying' stamp: ''! bestNodeFor: anInterval (self intersectsInterval: anInterval) ifFalse: [^nil]. (self containedBy: anInterval) ifTrue: [^self]. assignment isNil ifTrue: [^super bestNodeFor: anInterval]. ((anInterval first between: assignment and: assignment + 1) or: [assignment between: anInterval first and: anInterval last]) ifTrue: [^self]. self children do: [:each | | node | node := each bestNodeFor: anInterval. node notNil ifTrue: [^node]]! ! !RBAssignmentNode methodsFor: 'accessing' stamp: ''! children ^Array with: value with: variable! ! !RBAssignmentNode methodsFor: 'matching' stamp: ''! copyInContext: aDictionary ^(self class new) variable: (variable copyInContext: aDictionary); value: (value copyInContext: aDictionary); yourself! ! !RBAssignmentNode methodsFor: 'testing' stamp: ''! directlyUses: aNode ^aNode = value ifTrue: [true] ifFalse: [self isDirectlyUsed]! ! !RBAssignmentNode methodsFor: 'comparing' stamp: ''! equalTo: anObject withMapping: aDictionary ^self class = anObject class and: [(self variable equalTo: anObject variable withMapping: aDictionary) and: [self value equalTo: anObject value withMapping: aDictionary]]! ! !RBAssignmentNode methodsFor: 'comparing' stamp: ''! hash ^self variable hash bitXor: self value hash! ! !RBAssignmentNode methodsFor: 'testing' stamp: ''! isAssignment ^true! ! !RBAssignmentNode methodsFor: 'matching' stamp: ''! match: aNode inContext: aDictionary aNode class == self class ifFalse: [^false]. ^(variable match: aNode variable inContext: aDictionary) and: [value match: aNode value inContext: aDictionary]! ! !RBAssignmentNode methodsFor: 'testing' stamp: ''! needsParenthesis ^parent isNil ifTrue: [false] ifFalse: [self precedence > parent precedence]! ! !RBAssignmentNode methodsFor: 'copying' stamp: 'lr 11/1/2009 22:35'! postCopy super postCopy. self variable: self variable copy. self value: self value copy! ! !RBAssignmentNode methodsFor: 'accessing' stamp: ''! precedence ^5! ! !RBAssignmentNode methodsFor: 'replacing' stamp: ''! replaceNode: aNode withNode: anotherNode value == aNode ifTrue: [self value: anotherNode]. variable == aNode ifTrue: [self variable: anotherNode]! ! !RBAssignmentNode methodsFor: 'replacing' stamp: ''! replaceSourceWith: aNode "Check if we are being replaced with a setter message send. If so, create the replacements to edit the original source." aNode isMessage ifFalse: [^super replaceSourceWith: aNode]. aNode receiver isVariable ifFalse: [^super replaceSourceWith: aNode]. aNode numArgs = 1 ifFalse: [^super replaceSourceWith: aNode]. (self mappingFor: self value) = aNode arguments first ifFalse: [^super replaceSourceWith: aNode]. (self value hasParentheses not and: [aNode arguments first precedence >= aNode precedence]) ifTrue: [self addReplacement: (RBStringReplacement replaceFrom: self value start to: self value start - 1 with: '('); addReplacement: (RBStringReplacement replaceFrom: self value stop + 1 to: self value stop with: ')')]. self addReplacement: (RBStringReplacement replaceFrom: self variable start to: self assignmentPosition + 1 with: aNode receiver name , ' ' , aNode selector)! ! !RBAssignmentNode methodsFor: 'accessing' stamp: ''! startWithoutParentheses ^variable start! ! !RBAssignmentNode methodsFor: 'accessing' stamp: ''! stopWithoutParentheses ^value stop! ! !RBAssignmentNode methodsFor: 'testing' stamp: ''! uses: aNode ^aNode = value ifTrue: [true] ifFalse: [self isUsed]! ! !RBAssignmentNode methodsFor: 'accessing' stamp: ''! value ^value! ! !RBAssignmentNode methodsFor: 'accessing' stamp: ''! value: aValueNode value := aValueNode. value parent: self! ! !RBAssignmentNode methodsFor: 'accessing' stamp: ''! variable ^variable! ! !RBAssignmentNode methodsFor: 'accessing' stamp: ''! variable: varNode variable := varNode. variable parent: self! ! !RBAssignmentNode methodsFor: 'initialize-release' stamp: ''! variable: aVariableNode value: aValueNode position: anInteger self variable: aVariableNode. self value: aValueNode. assignment := anInteger! ! RBValueNode subclass: #RBBlockNode instanceVariableNames: 'left right colons body arguments bar' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBBlockNode commentStamp: '' prior: 0! RBBlockNode is an AST node that represents a block "[...]". Instance Variables: arguments the arguments for the block bar position of the | after the arguments body the code inside the block colons positions of each : before each argument left position of [ right position of ] ! !RBBlockNode class methodsFor: 'instance creation' stamp: ''! arguments: argNodes body: sequenceNode ^(self new) arguments: argNodes; body: sequenceNode; yourself! ! !RBBlockNode class methodsFor: 'instance creation' stamp: ''! body: sequenceNode ^self arguments: #() body: sequenceNode! ! !RBBlockNode methodsFor: 'comparing' stamp: ''! = anObject self == anObject ifTrue: [^true]. self class = anObject class ifFalse: [^false]. self body = anObject body ifFalse: [^false]. self arguments size = anObject arguments size ifFalse: [^false]. 1 to: self arguments size do: [:i | (self arguments at: i) = (anObject arguments at: i) ifFalse: [^false]]. ^true! ! !RBBlockNode methodsFor: 'visitor' stamp: ''! acceptVisitor: aProgramNodeVisitor ^aProgramNodeVisitor acceptBlockNode: self! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! allArgumentVariables ^(self argumentNames asOrderedCollection) addAll: super allArgumentVariables; yourself! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! allDefinedVariables ^(self argumentNames asOrderedCollection) addAll: super allDefinedVariables; yourself! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! argumentNames ^self arguments collect: [:each | each name]! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! arguments ^arguments! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! arguments: argCollection arguments := argCollection. arguments do: [:each | each parent: self]! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! bar ^bar! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! bar: anObject bar := anObject! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! blockVariables | vars | vars := super blockVariables asOrderedCollection. vars addAll: self argumentNames. ^vars! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! body ^body! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! body: stmtsNode body := stmtsNode. body parent: self! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! children ^self arguments copyWith: self body! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! colons: aCollection colons := aCollection! ! !RBBlockNode methodsFor: 'matching' stamp: ''! copyInContext: aDictionary ^(self class new) arguments: (self copyList: arguments inContext: aDictionary); body: (body copyInContext: aDictionary); yourself! ! !RBBlockNode methodsFor: 'testing' stamp: ''! defines: aName ^arguments anySatisfy: [:each | each name = aName]! ! !RBBlockNode methodsFor: 'testing' stamp: ''! directlyUses: aNode ^false! ! !RBBlockNode methodsFor: 'comparing' stamp: ''! equalTo: anObject withMapping: aDictionary self class = anObject class ifFalse: [^false]. self arguments size = anObject arguments size ifFalse: [^false]. 1 to: self arguments size do: [:i | ((self arguments at: i) equalTo: (anObject arguments at: i) withMapping: aDictionary) ifFalse: [^false]]. (self body equalTo: anObject body withMapping: aDictionary) ifFalse: [^false]. self arguments do: [:each | aDictionary removeKey: each name]. ^true! ! !RBBlockNode methodsFor: 'comparing' stamp: ''! hash ^self arguments hash bitXor: self body hash! ! !RBBlockNode methodsFor: 'testing' stamp: ''! isBlock ^true! ! !RBBlockNode methodsFor: 'testing' stamp: ''! isImmediateNode ^true! ! !RBBlockNode methodsFor: 'testing' stamp: ''! isLast: aNode ^body isLast: aNode! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! left ^left! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! left: anObject left := anObject! ! !RBBlockNode methodsFor: 'matching' stamp: ''! match: aNode inContext: aDictionary aNode class == self class ifFalse: [^false]. ^(self matchList: arguments against: aNode arguments inContext: aDictionary) and: [body match: aNode body inContext: aDictionary]! ! !RBBlockNode methodsFor: 'testing' stamp: ''! needsParenthesis ^false! ! !RBBlockNode methodsFor: 'copying' stamp: 'lr 11/1/2009 22:36'! postCopy super postCopy. self arguments: (self arguments collect: [ :each | each copy ]). self body: self body copy! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! precedence ^0! ! !RBBlockNode methodsFor: 'testing' stamp: ''! references: aVariableName ^body references: aVariableName! ! !RBBlockNode methodsFor: 'replacing' stamp: ''! replaceNode: aNode withNode: anotherNode body == aNode ifTrue: [self body: anotherNode]. self arguments: (arguments collect: [:each | each == aNode ifTrue: [anotherNode] ifFalse: [each]])! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! right ^right! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! right: anObject right := anObject! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! startWithoutParentheses ^left! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! statementComments ^self comments! ! !RBBlockNode methodsFor: 'accessing' stamp: ''! stopWithoutParentheses ^right! ! !RBBlockNode methodsFor: 'testing' stamp: ''! uses: aNode aNode = body ifFalse: [^false]. ^parent isMessage ifTrue: [(#(#ifTrue:ifFalse: #ifTrue: #ifFalse: #ifFalse:ifTrue:) includes: parent selector) not or: [parent isUsed]] ifFalse: [self isUsed]! ! RBBlockNode subclass: #RBPatternBlockNode instanceVariableNames: 'valueBlock' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBPatternBlockNode commentStamp: 'md 8/9/2005 14:56' prior: 0! RBPatternBlockNode is the node in matching parse trees (it never occurs in normal Smalltalk code) that executes a block to determine if a match occurs. valueBlock takes two arguments, the first is the actual node that we are trying to match against, and second node is the dictionary that contains all the metavariable bindings that the matcher has made thus far. Instance Variables: valueBlock The block to execute when attempting to match this to a node. ! !RBPatternBlockNode methodsFor: 'visitor' stamp: ''! acceptVisitor: aProgramNodeVisitor ^aProgramNodeVisitor acceptPatternBlockNode: self! ! !RBPatternBlockNode methodsFor: 'matching' stamp: ''! addArgumentWithNameBasedOn: aString to: aRBBlockNode | name index vars | name := aString. vars := aRBBlockNode allDefinedVariables. index := 0. [vars includes: name] whileTrue: [index := index + 1. name := name , index printString]. aRBBlockNode arguments: (aRBBlockNode arguments copyWith: (RBVariableNode named: name))! ! !RBPatternBlockNode methodsFor: 'matching' stamp: ''! constructLookupNodeFor: aString in: aRBBlockNode | argumentNode | argumentNode := RBLiteralNode literalToken: (RBLiteralToken value: aString start: nil stop: nil). ^RBMessageNode receiver: (RBVariableNode named: 'self') selector: #lookupMatchFor:in: arguments: (Array with: argumentNode with: aRBBlockNode arguments last)! ! !RBPatternBlockNode methodsFor: 'matching' stamp: ''! copyInContext: aDictionary ^self replacingBlock value: aDictionary! ! !RBPatternBlockNode methodsFor: 'matching' stamp: ''! createBlockFor: aRBBlockNode | source | self replacePatternNodesIn: aRBBlockNode. source := aRBBlockNode formattedCode. ^Compiler evaluate: source for: self logged: false! ! !RBPatternBlockNode methodsFor: 'matching' stamp: ''! createMatchingBlock | newBlock | self arguments size > 2 ifTrue: [self error: 'Search blocks can only contain arguments for the node and matching dictionary']. newBlock := RBBlockNode arguments: arguments body: body. newBlock arguments isEmpty ifTrue: [self addArgumentWithNameBasedOn: 'aNode' to: newBlock]. newBlock arguments size = 1 ifTrue: [self addArgumentWithNameBasedOn: 'aDictionary' to: newBlock]. ^self createBlockFor: newBlock! ! !RBPatternBlockNode methodsFor: 'matching' stamp: ''! createReplacingBlock | newBlock | self arguments size > 1 ifTrue: [self error: 'Replace blocks can only contain an argument for the matching dictionary']. newBlock := RBBlockNode arguments: arguments body: body. self arguments isEmpty ifTrue: [self addArgumentWithNameBasedOn: 'aDictionary' to: newBlock]. ^self createBlockFor: newBlock! ! !RBPatternBlockNode methodsFor: 'matching' stamp: ''! lookupMatchFor: aString in: aDictionary ^aDictionary at: aString ifAbsent: [| variableNode | variableNode := RBPatternVariableNode named: aString. aDictionary at: variableNode ifAbsent: [nil]]! ! !RBPatternBlockNode methodsFor: 'matching' stamp: ''! match: aNode inContext: aDictionary ^self matchingBlock value: aNode value: aDictionary! ! !RBPatternBlockNode methodsFor: 'matching' stamp: ''! matchingBlock ^valueBlock isNil ifTrue: [valueBlock := self createMatchingBlock] ifFalse: [valueBlock]! ! !RBPatternBlockNode methodsFor: 'matching' stamp: ''! replacePatternNodesIn: aRBBlockNode aRBBlockNode body nodesDo: [:each | (each isVariable and: [each isPatternNode]) ifTrue: [each replaceWith: (self constructLookupNodeFor: each name in: aRBBlockNode)]]! ! !RBPatternBlockNode methodsFor: 'matching' stamp: ''! replacingBlock ^valueBlock isNil ifTrue: [valueBlock := self createReplacingBlock] ifFalse: [valueBlock]! ! !RBPatternBlockNode methodsFor: 'accessing' stamp: ''! sentMessages ^OrderedCollection new! ! RBPatternBlockNode subclass: #RBPatternWrapperBlockNode instanceVariableNames: 'wrappedNode' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBPatternWrapperBlockNode commentStamp: '' prior: 0! RBPatternWrapperBlockNode allows further matching using a block after a node has been matched by a pattern node. Instance Variables: wrappedNode The original pattern node to match! !RBPatternWrapperBlockNode methodsFor: 'visitor' stamp: ''! acceptVisitor: aProgramNodeVisitor ^aProgramNodeVisitor acceptPatternWrapperBlockNode: self! ! !RBPatternWrapperBlockNode methodsFor: 'matching' stamp: ''! copyInContext: aDictionary "I don't know what this would mean, so ignore it." ^wrappedNode copyInContext: aDictionary! ! !RBPatternWrapperBlockNode methodsFor: 'matching' stamp: ''! match: aNode inContext: aDictionary (wrappedNode match: aNode inContext: aDictionary) ifFalse: [^false]. ^super match: aNode inContext: aDictionary! ! !RBPatternWrapperBlockNode methodsFor: 'accessing' stamp: ''! precedence ^1! ! !RBPatternWrapperBlockNode methodsFor: 'accessing' stamp: ''! wrappedNode ^wrappedNode! ! !RBPatternWrapperBlockNode methodsFor: 'accessing' stamp: ''! wrappedNode: aRBProgramNode wrappedNode := aRBProgramNode. aRBProgramNode parent: self! ! RBValueNode subclass: #RBCascadeNode instanceVariableNames: 'messages semicolons' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBCascadeNode commentStamp: '' prior: 0! RBCascadeNode is an AST node for cascaded messages (e.g., "self print1 ; print2"). Instance Variables: messages the messages semicolons positions of the ; between messages ! !RBCascadeNode class methodsFor: 'instance creation' stamp: ''! messages: messageNodes ^self new messages: messageNodes! ! !RBCascadeNode class methodsFor: 'instance creation' stamp: ''! messages: messageNodes semicolons: integerCollection ^self new messages: messageNodes semicolons: integerCollection! ! !RBCascadeNode methodsFor: 'comparing' stamp: ''! = anObject self == anObject ifTrue: [^true]. self class = anObject class ifFalse: [^false]. self messages size = anObject messages size ifFalse: [^false]. 1 to: self messages size do: [:i | (self messages at: i) = (anObject messages at: i) ifFalse: [^false]]. ^true! ! !RBCascadeNode methodsFor: 'visitor' stamp: ''! acceptVisitor: aProgramNodeVisitor ^aProgramNodeVisitor acceptCascadeNode: self! ! !RBCascadeNode methodsFor: 'querying' stamp: ''! bestNodeFor: anInterval | selectedChildren | (self intersectsInterval: anInterval) ifFalse: [^nil]. (self containedBy: anInterval) ifTrue: [^self]. messages reverseDo: [:each | (each containedBy: anInterval) ifTrue: [^each]]. selectedChildren := (messages collect: [:each | each bestNodeFor: anInterval]) reject: [:each | each isNil]. ^selectedChildren detect: [:each | true] ifNone: [nil]! ! !RBCascadeNode methodsFor: 'accessing' stamp: ''! children ^self messages! ! !RBCascadeNode methodsFor: 'matching' stamp: ''! copyInContext: aDictionary ^(self class new) messages: (self copyList: messages inContext: aDictionary); yourself! ! !RBCascadeNode methodsFor: 'testing' stamp: ''! directlyUses: aNode ^messages last = aNode and: [self isDirectlyUsed]! ! !RBCascadeNode methodsFor: 'comparing' stamp: ''! equalTo: anObject withMapping: aDictionary self class = anObject class ifFalse: [^false]. self messages size == anObject messages size ifFalse: [^false]. 1 to: self messages size do: [:i | ((self messages at: i) equalTo: (anObject messages at: i) withMapping: aDictionary) ifFalse: [^false]]. ^true! ! !RBCascadeNode methodsFor: 'comparing' stamp: ''! hash ^self messages hash! ! !RBCascadeNode methodsFor: 'testing' stamp: ''! isCascade ^true! ! !RBCascadeNode methodsFor: 'matching' stamp: ''! match: aNode inContext: aDictionary aNode class == self class ifFalse: [^false]. ^self matchList: messages against: aNode messages inContext: aDictionary! ! !RBCascadeNode methodsFor: 'accessing' stamp: ''! messages ^messages! ! !RBCascadeNode methodsFor: 'accessing' stamp: ''! messages: messageNodeCollection messages := messageNodeCollection. messages do: [:each | each parent: self]! ! !RBCascadeNode methodsFor: 'initialize-release' stamp: ''! messages: messageNodes semicolons: integerCollection self messages: messageNodes. semicolons := integerCollection! ! !RBCascadeNode methodsFor: 'testing' stamp: ''! needsParenthesis ^parent isNil ifTrue: [false] ifFalse: [self precedence > parent precedence]! ! !RBCascadeNode methodsFor: 'copying' stamp: 'lr 11/1/2009 22:36'! postCopy super postCopy. self messages: (self messages collect: [ :each | each copy ])! ! !RBCascadeNode methodsFor: 'accessing' stamp: ''! precedence ^4! ! !RBCascadeNode methodsFor: 'accessing' stamp: ''! receiver ^self messages first receiver! ! !RBCascadeNode methodsFor: 'replacing' stamp: ''! replaceNode: aNode withNode: anotherNode self messages: (messages collect: [:each | each == aNode ifTrue: [anotherNode] ifFalse: [each]])! ! !RBCascadeNode methodsFor: 'accessing' stamp: ''! startWithoutParentheses ^messages first start! ! !RBCascadeNode methodsFor: 'accessing' stamp: ''! statementComments | statementComments | statementComments := OrderedCollection withAll: self comments. statementComments addAll: messages first receiver statementComments. messages do: [:each | each arguments do: [:arg | statementComments addAll: arg statementComments]]. ^statementComments asSortedCollection: [:a :b | a first < b first]! ! !RBCascadeNode methodsFor: 'accessing' stamp: ''! stopWithoutParentheses ^messages last stop! ! !RBCascadeNode methodsFor: 'testing' stamp: ''! uses: aNode ^messages last = aNode and: [self isUsed]! ! !RBCascadeNode methodsFor: 'querying' stamp: ''! whichNodeIsContainedBy: anInterval | selectedChildren | (self intersectsInterval: anInterval) ifFalse: [^nil]. (self containedBy: anInterval) ifTrue: [^self]. messages reverseDo: [:each | (each containedBy: anInterval) ifTrue: [^each]]. selectedChildren := (messages collect: [:each | each whichNodeIsContainedBy: anInterval]) reject: [:each | each isNil]. ^selectedChildren detect: [:each | true] ifNone: [nil]! ! RBValueNode subclass: #RBLiteralNode instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBLiteralNode commentStamp: 'md 8/9/2005 14:58' prior: 0! RBLiteralNode is an AST node that represents literals (e.g., #foo, #(1 2 3), true, etc.). Instance Variables: token the token that contains the literal value as well as its source positions ! RBLiteralNode subclass: #RBLiteralArrayNode instanceVariableNames: 'isByteArray stop contents start' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBLiteralArrayNode class methodsFor: 'instance creation' stamp: ''! startPosition: startInteger contents: anArray stopPosition: stopInteger isByteArray: aBoolean ^(self new) startPosition: startInteger contents: anArray stopPosition: stopInteger isByteArray: aBoolean; yourself! ! !RBLiteralArrayNode class methodsFor: 'instance creation' stamp: ''! value: aValue ^(self new) startPosition: nil contents: (aValue asArray collect: [:each | RBLiteralNode value: each]) stopPosition: nil isByteArray: aValue class ~~ Array; yourself! ! !RBLiteralArrayNode methodsFor: 'comparing' stamp: ''! = anObject super = anObject ifFalse: [^false]. self isForByteArray = anObject isForByteArray ifFalse: [^false]. self contents size = anObject contents size ifFalse: [^false]. 1 to: self contents size do: [:i | (self contents at: i) = (anObject contents at: i) ifFalse: [^false]]. ^true! ! !RBLiteralArrayNode methodsFor: 'visitor' stamp: ''! acceptVisitor: aProgramNodeVisitor ^aProgramNodeVisitor acceptLiteralArrayNode: self! ! !RBLiteralArrayNode methodsFor: 'accessing' stamp: ''! children ^contents! ! !RBLiteralArrayNode methodsFor: 'accessing' stamp: ''! contents ^contents! ! !RBLiteralArrayNode methodsFor: 'initialize-release' stamp: ''! contents: aRBLiteralNodeCollection contents := aRBLiteralNodeCollection. contents do: [:each | each parent: self]! ! !RBLiteralArrayNode methodsFor: 'matching' stamp: ''! copyInContext: aDictionary ^self class startPosition: nil contents: (self copyList: contents inContext: aDictionary) stopPosition: nil isByteArray: isByteArray! ! !RBLiteralArrayNode methodsFor: 'comparing' stamp: ''! equalTo: anObject withMapping: aDictionary self class = anObject class ifFalse: [^false]. self isForByteArray = anObject isForByteArray ifFalse: [^false]. self contents size = anObject contents size ifFalse: [^false]. 1 to: self contents size do: [:i | ((self contents at: i) equalTo: (anObject contents at: i) withMapping: aDictionary) ifFalse: [^false]]. ^true! ! !RBLiteralArrayNode methodsFor: 'testing' stamp: ''! isForByteArray ^isByteArray! ! !RBLiteralArrayNode methodsFor: 'testing' stamp: ''! isLiteralArray ^true! ! !RBLiteralArrayNode methodsFor: 'matching' stamp: ''! match: aNode inContext: aDictionary aNode class == self class ifFalse: [^false]. self isForByteArray = aNode isForByteArray ifFalse: [^false]. ^self matchList: contents against: aNode contents inContext: aDictionary! ! !RBLiteralArrayNode methodsFor: 'copying' stamp: 'lr 11/1/2009 22:35'! postCopy super postCopy. self contents: (self contents collect: [ :each | each copy ])! ! !RBLiteralArrayNode methodsFor: 'replacing' stamp: ''! replaceNode: aNode withNode: anotherNode self contents: (contents collect: [:each | each == aNode ifTrue: [anotherNode] ifFalse: [each]])! ! !RBLiteralArrayNode methodsFor: 'private-replacing' stamp: ''! replaceSourceWith: aNode (self class = aNode class and: [self isForByteArray = aNode isForByteArray and: [self contents size = aNode contents size]]) ifFalse: [^super replaceSourceWith: aNode]. 1 to: self contents size do: [:i | (self contents at: i) = (aNode contents at: i) ifFalse: [(self contents at: i) replaceSourceWith: (aNode contents at: i)]]! ! !RBLiteralArrayNode methodsFor: 'initialize-release' stamp: ''! startPosition: startInteger contents: anArray stopPosition: stopInteger isByteArray: aBoolean start := startInteger. self contents: anArray. stop := stopInteger. isByteArray := aBoolean! ! !RBLiteralArrayNode methodsFor: 'accessing' stamp: ''! startWithoutParentheses ^start! ! !RBLiteralArrayNode methodsFor: 'accessing' stamp: ''! stopWithoutParentheses ^stop! ! !RBLiteralArrayNode methodsFor: 'accessing' stamp: ''! value | array | array := (isByteArray ifTrue: [ByteArray] ifFalse: [Array]) new: contents size. 1 to: contents size do: [:each | array at: each put: (contents at: each) value]. ^array! ! !RBLiteralNode class methodsFor: 'instance creation' stamp: ''! literalToken: aLiteralToken ^(aLiteralToken realValue class == Array or: [aLiteralToken realValue class == ByteArray]) ifTrue: [RBLiteralArrayNode startPosition: aLiteralToken start contents: (aLiteralToken value asArray collect: [:each | RBLiteralNode literalToken: each]) stopPosition: aLiteralToken stop isByteArray: aLiteralToken value class ~~ Array] ifFalse: [RBLiteralValueNode literalToken: aLiteralToken]! ! !RBLiteralNode class methodsFor: 'instance creation' stamp: ''! value: aValue ^((aValue class == Array or: [aValue class == ByteArray]) ifTrue: [RBLiteralArrayNode] ifFalse: [RBLiteralValueNode]) value: aValue! ! !RBLiteralNode methodsFor: 'comparing' stamp: ''! = anObject self == anObject ifTrue: [^true]. ^self class == anObject class! ! !RBLiteralNode methodsFor: 'comparing' stamp: ''! hash ^self value hash! ! !RBLiteralNode methodsFor: 'testing' stamp: ''! isImmediateNode ^true! ! !RBLiteralNode methodsFor: 'testing' stamp: ''! isLiteralNode ^true! ! !RBLiteralNode methodsFor: 'testing' stamp: ''! needsParenthesis ^false! ! !RBLiteralNode methodsFor: 'accessing' stamp: ''! precedence ^0! ! !RBLiteralNode methodsFor: 'private-replacing' stamp: ''! replaceSourceFrom: aNode self addReplacement: (RBStringReplacement replaceFrom: aNode start to: aNode stop with: self formattedCode)! ! !RBLiteralNode methodsFor: 'private-replacing' stamp: ''! replaceSourceWith: aNode self addReplacement: (RBStringReplacement replaceFrom: self start to: self stop with: aNode formattedCode)! ! !RBLiteralNode methodsFor: 'accessing' stamp: ''! value ^self subclassResponsibility! ! RBLiteralNode subclass: #RBLiteralValueNode instanceVariableNames: 'token' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBLiteralValueNode commentStamp: '' prior: 0! RBLiteralNode is an AST node that represents literals (e.g., #foo, #(1 2 3), true, etc.). Instance Variables: token the token that contains the literal value as well as its source positions ! !RBLiteralValueNode class methodsFor: 'instance creation' stamp: ''! literalToken: aLiteralToken ^(self new) literalToken: aLiteralToken; yourself! ! !RBLiteralValueNode class methodsFor: 'instance creation' stamp: ''! value: aValue ^self literalToken: (RBLiteralToken value: aValue)! ! !RBLiteralValueNode methodsFor: 'comparing' stamp: ''! = anObject ^super = anObject and: [self value = anObject value and: [self value species == anObject value species]]! ! !RBLiteralValueNode methodsFor: 'visitor' stamp: ''! acceptVisitor: aProgramNodeVisitor ^aProgramNodeVisitor acceptLiteralNode: self! ! !RBLiteralValueNode methodsFor: 'matching' stamp: ''! copyInContext: aDictionary ^self class literalToken: token removePositions! ! !RBLiteralValueNode methodsFor: 'initialize-release' stamp: ''! literalToken: aLiteralToken token := aLiteralToken! ! !RBLiteralValueNode methodsFor: 'accessing' stamp: ''! startWithoutParentheses ^token start! ! !RBLiteralValueNode methodsFor: 'accessing' stamp: ''! stopWithoutParentheses ^token stop! ! !RBLiteralValueNode methodsFor: 'accessing' stamp: ''! token ^token! ! !RBLiteralValueNode methodsFor: 'accessing' stamp: ''! value ^token realValue! ! RBValueNode subclass: #RBMessageNode instanceVariableNames: 'receiver selector selectorParts arguments' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBMessageNode commentStamp: 'md 8/9/2005 14:58' prior: 0! RBMessageNode is an AST node that represents a message send. Instance Variables: arguments our argument nodes receiver the receiver's node selector the selector we're sending (cached) selectorParts the tokens for each keyword ! !RBMessageNode class methodsFor: 'instance creation' stamp: ''! receiver: aValueNode selector: aSymbol ^self receiver: aValueNode selector: aSymbol arguments: #()! ! !RBMessageNode class methodsFor: 'instance creation' stamp: ''! receiver: aValueNode selector: aSymbol arguments: valueNodes ^(self new) receiver: aValueNode; arguments: valueNodes; selector: aSymbol; yourself! ! !RBMessageNode class methodsFor: 'instance creation' stamp: ''! receiver: aValueNode selectorParts: keywordTokens arguments: valueNodes ^(self new) receiver: aValueNode selectorParts: keywordTokens arguments: valueNodes; yourself! ! !RBMessageNode methodsFor: 'comparing' stamp: ''! = anObject self == anObject ifTrue: [^true]. self class = anObject class ifFalse: [^false]. (self receiver = anObject receiver and: [self selector = anObject selector]) ifFalse: [^false]. 1 to: self arguments size do: [:i | (self arguments at: i) = (anObject arguments at: i) ifFalse: [^false]]. ^true! ! !RBMessageNode methodsFor: 'visitor' stamp: ''! acceptVisitor: aProgramNodeVisitor ^aProgramNodeVisitor acceptMessageNode: self! ! !RBMessageNode methodsFor: 'accessing' stamp: ''! arguments ^arguments isNil ifTrue: [#()] ifFalse: [arguments]! ! !RBMessageNode methodsFor: 'accessing' stamp: ''! arguments: argCollection arguments := argCollection. arguments do: [:each | each parent: self]! ! !RBMessageNode methodsFor: 'querying' stamp: ''! bestNodeFor: anInterval (self intersectsInterval: anInterval) ifFalse: [^nil]. (self containedBy: anInterval) ifTrue: [^self]. selectorParts do: [:each | ((anInterval first between: each start and: each stop) or: [each start between: anInterval first and: anInterval last]) ifTrue: [^self]]. self children do: [:each | | node | node := each bestNodeFor: anInterval. node notNil ifTrue: [^node]]! ! !RBMessageNode methodsFor: 'private' stamp: ''! buildSelector | selectorStream | selectorStream := WriteStream on: (String new: 50). selectorParts do: [:each | selectorStream nextPutAll: each value]. ^selectorStream contents asSymbol! ! !RBMessageNode methodsFor: 'accessing' stamp: ''! children ^(OrderedCollection with: self receiver) addAll: self arguments; yourself! ! !RBMessageNode methodsFor: 'matching' stamp: ''! copyInContext: aDictionary ^(self class new) receiver: (receiver copyInContext: aDictionary); selectorParts: (selectorParts collect: [:each | each removePositions]); arguments: (arguments collect: [:each | each copyInContext: aDictionary]); yourself! ! !RBMessageNode methodsFor: 'comparing' stamp: ''! equalTo: anObject withMapping: aDictionary self class = anObject class ifFalse: [^false]. ((self receiver equalTo: anObject receiver withMapping: aDictionary) and: [self selector = anObject selector]) ifFalse: [^false]. 1 to: self arguments size do: [:i | ((self arguments at: i) equalTo: (anObject arguments at: i) withMapping: aDictionary) ifFalse: [^false]]. ^true! ! !RBMessageNode methodsFor: 'comparing' stamp: ''! hash ^(self receiver hash bitXor: self selector hash) bitXor: (self arguments isEmpty ifTrue: [0] ifFalse: [self arguments first hash])! ! !RBMessageNode methodsFor: 'testing' stamp: ''! isBinary ^(self isUnary or: [self isKeyword]) not! ! !RBMessageNode methodsFor: 'testing' stamp: ''! isCascaded ^parent notNil and: [parent isCascade]! ! !RBMessageNode methodsFor: 'private-replacing' stamp: ''! isContainmentReplacement: aNode ^(self mappingFor: self receiver) = aNode or: [self arguments anySatisfy: [:each | (self mappingFor: each) = aNode]]! ! !RBMessageNode methodsFor: 'testing' stamp: ''! isFirstCascaded ^self isCascaded and: [parent messages first == self]! ! !RBMessageNode methodsFor: 'testing' stamp: 'lr 11/2/2009 23:37'! isKeyword ^selectorParts first value last = $:! ! !RBMessageNode methodsFor: 'testing' stamp: ''! isMessage ^true! ! !RBMessageNode methodsFor: 'testing' stamp: 'lr 10/20/2009 11:43'! isSelfSend ^ self receiver isVariable and: [ self receiver name = 'self' ]! ! !RBMessageNode methodsFor: 'testing' stamp: 'lr 10/20/2009 11:43'! isSuperSend ^ self receiver isVariable and: [ self receiver name = 'super' ]! ! !RBMessageNode methodsFor: 'testing' stamp: ''! isUnary ^arguments isEmpty! ! !RBMessageNode methodsFor: 'testing' stamp: ''! lastIsReturn ^(#(#ifTrue:ifFalse: #ifFalse:ifTrue:) includes: self selector) and: [arguments first isBlock and: [arguments first body lastIsReturn and: [arguments last isBlock and: [arguments last body lastIsReturn]]]]! ! !RBMessageNode methodsFor: 'matching' stamp: ''! match: aNode inContext: aDictionary aNode class == self class ifFalse: [^false]. self selector == aNode selector ifFalse: [^false]. (receiver match: aNode receiver inContext: aDictionary) ifFalse: [^false]. 1 to: arguments size do: [:i | ((arguments at: i) match: (aNode arguments at: i) inContext: aDictionary) ifFalse: [^false]]. ^true! ! !RBMessageNode methodsFor: 'testing' stamp: ''! needsParenthesis ^parent isNil ifTrue: [false] ifFalse: [self precedence > parent precedence or: [self precedence = parent precedence and: [self isUnary not]]]! ! !RBMessageNode methodsFor: 'accessing' stamp: ''! numArgs ^self selector numArgs! ! !RBMessageNode methodsFor: 'copying' stamp: 'lr 11/1/2009 22:36'! postCopy super postCopy. self receiver: self receiver copy. self arguments: (self arguments collect: [ :each | each copy ])! ! !RBMessageNode methodsFor: 'accessing' stamp: ''! precedence ^self isUnary ifTrue: [1] ifFalse: [self isKeyword ifTrue: [3] ifFalse: [2]]! ! !RBMessageNode methodsFor: 'accessing' stamp: ''! receiver ^receiver! ! !RBMessageNode methodsFor: 'accessing' stamp: ''! receiver: aValueNode receiver := aValueNode. receiver parent: self! ! !RBMessageNode methodsFor: 'initialize-release' stamp: ''! receiver: aValueNode selectorParts: keywordTokens arguments: valueNodes self receiver: aValueNode. selectorParts := keywordTokens. self arguments: valueNodes! ! !RBMessageNode methodsFor: 'accessing' stamp: ''! renameSelector: newSelector andArguments: varNodeCollection self arguments: varNodeCollection; selector: newSelector! ! !RBMessageNode methodsFor: 'private-replacing' stamp: ''! replaceContainmentSourceWith: aNode | originalNode needsParenthesis | needsParenthesis := aNode hasParentheses not and: [aNode needsParenthesis]. originalNode := (self mappingFor: self receiver) = aNode ifTrue: [self receiver] ifFalse: [self arguments detect: [:each | (self mappingFor: each) = aNode]]. self addReplacement: (RBStringReplacement replaceFrom: self start to: originalNode start - 1 with: (needsParenthesis ifTrue: ['('] ifFalse: [''])); addReplacement: (RBStringReplacement replaceFrom: originalNode stop + 1 to: self stop with: (needsParenthesis ifTrue: [')'] ifFalse: ['']))! ! !RBMessageNode methodsFor: 'replacing' stamp: ''! replaceNode: aNode withNode: anotherNode "If we're inside a cascade node and are changing the receiver, change all the receivers" receiver == aNode ifTrue: [self receiver: anotherNode. (parent notNil and: [parent isCascade]) ifTrue: [parent messages do: [:each | each receiver: anotherNode]]]. self arguments: (arguments collect: [:each | each == aNode ifTrue: [anotherNode] ifFalse: [each]])! ! !RBMessageNode methodsFor: 'private-replacing' stamp: ''! replaceSourceWith: aNode (self isContainmentReplacement: aNode) ifTrue: [^self replaceContainmentSourceWith: aNode]. aNode isMessage ifFalse: [^super replaceSourceWith: aNode]. ^self replaceSourceWithMessageNode: aNode! ! !RBMessageNode methodsFor: 'private-replacing' stamp: ''! replaceSourceWithMessageNode: aNode | isBinaryToKeyword | self numArgs = aNode numArgs ifFalse: [^super replaceSourceWith: aNode]. (self mappingFor: self receiver) = aNode receiver ifFalse: [^super replaceSourceWith: aNode]. self arguments with: aNode arguments do: [:old :new | (self mappingFor: old) = new ifFalse: [^super replaceSourceWith: aNode]]. (isBinaryToKeyword := self isBinary and: [aNode isKeyword]) ifTrue: [(self hasParentheses not and: [self parent precedence <= aNode precedence]) ifTrue: [self addReplacement: (RBStringReplacement replaceFrom: self start to: self start - 1 with: '('); addReplacement: (RBStringReplacement replaceFrom: self stop + 1 to: self stop with: ')')]]. self selectorParts with: aNode selectorParts do: [:old :new | old value ~= new value ifTrue: [self addReplacement: (RBStringReplacement replaceFrom: old start to: old stop with: ((isBinaryToKeyword and: [(self source at: old start - 1) isSeparator not]) ifTrue: [' ' , new value] ifFalse: [new value]))]]! ! !RBMessageNode methodsFor: 'accessing' stamp: ''! selector ^selector isNil ifTrue: [selector := self buildSelector] ifFalse: [selector]! ! !RBMessageNode methodsFor: 'accessing' stamp: 'lr 11/2/2009 23:37'! selector: aSelector | keywords numArgs | keywords := aSelector keywords. numArgs := aSelector numArgs. numArgs == arguments size ifFalse: [self error: 'Attempting to assign selector with wrong number of arguments.']. selectorParts := numArgs == 0 ifTrue: [Array with: (RBIdentifierToken value: keywords first start: nil)] ifFalse: [keywords first last = $: ifTrue: [keywords collect: [:each | RBKeywordToken value: each start: nil]] ifFalse: [Array with: (RBBinarySelectorToken value: aSelector start: nil)]]. selector := aSelector! ! !RBMessageNode methodsFor: 'private' stamp: ''! selectorParts ^selectorParts! ! !RBMessageNode methodsFor: 'private' stamp: ''! selectorParts: tokenCollection selectorParts := tokenCollection! ! !RBMessageNode methodsFor: 'accessing' stamp: ''! sentMessages ^(super sentMessages) add: self selector; yourself! ! !RBMessageNode methodsFor: 'accessing' stamp: ''! startWithoutParentheses ^receiver start! ! !RBMessageNode methodsFor: 'accessing' stamp: ''! stopWithoutParentheses ^arguments isEmpty ifTrue: [selectorParts first stop] ifFalse: [arguments last stop]! ! RBMessageNode subclass: #RBPatternMessageNode instanceVariableNames: 'isList isCascadeList' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBPatternMessageNode commentStamp: 'md 8/9/2005 14:58' prior: 0! RBPatternMessageNode is a RBMessageNode that will match other message nodes without their selectors being equal. Instance Variables: isCascadeList are we matching a list of message nodes in a cascaded message isList are we matching each keyword or matching all keywords together (e.g., `keyword1: would match a one argument method whereas `@keywords: would match 0 or more arguments)! !RBPatternMessageNode class methodsFor: 'instance creation' stamp: ''! receiver: aValueNode selectorParts: keywordTokens arguments: valueNodes ^(keywordTokens anySatisfy: [:each | each isPatternVariable]) ifTrue: [super receiver: aValueNode selectorParts: keywordTokens arguments: valueNodes] ifFalse: [RBMessageNode receiver: aValueNode selectorParts: keywordTokens arguments: valueNodes]! ! !RBPatternMessageNode methodsFor: 'matching' stamp: 'lr 11/2/2009 23:37'! copyInContext: aDictionary | selectors | self isList ifTrue: [^aDictionary at: self]. selectors := self isSelectorList ifTrue: [(aDictionary at: selectorParts first value) keywords] ifFalse: [selectorParts collect: [:each | aDictionary at: each value]]. ^(RBMessageNode new) receiver: (receiver copyInContext: aDictionary); selectorParts: (selectors collect: [:each | (each last = $: ifTrue: [RBKeywordToken] ifFalse: [RBIdentifierToken]) value: each start: nil]); arguments: (self copyList: arguments inContext: aDictionary); yourself! ! !RBPatternMessageNode methodsFor: 'testing-matching' stamp: ''! isList ^isCascadeList and: [parent notNil and: [parent isCascade]]! ! !RBPatternMessageNode methodsFor: 'testing-matching' stamp: ''! isPatternNode ^true! ! !RBPatternMessageNode methodsFor: 'testing-matching' stamp: ''! isSelectorList ^isList! ! !RBPatternMessageNode methodsFor: 'matching' stamp: ''! match: aNode inContext: aDictionary aNode class == self matchingClass ifFalse: [^false]. (receiver match: aNode receiver inContext: aDictionary) ifFalse: [^false]. self isSelectorList ifTrue: [^(aDictionary at: selectorParts first value ifAbsentPut: [aNode selector]) == aNode selector and: [(aDictionary at: arguments first ifAbsentPut: [aNode arguments]) = aNode arguments]]. ^self matchArgumentsAgainst: aNode inContext: aDictionary! ! !RBPatternMessageNode methodsFor: 'matching' stamp: ''! matchArgumentsAgainst: aNode inContext: aDictionary self arguments size == aNode arguments size ifFalse: [^false]. (self matchSelectorAgainst: aNode inContext: aDictionary) ifFalse: [^false]. 1 to: arguments size do: [:i | ((arguments at: i) match: (aNode arguments at: i) inContext: aDictionary) ifFalse: [^false]]. ^true! ! !RBPatternMessageNode methodsFor: 'matching' stamp: ''! matchSelectorAgainst: aNode inContext: aDictionary | keyword | 1 to: selectorParts size do: [:i | keyword := selectorParts at: i. (aDictionary at: keyword value ifAbsentPut: [keyword isPatternVariable ifTrue: [(aNode selectorParts at: i) value] ifFalse: [keyword value]]) = (aNode selectorParts at: i) value ifFalse: [^false]]. ^true! ! !RBPatternMessageNode methodsFor: 'private' stamp: ''! matchingClass ^RBMessageNode! ! !RBPatternMessageNode methodsFor: 'initialize-release' stamp: ''! receiver: aValueNode selectorParts: keywordTokens arguments: valueNodes | message | super receiver: aValueNode selectorParts: keywordTokens arguments: valueNodes. isCascadeList := isList := false. message := keywordTokens first value. 2 to: message size do: [:i | | character | character := message at: i. character == self listCharacter ifTrue: [isList := true] ifFalse: [character == self cascadeListCharacter ifTrue: [isCascadeList := true] ifFalse: [^self]]]! ! !RBPatternMessageNode methodsFor: 'accessing' stamp: ''! sentMessages ^(super sentMessages) remove: self selector ifAbsent: []; yourself! ! !RBValueNode methodsFor: 'accessing' stamp: ''! addParenthesis: anInterval parentheses isNil ifTrue: [parentheses := OrderedCollection new: 1]. parentheses add: anInterval! ! !RBValueNode methodsFor: 'testing' stamp: ''! containedBy: anInterval ^anInterval first <= self startWithoutParentheses and: [anInterval last >= self stopWithoutParentheses]! ! !RBValueNode methodsFor: 'testing' stamp: ''! hasParentheses ^self parentheses notEmpty! ! !RBValueNode methodsFor: 'testing' stamp: ''! isValue ^true! ! !RBValueNode methodsFor: 'testing' stamp: ''! needsParenthesis ^self subclassResponsibility! ! !RBValueNode methodsFor: 'accessing' stamp: ''! parentheses ^parentheses isNil ifTrue: [#()] ifFalse: [parentheses]! ! !RBValueNode methodsFor: 'accessing' stamp: ''! start ^parentheses isNil ifTrue: [self startWithoutParentheses] ifFalse: [parentheses last first]! ! !RBValueNode methodsFor: 'accessing' stamp: ''! startWithoutParentheses ^self subclassResponsibility! ! !RBValueNode methodsFor: 'accessing' stamp: ''! stop ^parentheses isNil ifTrue: [self stopWithoutParentheses] ifFalse: [parentheses last last]! ! !RBValueNode methodsFor: 'accessing' stamp: ''! stopWithoutParentheses ^self subclassResponsibility! ! RBValueNode subclass: #RBVariableNode instanceVariableNames: 'token' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBVariableNode commentStamp: 'md 8/9/2005 15:00' prior: 0! RBVariableNode is an AST node that represent a variable (global, inst var, temp, etc.). Instance Variables: token the token that contains our name and position ! RBVariableNode subclass: #RBPatternVariableNode instanceVariableNames: 'recurseInto isList isLiteral isStatement isAnything' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBPatternVariableNode commentStamp: 'md 8/9/2005 14:59' prior: 0! RBPatternVariableNode is an AST node that is used to match several other types of nodes (literals, variables, value nodes, statement nodes, and sequences of statement nodes). The different types of matches are determined by the name of the node. If the name contains a # character, then it will match a literal. If it contains, a . then it matches statements. If it contains no extra characters, then it matches only variables. These options are mutually exclusive. The @ character can be combined with the name to match lists of items. If combined with the . character, then it will match a list of statement nodes (0 or more). If used without the . or # character, then it matches anything except for list of statements. Combining the @ with the # is not supported. Adding another ` in the name will cause the search/replace to look for more matches inside the node that this node matched. This option should not be used for top level expressions since that would cause infinite recursion (e.g., searching only for "``@anything"). Instance Variables: isAnything can we match any type of node isList can we match a list of items (@) isLiteral only match a literal node (#) isStatement only match statements (.) recurseInto search for more matches in the node we match (`) ! !RBPatternVariableNode class methodsFor: 'instance creation' stamp: ''! identifierToken: anIdentifierToken ^anIdentifierToken isPatternVariable ifTrue: [super identifierToken: anIdentifierToken] ifFalse: [RBVariableNode identifierToken: anIdentifierToken]! ! !RBPatternVariableNode methodsFor: 'matching' stamp: ''! copyInContext: aDictionary ^(aDictionary at: self) copy! ! !RBPatternVariableNode methodsFor: 'initialize-release' stamp: ''! identifierToken: anIdentifierToken super identifierToken: anIdentifierToken. self initializePatternVariables! ! !RBPatternVariableNode methodsFor: 'initialize-release' stamp: ''! initializePatternVariables | name | name := self name. isAnything := isList := isLiteral := isStatement := recurseInto := false. 2 to: name size do: [:i | | character | character := name at: i. character == self listCharacter ifTrue: [isAnything := isList := true] ifFalse: [character == self literalCharacter ifTrue: [isLiteral := true] ifFalse: [character == self statementCharacter ifTrue: [isStatement := true] ifFalse: [character == self recurseIntoCharacter ifTrue: [recurseInto := true] ifFalse: [^self]]]]]! ! !RBPatternVariableNode methodsFor: 'testing-matching' stamp: ''! isAnything ^isAnything! ! !RBPatternVariableNode methodsFor: 'testing-matching' stamp: ''! isList ^isList! ! !RBPatternVariableNode methodsFor: 'testing-matching' stamp: ''! isLiteralNode ^isLiteral! ! !RBPatternVariableNode methodsFor: 'testing-matching' stamp: ''! isPatternNode ^true! ! !RBPatternVariableNode methodsFor: 'testing-matching' stamp: ''! isStatement ^isStatement! ! !RBPatternVariableNode methodsFor: 'matching' stamp: ''! match: aNode inContext: aDictionary self isAnything ifTrue: [^(aDictionary at: self ifAbsentPut: [aNode]) = aNode]. self isLiteralNode ifTrue: [^self matchLiteral: aNode inContext: aDictionary]. self isStatement ifTrue: [^self matchStatement: aNode inContext: aDictionary]. aNode class == self matchingClass ifFalse: [^false]. ^(aDictionary at: self ifAbsentPut: [aNode]) = aNode! ! !RBPatternVariableNode methodsFor: 'matching' stamp: ''! matchLiteral: aNode inContext: aDictionary ^aNode isLiteralNode and: [(aDictionary at: self ifAbsentPut: [aNode]) = aNode]! ! !RBPatternVariableNode methodsFor: 'matching' stamp: ''! matchStatement: aNode inContext: aDictionary (aNode parent notNil and: [aNode parent isSequence]) ifFalse: [^false]. ^(aDictionary at: self ifAbsentPut: [aNode]) = aNode! ! !RBPatternVariableNode methodsFor: 'private' stamp: ''! matchingClass ^RBVariableNode! ! !RBPatternVariableNode methodsFor: 'accessing' stamp: ''! parent: aBRProgramNode "Fix the case where '``@node' should match a single node, not a sequence node." super parent: aBRProgramNode. parent isSequence ifTrue: [(self isStatement or: [parent temporaries includes: self]) ifFalse: [isList := false]]! ! !RBPatternVariableNode methodsFor: 'testing-matching' stamp: ''! recurseInto ^recurseInto! ! !RBVariableNode class methodsFor: 'instance creation' stamp: ''! identifierToken: anIdentifierToken ^(self new) identifierToken: anIdentifierToken; yourself! ! !RBVariableNode class methodsFor: 'instance creation' stamp: ''! named: aString ^self identifierToken: (RBIdentifierToken value: aString start: 0)! ! !RBVariableNode methodsFor: 'comparing' stamp: ''! = anObject self == anObject ifTrue: [^true]. self class = anObject class ifFalse: [^false]. ^self name = anObject name! ! !RBVariableNode methodsFor: 'visitor' stamp: ''! acceptVisitor: aProgramNodeVisitor ^aProgramNodeVisitor acceptVariableNode: self! ! !RBVariableNode methodsFor: 'matching' stamp: ''! copyInContext: aDictionary ^self class identifierToken: token removePositions! ! !RBVariableNode methodsFor: 'comparing' stamp: ''! equalTo: anObject withMapping: aDictionary ^self class = anObject class and: [(aDictionary at: self name ifAbsentPut: [anObject name]) = anObject name]! ! !RBVariableNode methodsFor: 'comparing' stamp: ''! hash ^self name hash! ! !RBVariableNode methodsFor: 'initialize-release' stamp: ''! identifierToken: anIdentifierToken token := anIdentifierToken! ! !RBVariableNode methodsFor: 'testing' stamp: ''! isImmediateNode ^true! ! !RBVariableNode methodsFor: 'testing' stamp: 'TestRunner 11/2/2009 21:18'! isRead ^ self isWrite not and: [ self isUsed ]! ! !RBVariableNode methodsFor: 'testing' stamp: ''! isVariable ^true! ! !RBVariableNode methodsFor: 'testing' stamp: 'TestRunner 11/2/2009 21:21'! isWrite ^ self parent notNil and: [ self parent isAssignment and: [ self parent variable == self ] ]! ! !RBVariableNode methodsFor: 'accessing' stamp: ''! name ^token value! ! !RBVariableNode methodsFor: 'testing' stamp: ''! needsParenthesis ^false! ! !RBVariableNode methodsFor: 'accessing' stamp: ''! precedence ^0! ! !RBVariableNode methodsFor: 'testing' stamp: ''! references: aVariableName ^self name = aVariableName! ! !RBVariableNode methodsFor: 'replacing' stamp: ''! replaceSourceFrom: aNode self addReplacement: (RBStringReplacement replaceFrom: aNode start to: aNode stop with: self name)! ! !RBVariableNode methodsFor: 'replacing' stamp: ''! replaceSourceWith: aNode self addReplacement: (RBStringReplacement replaceFrom: self start to: self stop with: aNode formattedCode)! ! !RBVariableNode methodsFor: 'accessing' stamp: ''! startWithoutParentheses ^token start! ! !RBVariableNode methodsFor: 'accessing' stamp: ''! stopWithoutParentheses ^token stop! ! Object subclass: #RBProgramNodeVisitor instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Visitors'! !RBProgramNodeVisitor commentStamp: '' prior: 0! RBProgramNodeVisitor is an abstract visitor for the RBProgramNodes. ! RBProgramNodeVisitor subclass: #RBConfigurableFormatter instanceVariableNames: 'codeStream indent lookaheadCode originalSource lineStart' classVariableNames: 'FormatCommentWithStatements IndentString IndentsForKeywords LineUpBlockBrackets MaxLineLength MethodSignatureOnMultipleLines MinimumNewLinesBetweenStatements NewLineAfterCascade NewLineBeforeFirstCascade NewLineBeforeFirstKeyword NewLinesAfterMethodComment NewLinesAfterMethodPattern NewLinesAfterTemporaries NumberOfArgumentsForMultiLine PeriodsAsTerminators PeriodsAtEndOfBlock PeriodsAtEndOfMethod RetainBlankLinesBetweenStatements StringFollowingReturn StringInsideBlocks StringInsideParentheses TraditionalBinaryPrecedence UseTraditionalBinaryPrecedenceForParentheses' poolDictionaries: '' category: 'AST-Core-Visitors'! !RBConfigurableFormatter commentStamp: '' prior: 0! RBConfigurableFormatter formats the Refactoring Browser's parse trees. It has many more formatting options than the default formatter used by the RB. To change the RB to use this formatter, execute "RBProgramNode formatterClass: RBConfigurableFormatter". For some refactorings the RB must reformat the code after the change, so it is good to have a formatter configured to your tastes. Instance Variables: codeStream the stream we are writing our output to indent how many times are we indenting a new line -- indents are normally tabs but could be any whitespace string lineStart the position of the character that started the current line. This is used for calculating the line length. lookaheadCode sometimes we need to lookahead while formatting, this dictionary contains the nodes that have already been formatted by lookahead originalSource the original source before we started formatting. This is used to extract the comments from the original source. ! !RBConfigurableFormatter class methodsFor: 'public interface' stamp: ''! format: aParseTree ^self format: aParseTree withIndents: 0! ! !RBConfigurableFormatter class methodsFor: 'public interface' stamp: ''! format: aParseTree withIndents: anInteger ^(self new) indent: anInteger; format: aParseTree! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! formatCommentWithStatements ^FormatCommentWithStatements! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! formatCommentWithStatements: aBoolean FormatCommentWithStatements := aBoolean! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! indentString ^IndentString! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! indentString: aString IndentString := aString! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! indentsForKeywords ^IndentsForKeywords! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! indentsForKeywords: anInteger IndentsForKeywords := anInteger! ! !RBConfigurableFormatter class methodsFor: 'initialization' stamp: 'lr 11/2/2009 10:17'! initialize FormatCommentWithStatements := true. IndentString := String with: Character tab. IndentsForKeywords := 1. LineUpBlockBrackets := false. MaxLineLength := 90. MethodSignatureOnMultipleLines := false. MinimumNewLinesBetweenStatements := 1. NewLineAfterCascade := true. NewLineBeforeFirstCascade := true. NewLineBeforeFirstKeyword := false. NewLinesAfterMethodComment := 2. NewLinesAfterMethodPattern := 1. NewLinesAfterTemporaries := 1. NumberOfArgumentsForMultiLine := 2. PeriodsAsTerminators := false. PeriodsAtEndOfBlock := false. PeriodsAtEndOfMethod := false. RetainBlankLinesBetweenStatements := false. StringFollowingReturn := ' '. StringInsideBlocks := ' '. StringInsideParentheses := ''. TraditionalBinaryPrecedence := #(#($| $& $?) #($= $~ $< $>) #($- $+) #($* $/ $% $\) #($@)). UseTraditionalBinaryPrecedenceForParentheses := true! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! lineUpBlockBrackets ^LineUpBlockBrackets! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! lineUpBlockBrackets: aBoolean LineUpBlockBrackets := aBoolean! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! maxLineLength ^MaxLineLength! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! maxLineLength: anInteger MaxLineLength := anInteger! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! methodSignatureOnMultipleLines ^MethodSignatureOnMultipleLines! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! methodSignatureOnMultipleLines: aBoolean MethodSignatureOnMultipleLines := aBoolean! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! minimumNewLinesBetweenStatements ^MinimumNewLinesBetweenStatements! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! minimumNewLinesBetweenStatements: anInteger MinimumNewLinesBetweenStatements := anInteger! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! newLineAfterCascade ^NewLineAfterCascade! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! newLineAfterCascade: aBoolean NewLineAfterCascade := aBoolean! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! newLineBeforeFirstCascade ^NewLineBeforeFirstCascade! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! newLineBeforeFirstCascade: aBoolean NewLineBeforeFirstCascade := aBoolean! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! newLineBeforeFirstKeyword ^NewLineBeforeFirstKeyword! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! newLineBeforeFirstKeyword: aBoolean NewLineBeforeFirstKeyword := aBoolean! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! newLinesAfterMethodComment ^NewLinesAfterMethodComment! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! newLinesAfterMethodComment: anInteger NewLinesAfterMethodComment := anInteger.! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! newLinesAfterMethodPattern ^NewLinesAfterMethodPattern! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! newLinesAfterMethodPattern: anInteger NewLinesAfterMethodPattern := anInteger! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! newLinesAfterTemporaries ^NewLinesAfterTemporaries! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! newLinesAfterTemporaries: anInteger NewLinesAfterTemporaries := anInteger! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! numberOfArgumentsForMultiLine ^NumberOfArgumentsForMultiLine! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! numberOfArgumentsForMultiLine: anInteger NumberOfArgumentsForMultiLine := anInteger! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! periodsAsTerminators ^PeriodsAtEndOfBlock and: [PeriodsAtEndOfMethod]! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! periodsAsTerminators: aBoolean PeriodsAtEndOfBlock := aBoolean. PeriodsAtEndOfMethod := aBoolean! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! periodsAtEndOfBlock ^PeriodsAtEndOfBlock! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! periodsAtEndOfBlock: aBoolean PeriodsAtEndOfBlock := aBoolean! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! periodsAtEndOfMethod ^PeriodsAtEndOfMethod! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! periodsAtEndOfMethod: aBoolean PeriodsAtEndOfMethod := aBoolean! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! retainBlankLinesBetweenStatements ^RetainBlankLinesBetweenStatements! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! retainBlankLinesBetweenStatements: aBoolean RetainBlankLinesBetweenStatements := aBoolean! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! stringFollowingReturn ^StringFollowingReturn! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! stringFollowingReturn: aString StringFollowingReturn := aString! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! stringInsideBlocks ^StringInsideBlocks! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! stringInsideBlocks: aString StringInsideBlocks := aString! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! stringInsideParentheses ^StringInsideParentheses! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! stringInsideParentheses: aString StringInsideParentheses := aString! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! traditionalBinaryPrecedence ^TraditionalBinaryPrecedence! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! traditionalBinaryPrecedence: anArray TraditionalBinaryPrecedence := anArray! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! useTraditionalBinaryPrecedenceForParentheses ^UseTraditionalBinaryPrecedenceForParentheses! ! !RBConfigurableFormatter class methodsFor: 'settings' stamp: ''! useTraditionalBinaryPrecedenceForParentheses: aBoolean UseTraditionalBinaryPrecedenceForParentheses := aBoolean! ! !RBConfigurableFormatter methodsFor: 'visitor-double dispatching' stamp: 'lr 11/2/2009 09:31'! acceptArrayNode: anArrayNode self bracketWith: '{}' around: [ self formatArray: anArrayNode ]! ! !RBConfigurableFormatter methodsFor: 'visitor-double dispatching' stamp: 'lr 11/2/2009 20:53'! acceptAssignmentNode: anAssignmentNode self visitNode: anAssignmentNode variable. codeStream space; nextPutAll: anAssignmentNode assignmentOperator; space. self visitNode: anAssignmentNode value! ! !RBConfigurableFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptBlockNode: aBlockNode self bracketWith: '[]' around: [self formatBlock: aBlockNode]! ! !RBConfigurableFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptCascadeNode: aCascadeNode self visitNode: aCascadeNode receiver. self indentAround: [NewLineBeforeFirstCascade ifTrue: [self newLine] ifFalse: [self space]. aCascadeNode messages do: [:each | self indentAround: [self formatSelectorAndArguments: each firstSeparator: [] restSeparator: ((self isMultiLineMessage: each) ifTrue: [[self newLine]] ifFalse: [[self space]])]] separatedBy: [codeStream nextPut: $;. NewLineAfterCascade ifTrue: [self newLine] ifFalse: [self space]]]! ! !RBConfigurableFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptLiteralArrayNode: aRBArrayLiteralNode | brackets | codeStream nextPut: $#. brackets := aRBArrayLiteralNode isForByteArray ifTrue: ['[]'] ifFalse: ['()']. self bracketWith: brackets around: [aRBArrayLiteralNode contents do: [:each | self visitNode: each] separatedBy: [self space]]! ! !RBConfigurableFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptLiteralNode: aLiteralNode self writeString: aLiteralNode token storeString! ! !RBConfigurableFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptMessageNode: aMessageNode self visitNode: aMessageNode receiver. self formatSelectorAndArguments: aMessageNode! ! !RBConfigurableFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptMethodNode: aMethodNode self formatMethodPatternFor: aMethodNode. self formatMethodBodyFor: aMethodNode! ! !RBConfigurableFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptPatternBlockNode: aRBPatternBlockNode codeStream nextPut: $`. self bracketWith: '{}' around: [self formatBlock: aRBPatternBlockNode]! ! !RBConfigurableFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptPatternWrapperBlockNode: aRBPatternWrapperBlockNode self visitNode: aRBPatternWrapperBlockNode wrappedNode. codeStream nextPut: $`. self bracketWith: '{}' around: [self formatBlock: aRBPatternWrapperBlockNode]! ! !RBConfigurableFormatter methodsFor: 'visitor-double dispatching' stamp: 'lr 11/2/2009 09:27'! acceptPragmaNode: aPragmaNode codeStream nextPut: $<. self formatSelectorAndArguments: aPragmaNode firstSeparator: [ ] restSeparator: [ self space ]. codeStream nextPut: $>! ! !RBConfigurableFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptReturnNode: aReturnNode codeStream nextPut: $^; nextPutAll: StringFollowingReturn. self visitNode: aReturnNode value! ! !RBConfigurableFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptSequenceNode: aSequenceNode self formatTemporariesFor: aSequenceNode. self formatSequenceCommentsFor: aSequenceNode. self formatSequenceNodeStatementsFor: aSequenceNode! ! !RBConfigurableFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptVariableNode: aVariableNode codeStream nextPutAll: aVariableNode name! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: ''! addNewLinesBeforeStatementStartingAt: anInteger | newLines | newLines := MinimumNewLinesBetweenStatements max: (RetainBlankLinesBetweenStatements ifTrue: [self newLinesBeforeStartingAt: anInteger] ifFalse: [0]). newLines = 0 ifTrue: [self space] ifFalse: [self newLines: newLines]! ! !RBConfigurableFormatter methodsFor: 'private' stamp: ''! bracketWith: bracketString around: aBlock bracketString isEmpty ifTrue: [^aBlock value]. codeStream nextPut: bracketString first. ^aBlock ensure: [codeStream nextPut: bracketString last]! ! !RBConfigurableFormatter methodsFor: 'private' stamp: ''! currentLineLength ^codeStream position - lineStart! ! !RBConfigurableFormatter methodsFor: 'public interface' stamp: ''! format: aParseTree originalSource := aParseTree source. self visitNode: aParseTree. ^codeStream contents! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: 'lr 11/2/2009 09:31'! formatArray: anArrayNode self formatSequenceCommentsFor: anArrayNode. self formatSequenceNodeStatementsFor: anArrayNode! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: ''! formatBlock: aBlockNode (LineUpBlockBrackets and: [self willBeMultiline: aBlockNode body]) ifTrue: [self newLine] ifFalse: [codeStream nextPutAll: StringInsideBlocks]. self formatBlockArgumentsFor: aBlockNode. self visitNode: aBlockNode body. ^(LineUpBlockBrackets and: [self willBeMultiline: aBlockNode body]) ifTrue: [self newLine] ifFalse: [codeStream nextPutAll: StringInsideBlocks]! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: ''! formatBlockArgumentsFor: aBlockNode aBlockNode arguments isEmpty ifTrue: [^self]. aBlockNode arguments do: [:each | codeStream nextPut: $:. self visitNode: each. FormatCommentWithStatements ifTrue: [self formatCommentsFor: each]. self space]. codeStream nextPutAll: '| '. (self willBeMultiline: aBlockNode body) ifTrue: [self newLine]! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: ''! formatCommentsFor: aNode originalSource isNil ifTrue: [^self]. aNode comments do: [:each | codeStream space; nextPutAll: (originalSource copyFrom: each first to: each last)]! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: 'lr 11/2/2009 09:22'! formatMethodBodyFor: aMethodNode self indentAround: [self newLines: NewLinesAfterMethodPattern. self formatMethodCommentFor: aMethodNode. self formatPragmasFor: aMethodNode. self visitNode: aMethodNode body]! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: ''! formatMethodCommentFor: aMethodNode originalSource isNil ifTrue: [^self]. (FormatCommentWithStatements ifTrue: [aMethodNode methodComments] ifFalse: [aMethodNode comments]) do: [:each | codeStream nextPutAll: (originalSource copyFrom: each first to: each last). self newLines: NewLinesAfterMethodComment]! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: ''! formatMethodPatternFor: aMethodNode aMethodNode arguments isEmpty ifTrue: [codeStream nextPutAll: aMethodNode selector] ifFalse: [self with: aMethodNode selectorParts and: aMethodNode arguments do: [:key :arg | codeStream nextPutAll: key value. self space. self visitNode: arg] separatedBy: [MethodSignatureOnMultipleLines ifTrue: [self newLine] ifFalse: [self space]]]! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: 'lr 11/2/2009 10:09'! formatPragmasFor: aMethodNode aMethodNode pragmas do: [ :each | self visitNode: each; newLine ]! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: ''! formatSelectorAndArguments: aMessageNode | newLineBetweenArguments | newLineBetweenArguments := self isMultiLineMessage: aMessageNode. self indent: IndentsForKeywords around: [self formatSelectorAndArguments: aMessageNode firstSeparator: (newLineBetweenArguments & NewLineBeforeFirstKeyword ifTrue: [[self newLine]] ifFalse: [[self space]]) restSeparator: (newLineBetweenArguments ifTrue: [[self newLine]] ifFalse: [[self space]])]! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: ''! formatSelectorAndArguments: aMessageNode firstSeparator: firstBlock restSeparator: restBlock | separatorBlock | separatorBlock := firstBlock. aMessageNode isUnary ifTrue: [(self isLineTooLong: aMessageNode selector) ifTrue: [self newLine] ifFalse: [separatorBlock value]. codeStream nextPutAll: aMessageNode selector] ifFalse: [aMessageNode selectorParts with: aMessageNode arguments do: [:selector :argument | (self isLineTooLong: selector value) ifTrue: [self newLine] ifFalse: [separatorBlock value]. separatorBlock := restBlock. self indentAround: [codeStream nextPutAll: selector value. ((self willBeMultiline: argument) or: [self isLineTooLong: (self formattedSourceFor: argument)]) ifTrue: [self newLine] ifFalse: [self space]. self visitNode: argument]]]! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: ''! formatSequenceCommentsFor: aSequenceNode originalSource isNil ifTrue: [^self]. aSequenceNode comments do: [:each | codeStream nextPutAll: (originalSource copyFrom: each first to: each last). self newLine]! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: ''! formatSequenceNodeStatementsFor: aSequenceNode | statements | statements := aSequenceNode statements. statements isEmpty ifTrue: [^self]. 1 to: statements size do: [:i | self visitNode: (statements at: i). (i < statements size or: [aSequenceNode parent ifNil: [self class periodsAsTerminators] ifNotNil: [:parent | parent isBlock ifTrue: [self class periodsAtEndOfBlock] ifFalse: [self class periodsAtEndOfMethod]]]) ifTrue: [codeStream nextPut: $.]. self formatStatementCommentsFor: (statements at: i). i < statements size ifTrue: [self addNewLinesBeforeStatementStartingAt: (statements at: i + 1) start]]! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: ''! formatStatementCommentsFor: aStatementNode originalSource isNil ifTrue: [^self]. FormatCommentWithStatements ifFalse: [^self]. aStatementNode statementComments do: [:each | codeStream tab; nextPutAll: (originalSource copyFrom: each first to: each last)]! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: ''! formatTagsFor: aMethodNode aMethodNode primitiveSources do: [:each | codeStream nextPutAll: each. self newLine]! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: ''! formatTemporariesFor: aSequenceNode aSequenceNode temporaries isEmpty ifTrue: [^self]. self bracketWith: '|' around: [self space. aSequenceNode temporaries do: [:each | self visitNode: each. FormatCommentWithStatements ifTrue: [self formatCommentsFor: each]. self space]]. self newLines: NewLinesAfterTemporaries! ! !RBConfigurableFormatter methodsFor: 'private' stamp: ''! formattedSourceFor: aNode ^lookaheadCode at: aNode ifAbsentPut: [self class format: aNode withIndents: indent]! ! !RBConfigurableFormatter methodsFor: 'initialize-release' stamp: ''! indent: anInteger indent := anInteger! ! !RBConfigurableFormatter methodsFor: 'private' stamp: ''! indent: anInteger around: aBlock indent := indent + anInteger. ^aBlock ensure: [indent := indent - anInteger]! ! !RBConfigurableFormatter methodsFor: 'private' stamp: ''! indentAround: aBlock self indent: 1 around: aBlock! ! !RBConfigurableFormatter methodsFor: 'initialize-release' stamp: ''! initialize super initialize. lineStart := 0. indent := 0. lookaheadCode := IdentityDictionary new. codeStream := WriteStream on: (String new: 256)! ! !RBConfigurableFormatter methodsFor: 'private' stamp: ''! isLineTooLong: aString ^self currentLineLength + aString size >= MaxLineLength! ! !RBConfigurableFormatter methodsFor: 'private-formatting' stamp: ''! isMultiLineMessage: aMessageNode | messageStream | aMessageNode arguments size >= NumberOfArgumentsForMultiLine ifTrue: [^true]. (aMessageNode arguments anySatisfy: [:each | self indent: IndentsForKeywords + 1 around: [self willBeMultiline: each]]) ifTrue: [^true]. aMessageNode isUnary ifTrue: [^self isLineTooLong: aMessageNode selector]. messageStream := WriteStream on: (String new: 100). self with: aMessageNode selectorParts and: aMessageNode arguments do: [:sel :arg | messageStream nextPutAll: sel value; space; nextPutAll: (self formattedSourceFor: arg)] separatedBy: [messageStream space]. ^self isLineTooLong: messageStream contents! ! !RBConfigurableFormatter methodsFor: 'private' stamp: ''! needsParenthesisFor: aNode | parent grandparent | parent := aNode parent. (parent isNil or: [parent isMessage not]) ifTrue: [^false]. (aNode isMessage and: [parent isMessage and: [parent receiver == aNode]]) ifTrue: [grandparent := parent parent. (grandparent notNil and: [grandparent isCascade]) ifTrue: [^true]]. aNode precedence < parent precedence ifTrue: [^false]. (parent receiver ~~ aNode and: [aNode precedence >= parent precedence]) ifTrue: [^true]. (parent receiver == aNode and: [aNode precedence > parent precedence or: [aNode isKeyword]]) ifTrue: [^true]. aNode isBinary ifFalse: [^false]. ^UseTraditionalBinaryPrecedenceForParentheses and: [self precedenceOf: parent selector greaterThan: aNode selector]! ! !RBConfigurableFormatter methodsFor: 'private' stamp: ''! newLine self newLines: 1! ! !RBConfigurableFormatter methodsFor: 'private' stamp: ''! newLines: anInteger anInteger + IndentString size = 0 ifTrue: [codeStream space]. anInteger timesRepeat: [codeStream cr]. lineStart := codeStream position. indent timesRepeat: [codeStream nextPutAll: IndentString]! ! !RBConfigurableFormatter methodsFor: 'private' stamp: ''! newLinesBeforeStartingAt: anIndex | count cr lf index char | (anIndex isNil or: [anIndex > originalSource size]) ifTrue: [^0]. cr := Character value: 13. lf := Character value: 10. count := 0. index := anIndex - 1. [index > 0 and: [(char := originalSource at: index) isSeparator]] whileTrue: [char == lf ifTrue: [count := count + 1. (originalSource at: (index - 1 max: 1)) == cr ifTrue: [index := index - 1]]. char == cr ifTrue: [count := count + 1]. index := index - 1]. ^count! ! !RBConfigurableFormatter methodsFor: 'private' stamp: ''! precedenceOf: parentSelector greaterThan: childSelector "Put parenthesis around things that are preceived to have 'lower' precedence. For example, 'a + b * c' -> '(a + b) * c' but 'a * b + c' -> 'a * b + c'" | childIndex parentIndex | childIndex := 0. parentIndex := 0. 1 to: TraditionalBinaryPrecedence size do: [:i | ((TraditionalBinaryPrecedence at: i) includes: parentSelector first) ifTrue: [parentIndex := i]. ((TraditionalBinaryPrecedence at: i) includes: childSelector first) ifTrue: [childIndex := i]]. ^childIndex < parentIndex! ! !RBConfigurableFormatter methodsFor: 'private' stamp: ''! space codeStream space! ! !RBConfigurableFormatter methodsFor: 'visiting' stamp: ''! visitNode: aNode | needsParenthesis | (lookaheadCode includesKey: aNode) ifTrue: [^self writeString: (lookaheadCode at: aNode)]. needsParenthesis := self needsParenthesisFor: aNode. self bracketWith: (needsParenthesis ifTrue: ['()'] ifFalse: ['']) around: [needsParenthesis ifTrue: [codeStream nextPutAll: StringInsideParentheses]. super visitNode: aNode. (FormatCommentWithStatements or: [aNode isMethod or: [aNode isSequence]]) ifFalse: [self formatCommentsFor: aNode]. needsParenthesis ifTrue: [codeStream nextPutAll: StringInsideParentheses]]! ! !RBConfigurableFormatter methodsFor: 'private' stamp: ''! willBeMultiline: aNode ^(self formattedSourceFor: aNode) includes: Character cr! ! !RBConfigurableFormatter methodsFor: 'utility' stamp: ''! with: firstCollection and: secondCollection do: aBlock separatedBy: separatorBlock firstCollection isEmpty ifTrue: [^self]. aBlock value: firstCollection first value: secondCollection first. 2 to: firstCollection size do: [:i | separatorBlock value. aBlock value: (firstCollection at: i) value: (secondCollection at: i)]! ! !RBConfigurableFormatter methodsFor: 'private' stamp: ''! writeString: aString | index | index := aString lastIndexOf: Character cr ifAbsent: [0]. codeStream nextPutAll: aString. index > 0 ifTrue: [lineStart := codeStream position - (aString size - index)]! ! RBProgramNodeVisitor subclass: #RBFormatter instanceVariableNames: 'codeStream lineStart firstLineLength tabs' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Visitors'! !RBFormatter commentStamp: '' prior: 0! RBFormatter formats a parse tree. It is an example of a Visitor. This is rarely called directly. Sending 'formattedCode' to a parse tree uses this algorithm to return a pretty-printed version. Instance Variables: codeStream The buffer where the output is accumulated. firstLineLength The length of the first line of a message send. lineStart The position of the current line's start. tabs The number of tabs currently indented. ! !RBFormatter methodsFor: 'visitor-double dispatching' stamp: 'lr 11/1/2009 20:23'! acceptArrayNode: anArrayNode codeStream nextPut: ${. anArrayNode statements isEmpty ifFalse: [ anArrayNode statements size > 1 ifTrue: [ self indent: 1 while: [ self indent. self formatStatementsFor: anArrayNode ]. self indent ] ifFalse: [ self formatStatementsFor: anArrayNode ] ]. codeStream nextPut: $}! ! !RBFormatter methodsFor: 'visitor-double dispatching' stamp: 'lr 11/2/2009 20:53'! acceptAssignmentNode: anAssignmentNode self indent: 2 while: [self visitNode: anAssignmentNode variable. codeStream space; nextPutAll: anAssignmentNode assignmentOperator; space. self visitNode: anAssignmentNode value]! ! !RBFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptBlockNode: aBlockNode self acceptBlockNode: aBlockNode startBlockString: '[' endBlockString: ']'! ! !RBFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptBlockNode: aBlockNode startBlockString: startBlockString endBlockString: endBlockString | seqNode multiline formattedBody formatter | seqNode := aBlockNode body. formatter := (self copy) lineStart: 0; yourself. formattedBody := formatter format: seqNode. multiline := self lineLength + formattedBody size > self maxLineSize or: [formatter isMultiLine]. multiline ifTrue: [self indent]. codeStream nextPutAll: startBlockString. aBlockNode arguments do: [:each | codeStream nextPut: $:. self visitNode: each. codeStream nextPut: $ ]. aBlockNode arguments notEmpty ifTrue: [codeStream nextPutAll: '| '. multiline ifTrue: [self indent]]. codeStream nextPutAll: formattedBody; nextPutAll: endBlockString! ! !RBFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptCascadeNode: aCascadeNode | messages | messages := aCascadeNode messages. self visitNode: messages first receiver. self indentWhile: [self for: messages do: [:each | self indent; indentWhile: [self formatMessage: each cascade: true]] separatedBy: [codeStream nextPut: $;]]! ! !RBFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptLiteralArrayNode: aRBArrayLiteralNode codeStream nextPutAll: (aRBArrayLiteralNode isForByteArray ifTrue: ['#['] ifFalse: ['#(']). self for: aRBArrayLiteralNode contents do: [:each | self visitNode: each] separatedBy: [codeStream nextPut: $ ]. codeStream nextPut: (aRBArrayLiteralNode isForByteArray ifTrue: [$]] ifFalse: [$)])! ! !RBFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptLiteralNode: aLiteralNode aLiteralNode token storeOn: codeStream! ! !RBFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptMessageNode: aMessageNode | newFormatter code | newFormatter := self copy. code := newFormatter format: aMessageNode receiver. codeStream nextPutAll: code. codeStream nextPut: $ . newFormatter isMultiLine ifTrue: [lineStart := codeStream position - newFormatter lastLineLength]. self indent: (newFormatter isMultiLine ifTrue: [2] ifFalse: [1]) while: [self formatMessage: aMessageNode cascade: false]! ! !RBFormatter methodsFor: 'visitor-double dispatching' stamp: 'lr 11/2/2009 10:10'! acceptMethodNode: aMethodNode self formatMethodPatternFor: aMethodNode. self indentWhile: [ self formatMethodCommentFor: aMethodNode indentBefore: true. self indent. self formatPragmasFor: aMethodNode. aMethodNode body statements notEmpty ifTrue: [ self visitNode: aMethodNode body ] ]! ! !RBFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptPatternBlockNode: aRBPatternBlockNode self acceptBlockNode: aRBPatternBlockNode startBlockString: '`{' endBlockString: '}'! ! !RBFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptPatternWrapperBlockNode: aRBPatternWrapperBlockNode self visitNode: aRBPatternWrapperBlockNode wrappedNode. self acceptBlockNode: aRBPatternWrapperBlockNode startBlockString: '`{' endBlockString: '}'! ! !RBFormatter methodsFor: 'visitor-double dispatching' stamp: 'lr 11/2/2009 10:08'! acceptPragmaNode: aPragmaNode codeStream nextPut: $<. self formatMessage: aPragmaNode cascade: false. codeStream nextPut: $>! ! !RBFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptReturnNode: aReturnNode codeStream nextPut: $^. self visitNode: aReturnNode value! ! !RBFormatter methodsFor: 'visitor-double dispatching' stamp: 'lr 11/1/2009 20:04'! acceptSequenceNode: aSequenceNode self formatMethodCommentFor: aSequenceNode indentBefore: false. self formatTemporariesFor: aSequenceNode. self formatStatementsFor: aSequenceNode! ! !RBFormatter methodsFor: 'visitor-double dispatching' stamp: ''! acceptVariableNode: aVariableNode codeStream nextPutAll: aVariableNode name! ! !RBFormatter methodsFor: 'accessing' stamp: ''! firstLineLength ^firstLineLength isNil ifTrue: [codeStream position] ifFalse: [firstLineLength]! ! !RBFormatter methodsFor: 'private' stamp: ''! for: aValue do: doBlock separatedBy: separatorBlock "This is implemented here since IBM Smalltalk doesn't implement a do:separatedBy: method" aValue isEmpty ifTrue: [^self]. 1 to: aValue size - 1 do: [:i | doBlock value: (aValue at: i). separatorBlock value]. doBlock value: aValue last! ! !RBFormatter methodsFor: 'accessing' stamp: ''! format: aNode self visitNode: aNode. ^codeStream contents! ! !RBFormatter methodsFor: 'private-formatting' stamp: ''! formatMessage: aMessageNode cascade: cascadeBoolean | selectorParts arguments multiLine formattedArgs indentFirst firstArgLength length | selectorParts := aMessageNode selectorParts. arguments := aMessageNode arguments. formattedArgs := OrderedCollection new. multiLine := aMessageNode selector numArgs > self maximumArgumentsPerLine. length := aMessageNode selector size + arguments size + 1. firstArgLength := 0. self indentWhile: [1 to: arguments size do: [:i | | formatter string | formatter := (self copy) lineStart: (selectorParts at: i) length negated; yourself. string := formatter format: (arguments at: i). formattedArgs add: string. i == 1 ifTrue: [firstArgLength := formatter firstLineLength]. length := length + string size. multiLine := multiLine or: [formatter isMultiLine]]]. multiLine := multiLine or: [length + self lineLength > self maxLineSize]. indentFirst := cascadeBoolean not and: [multiLine and: [(self startMessageSendOnNewLine: aMessageNode) or: [self lineLength + selectorParts first length + 2 + firstArgLength > self maxLineSize]]]. indentFirst ifTrue: [self indent]. self formatMessageSelector: selectorParts withArguments: formattedArgs multiline: multiLine! ! !RBFormatter methodsFor: 'private-formatting' stamp: ''! formatMessageSelector: selectorParts withArguments: formattedArgs multiline: multiLine formattedArgs isEmpty ifTrue: [codeStream nextPutAll: selectorParts first value] ifFalse: [1 to: formattedArgs size do: [:i | i ~~ 1 & multiLine not ifTrue: [codeStream nextPut: $ ]. codeStream nextPutAll: (selectorParts at: i) value; nextPut: $ ; nextPutAll: (formattedArgs at: i). (multiLine and: [i < formattedArgs size]) ifTrue: [self indent]]]! ! !RBFormatter methodsFor: 'private-formatting' stamp: ''! formatMethodCommentFor: aNode indentBefore: aBoolean | source | source := aNode source. source isNil ifTrue: [^self]. aNode methodComments do: [:each | aBoolean ifTrue: [self indent]. codeStream nextPutAll: (aNode source copyFrom: each first to: each last); cr. aBoolean ifFalse: [self indent]]! ! !RBFormatter methodsFor: 'private-formatting' stamp: ''! formatMethodPatternFor: aMethodNode | selectorParts arguments | selectorParts := aMethodNode selectorParts. arguments := aMethodNode arguments. arguments isEmpty ifTrue: [codeStream nextPutAll: selectorParts first value] ifFalse: [selectorParts with: arguments do: [:selector :arg | codeStream nextPutAll: selector value; nextPut: $ . self visitArgument: arg. codeStream nextPut: $ ]]! ! !RBFormatter methodsFor: 'private-formatting' stamp: 'lr 11/2/2009 10:08'! formatPragmasFor: aNode aNode pragmas do: [ :each | self visitNode: each; indent ]! ! !RBFormatter methodsFor: 'private-formatting' stamp: ''! formatStatementCommentFor: aNode | source | source := aNode source. source isNil ifTrue: [^self]. aNode statementComments do: [:each | | crs | crs := self newLinesFor: source startingAt: each first. (crs - 1 max: 0) timesRepeat: [codeStream cr]. crs == 0 ifTrue: [codeStream tab] ifFalse: [self indent]. codeStream nextPutAll: (source copyFrom: each first to: each last)]! ! !RBFormatter methodsFor: 'private-formatting' stamp: ''! formatStatementsFor: aSequenceNode | statements | statements := aSequenceNode statements. statements isEmpty ifTrue: [^self]. 1 to: statements size - 1 do: [:i | self visitNode: (statements at: i). codeStream nextPut: $.. self formatStatementCommentFor: (statements at: i). self indent]. self visitNode: statements last. self formatStatementCommentFor: statements last! ! !RBFormatter methodsFor: 'private-formatting' stamp: ''! formatTemporariesFor: aSequenceNode | temps | temps := aSequenceNode temporaries. temps isEmpty ifTrue: [^self]. codeStream nextPutAll: '| '. temps do: [:each | self visitArgument: each. codeStream nextPut: $ ]. codeStream nextPut: $|. self indent! ! !RBFormatter methodsFor: 'private' stamp: ''! indent firstLineLength isNil ifTrue: [firstLineLength := codeStream position]. codeStream cr. tabs timesRepeat: [codeStream tab]. lineStart := codeStream position! ! !RBFormatter methodsFor: 'private' stamp: ''! indent: anInteger while: aBlock tabs := tabs + anInteger. aBlock value. tabs := tabs - anInteger! ! !RBFormatter methodsFor: 'private' stamp: ''! indentWhile: aBlock self indent: 1 while: aBlock! ! !RBFormatter methodsFor: 'initialize-release' stamp: ''! initialize super initialize. codeStream := WriteStream on: (String new: 60). tabs := 0. lineStart := 0! ! !RBFormatter methodsFor: 'accessing' stamp: ''! isMultiLine ^firstLineLength notNil! ! !RBFormatter methodsFor: 'accessing' stamp: ''! lastLineLength ^codeStream position - (lineStart max: 0)! ! !RBFormatter methodsFor: 'private' stamp: ''! lineLength ^codeStream position - lineStart! ! !RBFormatter methodsFor: 'private' stamp: ''! lineStart: aPosition lineStart := aPosition! ! !RBFormatter methodsFor: 'private' stamp: ''! maxLineSize ^75! ! !RBFormatter methodsFor: 'private' stamp: ''! maximumArgumentsPerLine ^2! ! !RBFormatter methodsFor: 'private' stamp: 'lr 11/2/2009 10:28'! needsParenthesisFor: aNode | parent grandparent | parent := aNode parent. (parent isNil or: [parent isMessage not]) ifTrue: [^false]. (aNode isMessage and: [parent isMessage and: [parent receiver == aNode]]) ifTrue: [grandparent := parent parent. (grandparent notNil and: [grandparent isCascade]) ifTrue: [^true]]. aNode precedence < parent precedence ifTrue: [^false]. aNode isAssignment & parent isAssignment ifTrue: [^false]. aNode isAssignment | aNode isCascade ifTrue: [^true]. aNode precedence == 0 ifTrue: [^false]. aNode isMessage ifFalse: [^true]. aNode precedence = parent precedence ifFalse: [^true]. aNode isUnary ifTrue: [^false]. aNode isKeyword ifTrue: [^true]. parent receiver == aNode ifFalse: [^true]. ^self precedenceOf: parent selector greaterThan: aNode selector! ! !RBFormatter methodsFor: 'private-formatting' stamp: ''! newLinesFor: aString startingAt: anIndex | count cr lf index char | cr := Character value: 13. lf := Character value: 10. count := 0. index := anIndex - 1. [index > 0 and: [char := aString at: index. char isSeparator]] whileTrue: [char == lf ifTrue: [count := count + 1. (aString at: (index - 1 max: 1)) == cr ifTrue: [index := index - 1]]. char == cr ifTrue: [count := count + 1]. index := index - 1]. ^count! ! !RBFormatter methodsFor: 'copying' stamp: ''! postCopy super postCopy. lineStart := self lineLength negated. codeStream := WriteStream on: (String new: 60). firstLineLength := nil! ! !RBFormatter methodsFor: 'private' stamp: ''! precedenceOf: parentSelector greaterThan: childSelector "Put parenthesis around things that are preceived to have 'lower' precedence. For example, 'a + b * c' -> '(a + b) * c' but 'a * b + c' -> 'a * b + c'" | childIndex parentIndex operators | operators := #(#($| $& $?) #($= $~ $< $>) #($- $+) #($* $/ $% $\) #($@)). childIndex := 0. parentIndex := 0. 1 to: operators size do: [:i | ((operators at: i) includes: parentSelector first) ifTrue: [parentIndex := i]. ((operators at: i) includes: childSelector first) ifTrue: [childIndex := i]]. ^childIndex < parentIndex! ! !RBFormatter methodsFor: 'private' stamp: ''! selectorsToLeaveOnLine ^#(#to:do: #to:by: #to:by:do:)! ! !RBFormatter methodsFor: 'private' stamp: ''! selectorsToStartOnNewLine ^#(#ifTrue:ifFalse: #ifFalse:ifTrue: #ifTrue: #ifFalse:)! ! !RBFormatter methodsFor: 'testing' stamp: ''! startMessageSendOnNewLine: aMessageNode (self selectorsToStartOnNewLine includes: aMessageNode selector) ifTrue: [^true]. (self selectorsToLeaveOnLine includes: aMessageNode selector) ifTrue: [^false]. ^aMessageNode selector numArgs > self maximumArgumentsPerLine! ! !RBFormatter methodsFor: 'testing' stamp: ''! tagBeforeTemporaries ^RBParser isVisualWorks! ! !RBFormatter methodsFor: 'visiting' stamp: ''! visitNode: aNode | parenthesis | parenthesis := self needsParenthesisFor: aNode. parenthesis ifTrue: [codeStream nextPut: $(]. aNode acceptVisitor: self. parenthesis ifTrue: [codeStream nextPut: $)]! ! RBProgramNodeVisitor subclass: #RBParseTreeSearcher instanceVariableNames: 'searches answer argumentSearches context messages' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Matching'! !RBParseTreeSearcher commentStamp: '' prior: 0! ParseTreeSearcher walks over a normal source code parse tree using the visitor pattern, and then matches these nodes against the meta-nodes using the match:inContext: methods defined for the meta-nodes. Instance Variables: answer the "answer" that is propagated between matches argumentSearches argument searches (search for the BRProgramNode and perform the BlockClosure when its found) context a dictionary that contains what each meta-node matches against. This could be a normal Dictionary that is created for each search, but is created once and reused (efficiency). messages the sent messages in our searches searches non-argument searches (search for the BRProgramNode and perform the BlockClosure when its found)! RBParseTreeSearcher subclass: #RBParseTreeRewriter instanceVariableNames: 'tree' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Matching'! !RBParseTreeRewriter commentStamp: '' prior: 0! ParseTreeRewriter walks over and transforms its RBProgramNode (tree). If the tree is modified, then answer is set to true, and the modified tree can be retrieved by the #tree method. Instance Variables: tree the parse tree we're transforming! !RBParseTreeRewriter class methodsFor: 'instance creation' stamp: ''! removeTemporaryNamed: aName | rewriteRule | rewriteRule := self new. rewriteRule replace: '| `@temps1 ' , aName , ' `@temps2 | ``@.Statements' with: '| `@temps1 `@temps2 | ``@.Statements'. ^rewriteRule! ! !RBParseTreeRewriter class methodsFor: 'instance creation' stamp: ''! rename: varName to: newVarName | rewriteRule | rewriteRule := self new. rewriteRule replace: varName with: newVarName; replaceArgument: varName with: newVarName. ^rewriteRule! ! !RBParseTreeRewriter class methodsFor: 'instance creation' stamp: ''! rename: varName to: newVarName handler: aBlock "Rename varName to newVarName, evaluating aBlock if there is a temporary variable with the same name as newVarName. This does not change temporary variables with varName." | rewriteRule | rewriteRule := self new. rewriteRule replace: varName with: newVarName; replaceArgument: newVarName withValueFrom: [:aNode | aBlock value. aNode]. ^rewriteRule! ! !RBParseTreeRewriter class methodsFor: 'accessing' stamp: ''! replace: code with: newCode in: aParseTree ^(self replace: code with: newCode method: false) executeTree: aParseTree; tree! ! !RBParseTreeRewriter class methodsFor: 'accessing' stamp: ''! replace: code with: newCode in: aParseTree onInterval: anInterval | rewriteRule | rewriteRule := self new. ^rewriteRule replace: code with: newCode when: [:aNode | aNode intersectsInterval: anInterval]; executeTree: aParseTree; tree! ! !RBParseTreeRewriter class methodsFor: 'instance creation' stamp: ''! replace: code with: newCode method: aBoolean | rewriteRule | rewriteRule := self new. aBoolean ifTrue: [rewriteRule replaceMethod: code with: newCode] ifFalse: [rewriteRule replace: code with: newCode]. ^rewriteRule! ! !RBParseTreeRewriter class methodsFor: 'instance creation' stamp: ''! replaceLiteral: literal with: newLiteral | rewriteRule | rewriteRule := self new. rewriteRule replaceTree: (RBLiteralNode value: literal) withTree: (RBLiteralNode value: newLiteral). ^rewriteRule! ! !RBParseTreeRewriter class methodsFor: 'accessing' stamp: ''! replaceStatements: code with: newCode in: aParseTree onInterval: anInterval | tree replaceStmt | tree := self buildTree: code method: false. tree isSequence ifFalse: [tree := RBSequenceNode statements: (Array with: tree)]. tree temporaries: (Array with: (RBPatternVariableNode named: '`@temps')). tree addNodeFirst: (RBPatternVariableNode named: '`@.S1'). tree lastIsReturn ifTrue: [replaceStmt := '| `@temps | `@.S1. ^' , newCode] ifFalse: [tree addNode: (RBPatternVariableNode named: '`@.S2'). replaceStmt := '| `@temps | `@.S1. ' , newCode , '. `@.S2']. ^self replace: tree formattedCode with: replaceStmt in: aParseTree onInterval: anInterval! ! !RBParseTreeRewriter class methodsFor: 'instance creation' stamp: ''! variable: aVarName getter: getMethod setter: setMethod | rewriteRule | rewriteRule := self new. rewriteRule replace: aVarName , ' := ``@object' with: 'self ' , setMethod , ' ``@object'; replace: aVarName with: 'self ' , getMethod. ^rewriteRule! ! !RBParseTreeRewriter methodsFor: 'visitor-double dispatching' stamp: 'lr 11/1/2009 20:07'! acceptArrayNode: anArrayNode anArrayNode statements: (anArrayNode statements collect: [:each | self visitNode: each])! ! !RBParseTreeRewriter methodsFor: 'visitor-double dispatching' stamp: ''! acceptAssignmentNode: anAssignmentNode anAssignmentNode variable: (self visitNode: anAssignmentNode variable). anAssignmentNode value: (self visitNode: anAssignmentNode value)! ! !RBParseTreeRewriter methodsFor: 'visitor-double dispatching' stamp: ''! acceptBlockNode: aBlockNode aBlockNode arguments: (self visitArguments: aBlockNode arguments). aBlockNode body: (self visitNode: aBlockNode body)! ! !RBParseTreeRewriter methodsFor: 'visitor-double dispatching' stamp: ''! acceptCascadeNode: aCascadeNode | newMessages notFound | newMessages := OrderedCollection new: aCascadeNode messages size. notFound := OrderedCollection new: aCascadeNode messages size. aCascadeNode messages do: [:each | | newNode | newNode := self performSearches: searches on: each. newNode isNil ifTrue: [newNode := each. notFound add: newNode]. newNode isMessage ifTrue: [newMessages add: newNode] ifFalse: [newNode isCascade ifTrue: [newMessages addAll: newNode messages] ifFalse: [Transcript show: 'Cannot replace message node inside of cascaded node with non-message node.'; cr. newMessages add: each]]]. notFound size == aCascadeNode messages size ifTrue: [| receiver | receiver := self visitNode: aCascadeNode messages first receiver. newMessages do: [:each | each receiver: receiver]]. notFound do: [:each | each arguments: (each arguments collect: [:arg | self visitNode: arg])]. aCascadeNode messages: newMessages! ! !RBParseTreeRewriter methodsFor: 'visitor-double dispatching' stamp: ''! acceptLiteralArrayNode: aRBArrayLiteralNode aRBArrayLiteralNode contents: (aRBArrayLiteralNode contents collect: [:each | self visitNode: each])! ! !RBParseTreeRewriter methodsFor: 'visitor-double dispatching' stamp: ''! acceptMessageNode: aMessageNode aMessageNode receiver: (self visitNode: aMessageNode receiver). aMessageNode arguments: (aMessageNode arguments collect: [:each | self visitNode: each])! ! !RBParseTreeRewriter methodsFor: 'visitor-double dispatching' stamp: 'lr 11/1/2009 20:06'! acceptMethodNode: aMethodNode aMethodNode arguments: (self visitArguments: aMethodNode arguments). aMethodNode pragmas: (aMethodNode pragmas collect: [:each | self visitNode: each]). aMethodNode body: (self visitNode: aMethodNode body)! ! !RBParseTreeRewriter methodsFor: 'visitor-double dispatching' stamp: 'lr 11/1/2009 20:07'! acceptPragmaNode: aPragmaNode aPragmaNode arguments: (aPragmaNode arguments collect: [:each | self visitNode: each])! ! !RBParseTreeRewriter methodsFor: 'visitor-double dispatching' stamp: ''! acceptReturnNode: aReturnNode aReturnNode value: (self visitNode: aReturnNode value)! ! !RBParseTreeRewriter methodsFor: 'visitor-double dispatching' stamp: ''! acceptSequenceNode: aSequenceNode aSequenceNode temporaries: (self visitArguments: aSequenceNode temporaries). aSequenceNode statements: (aSequenceNode statements collect: [:each | self visitNode: each])! ! !RBParseTreeRewriter methodsFor: 'accessing' stamp: ''! executeTree: aParseTree | oldContext | oldContext := context. context := RBSmallDictionary new. answer := false. tree := self visitNode: aParseTree. context := oldContext. ^answer! ! !RBParseTreeRewriter methodsFor: 'private' stamp: ''! foundMatch answer := true! ! !RBParseTreeRewriter methodsFor: 'private' stamp: ''! lookForMoreMatchesInContext: oldContext oldContext keysAndValuesDo: [:key :value | (key isString not and: [key recurseInto]) ifTrue: [oldContext at: key put: (value collect: [:each | self visitNode: each])]]! ! !RBParseTreeRewriter methodsFor: 'replacing' stamp: ''! replace: searchString with: replaceString self addRule: (RBStringReplaceRule searchFor: searchString replaceWith: replaceString)! ! !RBParseTreeRewriter methodsFor: 'replacing' stamp: ''! replace: searchString with: replaceString when: aBlock self addRule: (RBStringReplaceRule searchFor: searchString replaceWith: replaceString when: aBlock)! ! !RBParseTreeRewriter methodsFor: 'replacing' stamp: ''! replace: searchString withValueFrom: replaceBlock self addRule: (RBBlockReplaceRule searchFor: searchString replaceWith: replaceBlock)! ! !RBParseTreeRewriter methodsFor: 'replacing' stamp: ''! replace: searchString withValueFrom: replaceBlock when: conditionBlock self addRule: (RBBlockReplaceRule searchFor: searchString replaceWith: replaceBlock when: conditionBlock)! ! !RBParseTreeRewriter methodsFor: 'replacing' stamp: ''! replaceArgument: searchString with: replaceString self addArgumentRule: (RBStringReplaceRule searchFor: searchString replaceWith: replaceString)! ! !RBParseTreeRewriter methodsFor: 'replacing' stamp: ''! replaceArgument: searchString with: replaceString when: aBlock self addArgumentRule: (RBStringReplaceRule searchFor: searchString replaceWith: replaceString when: aBlock)! ! !RBParseTreeRewriter methodsFor: 'replacing' stamp: ''! replaceArgument: searchString withValueFrom: replaceBlock self addArgumentRule: (RBBlockReplaceRule searchFor: searchString replaceWith: replaceBlock)! ! !RBParseTreeRewriter methodsFor: 'replacing' stamp: ''! replaceArgument: searchString withValueFrom: replaceBlock when: conditionBlock self addArgumentRule: (RBBlockReplaceRule searchFor: searchString replaceWith: replaceBlock when: conditionBlock)! ! !RBParseTreeRewriter methodsFor: 'replacing' stamp: ''! replaceMethod: searchString with: replaceString self addRule: (RBStringReplaceRule searchForMethod: searchString replaceWith: replaceString)! ! !RBParseTreeRewriter methodsFor: 'replacing' stamp: ''! replaceMethod: searchString with: replaceString when: aBlock self addRule: (RBStringReplaceRule searchForMethod: searchString replaceWith: replaceString when: aBlock)! ! !RBParseTreeRewriter methodsFor: 'replacing' stamp: ''! replaceMethod: searchString withValueFrom: replaceBlock self addRule: (RBBlockReplaceRule searchForMethod: searchString replaceWith: replaceBlock)! ! !RBParseTreeRewriter methodsFor: 'replacing' stamp: ''! replaceMethod: searchString withValueFrom: replaceBlock when: conditionBlock self addRule: (RBBlockReplaceRule searchForMethod: searchString replaceWith: replaceBlock when: conditionBlock)! ! !RBParseTreeRewriter methodsFor: 'replacing' stamp: ''! replaceTree: searchTree withTree: replaceTree self addRule: (RBStringReplaceRule searchForTree: searchTree replaceWith: replaceTree)! ! !RBParseTreeRewriter methodsFor: 'replacing' stamp: ''! replaceTree: searchTree withTree: replaceTree when: aBlock self addRule: (RBStringReplaceRule searchForTree: searchTree replaceWith: replaceTree when: aBlock)! ! !RBParseTreeRewriter methodsFor: 'accessing' stamp: ''! tree ^tree! ! !RBParseTreeRewriter methodsFor: 'visiting' stamp: ''! visitArguments: aNodeCollection ^aNodeCollection collect: [:each | self visitArgument: each]! ! !RBParseTreeSearcher class methodsFor: 'private' stamp: ''! buildSelectorString: aSelector | stream keywords | aSelector numArgs = 0 ifTrue: [^aSelector]. stream := WriteStream on: String new. keywords := aSelector keywords. 1 to: keywords size do: [:i | stream nextPutAll: (keywords at: i); nextPutAll: ' ``@arg'; nextPutAll: i printString; nextPut: $ ]. ^stream contents! ! !RBParseTreeSearcher class methodsFor: 'private' stamp: ''! buildSelectorTree: aSelector aSelector isEmpty ifTrue: [^nil]. ^RBParser parseRewriteExpression: '``@receiver ' , (self buildSelectorString: aSelector) onError: [:err :pos | ^nil]! ! !RBParseTreeSearcher class methodsFor: 'private' stamp: ''! buildTree: aString method: aBoolean ^aBoolean ifTrue: [RBParser parseRewriteMethod: aString] ifFalse: [RBParser parseRewriteExpression: aString]! ! !RBParseTreeSearcher class methodsFor: 'instance creation' stamp: ''! getterMethod: aVarName ^(self new) matchesMethod: '`method ^' , aVarName do: [:aNode :ans | aNode selector]; yourself! ! !RBParseTreeSearcher class methodsFor: 'instance creation' stamp: ''! justSendsSuper ^(self new) matchesAnyMethodOf: #('`@method: `@Args ^super `@method: `@Args' '`@method: `@Args super `@method: `@Args') do: [:aNode :ans | true]; yourself! ! !RBParseTreeSearcher class methodsFor: 'instance creation' stamp: ''! returnSetterMethod: aVarName ^(self new) matchesMethod: '`method: `Arg ^' , aVarName , ' := `Arg' do: [:aNode :ans | aNode selector]; yourself! ! !RBParseTreeSearcher class methodsFor: 'instance creation' stamp: ''! setterMethod: aVarName ^(self new) matchesAnyMethodOf: (Array with: '`method: `Arg ' , aVarName , ' := `Arg' with: '`method: `Arg ^' , aVarName , ' := `Arg') do: [:aNode :ans | aNode selector]; yourself! ! !RBParseTreeSearcher class methodsFor: 'accessing' stamp: ''! treeMatching: aString in: aParseTree (self new) matches: aString do: [:aNode :answer | ^aNode]; executeTree: aParseTree. ^nil! ! !RBParseTreeSearcher class methodsFor: 'accessing' stamp: ''! treeMatchingStatements: aString in: aParseTree | notifier tree | notifier := self new. tree := RBParser parseExpression: aString. tree isSequence ifFalse: [tree := RBSequenceNode statements: (Array with: tree)]. tree temporaries: (Array with: (RBPatternVariableNode named: '`@temps')). tree addNodeFirst: (RBPatternVariableNode named: '`@.S1'). tree lastIsReturn ifFalse: [tree addNode: (RBPatternVariableNode named: '`@.S2')]. notifier matchesTree: tree do: [:aNode :answer | ^RBParser parseExpression: aString]. notifier executeTree: aParseTree. ^nil! ! !RBParseTreeSearcher methodsFor: 'accessing' stamp: ''! addArgumentRule: aParseTreeRule argumentSearches add: aParseTreeRule. aParseTreeRule owner: self! ! !RBParseTreeSearcher methodsFor: 'accessing' stamp: ''! addArgumentRules: ruleCollection ruleCollection do: [:each | self addArgumentRule: each]! ! !RBParseTreeSearcher methodsFor: 'accessing' stamp: ''! addRule: aParseTreeRule searches add: aParseTreeRule. aParseTreeRule owner: self! ! !RBParseTreeSearcher methodsFor: 'accessing' stamp: ''! addRules: ruleCollection ruleCollection do: [:each | self addRule: each]! ! !RBParseTreeSearcher methodsFor: 'accessing' stamp: ''! answer ^answer! ! !RBParseTreeSearcher methodsFor: 'initialize-release' stamp: ''! answer: anObject answer := anObject! ! !RBParseTreeSearcher methodsFor: 'testing' stamp: ''! canMatchMethod: aCompiledMethod ^self messages isEmpty or: [self messages anySatisfy: [:each | aCompiledMethod sendsSelector: each]]! ! !RBParseTreeSearcher methodsFor: 'accessing' stamp: ''! context ^context! ! !RBParseTreeSearcher methodsFor: 'accessing' stamp: ''! executeMethod: aParseTree initialAnswer: anObject answer := anObject. searches detect: [:each | (each performOn: aParseTree) notNil] ifNone: []. ^answer! ! !RBParseTreeSearcher methodsFor: 'accessing' stamp: ''! executeTree: aParseTree "Save our current context, in case someone is performing another search inside a match." | oldContext | oldContext := context. context := RBSmallDictionary new. self visitNode: aParseTree. context := oldContext. ^answer! ! !RBParseTreeSearcher methodsFor: 'accessing' stamp: ''! executeTree: aParseTree initialAnswer: aValue answer := aValue. ^self executeTree: aParseTree! ! !RBParseTreeSearcher methodsFor: 'private' stamp: ''! foundMatch! ! !RBParseTreeSearcher methodsFor: 'testing' stamp: ''! hasRules ^searches notEmpty! ! !RBParseTreeSearcher methodsFor: 'initialize-release' stamp: ''! initialize super initialize. context := RBSmallDictionary new. searches := OrderedCollection new. argumentSearches := OrderedCollection new: 0. answer := nil! ! !RBParseTreeSearcher methodsFor: 'private' stamp: ''! lookForMoreMatchesInContext: oldContext oldContext keysAndValuesDo: [:key :value | (key isString not and: [key recurseInto]) ifTrue: [value do: [:each | self visitNode: each]]]! ! !RBParseTreeSearcher methodsFor: 'searching' stamp: ''! matches: aString do: aBlock self addRule: (RBSearchRule searchFor: aString thenDo: aBlock)! ! !RBParseTreeSearcher methodsFor: 'searching' stamp: ''! matchesAnyArgumentOf: stringCollection do: aBlock stringCollection do: [:each | self matchesArgument: each do: aBlock]! ! !RBParseTreeSearcher methodsFor: 'searching' stamp: ''! matchesAnyMethodOf: aStringCollection do: aBlock aStringCollection do: [:each | self matchesMethod: each do: aBlock]! ! !RBParseTreeSearcher methodsFor: 'searching' stamp: ''! matchesAnyOf: aStringCollection do: aBlock aStringCollection do: [:each | self matches: each do: aBlock]! ! !RBParseTreeSearcher methodsFor: 'searching' stamp: ''! matchesAnyTreeOf: treeCollection do: aBlock treeCollection do: [:each | self matchesTree: each do: aBlock]! ! !RBParseTreeSearcher methodsFor: 'searching' stamp: ''! matchesArgument: aString do: aBlock self addArgumentRule: (RBSearchRule searchFor: aString thenDo: aBlock)! ! !RBParseTreeSearcher methodsFor: 'searching' stamp: ''! matchesArgumentTree: aBRProgramNode do: aBlock self addArgumentRule: (RBSearchRule searchForTree: aBRProgramNode thenDo: aBlock)! ! !RBParseTreeSearcher methodsFor: 'searching' stamp: ''! matchesMethod: aString do: aBlock self addRule: (RBSearchRule searchForMethod: aString thenDo: aBlock)! ! !RBParseTreeSearcher methodsFor: 'searching' stamp: ''! matchesTree: aBRProgramNode do: aBlock self addRule: (RBSearchRule searchForTree: aBRProgramNode thenDo: aBlock)! ! !RBParseTreeSearcher methodsFor: 'accessing' stamp: ''! messages messages notNil ifTrue: [^messages]. argumentSearches notEmpty ifTrue: [^messages := #()]. messages := Set new. searches do: [:each | | searchMessages | searchMessages := each sentMessages. RBProgramNode optimizedSelectors do: [:sel | searchMessages remove: sel ifAbsent: []]. searchMessages isEmpty ifTrue: [^messages := #()]. messages addAll: searchMessages]. ^messages := messages asArray! ! !RBParseTreeSearcher methodsFor: 'private' stamp: ''! performSearches: aSearchCollection on: aNode | value | 1 to: aSearchCollection size do: [:i | value := (aSearchCollection at: i) performOn: aNode. value notNil ifTrue: [self foundMatch. ^value]]. ^nil! ! !RBParseTreeSearcher methodsFor: 'private' stamp: ''! recusivelySearchInContext "We need to save the matched context since the other searches might overwrite it." | oldContext | oldContext := context. context := RBSmallDictionary new. self lookForMoreMatchesInContext: oldContext. context := oldContext! ! !RBParseTreeSearcher methodsFor: 'visiting' stamp: ''! visitArgument: aNode | value | value := self performSearches: argumentSearches on: aNode. ^value isNil ifTrue: [aNode acceptVisitor: self. aNode] ifFalse: [value]! ! !RBParseTreeSearcher methodsFor: 'visiting' stamp: ''! visitNode: aNode | value | value := self performSearches: searches on: aNode. ^value isNil ifTrue: [aNode acceptVisitor: self. aNode] ifFalse: [value]! ! !RBProgramNodeVisitor class methodsFor: 'instance creation' stamp: ''! new ^super new initialize! ! !RBProgramNodeVisitor methodsFor: 'visitor-double dispatching' stamp: 'ls 1/24/2000 00:31'! acceptArrayNode: anArrayNode anArrayNode children do: [:each | self visitNode: each]! ! !RBProgramNodeVisitor methodsFor: 'visitor-double dispatching' stamp: ''! acceptAssignmentNode: anAssignmentNode self visitNode: anAssignmentNode variable. self visitNode: anAssignmentNode value! ! !RBProgramNodeVisitor methodsFor: 'visitor-double dispatching' stamp: ''! acceptBlockNode: aBlockNode self visitArguments: aBlockNode arguments. self visitNode: aBlockNode body! ! !RBProgramNodeVisitor methodsFor: 'visitor-double dispatching' stamp: ''! acceptCascadeNode: aCascadeNode aCascadeNode messages do: [:each | self visitNode: each]! ! !RBProgramNodeVisitor methodsFor: 'visitor-double dispatching' stamp: ''! acceptLiteralArrayNode: aRBLiteralArrayNode aRBLiteralArrayNode contents do: [:each | self visitNode: each]! ! !RBProgramNodeVisitor methodsFor: 'visitor-double dispatching' stamp: ''! acceptLiteralNode: aLiteralNode! ! !RBProgramNodeVisitor methodsFor: 'visitor-double dispatching' stamp: ''! acceptMessageNode: aMessageNode (aMessageNode isCascaded not or: [aMessageNode isFirstCascaded]) ifTrue: [self visitNode: aMessageNode receiver]. aMessageNode arguments do: [:each | self visitNode: each]! ! !RBProgramNodeVisitor methodsFor: 'visitor-double dispatching' stamp: ''! acceptMethodNode: aMethodNode self visitArguments: aMethodNode arguments. self visitNode: aMethodNode body! ! !RBProgramNodeVisitor methodsFor: 'visitor-double dispatching' stamp: ''! acceptPatternBlockNode: aRBPatternBlockNode self visitArguments: aRBPatternBlockNode arguments. self visitNode: aRBPatternBlockNode body! ! !RBProgramNodeVisitor methodsFor: 'visitor-double dispatching' stamp: ''! acceptPatternWrapperBlockNode: aRBPatternWrapperBlockNode self visitNode: aRBPatternWrapperBlockNode wrappedNode. self visitArguments: aRBPatternWrapperBlockNode arguments. self visitNode: aRBPatternWrapperBlockNode body! ! !RBProgramNodeVisitor methodsFor: 'visitor-double dispatching' stamp: 'lr 10/18/2009 12:37'! acceptPragmaNode: aPragmaNode aPragmaNode arguments do: [ :each | self visitNode: each ]! ! !RBProgramNodeVisitor methodsFor: 'visitor-double dispatching' stamp: ''! acceptReturnNode: aReturnNode self visitNode: aReturnNode value! ! !RBProgramNodeVisitor methodsFor: 'visitor-double dispatching' stamp: ''! acceptSequenceNode: aSequenceNode self visitArguments: aSequenceNode temporaries. aSequenceNode statements do: [:each | self visitNode: each]! ! !RBProgramNodeVisitor methodsFor: 'visitor-double dispatching' stamp: ''! acceptVariableNode: aVariableNode! ! !RBProgramNodeVisitor methodsFor: 'initialize-release' stamp: ''! initialize! ! !RBProgramNodeVisitor methodsFor: 'visiting' stamp: ''! visitArgument: each "Here to allow subclasses to detect arguments or temporaries." ^self visitNode: each! ! !RBProgramNodeVisitor methodsFor: 'visiting' stamp: ''! visitArguments: aNodeCollection ^aNodeCollection do: [:each | self visitArgument: each]! ! !RBProgramNodeVisitor methodsFor: 'visiting' stamp: ''! visitNode: aNode ^aNode acceptVisitor: self! ! RBProgramNodeVisitor subclass: #RBReadBeforeWrittenTester instanceVariableNames: 'read checkNewTemps scopeStack' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Visitors'! !RBReadBeforeWrittenTester commentStamp: '' prior: 0! RBReadBeforeWrittenTester is a visitor that identifies variables that may have been read before they are initialized. Instance Variables: checkNewTemps description of checkNewTemps read description of read scopeStack description of scopeStack ! !RBReadBeforeWrittenTester class methodsFor: 'accessing' stamp: ''! isVariable: aString readBeforeWrittenIn: aBRProgramNode ^(self isVariable: aString writtenBeforeReadIn: aBRProgramNode) not! ! !RBReadBeforeWrittenTester class methodsFor: 'accessing' stamp: ''! isVariable: aString writtenBeforeReadIn: aBRProgramNode ^(self readBeforeWritten: (Array with: aString) in: aBRProgramNode) isEmpty! ! !RBReadBeforeWrittenTester class methodsFor: 'accessing' stamp: ''! readBeforeWritten: varNames in: aParseTree ^(self new) checkNewTemps: false; initializeVars: varNames; executeTree: aParseTree; read! ! !RBReadBeforeWrittenTester class methodsFor: 'accessing' stamp: ''! variablesReadBeforeWrittenIn: aParseTree ^(self new) executeTree: aParseTree; read! ! !RBReadBeforeWrittenTester methodsFor: 'visitor-double dispatching' stamp: ''! acceptAssignmentNode: anAssignmentNode self visitNode: anAssignmentNode value. self variableWritten: anAssignmentNode! ! !RBReadBeforeWrittenTester methodsFor: 'visitor-double dispatching' stamp: ''! acceptBlockNode: aBlockNode self processBlock: aBlockNode! ! !RBReadBeforeWrittenTester methodsFor: 'visitor-double dispatching' stamp: ''! acceptMessageNode: aMessageNode ((#(#whileTrue: #whileFalse: #whileTrue #whileFalse) includes: aMessageNode selector) and: [aMessageNode receiver isBlock]) ifTrue: [self executeTree: aMessageNode receiver body] ifFalse: [(aMessageNode isCascaded not or: [aMessageNode isFirstCascaded]) ifTrue: [self visitNode: aMessageNode receiver]]. ((#(#ifTrue:ifFalse: #ifFalse:ifTrue:) includes: aMessageNode selector) and: [aMessageNode arguments allSatisfy: [:each | each isBlock]]) ifTrue: [^self processIfTrueIfFalse: aMessageNode]. aMessageNode arguments do: [:each | self visitNode: each]! ! !RBReadBeforeWrittenTester methodsFor: 'visitor-double dispatching' stamp: ''! acceptSequenceNode: aSequenceNode self processStatementNode: aSequenceNode! ! !RBReadBeforeWrittenTester methodsFor: 'visitor-double dispatching' stamp: ''! acceptVariableNode: aVariableNode self variableRead: aVariableNode! ! !RBReadBeforeWrittenTester methodsFor: 'initialize-release' stamp: ''! checkNewTemps: aBoolean checkNewTemps := aBoolean! ! !RBReadBeforeWrittenTester methodsFor: 'private' stamp: 'TestRunner 11/3/2009 09:10'! copyDictionary: aDictionary "We could send aDictionary the copy message, but that doesn't copy the associations." | newDictionary | newDictionary := Dictionary new: aDictionary size. aDictionary keysAndValuesDo: [ :key :value | newDictionary at: key put: value ]. ^ newDictionary! ! !RBReadBeforeWrittenTester methodsFor: 'private' stamp: ''! createScope scopeStack add: (self copyDictionary: scopeStack last)! ! !RBReadBeforeWrittenTester methodsFor: 'private' stamp: ''! currentScope ^scopeStack last! ! !RBReadBeforeWrittenTester methodsFor: 'accessing' stamp: ''! executeTree: aParseTree ^self visitNode: aParseTree! ! !RBReadBeforeWrittenTester methodsFor: 'initialize-release' stamp: ''! initialize super initialize. scopeStack := OrderedCollection with: Dictionary new. read := Set new. checkNewTemps := true! ! !RBReadBeforeWrittenTester methodsFor: 'initialize-release' stamp: ''! initializeVars: varNames varNames do: [:each | self currentScope at: each put: nil]! ! !RBReadBeforeWrittenTester methodsFor: 'private' stamp: ''! processBlock: aNode | newScope | self createScope. self executeTree: aNode body. newScope := self removeScope. newScope keysAndValuesDo: [:key :value | (value == true and: [(self currentScope at: key) isNil]) ifTrue: [self currentScope at: key put: value]]! ! !RBReadBeforeWrittenTester methodsFor: 'private' stamp: ''! processIfTrueIfFalse: aNode | trueScope falseScope | self createScope. self executeTree: aNode arguments first body. trueScope := self removeScope. self createScope. self executeTree: aNode arguments last body. falseScope := self removeScope. self currentScope keysAndValuesDo: [:key :value | value isNil ifTrue: [(trueScope at: key) == (falseScope at: key) ifTrue: [self currentScope at: key put: (trueScope at: key)] ifFalse: [((trueScope at: key) == true or: [(falseScope at: key) == true]) ifTrue: [self currentScope at: key put: true]]]]! ! !RBReadBeforeWrittenTester methodsFor: 'private' stamp: ''! processStatementNode: aNode | temps | (checkNewTemps not or: [aNode temporaries isEmpty]) ifTrue: [aNode statements do: [:each | self executeTree: each]. ^self]. self createScope. temps := aNode temporaries collect: [:each | each name]. self initializeVars: temps. aNode statements do: [:each | self executeTree: each]. self removeScope keysAndValuesDo: [:key :value | (temps includes: key) ifTrue: [value == true ifTrue: [read add: key]] ifFalse: [(self currentScope at: key) isNil ifTrue: [self currentScope at: key put: value]]]! ! !RBReadBeforeWrittenTester methodsFor: 'accessing' stamp: ''! read self currentScope keysAndValuesDo: [:key :value | value == true ifTrue: [read add: key]]. ^read! ! !RBReadBeforeWrittenTester methodsFor: 'private' stamp: ''! removeScope ^scopeStack removeLast! ! !RBReadBeforeWrittenTester methodsFor: 'private' stamp: ''! variableRead: aNode (self currentScope includesKey: aNode name) ifTrue: [(self currentScope at: aNode name) isNil ifTrue: [self currentScope at: aNode name put: true]]! ! !RBReadBeforeWrittenTester methodsFor: 'private' stamp: ''! variableWritten: aNode (self currentScope includesKey: aNode variable name) ifTrue: [(self currentScope at: aNode variable name) isNil ifTrue: [self currentScope at: aNode variable name put: false]]! ! Object subclass: #RBScanner instanceVariableNames: 'stream buffer tokenStart currentCharacter characterType classificationTable comments errorBlock' classVariableNames: 'PatternVariableCharacter classificationTable' poolDictionaries: '' category: 'AST-Core-Parser'! RBScanner class instanceVariableNames: 'classificationTable'! !RBScanner commentStamp: '' prior: 0! RBScanner is a stream that returns a sequence of token from the string that it is created on. The tokens know where they came from in the source code and which comments were attached to them. Instance Variables: buffer Accumulates the text for the current token. characterType The type of the next character. (e.g. #alphabetic, etc.) classificationTable Mapping from Character values to their characterType. comments Source intervals of scanned comments that must be attached to the next token. currentCharacter The character currently being processed. errorBlock The block to execute on lexical errors. extendedLiterals True if IBM-type literals are allowed. In VW, this is false. nameSpaceCharacter The character used to separate namespaces. numberType The method to perform: to scan a number. separatorsInLiterals True if separators are allowed within literals. stream Contains the text to be scanned. tokenStart The source position of the beginning of the current token Class Instance Variables: classificationTable the default classification table for all characters Shared Variables: PatternVariableCharacter the character that starts a pattern node! RBScanner class instanceVariableNames: 'classificationTable'! RBScanner subclass: #RBPatternScanner instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Parser'! !RBPatternScanner commentStamp: '' prior: 0! RBPatternScanner is a subclass of RBScanner that allows the extended syntax of pattern matching trees. ! !RBPatternScanner methodsFor: 'accessing' stamp: 'lr 11/2/2009 23:37'! scanToken currentCharacter == PatternVariableCharacter ifTrue: [^self scanPatternVariable]. currentCharacter = $} ifTrue: [^self scanSpecialCharacter]. ^super scanToken! ! !RBScanner class methodsFor: 'accessing' stamp: ''! classificationTable classificationTable isNil ifTrue: [self initializeClassificationTable]. ^classificationTable! ! !RBScanner class methodsFor: 'class initialization' stamp: ''! initialize self initializeClassificationTable! ! !RBScanner class methodsFor: 'class initialization' stamp: ''! initializeChars: characters to: aSymbol characters do: [:c | classificationTable at: c asInteger put: aSymbol]! ! !RBScanner class methodsFor: 'class initialization' stamp: 'lr 11/1/2009 20:16'! initializeClassificationTable | allCharacters | PatternVariableCharacter := $`. classificationTable := Array new: 255. allCharacters := (1 to: 255) collect: [:i | Character value: i]. self initializeChars: (allCharacters select: [:each | each isLetter]) to: #alphabetic. "self initializeChars: '_' to: #alphabetic." self initializeChars: '01234567890' to: #digit. self initializeChars: '!!%&*+,-/<=>?@\~|' to: #binary. classificationTable at: 177 put: #binary. "plus-or-minus" classificationTable at: 183 put: #binary. "centered dot" classificationTable at: 215 put: #binary. "times" classificationTable at: 247 put: #binary. "divide" self initializeChars: '().:;[]{}^_' to: #special. self initializeChars: (allCharacters select: [:each | each isSeparator]) to: #separator! ! !RBScanner class methodsFor: 'testing' stamp: ''! isSelector: aSymbol | scanner token | scanner := self basicNew. scanner on: (ReadStream on: aSymbol asString). scanner step. token := scanner scanAnySymbol. token isLiteralToken ifFalse: [^false]. token value isEmpty ifTrue: [^false]. ^scanner atEnd! ! !RBScanner class methodsFor: 'testing' stamp: ''! isVariable: aString | scanner token | scanner := self on: (ReadStream on: aString) errorBlock: [:s :p | ^false]. token := scanner next. token isIdentifier ifFalse: [^false]. (token start = 1 and: [token stop = aString size]) ifFalse: [^false]. ^(aString includes: $.) not! ! !RBScanner class methodsFor: 'instance creation' stamp: ''! on: aStream | str | str := self basicNew on: aStream. str step; stripSeparators. ^str! ! !RBScanner class methodsFor: 'instance creation' stamp: ''! on: aStream errorBlock: aBlock | str | str := self basicNew on: aStream. str errorBlock: aBlock; step; stripSeparators. ^str! ! !RBScanner class methodsFor: 'accessing' stamp: ''! patternVariableCharacter ^PatternVariableCharacter! ! !RBScanner methodsFor: 'testing' stamp: 'lr 11/2/2009 23:37'! atEnd ^characterType = #eof! ! !RBScanner methodsFor: 'private' stamp: ''! classify: aCharacter | index | aCharacter isNil ifTrue: [^nil]. index := aCharacter asInteger. index == 0 ifTrue: [^#separator]. index > 255 ifTrue: [^aCharacter isLetter ifTrue: [#alphabetic] ifFalse: [aCharacter isSeparator ifTrue: [#separator] ifFalse: [nil]]]. ^classificationTable at: index! ! !RBScanner methodsFor: 'accessing' stamp: ''! contents | contentsStream | contentsStream := WriteStream on: (Array new: 50). self do: [:each | contentsStream nextPut: each]. ^contentsStream contents! ! !RBScanner methodsFor: 'error handling' stamp: ''! errorBlock ^errorBlock isNil ifTrue: [[:message :position | ]] ifFalse: [errorBlock]! ! !RBScanner methodsFor: 'accessing' stamp: ''! errorBlock: aBlock errorBlock := aBlock! ! !RBScanner methodsFor: 'error handling' stamp: ''! errorPosition ^stream position! ! !RBScanner methodsFor: 'accessing' stamp: ''! flush! ! !RBScanner methodsFor: 'accessing' stamp: ''! getComments | oldComments | comments isEmpty ifTrue: [^nil]. oldComments := comments. comments := OrderedCollection new: 1. ^oldComments! ! !RBScanner methodsFor: 'initialize-release' stamp: 'lr 11/1/2009 19:27'! initialize ! ! !RBScanner methodsFor: 'testing' stamp: ''! isReadable ^true! ! !RBScanner methodsFor: 'testing' stamp: ''! isWritable ^false! ! !RBScanner methodsFor: 'accessing' stamp: 'lr 11/2/2009 23:37'! next | token | buffer reset. tokenStart := stream position. token := characterType = #eof ifTrue: [RBToken start: tokenStart + 1 "The EOF token should occur after the end of input"] ifFalse: [self scanToken]. self stripSeparators. token comments: self getComments. ^token! ! !RBScanner methodsFor: 'accessing' stamp: ''! nextPut: anObject "Provide an error notification that the receiver does not implement this message." self shouldNotImplement! ! !RBScanner methodsFor: 'initialize-release' stamp: 'lr 11/1/2009 18:31'! on: aStream buffer := WriteStream on: (String new: 60). stream := aStream. classificationTable := self class classificationTable. comments := OrderedCollection new! ! !RBScanner methodsFor: 'private' stamp: 'lr 11/2/2009 23:37'! previousStepPosition ^characterType = #eof ifTrue: [stream position] ifFalse: [stream position - 1]! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! scanAnySymbol characterType = #alphabetic ifTrue: [^self scanSymbol]. characterType = #binary ifTrue: [^self scanBinary: RBLiteralToken]. ^RBToken new! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! scanBinary: aClass | val | buffer nextPut: currentCharacter. self step. [ characterType = #binary and: [ currentCharacter ~~ $- ] ] whileTrue: [ buffer nextPut: currentCharacter. self step ]. val := buffer contents. val := val asSymbol. ^aClass value: val start: tokenStart! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! scanExponentMultipler | exponent isExpNegative position | currentCharacter = $e ifTrue: [position := stream position. self step. (isExpNegative := currentCharacter = $-) ifTrue: [self step]. exponent := self scanNumberOfBase: 10. exponent isNil ifTrue: ["Did not read a valid exponent, e must be start of a message send" stream position: position - 1. self step. exponent := 0] ifFalse: [isExpNegative ifTrue: [exponent := exponent negated]]] ifFalse: [exponent := 0]. ^10 raisedToInteger: exponent! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! scanExtendedLiterals | token | self step. token := characterType = #alphabetic ifTrue: [self scanSymbol] ifFalse: [characterType = #binary ifTrue: [(self scanBinary: RBLiteralToken) stop: self previousStepPosition] ifFalse: [currentCharacter = $' ifTrue: [self scanStringSymbol]]]. token isNil ifTrue: [self scannerError: 'Expecting a extended literal']. token value: ((Smalltalk at: #EsAtom) intern: token value asString). ^token! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! scanIdentifierOrKeyword | name | self scanName. (currentCharacter = $: and: [stream peek ~~ $=]) ifTrue: [^self scanKeyword]. name := buffer contents. name = 'true' ifTrue: [^RBLiteralToken value: true start: tokenStart stop: self previousStepPosition]. name = 'false' ifTrue: [^RBLiteralToken value: false start: tokenStart stop: self previousStepPosition]. name = 'nil' ifTrue: [^RBLiteralToken value: nil start: tokenStart stop: self previousStepPosition]. ^RBIdentifierToken value: name start: tokenStart! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! scanKeyword | outputPosition inputPosition name | [currentCharacter = $:] whileTrue: [buffer nextPut: currentCharacter. outputPosition := buffer position. inputPosition := stream position. self step. ":" [characterType = #alphabetic] whileTrue: [self scanName]]. buffer position: outputPosition. stream position: inputPosition. self step. name := buffer contents. ^(name occurrencesOf: $:) == 1 ifTrue: [RBKeywordToken value: name start: tokenStart] ifFalse: [RBMultiKeywordLiteralToken value: name asSymbol start: tokenStart stop: tokenStart + name size - 1]! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! scanLiteral self step. characterType = #alphabetic ifTrue: [^self scanSymbol]. characterType = #binary ifTrue: [^(self scanBinary: RBLiteralToken) stop: self previousStepPosition]. currentCharacter = $' ifTrue: [^self scanStringSymbol]. (currentCharacter = $( or: [currentCharacter = $[]) ifTrue: [^self scanLiteralArrayToken]. (currentCharacter = $#) ifTrue: [^self scanExtendedLiterals]. self scannerError: 'Expecting a literal type'! ! !RBScanner methodsFor: 'private-scanning' stamp: ''! scanLiteralArrayToken | token | token := RBLiteralArrayToken value: (String with: $# with: currentCharacter) start: tokenStart. self step. ^token! ! !RBScanner methodsFor: 'private-scanning' stamp: ''! scanLiteralCharacter | token | self step. "$" token := RBLiteralToken value: currentCharacter start: tokenStart stop: stream position. self step. "char" ^token! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! scanLiteralString self step. [currentCharacter isNil ifTrue: [self scannerError: 'Unmatched '' in string literal.']. currentCharacter = $' and: [self step ~~ $']] whileFalse: [buffer nextPut: currentCharacter. self step]. ^RBLiteralToken value: buffer contents start: tokenStart stop: self previousStepPosition! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! scanName [characterType = #alphabetic or: [characterType = #digit]] whileTrue: [buffer nextPut: currentCharacter. self step]! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/1/2009 19:19'! scanNumber | start number stop string currentPosition | start := stream position. number := self scanNumberIBM. currentPosition := stream position. stop := self atEnd ifTrue: [currentPosition] ifFalse: [currentPosition - 1]. stream position: start - 1. string := stream next: stop - start + 1. stream position: currentPosition. ^RBNumberLiteralToken value: number start: start stop: stop source: string! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! scanNumberIBM | number isNegative | isNegative := false. currentCharacter = $- ifTrue: [isNegative := true. self step]. number := self scanNumberWithoutExponent. ^(isNegative ifTrue: [number negated] ifFalse: [number]) * self scanExponentMultipler! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! scanNumberOfBase: anInteger "Scan a number. Return the number or nil if the current input isn't a valid number." | number digits fraction isFloat succeeded | digits := '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' copyFrom: 1 to: anInteger. number := 0. succeeded := false. [digits includes: currentCharacter] whileTrue: [number := number * anInteger + (digits indexOf: currentCharacter) - 1. self step. succeeded := true]. succeeded ifFalse: [^nil]. isFloat := false. (currentCharacter = $. and: [digits includes: stream peek]) ifTrue: [self step. isFloat := true. fraction := 1 / anInteger. [digits includes: currentCharacter] whileTrue: [number := number + (((digits indexOf: currentCharacter) - 1) * fraction). fraction := fraction / anInteger. self step]]. ^isFloat ifTrue: [number asFloat] ifFalse: [number]! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! scanNumberWithoutExponent | number base | base := self scanNumberOfBase: 10. (currentCharacter = $r and: [base isInteger]) ifTrue: [| position | position := stream position. self step. number := self scanNumberOfBase: base. number isNil ifTrue: ["Did not read a correct number, r must be start of a message send." stream position: position - 1. self step. number := base]] ifFalse: [number := base]. ^number! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! scanPatternVariable buffer nextPut: currentCharacter. self step. currentCharacter = ${ ifTrue: [self step. ^RBPatternBlockToken value: '`{' start: tokenStart]. [characterType = #alphabetic] whileFalse: [characterType = #eof ifTrue: [self scannerError: 'Meta variable expected']. buffer nextPut: currentCharacter. self step]. ^self scanIdentifierOrKeyword! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! scanSpecialCharacter | character | currentCharacter = $: ifTrue: [self step. ^currentCharacter = $= ifTrue: [self step. RBAssignmentToken start: tokenStart] ifFalse: [RBSpecialCharacterToken value: $: start: tokenStart]]. currentCharacter = $_ ifTrue: [ self step. ^RBShortAssignmentToken start: tokenStart ]. character := currentCharacter. self step. ^RBSpecialCharacterToken value: character start: tokenStart! ! !RBScanner methodsFor: 'private-scanning' stamp: ''! scanStringSymbol | literalToken | literalToken := self scanLiteralString. literalToken value: literalToken value asSymbol. ^literalToken! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! scanSymbol | lastPosition hasColon value startPosition | hasColon := false. startPosition := lastPosition := stream position. [characterType = #alphabetic] whileTrue: [self scanName. currentCharacter = $: ifTrue: [buffer nextPut: $:. hasColon := true. lastPosition := stream position. self step]]. value := buffer contents. (hasColon and: [value last ~~ $:]) ifTrue: [stream position: lastPosition. self step. value := value copyFrom: 1 to: lastPosition - startPosition + 1]. ^RBLiteralToken value: value asSymbol start: tokenStart stop: self previousStepPosition! ! !RBScanner methodsFor: 'accessing' stamp: 'lr 11/2/2009 23:37'! scanToken "fast-n-ugly. Don't write stuff like this. Has been found to cause cancer in laboratory rats. Basically a case statement. Didn't use Dictionary because lookup is pretty slow." characterType = #alphabetic ifTrue: [^self scanIdentifierOrKeyword]. (characterType = #digit or: [currentCharacter = $- and: [(self classify: stream peek) = #digit]]) ifTrue: [^self scanNumber]. characterType = #binary ifTrue: [^self scanBinary: RBBinarySelectorToken]. characterType = #special ifTrue: [^self scanSpecialCharacter]. currentCharacter = $' ifTrue: [^self scanLiteralString]. currentCharacter = $# ifTrue: [^self scanLiteral]. currentCharacter = $$ ifTrue: [^self scanLiteralCharacter]. ^self scannerError: 'Unknown character'! ! !RBScanner methodsFor: 'error handling' stamp: ''! scannerError: aString "Evaluate the block. If it returns raise an error" self errorBlock value: aString value: self errorPosition. self error: aString! ! !RBScanner methodsFor: 'accessing' stamp: ''! skipUntil: aCharacter | position | (stream skipThrough: aCharacter) isNil ifTrue: [self scannerError: (String with: aCharacter) , ' not found']. position := stream position. self step. self stripSeparators. ^position! ! !RBScanner methodsFor: 'private' stamp: ''! step stream atEnd ifTrue: [characterType := #eof. ^currentCharacter := nil]. currentCharacter := stream next. characterType := self classify: currentCharacter. ^currentCharacter! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! stripComment | start stop | start := stream position. [self step = $"] whileFalse: [characterType = #eof ifTrue: [self scannerError: 'Unmatched " in comment.']]. stop := stream position. self step. comments add: (start to: stop)! ! !RBScanner methodsFor: 'private-scanning' stamp: 'lr 11/2/2009 23:37'! stripSeparators [[characterType = #separator] whileTrue: [self step]. currentCharacter = $"] whileTrue: [self stripComment]! ! Object subclass: #RBStringReplacement instanceVariableNames: 'startPosition stopPosition string' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Nodes'! !RBStringReplacement commentStamp: '' prior: 0! RBStringReplacement represents replacing source in the original method with a different string. These are used when reformatting code after a parse tree change has been made. Depending on the change, it may be possible to minimally change the parse tree without needing to format it. Instance Variables: startPosition the start position in the original source stopPosition the end position in the original source string replaces everything from the startPosition to the endPosition with this string ! !RBStringReplacement class methodsFor: 'instance creation' stamp: ''! replaceFrom: startInteger to: stopInteger with: aString ^(self new) startPosition: startInteger; stopPosition: stopInteger; string: aString; yourself! ! !RBStringReplacement methodsFor: 'accessing' stamp: ''! startPosition ^startPosition! ! !RBStringReplacement methodsFor: 'initialize-release' stamp: ''! startPosition: anInteger startPosition := anInteger! ! !RBStringReplacement methodsFor: 'accessing' stamp: ''! stopPosition ^stopPosition! ! !RBStringReplacement methodsFor: 'initialize-release' stamp: ''! stopPosition: anInteger stopPosition := anInteger! ! !RBStringReplacement methodsFor: 'accessing' stamp: ''! string ^string! ! !RBStringReplacement methodsFor: 'initialize-release' stamp: ''! string: aString string := aString! ! Object subclass: #RBToken instanceVariableNames: 'sourcePointer comments' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Tokens'! !RBToken commentStamp: '' prior: 0! RBToken is the abstract superclass of all of the RB tokens. These tokens (unlike the standard parser's) remember where they came from in the original source code. Subclasses must implement the following messages: accessing length Instance Variables: sourcePointer The position in the original source code where this token began. ! RBToken subclass: #RBAssignmentToken instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Tokens'! !RBAssignmentToken commentStamp: 'md 8/9/2005 14:51' prior: 0! RBAssignmentToken is the first-class representation of the assignment token ':=' ! !RBAssignmentToken methodsFor: 'testing' stamp: ''! isAssignment ^true! ! !RBAssignmentToken methodsFor: 'private' stamp: ''! length ^2! ! RBAssignmentToken subclass: #RBShortAssignmentToken instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Tokens'! !RBShortAssignmentToken methodsFor: 'private' stamp: 'lr 11/1/2009 20:45'! length ^ 1! ! !RBToken class methodsFor: 'instance creation' stamp: ''! start: anInterval ^self new start: anInterval! ! !RBToken methodsFor: 'accessing' stamp: ''! comments ^comments! ! !RBToken methodsFor: 'accessing' stamp: ''! comments: anObject comments := anObject! ! !RBToken methodsFor: 'testing' stamp: ''! isAssignment ^false! ! !RBToken methodsFor: 'testing' stamp: ''! isBinary ^false! ! !RBToken methodsFor: 'testing' stamp: ''! isIdentifier ^false! ! !RBToken methodsFor: 'testing' stamp: ''! isKeyword ^false! ! !RBToken methodsFor: 'testing' stamp: ''! isLiteral ^self isLiteralToken! ! !RBToken methodsFor: 'testing' stamp: ''! isLiteralArrayToken ^false! ! !RBToken methodsFor: 'testing' stamp: ''! isLiteralToken ^false! ! !RBToken methodsFor: 'testing' stamp: ''! isOptimized ^false! ! !RBToken methodsFor: 'testing' stamp: ''! isPatternBlock ^false! ! !RBToken methodsFor: 'testing' stamp: ''! isPatternVariable ^false! ! !RBToken methodsFor: 'testing' stamp: ''! isSpecial ^false! ! !RBToken methodsFor: 'accessing' stamp: ''! length ^self subclassResponsibility! ! !RBToken methodsFor: 'printing' stamp: ''! printOn: aStream aStream nextPut: $ ; nextPutAll: self class name! ! !RBToken methodsFor: 'accessing' stamp: ''! removePositions sourcePointer := nil! ! !RBToken methodsFor: 'accessing' stamp: ''! start ^sourcePointer! ! !RBToken methodsFor: 'initialize-release' stamp: ''! start: anInteger sourcePointer := anInteger! ! !RBToken methodsFor: 'accessing' stamp: ''! stop ^self start + self length - 1! ! RBToken subclass: #RBValueToken instanceVariableNames: 'value' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Tokens'! !RBValueToken commentStamp: '' prior: 0! RBValueToken is the abstract superclass of all tokens that have additional information attached. For example, the BinarySelector token holds onto the actual character (e.g. $+). Instance Variables: value The value of this token ! RBValueToken subclass: #RBBinarySelectorToken instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Tokens'! !RBBinarySelectorToken commentStamp: 'md 8/9/2005 14:51' prior: 0! RBBinarySelectorToken is the first-class representation of a binary selector (e.g. +) ! !RBBinarySelectorToken methodsFor: 'testing' stamp: ''! isBinary ^true! ! RBValueToken subclass: #RBIdentifierToken instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Tokens'! !RBIdentifierToken commentStamp: 'md 8/9/2005 14:51' prior: 0! RBIdentifierToken is the first class representation of an identifier token (e.g. Class) ! !RBIdentifierToken methodsFor: 'testing' stamp: ''! isIdentifier ^true! ! !RBIdentifierToken methodsFor: 'testing' stamp: ''! isPatternVariable ^value first == RBScanner patternVariableCharacter! ! RBValueToken subclass: #RBKeywordToken instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Tokens'! !RBKeywordToken commentStamp: 'md 8/9/2005 14:52' prior: 0! RBKeywordToken is the first-class representation of a keyword token (e.g. add:)! !RBKeywordToken methodsFor: 'testing' stamp: ''! isKeyword ^true! ! !RBKeywordToken methodsFor: 'testing' stamp: ''! isPatternVariable ^value first == RBScanner patternVariableCharacter! ! RBValueToken subclass: #RBLiteralArrayToken instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Tokens'! !RBLiteralArrayToken methodsFor: 'testing' stamp: ''! isForByteArray ^value last = $[! ! !RBLiteralArrayToken methodsFor: 'testing' stamp: ''! isLiteralArrayToken ^true! ! RBValueToken subclass: #RBLiteralToken instanceVariableNames: 'stopPosition' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Tokens'! !RBLiteralToken commentStamp: 'md 8/9/2005 14:52' prior: 0! RBLiteralToken is the first-class representation of a literal token (entire literals, even literal arrays, are a single token in the ST80 grammar.). Instance Variables: stopPosition The position within the source code where the token terminates. ! !RBLiteralToken class methodsFor: 'instance creation' stamp: ''! value: anObject | literal | literal := anObject class == Array ifTrue: [anObject collect: [:each | self value: each]] ifFalse: [anObject]. ^self value: literal start: nil stop: nil! ! !RBLiteralToken class methodsFor: 'instance creation' stamp: ''! value: aString start: anInteger stop: stopInteger ^(self new) value: aString start: anInteger stop: stopInteger; yourself! ! !RBLiteralToken methodsFor: 'testing' stamp: ''! isLiteralToken ^true! ! !RBLiteralToken methodsFor: 'testing' stamp: ''! isMultiKeyword ^false! ! !RBLiteralToken methodsFor: 'private' stamp: ''! length ^stopPosition - self start + 1! ! !RBLiteralToken methodsFor: 'accessing' stamp: ''! realValue ^value! ! !RBLiteralToken methodsFor: 'accessing' stamp: ''! stop: anObject stopPosition := anObject! ! !RBLiteralToken methodsFor: 'printing' stamp: ''! storeOn: aStream value isSymbol ifTrue: [aStream nextPut: $#. ((RBScanner isSelector: value) and: [value ~~ #'||']) ifTrue: [aStream nextPutAll: value] ifFalse: [value asString printOn: aStream]. ^self]. value class == Character ifTrue: [aStream nextPut: $$; nextPut: value. ^self]. value storeOn: aStream! ! !RBLiteralToken methodsFor: 'initialize-release' stamp: ''! value: aString start: anInteger stop: stopInteger value := aString. sourcePointer := anInteger. stopPosition := stopInteger! ! RBLiteralToken subclass: #RBMultiKeywordLiteralToken instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Tokens'! !RBMultiKeywordLiteralToken methodsFor: 'testing' stamp: ''! isMultiKeyword ^true! ! RBLiteralToken subclass: #RBNumberLiteralToken instanceVariableNames: 'source' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Tokens'! !RBNumberLiteralToken class methodsFor: 'instance creation' stamp: ''! value: aNumber start: anInteger stop: stopInteger source: sourceString ^(self value: aNumber start: anInteger stop: stopInteger) source: sourceString; yourself! ! !RBNumberLiteralToken methodsFor: 'accessing' stamp: ''! source ^source! ! !RBNumberLiteralToken methodsFor: 'initialize-release' stamp: ''! source: aString source := aString! ! !RBNumberLiteralToken methodsFor: 'printing' stamp: ''! storeOn: aStream aStream nextPutAll: source! ! RBValueToken subclass: #RBPatternBlockToken instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Tokens'! !RBPatternBlockToken commentStamp: 'md 8/9/2005 14:52' prior: 0! RBPatternBlockToken is the first-class representation of the pattern block token. ! !RBPatternBlockToken methodsFor: 'testing' stamp: ''! isPatternBlock ^true! ! RBValueToken subclass: #RBSpecialCharacterToken instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'AST-Core-Tokens'! !RBSpecialCharacterToken commentStamp: 'md 8/9/2005 14:53' prior: 0! RBSpecialCharacterToken is the first class representation of special characters. ! !RBSpecialCharacterToken methodsFor: 'testing' stamp: ''! isSpecial ^true! ! !RBSpecialCharacterToken methodsFor: 'private' stamp: ''! length ^1! ! !RBValueToken class methodsFor: 'instance creation' stamp: ''! value: aString start: anInteger ^self new value: aString start: anInteger! ! !RBValueToken methodsFor: 'private' stamp: ''! length ^value size! ! !RBValueToken methodsFor: 'printing' stamp: ''! printOn: aStream super printOn: aStream. aStream nextPut: $(. value printOn: aStream. aStream nextPutAll: ')'! ! !RBValueToken methodsFor: 'accessing' stamp: ''! value ^value! ! !RBValueToken methodsFor: 'accessing' stamp: ''! value: anObject value := anObject! ! !RBValueToken methodsFor: 'initialize-release' stamp: ''! value: aString start: anInteger value := aString. sourcePointer := anInteger! ! !CompiledMethod methodsFor: '*ast-core' stamp: 'lr 11/1/2009 18:32'! parseTree ^ RBParser parseMethod: self getSource asString onError: [ :msg :pos | ^ nil ]! ! RBConfigurableFormatter initialize! RBScanner initialize!