Home > Xtext > Xtext: Referencing Elements of one DSL from another DSL

Xtext: Referencing Elements of one DSL from another DSL

This is a blog post on Inter-Language-Cross-References in Xtext. Let us asume we have a DSL that contains Definitions. Now we want to create another DSL that references (uses) the Definitions defined in the the first DSL.

So let us start with the first DSL: We create a new Xtext Project

And here is the (for demonstration purpose oversimplyfied) Grammar

grammar org.xtext.example.definitions.Definitions with org.eclipse.xtext.common.Terminals

generate definitions "http://www.xtext.org/example/definitions/Definitions"

Model:
	definitions+=Definition*;

Definition:
	'define' name=ID;

We run GenerateDefinitions.mwe2 to generate the language.
This is all for the first DSL.

Now let us create a project for the second DSL.

To be able to reference the first DSL from the second we add a bundle dependency to the Manifest of the second

Here the resulting manifest

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: org.xtext.example.usages
Bundle-Vendor: My Company
Bundle-Version: 1.0.0.qualifier
Bundle-SymbolicName: org.xtext.example.usages; singleton:=true
Bundle-ActivationPolicy: lazy
Require-Bundle: org.eclipse.xtext;visibility:=reexport,
 org.eclipse.xtext.xbase;resolution:=optional;visibility:=reexport,
 org.eclipse.xtext.generator;resolution:=optional,
 org.apache.commons.logging;bundle-version="1.0.4";resolution:=optional,
 org.eclipse.emf.codegen.ecore;resolution:=optional,
 org.eclipse.emf.mwe.utils;resolution:=optional,
 org.eclipse.emf.mwe2.launch;resolution:=optional,
 org.xtext.example.definitions;bundle-version="1.0.0"
Import-Package: org.apache.log4j
Bundle-RequiredExecutionEnvironment: J2SE-1.5

Now we can create the Grammar from the Usage DSL

grammar org.xtext.example.usages.Usages with org.eclipse.xtext.common.Terminals

generate usages "http://www.xtext.org/example/usages/Usages"

import "http://www.xtext.org/example/definitions/Definitions" as def

Model:
	usages+=Usage*;
	
Usage:
	'use' definition=[def::Definition];

With "http://www.xtext.org/example/definitions/Definitions" as def we import the metamodel of our Define DSL to our Usage DSL.
Thus the Type Definition is available and can be used to define the cross reference definition=[def::Definition]

To get the thing running we have to do some adjustments to the workflow of the Usages language.

module org.xtext.example.usages.GenerateUsages

import org.eclipse.emf.mwe.utils.*
import org.eclipse.xtext.generator.*
import org.eclipse.xtext.ui.generator.*

var grammarURI = "classpath:/org/xtext/example/usages/Usages.xtext"
var file.extensions = "use"
var projectName = "org.xtext.example.usages"
var runtimeProject = "../${projectName}"

Workflow {
    bean = StandaloneSetup {
        scanClassPath = true
        platformUri = "${runtimeProject}/.."
        registerGeneratedEPackage = "org.xtext.example.definitions.definitions.DefinitionsPackage"
        registerGenModelFile = "platform:/resource/org.xtext.example.definitions/src-gen/org/xtext/example/definitions/Definitions.genmodel"
    }
    
        ...
}

we generate our Usage DSL (GenerateUsages.mwe2)

finally we start a new runtime eclipse and give it a try

