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?