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.
This 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;
but the InternalTypedStaticFieldReference
concept used in the former does not reference the class java.lang.System
,
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
compile.
Here is the full body of the class generated from the example above:
The 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:
InternalAnonymousClass
for defining anonymous inner classes, the extended class name is given as a string.InternalClassCreator
to create an instance of a class whose name is given as a string.InternalClassExpression
to use.class
on a class whose name is given as a string.InternalClassifierType
to use a class as a type, the class name being given as a string.InternalNewExpression
combines a generic new expression withInternalClassCreator
.InternalPartialFieldReference
,InternalStaticFieldReference
,InternalTypedStaticFieldReference
,InternalVariableReference
to refer to a field, a variable, or a parameter, whose name is given as a string.InternalPartialInstanceMethodCall
,InternalStaticMethodCall
to call a method whose name is given as a string.InternalSuperMethodCallOperation
to call a method on the superclass, the method name being given as a string.InternalThisExpression
to be used where the normalThisExpression
is 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.