SlideShare a Scribd company logo
Moritz Eysholdt, itemis AG
Serializing EMF
models with Xtext
Xtextcon 2014!
Kiel, Germany
Agenda
❖ Parsing vs. Serializing!
❖ Use Cases!
❖ Generating vs. Serializing!
❖ Challenges!
❖ The Contract!
❖ Architecture!
❖ Hooks!
❖ Advice
The New and the Old Serializer
org.eclipse.xtext.serializer	
•The New Serializer. This presentation is about it.
org.eclipse.xtext.parsetree.reconstr	
•The Old Serializer. Don’t used it.!
•Cryptic Error Messages!
•Bad Performance for Large Models!
•marked @Deprecated
Parser vs. Serializer
XtextResource
ModelModelTextual!
Model
Parser
Serializer
load()
save()
Parser vs. Serializer
XtextResource
ModelModelTextual!
Model
AST
Parser
Serializer
load()
save()
Use Cases
Quickfix
Refactoring
Non-Textual !
Editors
Non-Textual!
Persistence
Generators
Use Cases
Quickfix
Refactoring
Non-Textual !
Editors
Non-Textual!
Persistence
Generators
Use Cases
Quickfix
Non-Textual !
Editors
Non-Textual!
Persistence
Generators
Refactoring
Use Cases
Quickfix
Non-Textual!
Persistence
Generators
Refactoring
Non-Textual !
Editors
EMF Store
<?XML?>
<?XMI?>
Use Cases
Quickfix
Non-Textual!
Persistence
Generators
Refactoring
Non-Textual !
Editors
Read
Transform
Serialize
Generate, Transpile, Compile, Migrate…
Why Serialize?
❖ object model usually easier to modify than
its textual representation!
❖ guaranteed syntactical correctness!
❖ automatic handling of comments!
❖ automatic handling of whitespace
…when I have Xtend, THE language for generators
When not to Serialize
❖ Avoid serializing models that are broken due
to parse errors.!
❖ Template languages are simpler when
writing out large text chunks that never
change.
The hard Contract
Parsing a serialized Model must result !
in a model equal to the original.
Model original = createModel()	
String serialized = serialize(original)	
Model parsed = parse(serialized)	
assertEqual(original, parsed)
…enables textual models as persistence format
The soft Contract
Serializing a model that has been modified after parsing!
should only change the smallest number of characters necessary.
…keep diffs small
String originalDoc = loadDocument()	
Model parsed = parse(originalDoc)	
Model modified = applyModifications(parsed)	
String newDoc = serialize(modified)	
int numberOfChars = diff(originalDoc, newDoc).size()
1. Keep textual diffs small.!
2. Strictly comply with the semantic model!
3. Loosely comply with the node model
Challenges
❖ unassigned elements!
❖ ambiguous mapping from EStructuralFeatures to Assignments!
❖ ambiguous mapping from EClasses to ParserRules and Actions
…because the AST is actually abstract, as in “lack of information”
API Frontends #1
package org.eclipse.xtext.resource;	
!
public class XtextResource extends ResourceImpl {	
!
	 (…)	
!
	 public void save(Map<?, ?> options) 	
	 	 throws IOException {}	
!
	 public final void save(OutputStream outputStream, Map<?, ?> options) 	
	 	 throws IOException {}	
}
API Frontends #2
package org.eclipse.xtext.serializer;	
!
@ImplementedBy(Serializer.class)	
public interface ISerializer {	
	 public String serialize(EObject obj);	
	 public String serialize(EObject obj, SaveOptions options);	
	 public void serialize(EObject obj, Writer writer, SaveOptions options)	
	 	 throws IOException;	
	 public ReplaceRegion serializeReplacement(EObject obj, SaveOptions options);	
}
Options:!
- format: !
- true: format all!
- false: format regions without whitespace information!
- validate: don’t use, it’s an old algorithm.
Before Serialization
Create one State Machine per Context per EClass
Root:	
	 "optional"? ID children+=(List | Path);	
!
List returns Child:	
	 "list" item+=ID ("," item+=ID)*;	
