Awesome
PharoCodeGenerator
A toolkit to generate Pharo code. See the demo-video presented at ESUG'19 here.
- Install
- How it works
- API overview
- DSL Examples
- Template support
- Generate PCG code from an existing method in the system
Install
Metacello new
repository: 'github://juliendelplanque/PharoCodeGenerator/src';
baseline: 'PharoCodeGenerator';
load
How it works
API overview
Consider the following code snippet:
ast := (PCGMethodNode selector: #answerToEverything)
bodyBlock: [ :body |
body << 42 asPCG returnIt ]
This code generates the following method:
answerToEverything
<generated>
^ 42
Here are explanations on the methods one can send to the object stored in ast
variable:
ast realAst
: Builds the Pharo AST corresponding to description made in DSLast checkAst
: Checks if the AST is ready for translation to Pharo AST. Raises an error if not.ast sourceCode
: Returns a String holding source code resulting from generation.ast withGeneratedPragma: true|false
: Adds or not<generated>
pragma in method source code.ast protocol: aString
: Sets the protocol for the methods to be generated.ast installOn: aBehaviour
: Install the method on aBehaviour object (class, meta-class, trait, etc...).
DSL Examples
This section contains method from the system and the PCG code to generate them.
Generating Object>>#yourself
The following source code taken Object>>#yourself
method:
yourself
^self
can be generated via the dsl:
(PCGMethodNode selector: #yourself)
bodyBlock: [ :body | body << #self asPCGNode returnIt ];
protocol: #accessing;
yourself
Generating Collection>>#collect:
The following source code taken Collection>>#collect:
method:
collect: aBlock
| newCollection |
newCollection := self species new.
self do: [:each | newCollection add: (aBlock value: each) ].
^newCollection
can be generated via the dsl:
(PCGMethodNode selector: #collect: arguments: {#aBlock asPCGArgument})
bodyBlock: [ :body |
body
<<
(#newCollection asPCGTemporary
assign: ((#self asPCGNode receiveMessage: #species) receiveMessage: #new)).
body
<<
(#self asPCGNode
receiveMessage: #do:
with:
((PCGBlockNode arguments: {#each asPCGArgument})
bodyBlock: [ :body1 |
body1
<<
(#newCollection asPCGTemporary
receiveMessage: #add:
with:
(#aBlock asPCGArgument
receiveMessage: #value:
with: #each asPCGArgument)) ])).
body << #newCollection asPCGTemporary returnIt ];
protocol: #enumerating;
yourself
Generating Integer>>#&
The following source code taken Integer>>#&
method:
& aNumber
^self bitAnd: aNumber
can be generated via the dsl:
(PCGMethodNode selector: #& arguments: {#aNumber asPCGArgument})
bodyBlock: [ :body |
body
<<
(#self asPCGNode receiveMessage: #bitAnd: with: #aNumber asPCGArgument)
returnIt ];
protocol: #'bit manipulation';
yourself
Generating True>>#ifTrue:ifFalse:
The following source code taken True>>#ifTrue:ifFalse:
method:
ifTrue: trueAlternativeBlock ifFalse: falseAlternativeBlock
^trueAlternativeBlock value
can be generated via the dsl:
(PCGMethodNode
selector: #ifTrue:ifFalse:
arguments:
{#trueAlternativeBlock asPCGArgument.
#falseAlternativeBlock asPCGArgument})
bodyBlock: [ :body |
body
<< (#trueAlternativeBlock asPCGArgument receiveMessage: #value) returnIt ];
protocol: #controlling;
yourself
Template support
PCG provides support for templating.
template := (PCGMethodNode selector: #answerSelector asPCGTemplateParameter)
bodyBlock: [ :body |
body << #answer asPCGTemplateParameter returnIt ]
From a template
, one can generate multiple methods using #substituteParametersWith:
message:
The following substitution
(template substituteParametersWith: {
#answerSelector -> #answerToEverything.
#answer -> 42 asPCG }) sourceCode.
generates
answerToEverything
<generated>
^ 42
This other substitution
(template substituteParametersWith: {
#answerSelector -> #answerToEverythingInParallelUniverse.
#answer -> 43 asPCG }) sourceCode
generates
answerToEverythingInParallelUniverse
<generated>
^ 43
Generate PCG code from an existing method in the system
If you see a method in your project that can be used as a basis for code generation, PCG can help you.
Let us import the source code of Object>>#yourself
as a PCG AST:
pcgAst := (Object >> #yourself) asPCGAST.
Then,
pcgAst sourceCode
generates:
yourself
<generated>
^ self
Now, one might need to retrieve the PCG script leading to the PCG AST that is able to generate Object>>#yourself
. This can be achieve via #meta
message.
Thus,
pcgAst meta sourceCode
generates
(PCGMethodNode selector: #yourself)
bodyBlock: [ :body | body << #self asPCGNode returnIt ];
protocol: #accessing;
yourself
This mechanism is handy to generalize an exisiting methods in the system as a PCG template.