PPCConfiguration refactoring: [4/10]: introduced a class - PPCPass
... representing a compilation pass over the PPCNode tree.
The pass has a common api method: #run:in: which is not used in
PPCConfiguration. This simplifed the code and removed some code
duplication.
"{ Package: 'stx:goodies/petitparser/compiler' }"
"{ NameSpace: Smalltalk }"
PPCPass subclass:#PPCTokenCodeGenerator
instanceVariableNames:'codeGen'
classVariableNames:''
poolDictionaries:''
category:'PetitCompiler-Visitors-CodeGenerators'
!
!PPCTokenCodeGenerator methodsFor:'code support'!
consumeWhitespace: node
self assert: node isTokenNode.
node isTrimmingTokenNode ifTrue: [
codeGen code: 'self scan_consumeWhitespace.'
]
!
createTokenInstance: node id: idCode start: startVar end: endVar
codeGen startInline.
codeGen codeTranscriptShow: 'current token type: ', idCode.
codeGen codeAssign: node tokenClass asString, ' on: (context collection)
start: ', startVar, '
stop: ', endVar, '
value: nil.'
to: self retvalVar.
^ codeGen stopInline
!
scan: node start: startVar end: endVar
node child hasName ifFalse: [
node child name: node name
].
codeGen codeAssign: 'context position + 1.' to: startVar.
codeGen add: ((self generateScan: node child) callOn: 'scanner').
! !
!PPCTokenCodeGenerator methodsFor:'compiling support'!
retvalVar
^ codeGen currentReturnVariable
!
startMethodForNode:node
node isMarkedForInline ifTrue:[
codeGen startInline: (codeGen idFor: node).
codeGen codeComment: 'BEGIN inlined code of ' , node printString.
codeGen indent.
] ifFalse:[
codeGen startMethod: (codeGen idFor: node).
codeGen currentMethod category: 'generated - tokens'.
codeGen codeComment: 'GENERATED by ' , node printString.
codeGen allocateReturnVariable.
]
!
stopMethodForNode:aPPCNode
^ codeGen currentMethod isInline ifTrue:[
codeGen dedent.
codeGen add: '"END inlined code of ' , aPPCNode printString , '"'.
codeGen stopInline.
] ifFalse:[
codeGen stopMethod
].
! !
!PPCTokenCodeGenerator methodsFor:'initialization'!
initialize
super initialize.
codeGen := PPCCodeGen new.
! !
!PPCTokenCodeGenerator methodsFor:'running'!
run: ir
"Actually run the pass on given IR (tree of PPCNode) and return
(possibly transformed or completely new) another IR."
context isNil ifTrue:[
PPCCompilationError new signal: 'oops, no context set, use #context: before running a pass!!'.
].
codeGen options: context options.
codeGen clazz: context scannerClass.
self visit: ir.
^ ir
"Created: / 26-08-2015 / 22:39:27 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !
!PPCTokenCodeGenerator methodsFor:'visiting'!
visitToken: tokenNode
| scanId id |
self assert: tokenNode isMarkedForInline not.
self startMethodForNode: tokenNode.
id := codeGen idFor: tokenNode.
scanId := codeGen idFor: tokenNode fsa.
codeGen code: 'match isNil ifFalse: [ ^ match == ', id storeString, '].'.
codeGen profileTokenRead: id.
tokenNode child hasName ifFalse: [
tokenNode child name: tokenNode name
].
codeGen codeIf: 'self ', scanId then: [
self consumeWhitespace: tokenNode.
codeGen codeReturn: 'true'.
] else: [
codeGen codeReturn: 'false'.
].
^ self stopMethodForNode: tokenNode
!
visitTokenConsumeNode: node
| id nextId |
self startMethodForNode: node.
id := (codeGen idFor: node child).
nextId := (codeGen idFor: node nextFsa).
"this will inline scanner consumeXY in the parser"
node markForInline.
codeGen codeIf: 'self ', id asString then: [
codeGen codeAssign: [
self createTokenInstance: node child
id: id asString
start: 'position + 1'
end: 'matchPosition'.
] to: self retvalVar.
codeGen codeAssign: 'context position' to: 'position'; codeDot.
codeGen codeAssign: 'position' to: 'matchPosition'; codeDot.
codeGen codeAssign: 'nil' to: 'match'; codeDot.
node nextFsa hasDistinctRetvals ifTrue: [
codeGen codeIf: [ codeGen codeOnLine: ('self ', nextId) ] then: [
self consumeWhitespace: node child.
] else: [
self flag: 'imo should do something here and not wait...'.
codeGen codeComment: 'Looks like there is an error on its way...'.
]
].
codeGen codeReturn.
"Token not found"
] else: [
" codeGen code: 'PPCScannerError new signalWith: ''', id asString, ' expected'''."
codeGen codeReturn: 'nil.'.
].
^ self stopMethodForNode: node
!
visitTokenNode: node
^ self visitToken: node
!
visitTokenizingParserNode: node
"produces token_XY methods"
self visit: node tokens.
"TODO JK: hack alert, I don't like WS handling, think of something smarter,
perhaps allow for WS unique per token...
"
self visitWhitespace: node whitespace.
"produces tokenConsume_XY methods"
^ self visit: node parser
!
visitTrimmingTokenCharacterNode: node
| id |
self halt.
self assert: node isMarkedForInline not.
self startMethodForNode:node.
id := codeGen idFor: node.
codeGen add: 'match isNil ifFalse: [ ^ match == ', id storeString, '].'.
codeGen profileTokenRead: id.
codeGen add: '(context uncheckedPeek == ', node child character storeString, ') ifFalse: [ ^ false ].'.
codeGen add: 'context next.'.
self createTokenInstance: node id: id storeString start: 'context position' end: 'context position'.
self consumeWhitespace: node.
codeGen codeReturn: 'true'.
^ self stopMethodForNode: node
!
visitTrimmingTokenNode: node
^ self visitToken: node
!
visitWhitespace: whitespaceNode
self assert: whitespaceNode name = 'consumeWhitespace'.
! !