ParseNode.st
author Jan Vrany <jan.vrany@labware.com>
Thu, 27 Oct 2022 14:53:59 +0100
branchjv
changeset 4735 3b11fb3ede98
parent 4723 524785227024
permissions -rw-r--r--
Allow single underscore as method / block argument and temporaries This commit is a follow up for 38b221e.

"
 COPYRIGHT (c) 1989 by Claus Gittinger
 COPYRIGHT (c) 2015 Jan Vrany
 COPYRIGHT (c) 2018 Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
"{ Package: 'stx:libcomp' }"

"{ NameSpace: Smalltalk }"

Object subclass:#ParseNode
	instanceVariableNames:'parent type comments parenthized startPosition endPosition
		attributes'
	classVariableNames:''
	poolDictionaries:''
	category:'System-Compiler-Support'
!

!ParseNode class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1989 by Claus Gittinger
 COPYRIGHT (c) 2015 Jan Vrany
 COPYRIGHT (c) 2018 Claus Gittinger
	      All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    node for parse-trees; abstract class
    This is a helper class for the compiler.

    [author:]
        Claus Gittinger
"
! !

!ParseNode class methodsFor:'instance creation'!

type:t
    ^ (self basicNew) type:t
! !

!ParseNode methodsFor:'RBParser compatibility'!

bestNodeFor: anInterval 
    (self intersectsInterval: anInterval) ifFalse: [^nil].
    (self containedBy: anInterval) ifTrue: [^self].

    self childrenDo:[:each | 
        | node |
        node := each bestNodeFor: anInterval.
        node notNil ifTrue: [^node]
    ].
    ^ nil
!

children
    |children|

    children := OrderedCollection new.
    self childrenDo:[:each |
        self assert:(each notNil).
        children add:each
    ].
    ^ children.
!

childrenDo:aBlock
    ^ self
!

containedBy:anInterval
    "true if the interval contains me completely"

    startPosition isNil ifTrue:[^ false].
    endPosition isNil ifTrue:[^ false].
    ^ anInterval first <= startPosition and:[anInterval last >= endPosition]
!

intersectsInterval: anInterval 
    |myStart myStop ivStart ivStop|

    myStart := startPosition.
    myStop := endPosition.
    ivStart := anInterval first.
    ivStop := anInterval last.
    (myStart notNil and:[ivStop < myStart]) ifTrue:[^ false].
    (myStop notNil and:[ivStart > myStop]) ifTrue:[^ false].
    ^ true
!

nodesDo:aBlock 
    aBlock value: self.
    self children do: [:each | each nodesDo: aBlock]
!

start
    "for RBToken compat."

    ^ self startPosition
!

whichNodeIntersects: anInterval 
    | selectedChildren nChildren |

    (self intersectsInterval: anInterval) ifFalse: [^nil].

    selectedChildren := self children select:[:each | 
                            each intersectsInterval: anInterval
                        ].

    nChildren := selectedChildren size.
    nChildren == 0 ifTrue:[
        ^ self "/ I intersect
    ].
    nChildren == 1 ifTrue:[
        ^ selectedChildren first whichNodeIntersects: anInterval
    ].
"/ self halt:'should this happen ?'.
    ^ self "/ I intersect
!

whichNodeIsContainedBy:anInterval 
    |firstChildInInterval|

    (self intersectsInterval: anInterval) ifFalse: [^nil].
    (self containedBy: anInterval) ifTrue: [^self].

    self childrenDo:[:each | 
        (each intersectsInterval:anInterval) ifTrue:[
            firstChildInInterval notNil ifTrue:[
                "/ ouch: multiple children in interval
                ^ self
            ].
            firstChildInInterval := each
        ].
    ].

    firstChildInInterval isNil ifTrue:[
        "/ no children in interval; so it must be me
        ^ self
    ].
    "/ look deeper
    ^ firstChildInInterval whichNodeIsContainedBy:anInterval.
! !

