Home > DSLs, MDSD, The How, Xtext > Getting alternative cross-references to work with existing EPackages

Getting alternative cross-references to work with existing EPackages

Consider the following, recurring scenario: In your Xtext DSL you want to cross-reference either the type (rule) This or the type ext::That with the latter coming from an existing EPackage, i.e. an Ecore model. Naturally, you’d be inclined to say the following:

MyCrossReference:
    refThis=[This] | refThat=[ext::That]

Unfortunately, Xtext (or rather: ANTLR) now complains during the DSL generation that

Decision can match input such as "RULE_ID" using multiple alternatives: 1, 2
As a result, alternative(s) 2 were disabled for that input

The reason for that is that both cross-references use the same token (by default a string returned by the ID terminal rule) as the name to use for the look-up, so the parser can’t distinguish between the two, based solely on the token under consideration alone. Note that if the parser generator were a bit smarter, it could have simply tried to resolve both alternatives using the scoping providers and see which one returned first.

In case you’d wanted to cross-reference This and Other, both being type rules in the grammar itself, the solution would be to introduce a “convenience super type” like this:

CrossReferencableThingy: This | Other;

Unfortunately, if you try this with ext::That instead of Other, you’ll get a basic parsing error because alternatives in a type rule must be callable rules as well whereas That is “just” a type coming from some Ecore model.

Even if the Xtext grammar definition language allowed such a thing, we’d have a problem: a rule with alternatives like this normally translates into an EClass which should act as a super type for both of the alternative types. But that would mean that the ext::That type would have to be altered to (also) point to CrossReferencableThingy, which is impossible since ext::That is in an EPackage that is considered to be read-online as far as the Xtext generator is concerned.

The solution

The solution is really quite simple. The trick is to realize that ultimately instances of both This and ext::That are EObject-s. Therefore, it’s OK to say:

MyCrossReference: ref=[ecore::EObject];

(Of course, now you’ll have to import the Ecore EPackage as well.) This means that you could reference everything, which is not what we want so we’re going to use scoping to limit the possibilities by actually doing what we said earlier about the parser being smarter.

IScope scope_MyCrossReference_ref (Model model, EReference eRef) {
    List<EObject> crossRefTargets = new ArrayList<EObject>(model.getThings());
    Iterable<That> allThat =
        com.google.common.collect.Iterables.filter(
            model.getExternalModel().getStuff(), That.class);
    com.google.common.collect.Iterables.addAll(crossRefTargets, allThat);
    return org.eclipse.xtext.scoping.Scopes.scopeFor(crossRefTargets);
}

The reason I have the scoping called at the model’s root (of type Model) is that it’s usually quite easy to reach all This and (presumably) all ext::That from the root without having to traverse the Semantic Model (obviously, I’m reading Fowler’s DSL book!) upwards. Note also that I use the Google Collections Iterables class to do some of the tedious, heavy lifting for me. Alternatively, I could have used org.eclipse.xtext.EcoreUtil2#typeSelect.

Advertisements
Categories: DSLs, MDSD, The How, Xtext
  1. Fabian
    March 18, 2011 at 6:20 pm

    Hello Meinte,

    This is a very useful hint. I’ve started to build a programming language with Xtext 2.0 and I’m wondering if it is possible to have non EString references. My language will contain arrays, so a valid reference will be ‘a.b[expression].c’. Of course this will not be parsed as a string. anyway is there a way to handle such references with xtext?

    I would be very grateful for every hint!
    Fabian

    • March 18, 2011 at 10:36 pm

      Something like ‘a.b[expression].c’ cannot be a single reference, but should be an expression in which the dots are access operators and [] is the array index operator. So, to make references like that work, you need to build an expression sub language. The trick described in the blog can then be used for the ‘a’ and ‘c’ parts of the example expression in case the meta classes belonging to these come from an existing EPackage and/or from a different grammar. For a good explanation on how to build grammars for expression sub languages: see the Xtext 1.0.1 User Guide (§9.3) or Sven Efftinge’s blog on it (http://blog.efftinge.de/2010/08/parsing-expressions-with-xtext.html).

  2. Stefan
    August 21, 2013 at 1:29 pm

    I know, it’s an old topic, but do you have any Idea how to access the external model in the newest XText-Version? The method you used is not working anymore as it seems or I’m just searching in the wrong direction

    • August 21, 2013 at 1:51 pm

      “model.getExternalModel().getStuff()” is meant as a placeholder/stub and simply means “get your hands on all possible reference targets of type ext::That. For the rest this is simply normal scoping which works to date.

      • Stefan
        August 22, 2013 at 6:42 am

        OK, I guess I got something wrong then 🙂 Thank you!

  3. July 15, 2014 at 12:26 pm

    I’ve use this approach:

    MethodArgumentDeclaration returns Variable:
    type=TypeDeclaration name=ID
    ;
    LocalVariableDeclaration returns Variable:
    (‘var’| type=TypeDeclaration) name=ID (‘=’ expression=Expression)?
    ;
    VariableInvocation:
    variable=[Variable]
    ;

  1. December 20, 2011 at 10:26 am

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: