compiler/PPCTokenCodeGenerator.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Mon, 17 Aug 2015 12:56:02 +0100
changeset 516 3b81c9e53352
parent 503 ff58cd9f1f3c
parent 515 b5316ef15274
child 518 a6d8b93441b0
permissions -rw-r--r--
Merge

"{ Package: 'stx:goodies/petitparser/compiler' }"

"{ NameSpace: Smalltalk }"

PPCNodeVisitor subclass:#PPCTokenCodeGenerator
	instanceVariableNames:'compiler scannerGenerator fsaCache'
	classVariableNames:''
	poolDictionaries:''
	category:'PetitCompiler-Visitors'
!

!PPCTokenCodeGenerator methodsFor:'accessing'!

arguments: args
    super arguments: args.
    scannerGenerator arguments: args
!

compiler
    ^ compiler
!

compiler: anObject
    compiler := anObject.
    
    scannerGenerator compiler idGen: compiler idGen.
! !

!PPCTokenCodeGenerator methodsFor:'code support'!

consumeWhitespace: node
    self assert: node isTokenNode.

    node isTrimmingTokenNode ifTrue: [ 
        compiler code: 'self consumeWhitespace.'
    ]
!

createTokenInsance: node id: idCode start: startVar end: endVar
    compiler codeTranscriptShow: 'current token type: ', idCode.
    compiler codeAssign: idCode, '.' to: 'currentTokenType'.
    compiler codeAssign: node tokenClass asString, ' on: (context collection) 
                                                            start: ', startVar, ' 
                                                            stop: ', endVar, '
                                                            value: nil.'
               to: 'currentTokenValue'.
!

scan: node start: startVar end: endVar
    node child hasName ifFalse: [ 
        node child name: node name
    ].

    compiler codeAssign: 'context position + 1.' to: startVar.
    compiler add: ((self generateScan: node child) callOn: 'scanner').
!

unorderedChoiceFromFollowSet: followSet
    | followFsas  |
    
    ^ fsaCache at: followSet ifAbsentPut: [ 
        followFsas := followSet collect: [ :followNode | 
                (followNode asFsa) 
                    name: (compiler idFor: followNode);
                    retval: (compiler idFor: followNode); 
                    yourself
        ].
        self unorderedChoiceFromFsas: followFsas.
    ]
     
!

unorderedChoiceFromFsas: fsas
    | result startState |
    result := PEGFsa new.
    startState := PEGFsaState new.
    
    result addState: startState.
    result startState: startState.

    fsas do: [ :fsa | 
        result adopt: fsa.
        result addTransitionFrom: startState to: fsa startState.
    ].

    result determinizeStandard.
    ^ result
! !

!PPCTokenCodeGenerator methodsFor:'compiling support'!

compileScanner
    ^ scannerGenerator compileScannerClass
!

retvalVar
    ^ compiler currentReturnVariable
!

startMethodForNode:node
    node isMarkedForInline ifTrue:[ 
        compiler startInline: (compiler idFor: node).
        compiler codeComment: 'BEGIN inlined code of ' , node printString.
        compiler indent.
    ] ifFalse:[ 
        compiler startMethod: (compiler idFor: node).
        compiler currentMethod category: 'generated - tokens'.
        compiler codeComment: 'GENERATED by ' , node printString.
        compiler allocateReturnVariable.
    ]
!

stopMethodForNode:aPPCNode
    ^ aPPCNode isMarkedForInline ifTrue:[ 
                compiler dedent.
                compiler add: '"END inlined code of ' , aPPCNode printString , '"'.
                compiler stopInline.
    ] ifFalse:[ 
                compiler stopMethod
    ].
! !

!PPCTokenCodeGenerator methodsFor:'initialization'!

initialize
    super initialize.
    
    scannerGenerator := PPCScannerCodeGenerator new.
    scannerGenerator arguments: arguments.
    
    "for the given set of nodes, remember the unordered choice fsa
        see `unorderedChoiceFromFollowSet:`
    "
    fsaCache := Dictionary new.
! !

!PPCTokenCodeGenerator methodsFor:'scanning'!

generateNextScan: node
    | epsilon followSet  anFsa |
    followSet := node followSetWithTokens.
    
    epsilon := followSet anySatisfy: [ :e | e acceptsEpsilon ].
    followSet := followSet reject: [ :e | e acceptsEpsilon ].
    epsilon ifTrue: [ followSet add: PPCEndOfFileNode instance ].
    
    anFsa := self unorderedChoiceFromFollowSet: followSet.

    anFsa name: 'nextToken_', (compiler idFor: node).
    node nextFsa: anFsa.
    ^ scannerGenerator generate: anFsa.
!

generateScan: node
    | anFsa |
    anFsa := node asFsa determinize.
    anFsa name: (compiler idFor: node).
    anFsa retval: (compiler idFor: node).
    
    ^ scannerGenerator generate: anFsa.
! !

!PPCTokenCodeGenerator methodsFor:'visiting'!