!
Path returns Child:	
	 "path" seg=ID ({Segment.parent=current} seg=ID)+;
Context EClass
Root Root
List Child
Path Segment
Path_Segment_2_0 Child
Path_Segment_2_0 Segment
During Serialization #1
Find right state machines and !
find right path through them
Root:	
"optional"? ID children+=(List | Path);	
!
List returns Child:	
"list" item+=ID ("," item+=ID)*;
Context EClass
Root Root
Context EClass
List Child
optional Foo list a, b, cOutput:
Architecture #3
Context EClass
Root Root
Context EClass
List Child
Context EClass
Root Root
Context EClass
List Child
Semantic!
assigned grammar elements
Syntactic!
unassigned grammar elements
optional Foo list a, b, cOutput:
Architecture #4: Observer
Semantic!
Sequencer
Syntactic!
Sequencer
HiddenToken!
Sequencer
Formatter Writer
Events
Listens To
assigend!
* RuleCalls!
* Terminals!
* DataTypes!
* Keywords!
* CrossRefs
unassigned!
* RuleCalls!
* Terminals!
* DataTypes!
* Keywords
* whitespace!
* comments
modifies!
whitespace
writes to !
stream
Output: /*X*/ Foo list a
Serializer HiddenTokenSequencerSyntacticSequencerSemanticSequencer
createSequence
(Root, Root)
enter
AssignedParserRuleCall
(children=List)
enter
AssignedParserRuleCall
(children=List)
createSequence
(List, Children)
accept
UnassignedRuleCall
(ID)
accept
AssignedTerminalRuleCall
(itemi=ID)
accept
Keyword
("list")
accept
AssignedTerminalRuleCall
(itemi=ID)
leave
AssignedParserRuleCall
(children=List)
leave
AssignedParserRuleCall
(children=List)
Formatter Writer
enterAssignedParserRuleCall()
acceptWhitespace()
acceptWhitespace()
acceptComment(/*X*/)
acceptUnassignedRuleCall()
acceptKeyword()
acceptWhitespace()
acceptAssignedTerminalRuleCall()
acceptWhitespace()
enterAssignedParserRuleCall()
Root:	
"optional"? ID children+=(List | Path);
List returns Child:	
"list" item+=ID ("," item+=ID)*;
SerializerFragment
fragment = serializer.SerializerFragment auto-inject {	
generateStub = true	
// generateDebugData = true	
}
•SerializerFragment not required to use serializer!!
•Only purpose of SerializerFragment is to generate convenience API!
•generateDebugData to generate pretty state machine diagrams (graphviz dot)
Hooks #1: ITransientValueService
•selectively exclude model-objects and -values from serialization!
•transient == NOT serialized!
•Default setting from Ecore model: EStructuralFeature.isTransient()
public interface ITransientValueService {	
!
	 enum ListTransient {	
	 	 NO, SOME, YES	
	 }	
!
	 enum ValueTransient {	
	 	 NO, PREFERABLY, YES	
	 }	
!
	 public ListTransient isListTransient(EObject semanticObject, EStructuralFeature feature);	
!
	 public boolean isValueInListTransient(EObject semanticObject, int index, EStructuralFeature feature);	
!
	 public ValueTransient isValueTransient(EObject semanticObject, EStructuralFeature feature);	
}
Hooks #2: Token Serialization
public interface ICrossReferenceSerializer {	
	 boolean isValid(EObject context, CrossReference crossref, EObject target, INode node);	
	 String serializeCrossRef(EObject context, CrossReference crossref, EObject target, INode node);	
}
public interface IKeywordSerializer {	
	 boolean isValid(EObject context, Keyword keyword, Object value);	
	 String serializeAssignedKeyword(EObject context, Keyword keyword, Object value, INode node);	
}
public interface IValueSerializer {	
	 boolean isValid(EObject context, RuleCall ruleCall, Object value);	
	 String serializeAssignedValue(EObject context, RuleCall ruleCall, Object value, INode node);	
}
public interface IEnumLiteralSerializer {	
	 boolean isValid(EObject context, RuleCall ruleCall, Object value);	
	 String serializeAssignedEnumLiteral(EObject context, RuleCall ruleCall, Object value, INode node);	
}
Hooks #3: SemanticSequencer
class SerSipSemanticSequencer extends AbstractSerSipSemanticSequencer {	
!
	 @Inject SerSipGrammarAccess grammarAccess;	
!
	 /**	
	 * Constraint:	
	 * (children+=Path | children+=List)	
	 */	
	 override protected sequence_Root(EObject context, Root semanticObject) {	
	 	 val feeder = createSequencerFeeder(semanticObject)	
	 	 for (child : semanticObject.children) {	
	 	 	 if (child.seg != null) {	
	 	 	 	 feeder.accept(grammarAccess.rootAccess.childrenPathParserRuleCall_2_0_0, child)	
	 	 	 } else {	
	 	 	 	 feeder.accept(grammarAccess.rootAccess.childrenListParserRuleCall_2_0_1, child)	
	 	 	 }	
	 	 }	
	 	 feeder.finish()	
	 }	
}	
Root:	
	 "optional"? ID children+=(Path | List);	
