compiler/PPCNode.st
author Jan Vrany <jan.vrany@fit.cvut.cz>
Thu, 30 Apr 2015 23:43:14 +0200
changeset 438 20598d7ce9fa
parent 422 116d2b2af905
child 452 9f4558b3be66
permissions -rw-r--r--
Updated to PetitCompiler-JanKurs.100, PetitCompiler-Tests-JanKurs.44 and PetitCompiler-Benchmarks-JanKurs.4 Name: PetitCompiler-JanKurs.100 Author: JanKurs Time: 30-04-2015, 10:48:52.165 AM UUID: 80196870-5921-46d9-ac20-a43bf5c2f3c2 Name: PetitCompiler-Tests-JanKurs.44 Author: JanKurs Time: 30-04-2015, 10:49:22.489 AM UUID: 348c02e8-18ce-48f6-885d-fcff4516a298 Name: PetitCompiler-Benchmarks-JanKurs.4 Author: JanKurs Time: 30-04-2015, 10:58:44.890 AM UUID: 18cadb42-f9ef-45fb-82e9-8469ade56c8b

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

"{ NameSpace: Smalltalk }"

Object subclass:#PPCNode
	instanceVariableNames:'contextFree name firstFollowCache firstCharSet properties'
	classVariableNames:''
	poolDictionaries:''
	category:'PetitCompiler-Nodes'
!

!PPCNode class methodsFor:'as yet unclassified'!

new
	^ self basicNew initialize
! !

!PPCNode methodsFor:'accessing'!

children
	^ #()
!

markForInline
    "Marks receiver for inlining, i.e., it's parsing code
     should be inlined into parent's code"
    self propertyAt: #inlined put: true

    "Created: / 23-04-2015 / 15:39:01 / Jan Vrany <jan.vrany@fit.cvut.cz>"
!

name
	^ name
!

name: anObject
	
	name := anObject
!

prefix
	^ 'anode' 
!

suffix
	^ ''
!

unmarkForInline
    "Forbids inlining of receiver's parsing code"
    self propertyAt: #inlined put: false

    "Created: / 23-04-2015 / 15:39:39 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!PPCNode methodsFor:'accessing-properties'!

hasProperty: aKey
	"Test if the property aKey is present."
	
	^ properties notNil and: [ properties includesKey: aKey ]
!

properties
	^ properties
!

properties: aDictionary
	properties := aDictionary 
!

propertyAt: aKey 
	^ self propertyAt: [ aKey ] ifAbsent: [ nil ]
!

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 ]
		ifFalse: [ properties at: aKey ifAbsent: aBlock ]
!

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 := Dictionary new: 1 ])
		at: aKey put: anObject
! !

!PPCNode methodsFor:'analysis'!

acceptsEpsilon
	"return true, if parser can accept epsilon without failure"
	^ self subclassResponsibility
!

acceptsEpsilonOpenSet: set
	"private helper for acceptsEmpsilon that makes sure to avoid cycles (using open set)"
	self children isEmpty ifTrue: [ ^ self acceptsEpsilon ].
	
	self shouldBeImplemented .
!

allNodes
	| result |
	result := OrderedCollection new.
	self allParsersDo: [ :parser | result add: parser ].
	^ result
!

allNodesDo: aBlock
	"Iterate over all the parse nodes of the receiver."

	self allNodesDo: aBlock seen: IdentitySet new
!

allNodesDo: aBlock seen: aSet
	"Iterate over all the parse nodes of the receiver, do not visit and follow the ones contained in aSet."

	(aSet includes: self)
		ifTrue: [ ^ self ].
	aSet add: self.
	aBlock value: self.
	self children
		do: [ :each | each allNodesDo: aBlock seen: aSet ]
!

check
	"nothing to do"
	^ nil
!

firstCharSetCached
	firstCharSet ifNil: [ 
 		firstCharSet := self firstCharSet.
	].
	^ firstCharSet
!

firstSetSuchThat: block
	^ self firstSetSuchThat: block into: (OrderedCollection new) openSet: IdentitySet new.
!

firstSetSuchThat: block into: aCollection openSet: aSet
	(aSet includes: self) ifTrue: [ ^ aCollection ].
	aSet add: self.
	
	(block value: self) ifTrue: [aCollection add: self. ^ aCollection ].
	self children do: [ :child | 
		child firstSetSuchThat: block into: aCollection openSet: aSet 
	].
	^ aCollection