!ParseNode methodsFor:'accessing'!

enclosingBlock
    "Return closest enclosing block node or nil"

    | block |
    block := parent.
    [ block notNil and:[block realNode isBlockNode not] ] whileTrue:[
        block := block realNode parent.
    ].
    ^block.

    "Created: / 16-02-2012 / 22:46:12 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

endPosition
    "the node's last character position in the original source"

    ^ endPosition
!

endPosition:aCharacterPosition
    self assert:aCharacterPosition > 0.

    endPosition := aCharacterPosition

    "Modified: / 28-07-2011 / 10:03:53 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

line

    "Compatibility"

    ^self lineNumber

    "Created: / 09-07-2011 / 22:30:38 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

lineNumber

    ^nil

    "Created: / 09-07-2011 / 22:30:57 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

lineNumber:dummy
    "set linenumber - ignored here"

    ^ self
!

parent

    ^parent

    "Created: / 11-07-2011 / 17:36:43 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

parent: aParseNode

    parent := aParseNode

    "Created: / 11-07-2011 / 17:36:33 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

parenthesized
    ^ parenthized ? false
!

parenthesized:aBoolean
    parenthized := aBoolean
!

parenthized
    "obsolete - typo in message name - use parenthesized"

    ^ parenthized ? false
!

parenthized:aBoolean
    "obsolete - typo in message name - use parenthesized:"

    parenthized := aBoolean
!

realNode
    ^ self
!

selectorPartPositions:selectorPartPositions
    "/ ignored here, but implemented because sent without checking 
    "/ when parsing keyword messages to an optimized messageNode
    "/ (which become optimized to a constantNode by the folder)

    "Created: / 28-08-2013 / 21:17:22 / cg"
!

selectorPosition:aCharacterPosition
    "ignored here"

    "Created: 5.8.1997 / 16:32:17 / cg"
!

startPosition
    "the node's character position in the original source"

    ^ startPosition
!

startPosition:start
    self assert:start > 0.
    startPosition := start

    "Modified: / 08-04-2011 / 22:03:43 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

startPosition:start endPosition:end
    (start == -1 and:[end == -1]) ifTrue:[
        "/ a synthetic node
        "/ self halt.
    ] ifFalse:[
        "/ nil end means: up-to-end (but only for error nodes)
        self assert:(start notNil and:[start > 0]).
        end isNil ifTrue:[
            self assert:(self isErrorNode)
        ] ifFalse:[
            self assert:(end >= start).
        ].
    ].
    startPosition := start.
    endPosition := end.

    "Created: / 11-07-2011 / 17:44:11 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified: / 15-02-2019 / 14:39:10 / Claus Gittinger"
!

type
    "return the nodes type"

    ^ type
! !

!ParseNode methodsFor:'attributes access'!

objectAttributes
    "return a Collection of attributes - nil if there is none."

    ^attributes

    "Created: / 09-07-2011 / 12:17:55 / Jan Vrany <jan.vrany@fit.cvut.cz>"
    "Modified (comment): / 18-06-2020 / 12:20:25 / cg"
!

objectAttributes: anObject

    attributes := anObject

    "Created: / 09-07-2011 / 12:18:08 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!ParseNode methodsFor:'checks'!

plausibilityCheckIn:aParser
    ^ nil
! !

!ParseNode methodsFor:'code generation'!

codeForSideEffectOn:aStream inBlock:b for:aCompiler
    "generate code for this statement - value not needed"

    self codeThenDropOn:aStream inBlock:b for:aCompiler 
!

codeForSimpleReturnOn:aStream inBlock:b lineNumber:lineNrOrNil for:aCompiler
    "generate code to return myself as a simple method return"

    self codeOn:aStream inBlock:b for:aCompiler.
    lineNrOrNil notNil ifTrue:[
        self codeLineNumber:lineNrOrNil on:aStream for:aCompiler
    ].
    aStream nextPut:#retTop.


!

