Generate Java functions with n parameters

Hi everyone,

I mentioned in a previous thread that I am working on a toy-language as a learning-by-doing approach to MPS.

I would like to implement a generator that creates a Java function with n parameters.
My DSL allows the user to write something like

CLASS Name
    FUNC Name1
        PARAM P1:SomeTypeA
        PARAM P2:SomeTypeB
    FUNC Name2
        <...>

The generated Java output should be something like

public static void FuncName1(SomeTypeA P1, SomeTypeB P2)
{}

public static void FuncName2()
{}
prettyPrint();

I already created a $LOOP$ to generate the function stubs. But how do I create the lists of parameters? Can I achieve this with the $LOOP$ macro as well? If yes, how do I get the comma in between parameters?

One additional question: The name of a function is derived by saying "Func" + node.name in the respective property macro. Let's assume I have to refer to those names in other places. Can I reference a name somehow? Please note that a $LOOP$ generates the functions and their names but I would like to refer to only one of those functions (for instance to simulate a call).
11 comments
Comment actions Permalink
  1. Yes you can use $LOOP$ or $COPY_SRCL$ macros to generate parameters. Commas will tae care of themselves - you're generating an AST, not text, so no commas should bother you at this stage.
  2. You use reference macros to generate references - these either return a string matching the declaration to point to, or, preferably, the exact node to point to. Mapping labels are used to store the generated Java methods (in your case) so you could retrieve them.

Have you considered trying the generator tutorial to get a good grasp on these techniques? https://confluence.jetbrains.com/display/MPSD31/Generator+Demos
0
Comment actions Permalink
Hi Vaclav,

thanks, that brought me a step further.
I now have the following code fragment:

public static void init($LOOP$[PARAM $[p]]) { 
  <no statements> 
}
prettyPrint();

I want to generate this public, static init function which has multiple PARAMs (hence the $LOOP$). I use the $[p] property macro to generate unique names for those parameters. So that works.
public static void init(ParamTypeA p_a, ParamTypeB p_b, ParamTypeC p_c) { 
  <no statements> 
}
prettyPrint();

But how can I now refer to those parameters one by one in the function's body?
public static void init(ParamTypeA p_a, ParamTypeB p_b, ParamTypeC p_c) { 
  // how do I access the generated parameters individually?
  someStaticFunc(p_a, p_b);
  someStaticFunc(p_b, p_c);

  p_a.callFunc(); 
  P_b.callFunc();
  P_c.callFunc();
}
prettyPrint();
0
Comment actions Permalink
  1. You'll need to define a mapping label in your mapping configuration to map from your parameter concept to Java's ParameterDeclaration concept.
  2. Mark your parameters with the $LABEL$ macro, referring to the mapping label ($LOOP$$LABEL$PARAM $graphics)
  3. Use the "genContext.get output <choose mapping label> for (<inputNode>);" in the reference macro to retrieve the desired parameter from the mapping label using the current node (your param concept)

Vaclav
0
Comment actions Permalink
Hi Vaclav,
I just tried your proposal and it worked.
I now have a similar case for which I wonder what the most efficient way of implementation in MPS is.

Suppose I have generated classes ACls and BCls, which have been created from some root template that handles instances of concept Cls. Cls implements INamedConcept in order to generate the class-names, i.e. class ACls has been generated for an instance of concept Cls with the name property being set to "A". Additionally the template generates setter functions for child-concepts of Cls, where the setter-names are derived from the respective child's concept-name.  
A potential output:
class ACls
{
    public void setAlpha(Alpha a)
    { /* ... */ }
}

class BCls
{
    public void setBeta(Beta b)
    { /* ... */ }
}
prettyPrint();

A second root template is used to generate another class which has a static function. That function will take an instance of
ACls and BCls and calls their setter functions. The function is similar to the following
private static void func(ACls a, BCls b)
{
    a.setAlpha(someAlphaValue);
    b.setBeta(someBetaValue);
}
prettyPrint();

Note, that the generated names ACls and BCls appear as types. Similarly the generated setter functions for ACls and BCls should be used.

Now, I would like to know if there is an easy way to achieve this. This is very similar to my previous issue so I assume I have to use labels as well. But do I really need to create a separate label for the macro that generates the setter and the macro that generates the class names?
class $LABEL$cls_name$[Cls]
{
    public void $LABEL$set_label$[set](/* ... */)
    { /* ... */ }
}
prettyPrint();
Or can I use only one label somehow?
I hope my explanations were clear enough.
0
Comment actions Permalink
If my understanding is correct, you may only need a mapping label for either the generated class of the generated setters, if you can get the other one from the first one, e.g. get the class by retrieving the setter's parent. Is that right?

Vaclav
0
Comment actions Permalink
That is correct. The setters are children of the Cls "class" node.
0
Comment actions Permalink
Ok, I created a label myLabel that applies to Cls -> ClassConcept and attached it to the entire class template. I am able to generate the function's parameter-types via property macro and

genContext.get output myLabel for (node : Cls);

This gives me a parameter like: ACls a

How can I now call one of the generated setter functions, as in: a.setAlpha(...)?

I tried: a.->$[set]();
but how can I get the function name via genContext?
0
Comment actions Permalink
I guess you'll have to obtain it from retrieved ACl node, e.g.

genContext.get output myLabel for (node : Cls).methods().where {//Here you need to identify the required generated method//};

I assume there's some logic in naming the methods that can be used to retrieve the correct method.
0
Comment actions Permalink
Hi Vaclav,

everything came together nicely. I am able to generate the code I want. Thanks for your valuable support. It helped me a lot.

But I still do have a question:
How can I use the generated Java files in a solution/model?
I have a Sandbox solution which contains a model that generates code based on my DSL. I now want to use those generated Java files in the same sandbox solution but in a different model where I want to extend the generated classes.

My structure should be similar to:
[S] sandbox
   [M] gen
      (N) Using my DSL -> generate Java classes
   [M] impl
     (C) SomeClass extending generated Java class
0
Comment actions Permalink
You can add a new model root with javasources_stubs to your solution and set the source_gen folder as a Model in the model root.

Then in your impl model set a Dependency on the java_stub model and you should be ready.

You may check out the details on setting model roots at https://confluence.jetbrains.com/display/MPSD32/Getting+the+dependencies+right#Gettingthedependenciesright-Solution

Vaclav
0
Comment actions Permalink
perfect, got it, thanks Vaclav
0

Please sign in to leave a comment.