!

isContextFree
	^ contextFree ifNil: [ contextFree := self allNodes allSatisfy: [ :n | n isContextFreePrim  ] ]
!

isContextFreePrim
	^ true
!

isFirstSetTerminal
	"Answer true if the receiver is a terminal or leaf parser, that means it does not delegate to any other parser."

	^ self isTerminal
!

isNullable
	"Answer true if the receiver is a nullable parser, e.g. it can successfully parse nothing."
	
	^ false
!

isTerminal
	"Answer true if the receiver is a terminal or leaf parser, that means it does not delegate to any other parser."

	^ self children isEmpty
! !

!PPCNode methodsFor:'comparing'!

= anotherNode
	(self == anotherNode) ifTrue: [ ^ true ].
	(anotherNode class = self class) ifFalse: [ ^ false ].
	
	(anotherNode name = name) ifFalse: [ ^ false ].
	^ anotherNode children = self children.
!

hash
	"TODO JK: IMO not a good hashing function bacause of children size, 
		but at least it is not recursive, which would be worse :)
	"
	^ self class hash bitXor: (name hash bitXor: self children size hash)
! !

!PPCNode methodsFor:'first follow next'!

firstSet
	^ firstFollowCache at: #firstSet ifAbsentPut: [
		self firstSets at: self
	]
!

firstSet: set
	firstFollowCache at: #firstSet put: set
!

firstSets
	^ self firstSetsSuchThat: [ :e | e isFirstSetTerminal ]
!

firstSets: aFirstDictionary into: aSet suchThat: aBlock
	"PRIVATE: Try to add additional elements to the first-set aSet of the receiver, use the incomplete aFirstDictionary."

	(aBlock value: self) ifFalse: [ 
		self children do: [ :node | aSet addAll: (aFirstDictionary at: node) ]
	]
!

firstSetsSuchThat: block

	| firstSets |
	firstSets := IdentityDictionary new.
	self allParsersDo: [ :each |
		firstSets at: each put: ((block value: each)
			ifTrue: [ IdentitySet with: each ]
			ifFalse: [ IdentitySet new ]).
		each isNullable
			ifTrue: [ (firstSets at: each) add: PPCSentinelNode instance ] ].
		
		
	[	| changed tally |
		changed := false.
		firstSets keysAndValuesDo: [ :node :first |
			tally := first size.
			node firstSets: firstSets into: first suchThat: block.
			changed := changed or: [ tally ~= first size ] ].
		changed ] whileTrue.
	^ firstSets
!

followSet
	^ firstFollowCache at: #followSet ifAbsent: [ self error: 'no follow set cached' ]
!

followSet: aSet
	^ firstFollowCache at: #followSet put: aSet
!

followSetIn: rootNode
	^ rootNode followSets at: self
!

followSetWithTokens
	^ firstFollowCache at: #followSetWithTokens ifAbsent: [ self error: 'no follow with tokens cached' ]
!

followSetWithTokens: aSet
	^ firstFollowCache at: #followSetWithTokens put: aSet
!

followSets
	^ self followSetsSuchThat: [ :e | e isFirstSetTerminal ]
!

followSets: aFollowDictionary firstSets: aFirstDictionary into: aSet suchThat: aBlock
	"PRIVATE: Try to add additional elements to the follow-set aSet of the receiver, use the incomplete aFollowDictionary and the complete aFirstDictionary."
	
	self children do: [ :node | (aFollowDictionary at: node) addAll: aSet ]
!

followSetsSuchThat: aBlock
	"Answer a dictionary with all the parsers reachable from the receiver as key and their follow-set as value. The follow-set of a parser is the list of terminal parsers that can appear immediately to the right of that parser."
	
	| current previous continue firstSets followSets |
	current := previous := 0.
	firstSets := self firstSetsSuchThat: aBlock.
	followSets := IdentityDictionary new.
	self allNodesDo: [ :each | followSets at: each put: IdentitySet new ].
	(followSets at: self) add: PPCSentinelNode instance.
	[	followSets keysAndValuesDo: [ :node :follow |
			node 
				followSets: followSets
				firstSets: firstSets
				into: follow
				suchThat: aBlock ].
		current := followSets
			inject: 0
			into: [ :result :each | result + each size ].
		continue := previous < current.
		previous := current.
		continue ] whileTrue.
	^ followSets