codeInlineOn:aStream inBlock:b valueNeeded:valueNeeded for:aCompiler
    "generate code for this statement - value is needed"

    self codeOn:aStream inBlock:b for:aCompiler.
!

codeLineNumber:nr on:codeStream for:aCompiler
    "generate lineNumber information"

    aCompiler codeLineNumber:nr on:codeStream
!

codeOn:aStream inBlock:codeBlock for:aCompiler
    ^ self subclassResponsibility
!

codeSourcePosition:nr on:codeStream for:aCompiler
    "generate source position information"

    aCompiler codeSourcePosition:nr on:codeStream

    "Created: / 31-05-2015 / 03:45:10 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

codeThenDropOn:aStream inBlock:b for:aCompiler
    "generate code for this statement - value not needed"

    self codeOn:aStream inBlock:b for:aCompiler.
    aStream nextPut:#drop
! !

!ParseNode methodsFor:'code generation helpers'!

emitPushGlobalWithLiteralIndex:litIndex on:aTokenCodeStream for:aCompiler
    litIndex <= 255 ifTrue:[
        aTokenCodeStream nextPut:#pushGlobalS; nextPut:litIndex
    ] ifFalse:[
        litIndex <= 16rFFFF ifTrue:[
            aTokenCodeStream nextPut:#pushGlobalL; nextPut:litIndex; nextPut:0
        ] ifFalse:[
            aTokenCodeStream nextPut:#pushGlobalVL; nextPut:0; nextPut:litIndex; nextPut:0; nextPut:0; nextPut:0
        ].
    ].
!

emitPushLiteral:value on:aTokenCodeStream for:aCompiler
    |index|

    index := aCompiler addLiteral:value.
    self emitPushLiteralIndex:index on:aTokenCodeStream for:aCompiler
!

emitPushLiteralIndex:index on:aTokenCodeStream for:aCompiler
    index <= 8 ifTrue:[
        aTokenCodeStream 
            nextPut:(#(pushLit1 pushLit2 pushLit3 pushLit4
                       pushLit5 pushLit6 pushLit7 pushLit8) at:index).
    ] ifFalse:[
        index <= 255 ifTrue:[
            aTokenCodeStream nextPut:#pushLitS; nextPut:index
        ] ifFalse:[
            index <= 16rFFFF ifTrue:[
                aTokenCodeStream nextPut:#pushLitL; nextPut:index; nextPut:0
            ] ifFalse:[
                aTokenCodeStream nextPut:#pushLitVL; nextPut:0; nextPut:index; nextPut:0; nextPut:0; nextPut:0
            ]
        ].
    ].
!

