Scope issue when extending BaseLanguage?

As part of exploring MPS I've created a new language and defined it as "extends BaseLanguage".
For starter, all that this new language has is a root concept which contains children:
elements : BaseConcept[0..n]

When I create a new solution (using this new language)and I choose that new concept, I'm able to add to the solution some LocalVariableDecelaration (e.g. int myVal = 3;)
yet when I add an IfStatement I'm unable to choose myVal (as part of the condition or as part of the statement).

Is that a scope issue? How can it be fixed?
Yes, this is a scoping issue - VariableReferences search for VariableDeclarations they could point to by hierarchically climbing the model and searching for ScopeProvider implementations, as defined in the VariableReference's constraints.
In BaseLanguage, statements are typically contained within a StatementList, which implements ScopeProvider and thus allows the VariableReference to "see" the VariableDeclarations.

A detailed explanation with a sample can be found here:
I've already tried to add "implements ScopeProvider" to the new concept (the root concept of the new language) but it did not make any difference (that's why I wasn't sure that it is a scope issue).

There is only one concept (the root concept) in the new language. So if defining it as the ScopeProvider doesn't help, how can this scope issue be solved?
Perhaps your implementation of the getScope() method in your root concept is not quite right. Try this as a starting point:
public Scope getScope(conceptNode<> kind, node<> child) 
  overrides ScopeProvider.getScope { 
  new ListScope(this.descendants<concept = VariableDeclaration>) { 
    public string getName(node<> child) { 
      child :; 

This implementation of getScope() does provide the local variables names in the list (available for the ifStatement of the solution) but it also causes error messages for each variable name:
"Error: Variable myVar is already defined in the scope".
I guess this happens since any local variable is part of the root concept which is also the concept that provides the scope, so whichever name you give the variable it enters the list and therefore "is already defined".

I've tried another approach: I've cancelled the "implements ScopeProvider" for the root concept (and also deleted its behavior) and changed its children definition to:
statements : StatementList[0..n]
Since StatementList is implements ScopeProvider I've expected this will solve the problem. Yet this solves only the "is already defined" issue but scoping is not as expected - the local variable names are not available for the if statement.

I see that the getScope() of StatementList behavior is:
if (kind.isExactly(VariableDeclaration)) {
 return Scopes.forVariables(kind, getLocalVariableDeclarations(child)
, parent scope);

So the StatementList scope is meant for VariableDeclaration types only?
Is there a way to change my root concept (maybe define another type of children?) to enable usage of local variables and also have their names in the scope (for other statements)?
I guess the cardinality of the "statements" child should be "1", not "0..n", and all variables as well as the IfStatement nodes should be nested into this StatementList. I'm guessing that you're creating the variables in a different StatementList than the StatementList containing your IfStatement.
Once both VariableDeclarations and IfStatements are in the same StatementList, things will work the way you expect.
Of course you are right. It was the cardinality. Since it was "0..n" instead of "1" it caused each variable and each ifStatement to be contained in a separate StatementList therefore scope of the ifStatement did not contain the variable names in its list.

Please sign in to leave a comment.