visitToken: tokenNode
    |  id  startVar endVar  numberId |
    self startMethodForNode: tokenNode.

    "Tokens cannot be inlined, 
        - their result is true/false
        - the return value is always stored in currentTokenValue
        - the current token type is always stored in currentTokenType
    "
    self assert: tokenNode isMarkedForInline not.
    
    startVar := compiler allocateTemporaryVariableNamed: 'start'.
    endVar := compiler allocateTemporaryVariableNamed:  'end'.
    
    id := compiler idFor: tokenNode.
    numberId := compiler numberIdFor: id.
    
    compiler add: 'currentTokenType isNil ifFalse: [ ^ currentTokenType == ', id storeString, '].'.
    
"	compiler codeComment: 'number for: ', id storeString, ' is: ', numberId storeString.
    compiler codeIf: 'scanner match: ', numberId storeString then: [ 
        compiler codeAssign: '(scanner resultPosition: ', numberId storeString, ').' to: endVar.
        self createTokenInsance: tokenNode 
                id: id storeString 
                start: '(context position + 1)' 
                end: endVar.
        
        compiler code: 'context position: ', endVar, '.'.
        
        self consumeWhitespace: tokenNode.
        compiler codeReturn: 'true'.
    ].
    compiler codeIf: 'scanner backtracked not' then: [ 
        compiler codeReturn: 'false'.
    ].
    compiler codeComment: 'No match, no fail, scanner does not know about this...'.	
"
    compiler profileTokenRead: id.
    
"	self scan: tokenNode start: startVar end: endVar."
    "	compiler add: 'self assert: scanner isSingleMatch.'."
"	compiler codeIf: 'scanner match ' then: ["

    tokenNode child hasName ifFalse: [ 
        tokenNode child name: tokenNode name
    ].

    compiler codeAssign: 'context position + 1.' to: startVar.
    compiler codeIf: [ compiler code: ((self generateScan: tokenNode child) callOn: 'scanner') ] then: [ 
        compiler add: 'context position: scanner resultPosition.'.
        compiler codeAssign: 'context position.' to: endVar.	
        self consumeWhitespace: tokenNode.
        self createTokenInsance: tokenNode id: id storeString start: startVar end: endVar.
        compiler codeReturn: 'true'.
    ] else: [ 
        compiler code: 'scanner backtrackDistinct.'.
        compiler code: 'context position: ', startVar, ' - 1.'. 
        compiler codeReturn: 'false'.
    ].
    
    ^ self stopMethodForNode: tokenNode
!

visitTokenConsumeNode: node
    | id   nextScan |
    self startMethodForNode: node.
    id := (compiler idFor: node child).

    compiler add: 'self ', id asString, ' ifTrue: ['.
        compiler indent.

        nextScan := self generateNextScan: node.
        
        node nextFsa hasDistinctRetvals ifTrue: [ 
            compiler codeAssign: 'currentTokenValue.' to: self retvalVar.
        
            compiler add: (nextScan callOn: 'scanner'), '.'.
            compiler codeIf: 'scanner match' then: [ 
            compiler add: 'context position: scanner resultPosition.'.
                self createTokenInsance: node child 
                        id: 'scanner result' 
                        start: 'scanner position + 1' 
                        end: 'scanner resultPosition'.
                self consumeWhitespace: node child.
                compiler codeReturn.
            ] else: [ 
                compiler codeComment: 'Looks like there is an error on its way...'.
                compiler code: 'context position: scanner position.'.
                compiler codeAssign: 'nil.' to: 'currentTokenType'.
                compiler codeReturn.
            ]

        ] ifFalse: [ 
            compiler codeAssign: 'nil.' to: 'currentTokenType'.
            compiler codeReturn: 'currentTokenValue'.
        ].
        compiler dedent.

    "Token not found"
    compiler add: '] ifFalse: ['.
        compiler indent.
        compiler codeError: id asString, ' expected'.
        compiler dedent.
    compiler add: '].'.

    ^ self stopMethodForNode: node
!

visitTokenNode: node
    ^ self visitToken: node
!

visitTrimmingTokenCharacterNode: node
    |  id     |
    self startMethodForNode:node.

    "Tokens cannot be inlined, 
        - their result is true/false
        - the return value is always stored in currentTokenValue
        - the current token type is always stored in currentTokenType
    "
    self assert: node isMarkedForInline not.
    
    id := compiler idFor: node.
    
    compiler add: 'currentTokenType isNil ifFalse: [ ^ currentTokenType == ', id storeString, '].'.
    compiler profileTokenRead: id.

    compiler add: '(context peek == ', node child character storeString, ') ifFalse: [ ^ false ].'.
    compiler add: 'context next.'.

    self createTokenInsance: node id: id storeString  start: 'context position' end: 'context position'.
    self consumeWhitespace: node.
    
    compiler codeReturn: 'true'.

    ^ self stopMethodForNode: node
!

visitTrimmingTokenNode: node
    ^ self visitToken: node
! !