building dsls with xtext - eclipse modeling day 2009
DESCRIPTION
Slides of Eclipse Modeling Day in New York and Toronto http://wiki.eclipse.org/Eclipse_Modeling_Day Motivation of specific tools with apple corer analogy, Example of domain-specific language (chess notation), introduction to Xtext with demo plus outlookTRANSCRIPT
© itemis AG 2009 – All rights reserved
Building DSLs with Xtext
Eclipse Modeling Day
New York, 16 November 2009
Toronto, 18 November 2009
Heiko Behrens (itemis)
FOUNDATION
MEMBER TM
Model-Driven Software Development
with DSLs
Suppose...
You’d want to core an apple...
... for your kids.
Right tool for the job?
?
Your trusty swiss army knife!
Suppose...
You’d want to core a few more apples...
... for an apple cake.
Still the best tool for the job?
Better use this one.
and this one:
... a DSL is ...
A specific toolfor a specific job
A specific toolfor a specific job
Domain-Specific Language (DSL)
A DSL is a formal, processable language targeting at a specific viewpoint or
aspect of a software system.
Its semantics, flexibility and notation is designed in order to support working
with that viewpoint as good as possible.
Rd2-c2
Rd2-c2 ,rook at d2 moves to c2.”
Queen to c7.“”
“
Check.
Moves in Chess:
!ook at a1 moves to a5.
"ishop at c8 captures knight at h3.
# b1 x c3
$2 - g4
Piece
Piece
Piece
Square
Square
Square
Square
Action
Action
Action
Action
Destination
Destination
Destination
Destination
Rook a1 move a5
Bishop c8 capture h3 Knight
Knight b1 capture c3 Queen
Pawn g2 move g4
SourceDestinationPiece
Move
WhitePlayerBlackPlayer
Game«enum»
Piece*
"ishop at c8 captures knight at h3
" c8 x h3
Model (textfile)
White: "Mayfield"
Black: "Trinks"
pawn at e2 moves to e4
pawn at f7 moves to g5
K b1 - c3
f7 - f5
queen at d1 moves to h5
// 1-0
Xtext is a complete environment for development of textual - programming languages and - domain-specific languages.
It is implemented in Java and is based on Eclipse, EMF, and Antlr.
Superclass
Subclass Class
ecore meta modelLL(*) Parser editor
Model
Grammar
Generator
Runtime
Grammar (similar to EBNF)Game:
! "White:" whitePlayer=STRING
! "Black:" blackPlayer=STRING
! (moves+=Move)+;!
Move:
! AlgebraicMove | SpokenMove;
AlgebraicMove:
! (piece=Piece)? source=Square (captures?='x'|'-') dest=Square;!
SpokenMove:
! piece=Piece 'at' source=Square
! (captures?='captures' capturedPiece=Piece 'at' | 'moves to')
! dest=Square;!
terminal Square:
! ('a'..'h')('1'..'8'); !
enum Piece:
! pawn = 'P' | pawn = 'pawn' |
! knight = 'N' | knight = 'knight' |
! bishop = 'B' | bishop = 'bishop' |
! rook = 'R' | rook = 'rook' |
! queen = 'Q' | queen = 'queen' |
! king = 'K' | king = 'king';
© itemis AG 2009 – All rights reserved
Demo
• Model File within Editor + Custom View
• Xtext Grammar for writing simple chess games
• Derived meta model
• Java program that works with textual models
• Xpand-based generator
31
Real World DSLs
Regular Expressions
[-+]?[0-9]*\.?[0-9]+
Complex Event Processing
from FlightBookings F, CarBookings C, HotelBookings H matching [24 hours: F, C, H ] on F.name = C.name = H.name select F.name, true bookingWithCar, into TripleBookings
from FlightBookings F, CarBookings C, HotelBookings H matching [24 hours: F, !C, H ] on F.name = C.name = H.name select F.name, false bookingWithCar into TripleBookings
from TripleBookings where prev(bookingWithCar) and not bookingWithCar select name into LostCustomers
internal DSL
(with Java)
Mailer.mail()
.to(“[email protected]”)
.from(“[email protected]”)
.subject(“Writing DSLs in Java”)
.body(“...”)
.send();
ANT
<project name="MyProject" default="dist" basedir=".">
<property name="src" location="src"/>
<property name="build" location="build"/>
<property name="dist" location="dist"/>
<target name="init">
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init">
<javac srcdir="${src}" destdir="${build}"/>
</target>
<target name="dist" depends="compile">
<mkdir dir="${dist}/lib"/>
<jar jarfile="${dist}/lib/MyProject.jar"
basedir="${build}"/>
</target>
<target name="clean">
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target>
</project>
ANT ?
<project name="MyProject" default="dist" basedir=".">
<property name="src" location="src"/>
<property name="build" location="build"/>
<property name="dist" location="dist"/>
<target name="init">
<mkdir dir="${build}"/>
</target>
<target name="compile" depends="init">
<javac srcdir="${src}" destdir="${build}"/>
</target>
<target name="dist" depends="compile">
<mkdir dir="${dist}/lib"/>
<jar jarfile="${dist}/lib/MyProject.jar" basedir="${build}"/>
</target>
<target name="clean">
<delete dir="${build}"/>
<delete dir="${dist}"/>
</target></project>
vs.
Entities by Example
package templates;
import java.util.*;
import java.io.Serializable;
import javax.persistence.*;
@SuppressWarnings("serial")
@Entity
public class Customer implements Serializable {
! private Long id;
! private String name;
! private Address address;
! private Set<Order> orders = new HashSet<Order>();
! // No-arg constructor
! public Customer() {
! }
! @Id
! public Long getId() {
! ! return id;
! }
! public void setId(Long id) {
! ! this.id = id;
! }
! public String getName() {
! ! return name;
! }
! public void setName(String name) {
! ! this.name = name;
! }
! public Address getAddress() {
! ! return address;
! }
! public void setAddress(Address address) {
! ! this.address = address;
! }
! @OneToMany
! public Collection<Order> getOrders() {
! ! return orders;
! }
! public void setOrders(Set<Order> orders) {
! ! this.orders = orders;
! }
}
package templates;
import java.io.Serializable;
import java.util.*;
import javax.persistence.*;
@SuppressWarnings("serial")
@Entity
public class Customer implements Serializable {
! private Long id;
! private String name;
! private Address address;
! private Set<Order> orders = new HashSet<Order>();
! // No-arg constructor
! public Customer() {
! }
! @Id
! public Long getId() {
! ! return id;
! }
! public void setId(Long id) {
! ! this.id = id;
! }
! public String getName() {
! ! return name;
! }
! public void setName(String name) {
! ! this.name = name;
! }
! public Address getAddress() {
! ! return address;
! }
! public void setAddress(Address address) {
! ! this.address = address;
! }
! @OneToMany
! public Collection<Order> getOrders() {
! ! return orders;
! }
! public void setOrders(Set<Order> orders) {
! ! this.orders = orders;
! }
}
entity Customer {
! property name : String
! property address : Address
! property orders : Order[]
}
POJOs DAOs
The Finder Method
entity Customer {
! property name : String
! finder findFixed : name = "John Doe"
}
<entity name="Customer">
<attribute type="String" name="name"/>
<findMethod name="findFixed"> <expression> <equals> <attributeRef attr="name"/> <constant val="John Doe"/> </equals> </expression> <findMethod></entity>
Demo
! Implement simple Entity DSL
! Use prepared Generator
entity
*
*
Model
name: EStringType
SimpleType
Entity
name: EStringmany: EBoolean
Property
types
extends
properties
type
grammar org.xtext.webinar.Entity
! with org.eclipse.xtext.common.Terminals
generate entity
"http://www.xtext.org/webinar/Entity"
Model:
! (elements+=Type)*;
Type:
! SimpleType | Entity;
SimpleType:
! 'type' name=ID;
Entity:
! 'entity' name=ID
('extends' extends=[Entity])? '{'
! ! properties+=Property*
! '}';
Property:
! 'property' name=ID ':'
type=[Type] (many?='[]')?;
entity
*
*
Model
name: EStringType
SimpleType
Entity
name: EStringmany: EBoolean
Property
types
extends
properties
type
grammar org.xtext.webinar.Entity
! with org.eclipse.xtext.common.Terminals
generate entity
"http://www.xtext.org/webinar/Entity"
Model:
! (elements+=Type)*;
Type:
! SimpleType | Entity;
SimpleType:
! 'type' name=ID;
Entity:
! 'entity' name=ID
('extends' extends=[Entity])? '{'
! ! properties+=Property*
! '}';
Property:
! 'property' name=ID ':'
type=[Type] (many?='[]')?;
entity
*
*
Model
name: EStringType
SimpleType
Entity
name: EStringmany: EBoolean
Property
types
extends
properties
type
grammar org.xtext.webinar.Entity
! with org.eclipse.xtext.common.Terminals
generate entity
"http://www.xtext.org/webinar/Entity"
Model:
! (elements+=Type)*;
Type:
! SimpleType | Entity;
SimpleType:
! 'type' name=ID;
Entity:
! 'entity' name=ID
('extends' extends=[Entity])? '{'
! ! properties+=Property*
! '}';
Property:
! 'property' name=ID ':'
type=[Type] (many?='[]')?;
entity
*
*
Model
name: EStringType
SimpleType
Entity
name: EStringmany: EBoolean
Property
types
extends
properties
type
grammar org.xtext.webinar.Entity
! with org.eclipse.xtext.common.Terminals
generate entity
"http://www.xtext.org/webinar/Entity"
Model:
! (elements+=Type)*;
Type:
! SimpleType | Entity;
SimpleType:
! 'type' name=ID;
Entity:
! 'entity' name=ID
('extends' extends=[Entity])? '{'
! ! properties+=Property*
! '}';
Property:
! 'property' name=ID ':'
type=[Type] (many?='[]')?;
entity
*
*
Model
name: EStringType
SimpleType
Entity
name: EStringmany: EBoolean
Property
types
extends
properties
type
grammar org.xtext.webinar.Entity
! with org.eclipse.xtext.common.Terminals
generate entity
"http://www.xtext.org/webinar/Entity"
Model:
! (elements+=Type)*;
Type:
! SimpleType | Entity;
SimpleType:
! 'type' name=ID;
Entity:
! 'entity' name=ID
('extends' extends=[Entity])? '{'
! ! properties+=Property*
! '}';
Property:
! 'property' name=ID ':'
type=[Type] (many?='[]')?;
entity
*
*
Model
name: EStringType
SimpleType
Entity
name: EStringmany: EBoolean
Property
types
extends
properties
type
grammar org.xtext.webinar.Entity
! with org.eclipse.xtext.common.Terminals
generate entity
"http://www.xtext.org/webinar/Entity"
Model:
! (elements+=Type)*;
Type:
! SimpleType | Entity;
SimpleType:
! 'type' name=ID;
Entity:
! 'entity' name=ID
('extends' extends=[Entity])? '{'
! ! properties+=Property*
! '}';
Property:
! 'property' name=ID ':'
type=[Type] (many?='[]')?;
entity
*
*
Model
name: EStringType
SimpleType
Entity
name: EStringmany: EBoolean
Property
types
extends
properties
type
grammar org.xtext.webinar.Entity
! with org.eclipse.xtext.common.Terminals
generate entity
"http://www.xtext.org/webinar/Entity"
Model:
! (elements+=Type)*;
Type:
! SimpleType | Entity;
SimpleType:
! 'type' name=ID;
Entity:
! 'entity' name=ID
('extends' extends=[Entity])? '{'
! ! properties+=Property*
! '}';
Property:
! 'property' name=ID ':'
type=[Type] (many?='[]')?;
my WritePimp
Whitespace-Aware
Languages
Incremental Generation
Conclusion
Abstraction
Use a DSL to develop your tools
SupportNewsgroupCommunity ForumProfessional Support
@HBehrenshttp://HeikoBehrens.net
[email protected]://www.xing.com/profile/Heiko_Behrenshttp://www.linkedin.com/in/HeikoBehrens
twitterblog
mailxinglinkedin
www.xtext.org@XtexttwitterThe Committer Team
JanKöhnlein
DennisHübner
MoritzEysholdt
PeterFriese
MichaelClay
PatrickSchönbach
KnutWannheden
SebastianZarnekow
HeikoBehrens
Sven Efftinge