About these ads
  1. Duncan Krebs
    August 9, 2012 at 02:44 | #1

    Thanks,
    Your example was very useful. My next question is in my use case I would have both test.define and test.use as serialized emf xml strings. To deserialize the test.use I imagine I would have to first deserialize test.define and add that as an object in the same resource set?

    In runtime, the test.define would be a set of trading signals and the test.use would be a set of strategies that reference the signals from test.define which from your example I can now do in my IDE. The next trick is figuring out how to successfully load the test.use model object in runtime so I can execute the trading strategy. Any links or tips would be helpful! – Duncan

    • August 9, 2012 at 06:16 | #2

      Hi,

      Am not quite sure what your problem is: simply make sure that both files are loaded into the same resource set (rs.get resource(URI,true)) and that you then call ecoreutil.resolve for the resouceset.

      • February 21, 2013 at 10:23 | #3

        Thanks for this tip! Could not find this hint anywhere else. Do you have a hint for documentation on this issue?

  2. Duncan Krebs
    August 9, 2012 at 22:02 | #4

    Christian,
    I got stuck at the part where adding the registerGenModelFile as that does not seem to be a valid element in the StandaloneSetup do you think its because I’m not on the most recent version? Also, if you have your dls files in different project folders do you have to do an explicit import for example “import /definitions/def1.def”? Thanks again for helping move along. – Duncan

  3. Duncan Krebs
    August 9, 2012 at 22:14 | #5

    Also noticed then when I try to run the mwe2 with only the registerGeneratedPackage it complains that there is not a registeredGenModelFile, in my mw2 file the error I get when adding a registerGenModel is “Could not resolve reference to jvm feature registerGenModel”

    • August 10, 2012 at 05:29 | #6

      Hi, the Example is targeting Xtext 2.3.0 and might be runnable with Xtext 2.2.x (maybe you have to change the namespace import with an platform:/resource/…./..ecore one.

      Is there a reason why you still use Xtext 1.0?

      You won’t need explicit imports unless you change the config regarding global scoping in the workflow

  4. Süleyman Issiz
    September 4, 2012 at 13:51 | #7

    Hi,
    I am trying to reference elements of one DSL from another using Xtext Version 1.0.1. I have problem when I try to register genmodel file. I have tried to register the genmodel file in the workflow file of the DSL that I want to reference the other DSL but it is possible to define “RegisterGenModelFile” in the workflow file. Do you have any experience about it? How would I solve the same problem using XText 1.0.1?

    Thanks

    • September 4, 2012 at 14:27 | #8

      Hi you should be able to do the register thing a few lines below in the ecoregeneratorfragment

      • Süleyman Issiz
        September 4, 2012 at 15:41 | #9

        Hi Christian,

        it works now!

        Thanks

  5. Süleyman Issiz
    September 7, 2012 at 11:10 | #10

    Hi Christian,
    I have managed to reference to a top level element of another DSL. But I couldn’t manage to reference to the elements which contain other elements also. In your example what would you do when you have a DSL like below and you want to access to the extension from another DSL?

    Definition:
    ‘define’ name=ID
    ‘extensions’ ‘{‘ (extensions+=Extension)+ ‘}’;

    Extension:
    extensionName = ExtensionName ‘->’ extension_Definition = ExtensionDefinition’;’;

    ExtensionName :
    name=ID;
    ExtensionDefinition:
    name=ID;

    • September 7, 2012 at 11:36 | #11

      Xtext uses by default Full QualifiedNames. thus check which qualifiedname your stuff gets
      and how you reference them in the grammar (ref=[Thing] is short for ref=[Thing|ID] )
      which means that an ID will be parsed by you may need ref=[Thing|FQN] with FQN: ID (“.” ID)*;

  6. Michael Colburn
    October 26, 2012 at 14:24 | #12

    Christian, this was exactly what I needed! Thank you! I do have a question that is indirectly relevant. I have a customer who would like a definitions file such that there are unquoted strings. He hates having to put the quotes around strings. I am wondering is it possible for a definition dsl grammar to have something like this:

    Definition: name=ID ‘=’ value=unquotedSTRING

    So that in using the DSL editor on a .define file, we can have the following:

    Text1 = I am a text without quotes
    Text 2 = I also am a text without quotes

    Thank you in advance!

    • October 26, 2012 at 15:09 | #13

      Hi this should be possible by changing terminal rules e.g. for whitespace and the hidden() definition. Never the less this might be a kind of struggle for corner cases like the last line. You will find in the Xtext forum some topics on this.

    • Paul
      November 24, 2013 at 19:36 | #14

      Hi Michael,

      I am also interested about your question
      have you found a solution for that so far?

      Thanks

  7. Joern
    November 7, 2012 at 15:59 | #15

    Thanks – this has been helpful.
    Unfortunately, the generated Usages.ecore looks a little ugly: the referenced type is addressed using local paths (e.g., eType=”ecore:EClass ../../../../../../org.xtext.example.definitions/src-gen/org/xtext/example/definitions/Definitions.ecore#//Definition”/>).

    Is there a way to avoid this and to generate something like “http://www.xtext.org/example/definitions/Definitions” instead of the local path?

    Thank you in advance!

  8. October 20, 2013 at 07:18 | #17

    Dear All,

    I need your favor or suggestion regarding migrate the xtext version from 1.0.1 to 2.3.0 in SME application.

    I followed all following process for migrate the xtext version.
    1. Delete the old plug-ins and update the latest plug-ins in target platform.
    2. Update the Plug-in Dependencies and Import Statements.
    3. Introduction of the Qualified Name.
    4. Changes in the index and in find references.
    5. Rewritten Node Model.
    6. AutoEditStrategy.
    7. Other Noteworthy API Changes
    To consider the above steps, I started the work with Eclipse-4.2, Which has a xtext-2.3.0 dependency. Successfully I completed the all above steps and removed the compilation error.

    Problem: After that I start the testing and getting below error Messages:
    [XtextLinkingDiagnostic: null:6 Couldn't resolve reference to Material 'MPS_RECUR'.,
    XtextLinkingDiagnostic: null:9 Couldn't resolve reference to Cstic 'NUM_OF_ALLOC'.,
    XtextLinkingDiagnostic: null:15 Couldn't resolve reference to Cstic 'INSTANCE_NUM'.,
    XtextLinkingDiagnostic: null:14 Couldn't resolve reference to Class 'ALLOC'.]

    • October 20, 2013 at 13:04 | #18

      Please have a look at the docs (contains a mig guide) or ask (specific) questions on the Xtext forum

    • manoj
      November 19, 2013 at 11:01 | #19

      hi christian,
      can we use user created java objects or java classes in xtext grammar

  9. shahan
    January 5, 2014 at 15:22 | #21

    Hi christian,

    I had followed your article and managed to link two DSLs. However, i have faced problems when referencing some features. Grammar for my original two DSLs are huge, therefore i have recreated the problem using only one language.

    //Grammar example

    Model:
    package = PackageDec?
    greetings+=Greeting*;

    PackageDec:
    ‘package’ name=QualifiedName ;

    Greeting:
    ‘great’ name=ID (‘with’ super = [Greeting| QualifiedName])? ‘{‘ ‘}’ ;

    QualifiedName:
    ID (‘.’ ID)*;

    //Usage
    package p1.p2
    great G1 {}

    I need to generate a java class such as follows:
    package p1.p2;
    class G1 {}

    Therefore, i have sub-classed ‘DefaultDeclarativeQualifiedNameProvider’ with this:

    protected QualifiedName qualifiedName(Greeting ele) {
    Model m = (Model)ele.eContainer() ;
    if (m.getPackage() != null ){
    return QualifiedName.create(m.getPackage().getName(), ele.getName());
    }
    else {
    return QualifiedName.create(ele.getName());
    }
    }

    All are fine and only now my problems come.

    Suppose i wrote a script like this:

    package p3.p4
    great G2 with p1.p2.G1 {}

    Unfortunately, this gives a referencing error “Couldn’t resolve reference to Greeting ‘p1.p2.G1′.” , though quick fix suggest changing to ‘p1.p2.G1′ which is exactly same as what i have written.

    Having said that, lets say i have changed package declaration to p1 instead of p1.p2 and rewrite the script as “great G2 with p1.G1 {} “, Surprisingly, that doesn’t throw an error. It seems that it fails when package declaration has multiple ‘.’ separated segments.

    Could you please tell me why is it and how to make it right ?

    Thanks in advance.

    • January 6, 2014 at 16:20 | #22

      Hi the problem is how you create the Qualified Name

      public class MyDslNameProvider extends DefaultDeclarativeQualifiedNameProvider {

      protected QualifiedName qualifiedName(Greeting ele) {
      Model m = (Model) ele.eContainer();
      if (m.getPackage() != null) {
      return getConverter().toQualifiedName(m.getPackage().getName()).append(ele.getName());
      } else {
      return QualifiedName.create(ele.getName());
      }
      }

      }

      • shahan
        January 7, 2014 at 12:00 | #23

        Thanks a lot. It works now.

        On a separate note, I believe i have to re-impelment above function (qualified name for type ‘Greet’ ) in the seconds language again in case i need to get fully qualified name of a reference ?

      • January 8, 2014 at 15:32 | #24

        hi,

        i did not understand this question, please elaborate …

  10. shahan
    January 8, 2014 at 20:14 | #25

    Lets create a second grammar referenced to grammer i have written in my original question.

    Model:
    usages+=Usage*;
    Usage:
    ‘use’ greeting=[X::Greeting | QualifiedName];

    Suppose i have written following examples:

    //DSL 1
    package p1.p2.p3
    greet A {}
    greet B {}

    //DSL 2
    use p1.p2.p3.A
    use p1.p2.p3.B

    With your suggestion for the ‘qualifidName(Greeting ele)’ of the first DSL now references are resolved correctly. However, lets say i need to generate a text file for the second DSL with output as below (just writing full qualified name of the given ‘Greeting’):

    p1.p2.p3.A
    p1.p2.p3.B

    What i tried was retrieving full qualified name from the name provider (IQualifiedNameProvider) of second language. Not surprisingly, it returns only simple name but not the full qualified name as i haven’t customized qualifiedName for the ‘Greet’ type in the second language. So, my question is it possible to access qualified name provider (IQualifiedNameProvider) of first language from second ? Otherwise do i have to re-implement customization for ‘Greeting” type in the second language’s name provider also.

    What is the best approach to archive my goal ?

    Thanking you in advance
    Shahan

    • January 9, 2014 at 18:43 | #26

      hi you can use iglobalserviceprovider to obtain language specific things

      • shahan
        January 10, 2014 at 03:07 | #27

        Thank you very much Christian. I have got it working by using IGlobalServiceProvider.

  1. No trackbacks yet.

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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: