Last week we talked about cross-model generation, the issue of generating a model whose generated code depends on the generated code of another model.
To support cross-model generation in the general case you need to learn, and experiment with, generation plans and checkpoints. However, if you are generating Java, there is an easier solution: use the baseLanguageInternal language.
jetbrains.mps.baseLanguageInternal, contains concepts for references that can be used in place of the
usual base language concepts, but in place of references they let you enter text. This means that you no longer have to
bother with setting up reference macros in the generator and worry about checkpoints. You only need to know the exact
name of the class, method, or variable that you want to refer to.
This probably sounds complicated, so let’s look at an example. Here is what a “Hello, world!” program might look like, using base language internal:
The internal parts are marked with green. For example, this line:
generates this Java code:
PrintStream systemOut = System.out;
InternalTypedStaticFieldReference concept used in the former does not reference the class
nor the static field
out. Instead they are entered directly as text. It does reference
PrintStream in the brackets
to give a type to the expression.
Same for the
println method called using
InternalPartialInstanceMethodCall. Its name is set as a string rather than
by referencing the corresponding
InstanceMethodDeclaration from the model
java.io@java_stub. It is even possible to
insert a call to a non-existent method this way. The code will be generated without errors but will of course fail to
Here is the full body of the class generated from the example above:
baseLanguageInternal language provides about a dozen concepts built by the same principle, that let you refer to
variables or methods by their name even if you don’t have a direct reference available. Here is a list of all such
concepts in MPS 2021.3:
InternalAnonymousClassfor defining anonymous inner classes, the extended class name is given as a string.
InternalClassCreatorto create an instance of a class whose name is given as a string.
.classon a class whose name is given as a string.
InternalClassifierTypeto use a class as a type, the class name being given as a string.
InternalNewExpressioncombines a generic new expression with
InternalVariableReferenceto refer to a field, a variable, or a parameter, whose name is given as a string.
InternalStaticMethodCallto call a method whose name is given as a string.
InternalSuperMethodCallOperationto call a method on the superclass, the method name being given as a string.
InternalThisExpressionto be used where the normal
ThisExpressionis not allowed.
Besides these concepts, the language contains other concepts that can be useful when generating Java code, such as concepts that extract a particular expression into a field or a method. However, those concepts are not related to cross-model generation and so are out of scope of today’s post.
With the concepts listed above, it is possible to avoid the complexities of cross-model generation. They provide a nice escape hatch that lets us plug text directly into our projectional base language code.