To generate code, I used to use templates like the following (contrived 
example):

\`\`\`smalltalk

`template := 'method1`

`  ^ {returnValue}'.`

`template format: { #returnValue -> 2 } asDictionary`

\`\`\`

This is \*okay\* for simple cases, but can get unwieldy, and doesn't benefit 
from e.g. (early) compiler warnings. It is also lacking when one wants to offer 
the behavior as a method and also as a script with no dependencies (maybe for 
CI). 

To overcome some of these limitations, I started saving the code template as an 
actual method and transforming the AST, e.g.:

\`\`\`smalltalk

        `"Convert baseline##: method"`

`       methodTree := (self methodNamed: selector) parseTree.`

`       methodTree selector: #baseline:.`

`       methodTree pragmas at: 1 put: (RBPragmaNode selector: #baseline 
arguments: #()).`

`       commonBlockBody := methodTree statements first arguments last body.`

`       commonBlockBody statements`

`               detect: [ :e | e selector = #repository: ]`

`               ifFound: [ :repoSetter | commonBlockBody removeNode: repoSetter 
].`

`       `

`       "Compile baseline method"`

`       baseline compile: methodTree newSource classified: 'baseline'`

\`\`\`

When I started doing this, I noticed two things:

1\. There were idioms that were repeated over and over.

2\. It still isn't super easy to turn a method into a script with no 
dependencies (e.g. self sends)

So, I did a spike to wrap a few common idioms for this use case. It works like 
this. Say you have a method like this:

\`\`\`

`SmallRemoteGitRepository>>#addRemote: urlString as: nameString`

`       "Assumes repo is loaded"`

`       `

`       | repo remote |`

`       repo := IceRepository registry detect: [ :e | e name = self projectName 
].`

`       remote := IceGitRemote name: nameString url: urlString.`

`       repo addRemote: remote.`

`       remote fetch`

\`\`\`

And you want to also provide it as source code for a script with no 
dependencies. You can do:

\`\`\`

`SmallRemoteGitRepository >>#scriptToAddRemote: urlString as: nameString`

`       | method transformer |`

`       method := SmallRemoteGitRepository methodNamed: #addRemote:as:.`

`       transformer := method peAST_Transformer`

`               beScript;`

`               addStatementFirst: #projectName , ' := ' , self projectName 
printString;`

`               addStatementFirst: #nameString , ' := ' , nameString 
printString;`

`               addStatementFirst: #urlString , ' := ' , urlString printString;`

`               replaceNodeDetect: [ :e | e isMessage and: [ e selector = 
#projectName ] ]`

`                       withNode: (RBVariableNode named: #projectName).`

`       ^ transformer newSource`

\`\`\`

which would generate (for example, for a particular instance):

\`\`\`

`| repo remote projectName |`

`urlString := 'g...@github.com:magritte-metamodel/magritte.git'.`

`nameString := 'upstream'.`

`projectName := 'Magritte'.`

`repo := IceRepository registry detect: [ :e | e name = projectName ].`

`remote := IceGitRemote name: nameString url: urlString.`

`repo addRemote: remote.`

`remote fetch`

\`\`\`

So my questions are:

\- Does something like this already exist?

\- Are there better ways to solve this problem?

\- Does this solution look promising/generally-helpful?

Reply via email to