!
List returns Child:	
	 "list" item+=ID ("," item+=ID)*;	
!
Path returns Child:	
	 "path" seg=ID ({Segment.parent=current} seg=ID)+;
Hooks #4: SyntacticSequencer
class SerSipSyntacticSequencer extends AbstractSerSipSyntacticSequencer {	
!
	 /**	
	 * terminal ID 		 : '^'?('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*;	
	 */	
	 override getIDToken(EObject semanticObject, RuleCall ruleCall, INode node) {	
	 	 if (node != null)	
	 	 	 return getTokenText(node);	
	 	 return "";	
	 }	
!
	 /**	
	 * Syntax:	
	 * 'optional'?	
	 */	
	 override emit_Root_OptionalKeyword_0_q(EObject semanticObject, ISynNavigable transition, List<INode> nodes) {	
	 	 acceptNodes(transition, nodes);	
	 }	
}
Root:	
	 "optional"? ID children+=(Path | List);
Understanding Ambiguities
•A grammar is ambiguous for the serializer if there can be more
than one textual syntax for a given model.!
!
•A grammar is ambiguous for the parser if there can be more than
one model for a given textual syntax1.
R1:	
foo=ID | bar=ID;
R2:	
(“foo” val=ID) | 	
(“bar” val=ID);
Does “a” parse
to or ?foo=a bar=b
Does serialize to
“foo b” or “bar b”?
val=b
parseserialize
1. that is not completely correct. A grammar is ambiguous when there is more than one path to
consume a given input. That, however, usually leads to different models.
Understanding Ambiguities
Root: 	
(foos=Foo | bars=Bar)*;	
!
Foo: 	
“foo” foo=ID;	
!
Bar: 	
“bar” bar=ID;
(1) . 	
(2) bar b foo a	
(3) foo a bar b
Root
foo=a bar=b
Does (1) serialize
to (2) or (3)?
Avoiding Ambiguities
Analysis: Grammars can be ambiguous for the serializer.!
!
Example:
Root: 	
(foos=Foo | bars=Bar)*;	
!
Foo: 	
“foo” foo=ID;	
!
Bar: 	
“bar” bar=ID;
Root: 	
members+=Member;	
!
Member:	
Foo | Bar;	
!
Foo: 	
“foo” foo=ID;	
!
Bar: 	
“bar” bar=ID;
•Single “member” list to maintain order!
•“Foo” and “Bar” extend “Member”!
•implement getFoo() and getBar() as filters on getMembers()
Avoid Unserializeable Models
The Xtext grammar can imply constraints on your model. !
!
Serialization is only possible if the model complies with theses
constraints because the grammar does not define syntax for models that
don’t comply. Examples:
GRAMMAR CONSTRAINT
R1: name=ID; name != null
R2: (name=ID | title=STING); (name != null) ^ (title != null)
R3: items+=ID+; items.size() >= 1
R4: (a+=ID b+=ID)*; a.size() == b.size()
Avoid Unserializeable Models
Solution:!
!
a) Ensure your grammar does not imply constraints. !
b) Ensure your TransientValueService prevents constraint violations.!
Implement Xtext Validation to enforce constraints.!
!
This will also improve your error messages but may make content assist
too chatty.
RESTRICTIVE SAFE
R1: name=ID;
R1: name=ID?;	
R1: (name=ID | “?”);
R2: (name=ID | title=STING); R2: name=ID? title=STING?;
R3: items+=ID+; R3: items+=ID*;
R4: (a+=ID b+=ID)*;
R4: (a+=ID b+=ID)*	
(“spareA={“ a+=ID+ “}”)?	
(“spareB={“ b+=ID+ “}”)?;
Configure Your Scope Right
You can (and need) to configure global scoping for a ResourceSet!
XtextLiveScopeResourceSetProviderXtextResourceSetProvider
ResourceSet
Dirty Editor State
Index
(default) (what you need)
<shadows>
<shadows>
Dirty Editor State
Index
<shadows>
Happy Serializing!

