Workshop On Xtext
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Workshop On Xtext

on

  • 8,181 views

With Sebastian Zarnekow and Moritz Eysholdt

With Sebastian Zarnekow and Moritz Eysholdt
Hands-on Workshop at Code Generation 2009

Statistics

Views

Total Views
8,181
Views on SlideShare
7,972
Embed Views
209

Actions

Likes
5
Downloads
225
Comments
0

5 Embeds 209

http://www.slideshare.net 101
http://www.itemis.de 65
http://www.itemis.com 38
https://onion.net 4
http://www.linkedin.com 1

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Workshop On Xtext Presentation Transcript

  • 1. Software Installation • You‘ll find a CD with the required software on your desk • Unzip the Eclipse distribution matching your OS Windows users: Use a location close to the root directory • Run the Eclipse executable • Choose File -> Import -> Existing projects into workspace • Select Archive file and choose workspace.zip from the CD
  • 2. Workshop Moritz Eysholdt, Sebastian Zarnekow, Jan Köhnlein
  • 3. Moritz Eysholdt Jan Köhnlein Sebastian Zarnekow
  • 4. Moritz Eysholdt Jan Köhnlein Sebastian Zarnekow
  • 5. Moritz Eysholdt Jan Köhnlein Sebastian Zarnekow
  • 6. What is a Domain Specific Language (DSL)?
  • 7. A domain-specific language (DSL) is a formal, processable language targeting at a specific viewpoint or aspect of a system.
  • 8. A domain-specific language (DSL) is a formal, processable language targeting at a specific viewpoint or aspect of a system. Its semantics, flexibility and notation is designed in order to support working with that viewpoint as good as possible.
  • 9. Why would I want to use a DSL?
  • 10. GPL vs DSL
  • 11. GPL vs DSL
  • 12. GPL vs DSL
  • 13. Abstraction
  • 14. Why isn‘t everybody using DSLs?
  • 15. Complicated Expensive
  • 16. Isn‘t text a little bit old- fashioned?
  • 17. } protected String getErrorMessage() { return errorMessage.toString(); } Graphics public boolean isSameStructure(EObject left, EObject right) { counter = 0; return internalIsSameStructure(left, right); } public boolean internalIsSameStructure(EObject left, EObject right) { ++counter; if (!isSameClass(left.eClass(), right.eClass())) { errorMessage.append("Classes are not equal: " + left + " != " + ri return false; } List<EObject> leftChildren = getRelevantChildren(left); List<EObject> rightChildren = getRelevantChildren(right); if(leftChildren.size() != rightChildren.size()) { errorMessage.append("Number of children differs " + left + " " + r return false; } for (int i = 0; i < leftChildren.size(); ++i) { if(!internalIsSameStructure(leftChildren.get(i), rightChildren.get errorMessage.append("Children differ " + left + " " + right + return false; } } Text return true; } protected boolean isSameClass(EClass left, EClass right) { return left.getName().equals(right.getName()) && left.getEPackage().getNsURI().equals(right.getEPackage().g } protected List<EObject> getRelevantChildren(EObject _this) { List<EObject> relevantChildren = new ArrayList<EObject>(_this.eContents( for (Iterator<EObject> i = relevantChildren.iterator(); i.hasNext();) { EObject next = i.next(); if (!isRelevantChild(_this, next)) { i.remove(); } } return relevantChildren; } protected boolean isRelevantChild(EObject container, EObject child) { return true;
  • 18. } protected String getErrorMessage() { return errorMessage.toString(); } Graphics public boolean isSameStructure(EObject left, EObject right) { counter = 0; return internalIsSameStructure(left, right); } • High-level views public boolean internalIsSameStructure(EObject left, EObject right) { ++counter; if (!isSameClass(left.eClass(), right.eClass())) { errorMessage.append("Classes are not equal: " + left + " != " + ri return false; } List<EObject> leftChildren = getRelevantChildren(left); List<EObject> rightChildren = getRelevantChildren(right); if(leftChildren.size() != rightChildren.size()) { errorMessage.append("Number of children differs " + left + " " + r return false; } for (int i = 0; i < leftChildren.size(); ++i) { if(!internalIsSameStructure(leftChildren.get(i), rightChildren.get errorMessage.append("Children differ " + left + " " + right + return false; } } Text return true; } protected boolean isSameClass(EClass left, EClass right) { return left.getName().equals(right.getName()) && left.getEPackage().getNsURI().equals(right.getEPackage().g } protected List<EObject> getRelevantChildren(EObject _this) { List<EObject> relevantChildren = new ArrayList<EObject>(_this.eContents( for (Iterator<EObject> i = relevantChildren.iterator(); i.hasNext();) { EObject next = i.next(); if (!isRelevantChild(_this, next)) { i.remove(); } } return relevantChildren; } protected boolean isRelevantChild(EObject container, EObject child) { return true;
  • 19. } protected String getErrorMessage() { return errorMessage.toString(); } Graphics public boolean isSameStructure(EObject left, EObject right) { counter = 0; return internalIsSameStructure(left, right); } • High-level views public boolean internalIsSameStructure(EObject left, EObject right) { ++counter; if (!isSameClass(left.eClass(), right.eClass())) { errorMessage.append("Classes are not equal: " + left + " != " + ri return false; } List<EObject> leftChildren = getRelevantChildren(left); List<EObject> rightChildren = getRelevantChildren(right); if(leftChildren.size() != rightChildren.size()) { errorMessage.append("Number of children differs " + left + " " + r return false; } for (int i = 0; i < leftChildren.size(); ++i) { if(!internalIsSameStructure(leftChildren.get(i), rightChildren.get errorMessage.append("Children differ " + left + " " + right + return false; } } Text return true; } protected boolean isSameClass(EClass left, EClass right) { return left.getName().equals(right.getName()) } • Detailed views && left.getEPackage().getNsURI().equals(right.getEPackage().g protected List<EObject> getRelevantChildren(EObject _this) { List<EObject> relevantChildren = new ArrayList<EObject>(_this.eContents( for (Iterator<EObject> i = relevantChildren.iterator(); i.hasNext();) { EObject next = i.next(); if (!isRelevantChild(_this, next)) { i.remove(); } } return relevantChildren; } protected boolean isRelevantChild(EObject container, EObject child) { return true;
  • 20. } protected String getErrorMessage() { return errorMessage.toString(); } Graphics public boolean isSameStructure(EObject left, EObject right) { counter = 0; return internalIsSameStructure(left, right); } • High-level views public boolean internalIsSameStructure(EObject left, EObject right) { • ++counter; Suggests non-formalism if (!isSameClass(left.eClass(), right.eClass())) { errorMessage.append("Classes are not equal: " + left + " != " + ri return false; } List<EObject> leftChildren = getRelevantChildren(left); List<EObject> rightChildren = getRelevantChildren(right); if(leftChildren.size() != rightChildren.size()) { errorMessage.append("Number of children differs " + left + " " + r return false; } for (int i = 0; i < leftChildren.size(); ++i) { if(!internalIsSameStructure(leftChildren.get(i), rightChildren.get errorMessage.append("Children differ " + left + " " + right + return false; } } Text return true; } protected boolean isSameClass(EClass left, EClass right) { return left.getName().equals(right.getName()) } • Detailed views && left.getEPackage().getNsURI().equals(right.getEPackage().g protected List<EObject> getRelevantChildren(EObject _this) { List<EObject> relevantChildren = new ArrayList<EObject>(_this.eContents( for (Iterator<EObject> i = relevantChildren.iterator(); i.hasNext();) { EObject next = i.next(); if (!isRelevantChild(_this, next)) { i.remove(); } } return relevantChildren; } protected boolean isRelevantChild(EObject container, EObject child) { return true;
  • 21. } protected String getErrorMessage() { return errorMessage.toString(); } Graphics public boolean isSameStructure(EObject left, EObject right) { counter = 0; return internalIsSameStructure(left, right); } • High-level views public boolean internalIsSameStructure(EObject left, EObject right) { • ++counter; Suggests non-formalism if (!isSameClass(left.eClass(), right.eClass())) { errorMessage.append("Classes are not equal: " + left + " != " + ri return false; } List<EObject> leftChildren = getRelevantChildren(left); List<EObject> rightChildren = getRelevantChildren(right); if(leftChildren.size() != rightChildren.size()) { errorMessage.append("Number of children differs " + left + " " + r return false; } for (int i = 0; i < leftChildren.size(); ++i) { if(!internalIsSameStructure(leftChildren.get(i), rightChildren.get errorMessage.append("Children differ " + left + " " + right + return false; } } Text return true; } protected boolean isSameClass(EClass left, EClass right) { return left.getName().equals(right.getName()) } • Detailed views && left.getEPackage().getNsURI().equals(right.getEPackage().g • Formal protected List<EObject> getRelevantChildren(EObject _this) { List<EObject> relevantChildren = new ArrayList<EObject>(_this.eContents( for (Iterator<EObject> i = relevantChildren.iterator(); i.hasNext();) { EObject next = i.next(); if (!isRelevantChild(_this, next)) { i.remove(); } } return relevantChildren; } protected boolean isRelevantChild(EObject container, EObject child) { return true;
  • 22. } protected String getErrorMessage() { return errorMessage.toString(); } Graphics public boolean isSameStructure(EObject left, EObject right) { counter = 0; return internalIsSameStructure(left, right); } • High-level views public boolean internalIsSameStructure(EObject left, EObject right) { • ++counter; Suggests non-formalism if (!isSameClass(left.eClass(), right.eClass())) { errorMessage.append("Classes are not equal: " + left + " != " + ri • Individual tools return false; } List<EObject> leftChildren = getRelevantChildren(left); List<EObject> rightChildren = getRelevantChildren(right); if(leftChildren.size() != rightChildren.size()) { errorMessage.append("Number of children differs " + left + " " + r return false; } for (int i = 0; i < leftChildren.size(); ++i) { if(!internalIsSameStructure(leftChildren.get(i), rightChildren.get errorMessage.append("Children differ " + left + " " + right + return false; } } Text return true; } protected boolean isSameClass(EClass left, EClass right) { return left.getName().equals(right.getName()) } • Detailed views && left.getEPackage().getNsURI().equals(right.getEPackage().g • Formal protected List<EObject> getRelevantChildren(EObject _this) { List<EObject> relevantChildren = new ArrayList<EObject>(_this.eContents( for (Iterator<EObject> i = relevantChildren.iterator(); i.hasNext();) { EObject next = i.next(); if (!isRelevantChild(_this, next)) { i.remove(); } } return relevantChildren; } protected boolean isRelevantChild(EObject container, EObject child) { return true;
  • 23. } protected String getErrorMessage() { return errorMessage.toString(); } Graphics public boolean isSameStructure(EObject left, EObject right) { counter = 0; return internalIsSameStructure(left, right); } • High-level views public boolean internalIsSameStructure(EObject left, EObject right) { • ++counter; Suggests non-formalism if (!isSameClass(left.eClass(), right.eClass())) { errorMessage.append("Classes are not equal: " + left + " != " + ri • Individual tools return false; } List<EObject> leftChildren = getRelevantChildren(left); List<EObject> rightChildren = getRelevantChildren(right); if(leftChildren.size() != rightChildren.size()) { errorMessage.append("Number of children differs " + left + " " + r return false; } for (int i = 0; i < leftChildren.size(); ++i) { if(!internalIsSameStructure(leftChildren.get(i), rightChildren.get errorMessage.append("Children differ " + left + " " + right + return false; } } Text return true; } protected boolean isSameClass(EClass left, EClass right) { return left.getName().equals(right.getName()) } • Detailed views && left.getEPackage().getNsURI().equals(right.getEPackage().g • Formal protected List<EObject> getRelevantChildren(EObject _this) { List<EObject> relevantChildren = new ArrayList<EObject>(_this.eContents( • Well established tools for (Iterator<EObject> i = relevantChildren.iterator(); i.hasNext();) { EObject next = i.next(); if (!isRelevantChild(_this, next)) { i.remove(); } } return relevantChildren; } protected boolean isRelevantChild(EObject container, EObject child) { return true;
  • 24. } protected String getErrorMessage() { return errorMessage.toString(); } Graphics public boolean isSameStructure(EObject left, EObject right) { counter = 0; return internalIsSameStructure(left, right); } • High-level views public boolean internalIsSameStructure(EObject left, EObject right) { • ++counter; Suggests non-formalism if (!isSameClass(left.eClass(), right.eClass())) { errorMessage.append("Classes are not equal: " + left + " != " + ri • Individual tools return false; } List<EObject> leftChildren = getRelevantChildren(left); • Hard to evolve List<EObject> rightChildren = getRelevantChildren(right); if(leftChildren.size() != rightChildren.size()) { errorMessage.append("Number of children differs " + left + " " + r return false; } for (int i = 0; i < leftChildren.size(); ++i) { if(!internalIsSameStructure(leftChildren.get(i), rightChildren.get errorMessage.append("Children differ " + left + " " + right + return false; } } Text return true; } protected boolean isSameClass(EClass left, EClass right) { return left.getName().equals(right.getName()) } • Detailed views && left.getEPackage().getNsURI().equals(right.getEPackage().g • Formal protected List<EObject> getRelevantChildren(EObject _this) { List<EObject> relevantChildren = new ArrayList<EObject>(_this.eContents( • Well established tools for (Iterator<EObject> i = relevantChildren.iterator(); i.hasNext();) { EObject next = i.next(); if (!isRelevantChild(_this, next)) { i.remove(); } } return relevantChildren; } protected boolean isRelevantChild(EObject container, EObject child) { return true;
  • 25. } protected String getErrorMessage() { return errorMessage.toString(); } Graphics public boolean isSameStructure(EObject left, EObject right) { counter = 0; return internalIsSameStructure(left, right); } • High-level views public boolean internalIsSameStructure(EObject left, EObject right) { • ++counter; Suggests non-formalism if (!isSameClass(left.eClass(), right.eClass())) { errorMessage.append("Classes are not equal: " + left + " != " + ri • Individual tools return false; } List<EObject> leftChildren = getRelevantChildren(left); • Hard to evolve List<EObject> rightChildren = getRelevantChildren(right); if(leftChildren.size() != rightChildren.size()) { errorMessage.append("Number of children differs " + left + " " + r return false; } for (int i = 0; i < leftChildren.size(); ++i) { if(!internalIsSameStructure(leftChildren.get(i), rightChildren.get errorMessage.append("Children differ " + left + " " + right + return false; } } Text return true; } protected boolean isSameClass(EClass left, EClass right) { return left.getName().equals(right.getName()) } • Detailed views && left.getEPackage().getNsURI().equals(right.getEPackage().g • Formal protected List<EObject> getRelevantChildren(EObject _this) { List<EObject> relevantChildren = new ArrayList<EObject>(_this.eContents( • Well established tools for (Iterator<EObject> i = relevantChildren.iterator(); i.hasNext();) { EObject next = i.next(); if (!isRelevantChild(_this, next)) { } } • Easy to evolve i.remove(); return relevantChildren; } protected boolean isRelevantChild(EObject container, EObject child) { return true;
  • 26. } protected String getErrorMessage() { return errorMessage.toString(); } Graphics public boolean isSameStructure(EObject left, EObject right) { counter = 0; return internalIsSameStructure(left, right); } • High-level views public boolean internalIsSameStructure(EObject left, EObject right) { • ++counter; Suggests non-formalism if (!isSameClass(left.eClass(), right.eClass())) { errorMessage.append("Classes are not equal: " + left + " != " + ri • Individual tools return false; } List<EObject> leftChildren = getRelevantChildren(left); • Hard to evolve List<EObject> rightChildren = getRelevantChildren(right); if(leftChildren.size() != rightChildren.size()) { errorMessage.append("Number of children differs " + left + " " + r • Editing with mouse } return false; for (int i = 0; i < leftChildren.size(); ++i) { if(!internalIsSameStructure(leftChildren.get(i), rightChildren.get errorMessage.append("Children differ " + left + " " + right + return false; } } Text return true; } protected boolean isSameClass(EClass left, EClass right) { return left.getName().equals(right.getName()) } • Detailed views && left.getEPackage().getNsURI().equals(right.getEPackage().g • Formal protected List<EObject> getRelevantChildren(EObject _this) { List<EObject> relevantChildren = new ArrayList<EObject>(_this.eContents( • Well established tools for (Iterator<EObject> i = relevantChildren.iterator(); i.hasNext();) { EObject next = i.next(); if (!isRelevantChild(_this, next)) { } } • Easy to evolve i.remove(); return relevantChildren; } protected boolean isRelevantChild(EObject container, EObject child) { return true;
  • 27. } protected String getErrorMessage() { return errorMessage.toString(); } Graphics public boolean isSameStructure(EObject left, EObject right) { counter = 0; return internalIsSameStructure(left, right); } • High-level views public boolean internalIsSameStructure(EObject left, EObject right) { • ++counter; Suggests non-formalism if (!isSameClass(left.eClass(), right.eClass())) { errorMessage.append("Classes are not equal: " + left + " != " + ri • Individual tools return false; } List<EObject> leftChildren = getRelevantChildren(left); • Hard to evolve List<EObject> rightChildren = getRelevantChildren(right); if(leftChildren.size() != rightChildren.size()) { errorMessage.append("Number of children differs " + left + " " + r • Editing with mouse } return false; for (int i = 0; i < leftChildren.size(); ++i) { if(!internalIsSameStructure(leftChildren.get(i), rightChildren.get errorMessage.append("Children differ " + left + " " + right + return false; } } Text return true; } protected boolean isSameClass(EClass left, EClass right) { return left.getName().equals(right.getName()) } • Detailed views && left.getEPackage().getNsURI().equals(right.getEPackage().g • Formal protected List<EObject> getRelevantChildren(EObject _this) { List<EObject> relevantChildren = new ArrayList<EObject>(_this.eContents( • Well established tools for (Iterator<EObject> i = relevantChildren.iterator(); i.hasNext();) { EObject next = i.next(); if (!isRelevantChild(_this, next)) { } • Easy to evolve i.remove(); • } } Editing with keyboard return relevantChildren; protected boolean isRelevantChild(EObject container, EObject child) { return true;
  • 28. Showtime ! • In your Eclipse workbench • Choose Run->Run configurations... • Double click on Eclipse Application • Go to the Arguments tab and add -XX:MaxPermSize=128m to the VM arguments • Click on Run
  • 29. A Guided Example entity Conference { property Name : String on Attendees refs many Pers Sp eakers refs many Speaker } entity Person { property Name : String } { enti ty Speaker extends Person n Sessions refs many Sessio } entity Session { property Title: String property IsTutorial : Bool }
  • 30. Grammar Language grammar org.xtext.cg2009.Entities with org.eclipse.xtext.common.Terminals generate entities "http://www.xtext.org/cg2009/Entities" Model : (entities += Entity)+; Entity returns Entity: 'entity' name=ID ('extends' superType=[Entity])? '{' (properties+=Property)* '}'; Property: SimpleProperty | ReferenceProperty; SimpleProperty: 'property' name=ID ':' type=('String'|'Number'|'Bool'); ReferenceProperty : name=ID 'refs' ('one' | many?='many')? type=[Entity];
  • 31. Edit SQL with Xtext CREATE TABLE Conferences ( id INTEGER NOT NULL PRIMARY KEY, • Only ) name CHAR(50) Create-Statements • List of columns
  • 32. Edit SQL with Xtext CREATE TABLE Conferences ( id INTEGER NOT NULL PRIMARY KEY, name CHAR(50) • Enhance with ) Foreign Keys CREATE TABLE Speaker ( name CHAR(50), id INTEGER, FOREIGN KEY (id) REFERENCES Conference(id) )
  • 33. What’s wrong with it?
  • 34. Cross References
  • 35. Cross References Model Element
  • 36. Cross References Element Name Model Element
  • 37. Cross References Element Name resolve Model Element
  • 38. Cross References Element Name resolve determin Model Element
  • 39. Problem: Names may not be ambiguous.
  • 40. Problem: Names may not be ambiguous. Solution: Implement a ScopeProvider!
  • 41. Problem: Names may not be ambiguous. Solution: Implement a ScopeProvider! A Scope is a context specific, hierarchical collection of all reachable model elements and their respective names.
  • 42. Scope Provider Make Foreign Keys work! public IScope scope_ForeignKey_from( ForeignKey context, EReference reference) { Table fromTable = (Table) context.eContainer(); return createTableScope(fromTable); }
  • 43. Meta-Model Inference grammar org.xtext.codeGeneration.Entities with org.eclipse.xtext.common.Terminals generate entities "http://www.xtext.org/CG2009/Entities" Model : (entities += Entity)+; Entity returns Entity: 'entity' name=ID ('extends' superType=[Entity])? '{' (properties+=Property)* '}'; Property: SimpleProperty | ReferenceProperty; SimpleProperty: 'property' name=ID ':' type=('String'|'Number'|'Bool'); ReferenceProperty : name=ID 'refs' ('one' | many?='many')? type=[Entity];
  • 44. Model Conferences with Xtext How do I create a language for an existing meta-model?
  • 45. Model Conferences with Xtext
  • 46. Validate Your Conference 1. Have a look at the generated Validator-Stub. 2. Implement some checks for your language. 3. Check out the results. @Check public void checkSpeakersName(Speaker speaker) { assertTrue("Speaker's name is to short.", ConferencesLanguagePackage.SPEAKER__NAME, speaker.getName().length() >= 2); }
  • 47. Convention over Configuration
  • 48. Configured with Google Guice class MyDslRuntimeModule extends DefaultRuntimeModule { Class<? extends ServiceInterface> bindService() { return MyServiceImplementation.class; } }
  • 49. Extensible Language Generator • Generates a lot of Java code • Composed of fragments • Customizable • Add your own fragments!
  • 50. http://www.wordle.net/
  • 51. Formatting Pretty Printing Code Beautification import <Literature.entities> entity City {Int id; String name;} entity Store { Int id; String name; Int city references City. id; }
  • 52. Formatting Pretty Printing Code Beautification import <Literature.entities> entity City {Int id; String name;} entity Store { Int id; String name; Int import <Literature.entities> city references entity City { City. Int id; id; } String name; } entity Store { Int id; String name; Int city references City.id; }
  • 53. Formatting Pretty Printing Code Beautification import <Literature.entities> entity City {Int id; String name;} entity Store { Int id; String name; Int import <Literature.entities> city references entity City { City. Int id; id; } String name; } entity Store { Int id; String name; Int city references City.id; }
  • 54. Formatting Pretty Printing Code Beautification import <Literature.entities> entity City {Int id; String name;} entity Store { Int id; String name; Int import <Literature.entities> city references entity City { City. Int id; id; } String name; } entity Store { Int id; String name; Int city references City.id; § 1. At ... do ... } § 2. At ... do ... § 3. At ... do ... § 4. At ... do ... ...
  • 55. Integration with EMF
  • 56. Integration with EMF eclipse Any EMF-based modeling Code Generator GMF Editor P R O J E C T Component
  • 57. Integration with EMF eclipse Any EMF-based modeling Code Generator GMF Editor P R O J E C T Component <<abstract>> XMIResource Resource
  • 58. Integration with EMF eclipse Any EMF-based modeling Code Generator GMF Editor P R O J E C T Component <<abstract>> XMIResource Resource
  • 59. Integration with EMF eclipse Any EMF-based modeling Code Generator GMF Editor P R O J E C T Component <<abstract>> XMI XMIResource Resource
  • 60. Integration with EMF eclipse Any EMF-based modeling Code Generator GMF Editor P R O J E C T Component <<abstract>> XMI XMIResource Resource
  • 61. Integration with EMF eclipse Any EMF-based modeling Code Generator GMF Editor P R O J E C T Component <<abstract>> XMI XMIResource Resource XtextResource
  • 62. Integration with EMF eclipse Any EMF-based modeling Code Generator GMF Editor P R O J E C T Component <<abstract>> XMI XMIResource Resource XtextResource
  • 63. Integration with EMF eclipse Any EMF-based modeling Code Generator GMF Editor P R O J E C T Component <<abstract>> XMI XMIResource Resource XtextResource Text
  • 64. Integration with EMF eclipse Any EMF-based modeling Code Generator GMF Editor P R O J E C T Component <<abstract>> XMI XMIResource Resource XtextResource Text Parser Linker Serializer ValueConverter ScopeProvider Formatter
  • 65. EMF Index • Indexes all resources in workspace • Query elements and cross-references • Efficient scoping
  • 66. EMF Index • Indexes all resources in workspace • Query elements and cross-references • Efficient scoping Load Resources
  • 67. Learn more at http://www.xtext.org