PetitCommonMark/software/petitcompiler/PPCClass.class.st

394 lines
9.3 KiB
Smalltalk

"
I am a basic structure represing a class being compiled.
The CodeGen (or more of them) can operate on me, adding methods.
I keep track of methods, ids, and inlined methods as well as the return variables for code gen.
In the end ClassBuilder can create a Smalltalk class from my data.
"
Class {
#name : 'PPCClass',
#superclass : 'Object',
#instVars : [
'methodDictionary',
'currentMethod',
'constants',
'idGen',
'methodStack',
'returnVariable',
'properties'
],
#category : 'PetitCompiler-Compiler-Codegen'
}
{ #category : 'instance creation' }
PPCClass class >> new [
"return an initialized instance"
^ self basicNew initialize.
]
{ #category : 'constants' }
PPCClass >> addConstant: value as: name [
(constants includesKey: name) ifTrue:[
(constants at: name) ~= value ifTrue:[
self error:'Duplicate constant!'.
].
^ self.
].
constants at: name put: value
"Modified: / 29-05-2015 / 07:22:39 / Jan Vrany <jan.vrany@fit.cvut.cz>"
]
{ #category : 'variables' }
PPCClass >> allocateReturnVariable [
^ self allocateReturnVariableNamed: 'retval'
"Created: / 23-04-2015 / 18:03:40 / Jan Vrany <jan.vrany@fit.cvut.cz>"
"Modified: / 15-06-2015 / 17:52:56 / Jan Vrany <jan.vrany@fit.cvut.cz>"
]
{ #category : 'variables' }
PPCClass >> allocateReturnVariableNamed: name [
"Allocate (or return previously allocated one) temporary variable used for
storing a parser's return value (the parsed object)"
^ currentMethod allocateReturnVariableNamed: name
"Created: / 15-06-2015 / 18:04:48 / Jan Vrany <jan.vrany@fit.cvut.cz>"
]
{ #category : 'variables' }
PPCClass >> allocateTemporaryVariableNamed: preferredName [
"Allocate a new variable with (preferably) given name.
Returns a real variable name that should be used."
^ self currentNonInlineMethod allocateTemporaryVariableNamed: preferredName
"Created: / 23-04-2015 / 17:33:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
]
{ #category : 'ids' }
PPCClass >> asClassName: string [
"e.g. '234znak 43 ) 2' asLegalSelector = #v234znak432"
| toUse |
toUse := string asString select: [:char | char isAlphaNumeric or: [ char = $_ ] ].
(toUse isEmpty or: [ toUse first isLetter not ])
ifTrue: [ toUse := 'v', toUse ].
toUse first isLowercase ifTrue: [
toUse := toUse copy.
toUse at: 1 put: toUse first asUppercase
].
^ toUse asSymbol
"Modified: / 10-05-2015 / 07:29:57 / Jan Vrany <jan.vrany@fit.cvut.cz>"
]
{ #category : 'ids' }
PPCClass >> asSelector: string [
"e.g. '234znak 43 ) 2' asLegalSelector = #v234znak432"
| toUse |
toUse := string select: [:char | char isAlphaNumeric or: [ char = $_ ] ].
(toUse isEmpty or: [ toUse first isLetter not ])
ifTrue: [ toUse := 'v', toUse ].
toUse first isUppercase ifFalse:[
toUse := toUse copy.
toUse at: 1 put: toUse first asLowercase
].
^toUse
"Modified: / 10-05-2015 / 07:29:57 / Jan Vrany <jan.vrany@fit.cvut.cz>"
]
{ #category : 'method cache' }
PPCClass >> cachedMethod: id [
^ methodDictionary at: id ifAbsent: [ nil ]
]
{ #category : 'method cache' }
PPCClass >> cachedMethod: id ifPresent: aBlock [
^ methodDictionary at: id ifPresent: aBlock
]
{ #category : 'accessing' }
PPCClass >> classVariables [
^ constants keys asArray
]
{ #category : 'accessing' }
PPCClass >> constants [
^ constants
]
{ #category : 'accessing' }
PPCClass >> currentMethod [
^ currentMethod
]
{ #category : 'accessing' }
PPCClass >> currentNonInlineMethod [
^ methodStack
detect:[:m | m isInline not ]
ifNone:[ self error: 'No non-inlined method']
"Created: / 23-04-2015 / 17:33:31 / Jan Vrany <jan.vrany@fit.cvut.cz>"
]
{ #category : 'accessing' }
PPCClass >> currentReturnVariable [
^ currentMethod returnVariable
]
{ #category : 'accessing-properties' }
PPCClass >> hasProperty: aKey [
"Test if the property aKey is present."
^ properties notNil and: [ properties includesKey: aKey ]
]
{ #category : 'ids' }
PPCClass >> idFor: anObject [
^ idGen idFor: anObject
]
{ #category : 'ids' }
PPCClass >> idFor: anObject defaultName: defaultName [
^ idGen idFor: anObject defaultName: defaultName
]
{ #category : 'accessing' }
PPCClass >> idGen [
^ idGen
]
{ #category : 'accessing' }
PPCClass >> idGen: anObject [
idGen := anObject
]
{ #category : 'accessing' }
PPCClass >> ids [
^ idGen ids
]
{ #category : 'initialization' }
PPCClass >> initialize [
super initialize.
methodStack := Stack new.
methodDictionary := IdentityDictionary new.
constants := Dictionary new.
idGen := PPCIdGenerator new.
]
{ #category : 'accessing' }
PPCClass >> instanceAndClassVariables [
| collection |
collection := OrderedCollection new.
collection addAll: self instanceVariables.
collection addAll: self classVariables.
^ collection
]
{ #category : 'accessing' }
PPCClass >> instanceVariables [
^ #()
]
{ #category : 'accessing' }
PPCClass >> methodDictionary [
^ methodDictionary
]
{ #category : 'accessing' }
PPCClass >> name [
^ self asClassName: (self propertyAt: #name)
]
{ #category : 'accessing' }
PPCClass >> name: value [
^ self propertyAt: #name put: value
]
{ #category : 'ids' }
PPCClass >> numberIdFor: object [
^ idGen numericIdFor: object
]
{ #category : 'support' }
PPCClass >> parsedValueOf: aBlock to: aString [
| tmpVarirable method |
self assert:aBlock isBlock.
self assert:aString isNil not.
tmpVarirable := returnVariable.
returnVariable := aString.
method := [
aBlock value
] ensure:[ returnVariable := tmpVarirable ].
self assert: (method isMethod).
^ method
]
{ #category : 'support' }
PPCClass >> pop [
| retval |
retval := methodStack pop.
currentMethod := methodStack isEmpty
ifTrue: [ nil ]
ifFalse: [ methodStack top ].
^ retval
"Modified: / 21-11-2014 / 12:27:25 / Jan Vrany <jan.vrany@fit.cvut.cz>"
]
{ #category : 'accessing-properties' }
PPCClass >> properties [
^ properties
]
{ #category : 'accessing-properties' }
PPCClass >> properties: aDictionary [
properties := aDictionary
]
{ #category : 'accessing-properties' }
PPCClass >> propertyAt: aKey [
^ self propertyAt: aKey ifAbsent: [ nil ]
]
{ #category : 'accessing-properties' }
PPCClass >> 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 ]
]
{ #category : 'accessing-properties' }
PPCClass >> 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 ]
]
{ #category : 'accessing-properties' }
PPCClass >> 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
]
{ #category : 'support' }
PPCClass >> push [
methodStack push: currentMethod.
(methodStack size > 500 )ifTrue: [ self error: 'unless it is very complex grammar, there is an error somewhere' ]
"Modified: / 21-11-2014 / 12:27:18 / Jan Vrany <jan.vrany@fit.cvut.cz>"
]
{ #category : 'support' }
PPCClass >> returnVariable [
self error: 'Should never be called and accessed outside this class'.
^ returnVariable
]
{ #category : 'accessing' }
PPCClass >> rootMethod [
^ self propertyAt: #rootMethod ifAbsent: nil
]
{ #category : 'accessing' }
PPCClass >> rootMethod: value [
^ self propertyAt: #rootMethod put: value
]
{ #category : 'accessing' }
PPCClass >> selectors [
^ methodDictionary keys
]
{ #category : 'support' }
PPCClass >> startInline [
| indentationLevel |
indentationLevel := currentMethod indentationLevel.
currentMethod := PPCInlinedMethod new.
currentMethod returnVariable: returnVariable.
currentMethod indentationLevel: indentationLevel.
self push.
"Modified: / 01-06-2015 / 21:48:35 / Jan Vrany <jan.vrany@fit.cvut.cz>"
]
{ #category : 'support' }
PPCClass >> startInline: id [
| indentationLevel |
(methodDictionary includesKey: id) ifTrue: [ self error: 'OOOUPS!' ].
indentationLevel := currentMethod indentationLevel.
currentMethod := PPCInlinedMethod new.
currentMethod id: id.
currentMethod returnVariable: returnVariable.
currentMethod indentationLevel: indentationLevel.
self push.
"Modified: / 01-06-2015 / 21:48:35 / Jan Vrany <jan.vrany@fit.cvut.cz>"
]
{ #category : 'support' }
PPCClass >> startMethod: id category: category [
(methodDictionary includesKey: id) ifTrue: [ self error: 'OOOUPS!' ].
currentMethod := PPCMethod new.
currentMethod id: id.
currentMethod category: category.
self push.
self store: currentMethod as: id.
"Modified: / 01-06-2015 / 21:19:41 / Jan Vrany <jan.vrany@fit.cvut.cz>"
]
{ #category : 'support' }
PPCClass >> stopInline [
^ self pop.
"Modified: / 01-06-2015 / 21:37:59 / Jan Vrany <jan.vrany@fit.cvut.cz>"
]
{ #category : 'support' }
PPCClass >> stopMethod [
self store: currentMethod as: currentMethod methodName.
^ self pop.
]
{ #category : 'method cache' }
PPCClass >> store: method as: id [
self assert: (method isKindOf: PPCMethod).
methodDictionary at: id put: method.
]
{ #category : 'accessing' }
PPCClass >> superclass [
^ self propertyAt: #superclass
]
{ #category : 'accessing' }
PPCClass >> superclass: value [
^ self propertyAt: #superclass put: value
]