More Related Content

PDF
Xtext's new Formatter API
PDF
Xtext beyond the defaults - how to tackle performance problems
PDF
Deep dive into Xtext scoping local and global scopes explained
KEY
Test-Driven Development of Xtext DSLs
PDF
Support NodeJS avec TypeScript Express MongoDB
PDF
Java Quiz Questions
PDF
NodeJS for Beginner
PDF
Chapitre5: Classes et objets
Xtext's new Formatter API
Xtext beyond the defaults - how to tackle performance problems
Deep dive into Xtext scoping local and global scopes explained
Test-Driven Development of Xtext DSLs
Support NodeJS avec TypeScript Express MongoDB
Java Quiz Questions
NodeJS for Beginner
Chapitre5: Classes et objets

What's hot (20)

PDF
Using Xcore with Xtext
PDF
Chapitre4: Pointeurs et références
PPT
C# basics
PPTX
Nodejs functions & modules
PDF
Fundamental JavaScript [UTC, March 2014]
PPTX
PPTX
Introduction to Node.js
PPTX
Mysql creating stored function
PDF
Nodejs presentation
PPTX
Node js Introduction
PPT
programmation orienté objet c++
PDF
React Router: React Meetup XXL
PPT
Inheritance OOP Concept in C++.
PDF
PPTX
Clean Code: Chapter 3 Function
PPTX
C# in depth
PPTX
React Hooks
PPTX
Introduction to node.js
PPTX
Method Overloading in Java
PPTX
Template C++ OOP
Using Xcore with Xtext
Chapitre4: Pointeurs et références
C# basics
Nodejs functions & modules
Fundamental JavaScript [UTC, March 2014]
Introduction to Node.js
Mysql creating stored function
Nodejs presentation
Node js Introduction
programmation orienté objet c++
React Router: React Meetup XXL
Inheritance OOP Concept in C++.
Clean Code: Chapter 3 Function
C# in depth
React Hooks
Introduction to node.js
Method Overloading in Java
Template C++ OOP
Ad

Viewers also liked (20)