emitSendLiteralIndex:litIndex numArgs:nargs line:lineNr on:aStream for:aCompiler
    lineNr >= 255 ifTrue:[
        self codeLineNumber: lineNr on: aStream for:aCompiler.  
    ].
    (litIndex <= 255) ifTrue:[
        nargs <= 3 ifTrue:[
            aStream 
                nextPut:(#(send0 send1 send2 send3) at:(nargs+1)); nextPut:lineNr; 
                nextPut:litIndex.
            ^ self.
        ].
        aStream 
            nextPut:#send; nextPut:lineNr; 
            nextPut:litIndex; 
            nextPut:nargs.
        ^ self.
    ].

    (litIndex <= 16rFFFF) ifTrue:[
        aStream 
            nextPut:#sendL; nextPut:lineNr; 
            nextPut:litIndex; nextPut:0; 
            nextPut:nargs.
        ^ self.
    ].

    aStream 
        nextPut:#sendVL; nextPut:0; nextPut:lineNr; 
        nextPut:litIndex; nextPut:0; nextPut:0; nextPut:0; 
        nextPut:nargs.

    "Modified: / 13-04-2013 / 11:02:20 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

emitStoreGlobalWithLiteralIndex:litIndex on:aTokenCodeStream for:aCompiler
    litIndex <= 255 ifTrue:[
        aTokenCodeStream nextPut:#storeGlobalS; nextPut:litIndex
    ] ifFalse:[
        litIndex <= 16rFFFF ifTrue:[
            aTokenCodeStream nextPut:#storeGlobalL; nextPut:litIndex; nextPut:0
        ] ifFalse:[
            aTokenCodeStream nextPut:#storeGlobalVL; nextPut:0; nextPut:litIndex; nextPut:0; nextPut:0; nextPut:0; nextPut:0
        ].
    ].
!

emitSuperSendLiteralIndex:litIndex classLiteralIndex:clsLitIndex numArgs:nargs line:lineNr on:aStream for:aCompiler
    lineNr >= 255 ifTrue:[
        self codeLineNumber: lineNr on: aStream for:aCompiler.  
    ].
    (litIndex <= 255 and:[clsLitIndex <= 255]) ifTrue:[
        aStream 
            nextPut:#superSend; nextPut:lineNr; 
            nextPut:litIndex; 
            nextPut:nargs; 
            nextPut:clsLitIndex.
    ] ifFalse:[
        (litIndex <= 16rFFFF and:[clsLitIndex <= 16rFFFF]) ifTrue:[
            aStream 
                nextPut:#superSendL; nextPut:lineNr; 
                nextPut:litIndex; nextPut:0; 
                nextPut:nargs; 
                nextPut:clsLitIndex; nextPut:0.
        ] ifFalse:[
            aStream 
                nextPut:#superSendVL; nextPut:0; nextPut:lineNr; 
                nextPut:litIndex; nextPut:0; nextPut:0; nextPut:0;
                nextPut:nargs; 
                nextPut:clsLitIndex; nextPut:0; nextPut:0; nextPut:0.
        ].
    ].

    "Modified: / 13-04-2013 / 11:01:51 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!ParseNode methodsFor:'enumeration'!

allMessageSelectorsDo:aBlock
    "evaluate aBlock for each message-node here and in subnodes"

    ^ self messageSelectorsDo:aBlock
!

allMessagesDo:aBlock
    "evaluate aBlock for each message-node here and in subnodes"

    ^ self messagesDo:aBlock
!

allNodesDo:aBlock
    "evaluate aBlock for each variable-node here and in subnodes"

    aBlock value:self.
    self allSubNodesDo:aBlock.
!

allSubNodesDo:aBlock
    "/ self subclassResponsibility.
!

blockNodesDo:aBlock recursively: aBoolean
    "
    Evaluate `aBlock` for each `BlockNode` in receicer's subtree. 

    If, `aBoolean` is true, then recurse into block nodes themselves, 
    evaluating `aBlock` for (all) nested blocks.
    If `aBoolean` is false, stop at any block node.
    " 
    ^ self

    "Created: / 15-07-2018 / 10:35:02 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

messageNodesDo:aBlock
    "evaluate aBlock for each message-node here and in subnodes"

    self messagesDo:aBlock
!

messageSelectors
    "return a collection of message selectors"

    |setOfSymbols|

    setOfSymbols := Set new.
    self messageSelectorsDo:[:each | setOfSymbols add:each ].
    ^ setOfSymbols
!

messageSelectorsDo:aBlock
    "evaluate aBlock for each message-selector here and in subnodes"

    ^ self
!

messages
    "return a collection of messageNodes"

    |setOfMessages|

    setOfMessages := Set new.
    self messagesDo:[:each | setOfMessages add:each ].
    ^ setOfMessages
!

messagesDo:aBlock
    "evaluate aBlock for each message-node here and in subnodes"

    ^ self
!

statements:statements do:aBlock
    |s|

    statements isNil ifTrue:[^ self].

    "/ temporary hack
    statements isCollection ifTrue:[
        statements do:aBlock.
    ] ifFalse:[
        "/ bad hack
        (statements isKindOf:StatementNode) ifTrue:[
            s := statements.
            [ s notNil ] whileTrue:[
                aBlock value:s.
                s := s nextStatement
            ].
        ] ifFalse:[
            "/ an innerblock node
            aBlock value:statements.
        ].
    ].

    "Modified: / 10-07-2019 / 02:32:21 / Claus Gittinger"
!

variableNodesDo:aBlock
    "evaluate aBlock for each variable-node here and in subnodes"

    ^ self
! !

!ParseNode methodsFor:'evaluation'!

evaluate
    ^ self evaluateIn:nil
!

evaluateForCascade
    ^ self evaluateForCascadeIn:nil
!

evaluateForCascadeIn:anEnvironment
    ^ self evaluateIn:anEnvironment
!

evaluateIn:anEnvironment
    self subclassResponsibility
!

isStatement
^false.

    "Created: / 22-02-2011 / 16:29:50 / Jakub <zelenja7@fel.cvut.cz>"
! !

!ParseNode methodsFor:'printing & storing'!

printOn:aStream
    "append a user printed representation of the receiver to aStream.
     The format is suitable for a human - not meant to be read back."

    self printOn:aStream indent:0
!

printOn:aStream indent:indent
    "append a user printed representation of the receiver to aStream.
     The format is suitable for a human - not meant to be read back."

    self subclassResponsibility

    "Created: / 20-04-2005 / 14:21:46 / cg"
!

printOn:aStream indent:indent parenthized:parenthized
    parenthized ifTrue:[
        aStream nextPutAll:'('
    ].
    self printOn:aStream indent:indent.
    parenthized ifTrue:[
        aStream nextPutAll:')'
    ].

    "Created: / 20-04-2005 / 14:21:28 / cg"
!

printStatementListOn:aStream indent:i
    self printOn:aStream indent:i 
! !

!ParseNode methodsFor:'private'!

type:t
    "set the nodes type"

    type := t
! !

!ParseNode methodsFor:'queries'!

canReuseAsArg:anotherNode
    ^ false

    "Created: 14.4.1996 / 00:43:08 / cg"
!

collectBlocksInto:aCollection
    ^ self

    "Created: 23.10.1996 / 15:45:00 / cg"
!

containsReturn
    ^ false
!

parseNodeVisitorClass
    ^ ParseNodeVisitor
!

positionToInsertPeriodForStatementSeparation
    ^ self endPosition + (parenthized == true ifTrue:1 ifFalse:0)

    "Created: 14.4.1996 / 00:43:08 / cg"
!

precedence
    ^ 9999
!

whoDefines:aName
    "return the node (blockNode) in which this variable is defined.
     (nil if instvar, classvar or global)"

    |p|

    "/ TODO
"/    block notNil ifTrue:[
"/        self halt
"/    ].
    p := parent.
    [p notNil and:[p isFunctionNode not and:[p isBlockNode not]]] whileTrue:[ p := p parent].
    p notNil ifTrue:[
        ((p arguments ? #()) contains:[:var | var name = aName]) ifTrue:[^ p].
        ((p localVariables ? #()) contains:[:var | var name = aName]) ifTrue:[^ p].
    ].
    ^ nil
!

withConstantValueDo:aBlock
    "return true, if this evaluates to a constant value
     and evaluate aBlock with it"

    ^ false
! !

!ParseNode methodsFor:'testing'!

isAssignment
    "return true, if this is a node for an assignment"

    ^ false
!

isBinaryMessage
    "return true, if this is a node for a binary send"

    ^ false
!

isBlockNode
    "return true, if this is a node for a block"

    ^ false
!

isBreakPointNode
    ^ false

    "Created: / 05-07-2011 / 21:14:01 / cg"
!

isCascade
    ^ false

    "Created: / 16.7.1998 / 20:11:33 / cg"
!

isCascadeToSuper
    ^ false

    "Created: / 16.7.1998 / 19:51:07 / cg"
!

isConstant
    "return true, if this is a node for a constant"

    ^ false
!

isConstantNumber
    "return true, if this is a node for a constant number"

    ^ false

    "Created: / 16-06-2018 / 08:47:03 / Claus Gittinger"
!

isErrorNode
    ^ false
!

isFunctionCallNode
    ^ false

    "Created: / 13-12-2018 / 22:35:28 / Claus Gittinger"
!

isFunctionNode
    ^ false

    "Created: / 08-02-2019 / 10:53:58 / Claus Gittinger"
!

isGlobal
    "return true, if this is a node for a global variable"

    ^ false
!

isGlobalNamed:globalName
    "return true, if this is a node for a particular global variable"

    ^ false

    "Created: / 05-03-2007 / 15:13:23 / cg"
!

isGlobalVariable
    ^ false
!

isImmutable
    "not used with ST/X - 
     for JavaScript nodes return true here."

    ^ true
!

isImplicitJavaScriptMessage
    "return true, if this is a node for an implicit getter/setter send"

    ^ false

    "Created: / 05-07-2010 / 14:11:41 / cg"
!

isInnerFunction
    ^ false
!

isInnerJavaScriptBlock
    ^ false
!

isJAVA
    "Return true, if receiver is global variable node JAVA.
     Used to highlight Java class references."

    ^false

    "Created: / 19-04-2012 / 09:36:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isJavaPackageReference
    "Return true, given node is JAVA package reference in form:
        JAVA package1 package2 
    "

    ^false

    "Created: / 19-04-2012 / 09:53:21 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isJavaScriptAndExpression
    ^ false
!

isJavaScriptArrayAccess
    "return true, if this is a node for a javascript array reference i.e. expr[]"

    ^ false

    "Created: / 05-07-2010 / 14:04:27 / cg"
!

isJavaScriptBlock
    "return true, if this is a node for a javascript block"

    ^ false

    "Created: / 09-06-2010 / 01:53:19 / cg"
!

isJavaScriptClassNode
    ^ false
!

isJavaScriptConditionalExpression
    "return true, if this is a node for a ?: expression"

    ^ false

    "Created: / 09-06-2010 / 01:55:15 / cg"
!

isJavaScriptOrExpression
    ^ false
!

isLiteral
    "for compatibility with RB-AST"

    ^ self isConstant
!

isLiteralArray
    "for compatibility with RB-AST"

    ^ false
!

isLiteralCString
    "for compatibility with RB-AST"

    ^ false
!

isLocalVariable
    ^ false
!

isMessage
    "return true, if this is a node for a message expression"

    ^ false
!

isMethodNode
    ^ false
!

isMethodVariable
    ^ false
!

isNew
    "return true, if this is a new XXXX node"

    ^ false
!

isPostIncDec
    "for JavaScript"

    ^ false
!

isPreIncDec
    "for JavaScript"

    ^ false
!

isPrimary
    "return true, if this is a node for a primary (i.e. non-send)"

    ^ false
!

isReturnNode
    "return true, if this is a node for a return expression"

    ^ false
!

isSelf
    "return true, if this is a self-node"

    ^ false
!

isSequence
    "return true, if this is a sequence node (JS)"

    ^ false
!

isStatementNode
    "return true, if this is a statement node"
    
    ^ false

    "Created: / 13-06-2011 / 11:26:10 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

isSuper
    "return true, if this is a super-node"

    ^ false
!

isSynthetic
    "true if I am a synthetic node (generated from embedded expression strings)"

    ^ false
!

isThis
    "for JavaScript"

    ^ false
!

isUnaryMessage
    "return true, if this is a node for a unary send"

    ^ false
!

isVariable
    "return true, if this is a node for a variable"

    ^ false

    "Created: 14.4.1996 / 00:46:44 / cg"
!

isVariableNode
    "return true, if this is a node for a variable"

    ^ self isVariable

    "Created: / 12-09-2011 / 09:32:17 / cg"
! !

!ParseNode class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
! !