!

nextSetIn: rootNode
	^ rootNode nextSets at: self
!

nextSets
		| nextSets |
	
	nextSets := IdentityDictionary new.
	self allNodesDo: [ :each | nextSets at: each put: IdentitySet new ].
	
	(nextSets at: self) add: PPCSentinelNode instance.
	
	[ 	| changed |
		changed := false.
	
		nextSets keysAndValuesDo: [:node :next |
			changed := (node 
				nextSets: nextSets
				into: next) or: [ changed ].
		].
		changed ] whileTrue.
	
	^ nextSets
!

nextSets: aNextDictionary into: aSet
	"return true/false, if something has changed or not...."
	| childSet change tally |
	
	change := false.
	
	self children do: [:each | 
		childSet := aNextDictionary at: each.
		tally := childSet size.
		childSet addAll: aSet.
		change := change or: [ tally ~= childSet size ].
	].

	^ change
	
! !

!PPCNode methodsFor:'gt'!

gtTreeViewIn: composite
	<gtInspectorPresentationOrder: 40>

	composite tree
			title: 'Tree';
			children: [:n | n children ];
			format: [:n| String streamContents: [:s | n printOn: s. n printHashOn: s] ];
			shouldExpandToLevel: 6
! !

!PPCNode methodsFor:'initialization'!

initialize
	super initialize.
	firstFollowCache := IdentityDictionary new.
! !

!PPCNode methodsFor:'optimizing'!

checkTree
	| message |
	self allNodes do: [ :node | (message := node check) ifNotNil: [ self error: message ]  ].
!

optimizeTree
	^ self optimizeTree: #(#token #inline #rewrite #merge)
!

optimizeTree: params
	| retval | 

	"Default optimization sequence"
	retval := self.
	
	(params includes: #rewrite) ifTrue: [ retval := PPCOptimizingVisitor new visit: retval ].
	(params includes: #token) ifTrue: [ retval := PPCTokenDetector new visit: retval ].
	(params includes: #rewrite) ifTrue: [ retval := PPCOptimizingVisitor new visit: retval ].
	(params includes: #inline) ifTrue: [ retval := PPCInliningVisitor new visit: retval ].
	(params includes: #merge) ifTrue: [ retval := PPCMergingVisitor new visit: retval ].
	^ retval
! !

!PPCNode methodsFor:'printing'!

printHashOn: aStream
	aStream print: 'Hash:', self hash asString
!

printIdOn: aStream
	aStream print: 'ID:', self identityHash asString
!

printNameOn: aStream
	self name isNil
		ifFalse: [ aStream nextPutAll: self name. aStream nextPut: $-.  ].
		
	aStream print: self identityHash
!

printOn: aStream
	super printOn: aStream.
	aStream nextPut: $(.
	self printNameOn: aStream.
	aStream nextPut: $)
! !

!PPCNode methodsFor:'testing'!

isMarkedForInline
    ^ self propertyAt: #inlined ifAbsent: [ false ].

    "Created: / 23-04-2015 / 15:40:10 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!PPCNode methodsFor:'todel'!

allParsersDo: aBlock
	"Iterate over all the parse nodes of the receiver."

	self allParsersDo: aBlock seen: IdentitySet new
!

allParsersDo: aBlock seen: aSet
	"Iterate over all the parse nodes of the receiver, do not visit and follow the ones contained in aSet."

	(aSet includes: self)
		ifTrue: [ ^ self ].
	aSet add: self.
	aBlock value: self.
	self children
		do: [ :each | each allParsersDo: aBlock seen: aSet ]
! !

!PPCNode methodsFor:'transformation'!

asCompilerNode
	^ self
!

replace: node with: anotherNode
!

transform: aBlock
	"Answer a copy of all parsers reachable from the receiver transformed using aBlock."
	| mapping root |
	self halt: 'doprecate?'.
	mapping := IdentityDictionary new.
	self allParsersDo: [ :each |
		mapping
			at: each
			put: (aBlock value: each copy) ].
	root := mapping at: self.
	[	| changed |
		changed := false.
		root allParsersDo: [ :each |
			each children do: [ :old |
				mapping at: old ifPresent: [ :new |
					each replace: old with: new.
					changed := true ] ] ].
		changed ] whileTrue.
	^ root
! !

!PPCNode methodsFor:'visiting'!

accept: visitor
	visitor visitNode: self
! !