KEY
Codegeneration Goodies
PDF
Parsing Expression With Xtext
PDF
Future of Xtext
KEY
Xtext Best Practices
PDF
Turning Ideas Into Code Faster
PDF
Lightweight Xtext Editors as SWT Widgets
PDF
Recipes to build Code Generators for Non-Xtext Models with Xtend
PDF
The Xtext Grammar Language
KEY
EMF - Beyond The Basics
DOC
EMF Tips n Tricks
KEY
Xtend - A Language Made for Java Developers
PDF
DSLs for Java Developers
PDF
Graphical Views For Xtext With FXDiagram
PDF
Graphical Views For Xtext
PDF
Jazoon 2010 - Building DSLs with Eclipse
PPTX
Enhancing Xtext for General Purpose Languages
PDF
Eclipse DemoCamp in Paris: Language Development with Xtext
PDF
From Stairway to Heaven onto the Highway to Hell with Xtext
PDF
ARText - Driving Developments with Xtext
PDF
Xtext, diagrams and ux
Codegeneration Goodies
Parsing Expression With Xtext
Future of Xtext
Xtext Best Practices
Turning Ideas Into Code Faster
Lightweight Xtext Editors as SWT Widgets
Recipes to build Code Generators for Non-Xtext Models with Xtend
The Xtext Grammar Language
EMF - Beyond The Basics
EMF Tips n Tricks
Xtend - A Language Made for Java Developers
DSLs for Java Developers
Graphical Views For Xtext With FXDiagram
Graphical Views For Xtext
Jazoon 2010 - Building DSLs with Eclipse
Enhancing Xtext for General Purpose Languages
Eclipse DemoCamp in Paris: Language Development with Xtext
From Stairway to Heaven onto the Highway to Hell with Xtext
ARText - Driving Developments with Xtext
Xtext, diagrams and ux
Ad

Similar to Serializing EMF models with Xtext (20)

PPTX
Graph processing
PDF
Introducing JSONpedia
PDF
Scaling xtext
PPTX
Xcore meets IncQuery: How the New Generation of DSLs are Made
PDF
Turmeric SOA Cloud Mashups
PPTX
MathWorks Interview Lecture
PDF
Eclipse Modeling Framework (EMF) and Graphical Modeling Framework (GMF)
PDF
Graph processing - Powergraph and GraphX
ODP
SCDJWS 6. REST JAX-P
PDF
IQPC Canada XML 2001: How to Use XML Parsing to Enhance Electronic Communication
PPTX
Parsing XML & JSON in Apex
ODP
Graphs are everywhere! Distributed graph computing with Spark GraphX
PDF
RepreZen DSL: Pushing the limits of language usability with XText
PDF
RAPID - Building a highly usable API Design language with XText
PDF
Andrea Iacono - Graphs are everywhere!
PDF
ESUG 2025: Pharo 13 and Beyond (Stephane Ducasse)
PDF
sbt, history of JSON libraries, microservices, and schema evolution (Tokyo ver)
KEY
Combining Text and Graphics in Eclipse-based Modeling Tools
PPTX
ExtraV - Boosting Graph Processing Near Storage with a Coherent Accelerator
PDF
ApacheCon 2000 Everything you ever wanted to know about XML Parsing
Graph processing
Introducing JSONpedia
Scaling xtext
Xcore meets IncQuery: How the New Generation of DSLs are Made
Turmeric SOA Cloud Mashups
MathWorks Interview Lecture
Eclipse Modeling Framework (EMF) and Graphical Modeling Framework (GMF)
Graph processing - Powergraph and GraphX
SCDJWS 6. REST JAX-P
IQPC Canada XML 2001: How to Use XML Parsing to Enhance Electronic Communication
Parsing XML & JSON in Apex
Graphs are everywhere! Distributed graph computing with Spark GraphX
RepreZen DSL: Pushing the limits of language usability with XText
RAPID - Building a highly usable API Design language with XText
Andrea Iacono - Graphs are everywhere!
ESUG 2025: Pharo 13 and Beyond (Stephane Ducasse)
sbt, history of JSON libraries, microservices, and schema evolution (Tokyo ver)
Combining Text and Graphics in Eclipse-based Modeling Tools
ExtraV - Boosting Graph Processing Near Storage with a Coherent Accelerator
ApacheCon 2000 Everything you ever wanted to know about XML Parsing

Recently uploaded (20)

PDF
NewMind AI Monthly Chronicles - July 2025
PDF
Encapsulation_ Review paper, used for researhc scholars
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
PDF
Empathic Computing: Creating Shared Understanding
PPT
Teaching material agriculture food technology
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Unlocking AI with Model Context Protocol (MCP)
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
PDF
Building Integrated photovoltaic BIPV_UPV.pdf
PDF
cuic standard and advanced reporting.pdf
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PPTX
A Presentation on Artificial Intelligence
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Review of recent advances in non-invasive hemoglobin estimation
PPTX
Big Data Technologies - Introduction.pptx
DOCX
The AUB Centre for AI in Media Proposal.docx
NewMind AI Monthly Chronicles - July 2025
Encapsulation_ Review paper, used for researhc scholars
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
Empathic Computing: Creating Shared Understanding
Teaching material agriculture food technology
Advanced methodologies resolving dimensionality complications for autism neur...
Reach Out and Touch Someone: Haptics and Empathic Computing
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Diabetes mellitus diagnosis method based random forest with bat algorithm
Unlocking AI with Model Context Protocol (MCP)
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Dropbox Q2 2025 Financial Results & Investor Presentation
Building Integrated photovoltaic BIPV_UPV.pdf
cuic standard and advanced reporting.pdf
The Rise and Fall of 3GPP – Time for a Sabbatical?
A Presentation on Artificial Intelligence
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Review of recent advances in non-invasive hemoglobin estimation
Big Data Technologies - Introduction.pptx
The AUB Centre for AI in Media Proposal.docx

Serializing EMF models with Xtext

  • 1. Moritz Eysholdt, itemis AG Serializing EMF models with Xtext Xtextcon 2014! Kiel, Germany
  • 2. Agenda ❖ Parsing vs. Serializing! ❖ Use Cases! ❖ Generating vs. Serializing! ❖ Challenges! ❖ The Contract! ❖ Architecture! ❖ Hooks! ❖ Advice
  • 3. The New and the Old Serializer org.eclipse.xtext.serializer •The New Serializer. This presentation is about it. org.eclipse.xtext.parsetree.reconstr •The Old Serializer. Don’t used it.! •Cryptic Error Messages! •Bad Performance for Large Models! •marked @Deprecated
  • 11. Why Serialize? ❖ object model usually easier to modify than its textual representation! ❖ guaranteed syntactical correctness! ❖ automatic handling of comments! ❖ automatic handling of whitespace …when I have Xtend, THE language for generators
  • 12. When not to Serialize ❖ Avoid serializing models that are broken due to parse errors.! ❖ Template languages are simpler when writing out large text chunks that never change.
  • 13. The hard Contract Parsing a serialized Model must result ! in a model equal to the original. Model original = createModel() String serialized = serialize(original) Model parsed = parse(serialized) assertEqual(original, parsed) …enables textual models as persistence format
  • 14. The soft Contract Serializing a model that has been modified after parsing! should only change the smallest number of characters necessary. …keep diffs small String originalDoc = loadDocument() Model parsed = parse(originalDoc) Model modified = applyModifications(parsed) String newDoc = serialize(modified) int numberOfChars = diff(originalDoc, newDoc).size() 1. Keep textual diffs small.! 2. Strictly comply with the semantic model! 3. Loosely comply with the node model
  • 15. Challenges ❖ unassigned elements! ❖ ambiguous mapping from EStructuralFeatures to Assignments! ❖ ambiguous mapping from EClasses to ParserRules and Actions …because the AST is actually abstract, as in “lack of information”
  • 16. API Frontends #1 package org.eclipse.xtext.resource; ! public class XtextResource extends ResourceImpl { ! (…) ! public void save(Map<?, ?> options) throws IOException {} ! public final void save(OutputStream outputStream, Map<?, ?> options) throws IOException {} }
  • 17. API Frontends #2 package org.eclipse.xtext.serializer; ! @ImplementedBy(Serializer.class) public interface ISerializer { public String serialize(EObject obj); public String serialize(EObject obj, SaveOptions options); public void serialize(EObject obj, Writer writer, SaveOptions options) throws IOException; public ReplaceRegion serializeReplacement(EObject obj, SaveOptions options); } Options:! - format: ! - true: format all! - false: format regions without whitespace information! - validate: don’t use, it’s an old algorithm.
  • 18. Before Serialization Create one State Machine per Context per EClass Root: "optional"? ID children+=(List | Path); ! List returns Child: "list" item+=ID ("," item+=ID)*; ! Path returns Child: "path" seg=ID ({Segment.parent=current} seg=ID)+; Context EClass Root Root List Child Path Segment Path_Segment_2_0 Child Path_Segment_2_0 Segment
  • 19. During Serialization #1 Find right state machines and ! find right path through them Root: "optional"? ID children+=(List | Path); ! List returns Child: "list" item+=ID ("," item+=ID)*; Context EClass Root Root Context EClass List Child optional Foo list a, b, cOutput:
  • 20. Architecture #3 Context EClass Root Root Context EClass List Child Context EClass Root Root Context EClass List Child Semantic! assigned grammar elements Syntactic! unassigned grammar elements optional Foo list a, b, cOutput:
  • 21. Architecture #4: Observer Semantic! Sequencer Syntactic! Sequencer HiddenToken! Sequencer Formatter Writer Events Listens To assigend! * RuleCalls! * Terminals! * DataTypes! * Keywords! * CrossRefs unassigned! * RuleCalls! * Terminals! * DataTypes! * Keywords * whitespace! * comments modifies! whitespace writes to ! stream
  • 22. Output: /*X*/ Foo list a Serializer HiddenTokenSequencerSyntacticSequencerSemanticSequencer createSequence (Root, Root) enter AssignedParserRuleCall (children=List) enter AssignedParserRuleCall (children=List) createSequence (List, Children) accept UnassignedRuleCall (ID) accept AssignedTerminalRuleCall (itemi=ID) accept Keyword ("list") accept AssignedTerminalRuleCall (itemi=ID) leave AssignedParserRuleCall (children=List) leave AssignedParserRuleCall (children=List) Formatter Writer enterAssignedParserRuleCall() acceptWhitespace() acceptWhitespace() acceptComment(/*X*/) acceptUnassignedRuleCall() acceptKeyword() acceptWhitespace() acceptAssignedTerminalRuleCall() acceptWhitespace() enterAssignedParserRuleCall() Root: "optional"? ID children+=(List | Path); List returns Child: "list" item+=ID ("," item+=ID)*;
  • 23. SerializerFragment fragment = serializer.SerializerFragment auto-inject { generateStub = true // generateDebugData = true } •SerializerFragment not required to use serializer!! •Only purpose of SerializerFragment is to generate convenience API! •generateDebugData to generate pretty state machine diagrams (graphviz dot)
  • 24. Hooks #1: ITransientValueService •selectively exclude model-objects and -values from serialization! •transient == NOT serialized! •Default setting from Ecore model: EStructuralFeature.isTransient() public interface ITransientValueService { ! enum ListTransient { NO, SOME, YES } ! enum ValueTransient { NO, PREFERABLY, YES } ! public ListTransient isListTransient(EObject semanticObject, EStructuralFeature feature); ! public boolean isValueInListTransient(EObject semanticObject, int index, EStructuralFeature feature); ! public ValueTransient isValueTransient(EObject semanticObject, EStructuralFeature feature); }
  • 25. Hooks #2: Token Serialization public interface ICrossReferenceSerializer { boolean isValid(EObject context, CrossReference crossref, EObject target, INode node); String serializeCrossRef(EObject context, CrossReference crossref, EObject target, INode node); } public interface IKeywordSerializer { boolean isValid(EObject context, Keyword keyword, Object value); String serializeAssignedKeyword(EObject context, Keyword keyword, Object value, INode node); } public interface IValueSerializer { boolean isValid(EObject context, RuleCall ruleCall, Object value); String serializeAssignedValue(EObject context, RuleCall ruleCall, Object value, INode node); } public interface IEnumLiteralSerializer { boolean isValid(EObject context, RuleCall ruleCall, Object value); String serializeAssignedEnumLiteral(EObject context, RuleCall ruleCall, Object value, INode node); }
  • 26. Hooks #3: SemanticSequencer class SerSipSemanticSequencer extends AbstractSerSipSemanticSequencer { ! @Inject SerSipGrammarAccess grammarAccess; ! /** * Constraint: * (children+=Path | children+=List) */ override protected sequence_Root(EObject context, Root semanticObject) { val feeder = createSequencerFeeder(semanticObject) for (child : semanticObject.children) { if (child.seg != null) { feeder.accept(grammarAccess.rootAccess.childrenPathParserRuleCall_2_0_0, child) } else { feeder.accept(grammarAccess.rootAccess.childrenListParserRuleCall_2_0_1, child) } } feeder.finish() } } Root: "optional"? ID children+=(Path | List); ! List returns Child: "list" item+=ID ("," item+=ID)*; ! Path returns Child: "path" seg=ID ({Segment.parent=current} seg=ID)+;
  • 27. Hooks #4: SyntacticSequencer class SerSipSyntacticSequencer extends AbstractSerSipSyntacticSequencer { ! /** * terminal ID : '^'?('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')*; */ override getIDToken(EObject semanticObject, RuleCall ruleCall, INode node) { if (node != null) return getTokenText(node); return ""; } ! /** * Syntax: * 'optional'? */ override emit_Root_OptionalKeyword_0_q(EObject semanticObject, ISynNavigable transition, List<INode> nodes) { acceptNodes(transition, nodes); } } Root: "optional"? ID children+=(Path | List);
  • 28. Understanding Ambiguities •A grammar is ambiguous for the serializer if there can be more than one textual syntax for a given model.! ! •A grammar is ambiguous for the parser if there can be more than one model for a given textual syntax1. R1: foo=ID | bar=ID; R2: (“foo” val=ID) | (“bar” val=ID); Does “a” parse to or ?foo=a bar=b Does serialize to “foo b” or “bar b”? val=b parseserialize 1. that is not completely correct. A grammar is ambiguous when there is more than one path to consume a given input. That, however, usually leads to different models.
  • 29. Understanding Ambiguities Root: (foos=Foo | bars=Bar)*; ! Foo: “foo” foo=ID; ! Bar: “bar” bar=ID; (1) . (2) bar b foo a (3) foo a bar b Root foo=a bar=b Does (1) serialize to (2) or (3)?
  • 30. Avoiding Ambiguities Analysis: Grammars can be ambiguous for the serializer.! ! Example: Root: (foos=Foo | bars=Bar)*; ! Foo: “foo” foo=ID; ! Bar: “bar” bar=ID; Root: members+=Member; ! Member: Foo | Bar; ! Foo: “foo” foo=ID; ! Bar: “bar” bar=ID; •Single “member” list to maintain order! •“Foo” and “Bar” extend “Member”! •implement getFoo() and getBar() as filters on getMembers()
  • 31. Avoid Unserializeable Models The Xtext grammar can imply constraints on your model. ! ! Serialization is only possible if the model complies with theses constraints because the grammar does not define syntax for models that don’t comply. Examples: GRAMMAR CONSTRAINT R1: name=ID; name != null R2: (name=ID | title=STING); (name != null) ^ (title != null) R3: items+=ID+; items.size() >= 1 R4: (a+=ID b+=ID)*; a.size() == b.size()
  • 32. Avoid Unserializeable Models Solution:! ! a) Ensure your grammar does not imply constraints. ! b) Ensure your TransientValueService prevents constraint violations.! Implement Xtext Validation to enforce constraints.! ! This will also improve your error messages but may make content assist too chatty. RESTRICTIVE SAFE R1: name=ID; R1: name=ID?; R1: (name=ID | “?”); R2: (name=ID | title=STING); R2: name=ID? title=STING?; R3: items+=ID+; R3: items+=ID*; R4: (a+=ID b+=ID)*; R4: (a+=ID b+=ID)* (“spareA={“ a+=ID+ “}”)? (“spareB={“ b+=ID+ “}”)?;
  • 33. Configure Your Scope Right You can (and need) to configure global scoping for a ResourceSet! XtextLiveScopeResourceSetProviderXtextResourceSetProvider ResourceSet Dirty Editor State Index (default) (what you need) <shadows> <shadows> Dirty Editor State Index <shadows>