blob: 9795010fa3f694797917027aa248308309fc7fe1 [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Xtext - Little Tortoise</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description"
content="The website of Eclipse Xtext, an open-source framework for development of programming languages and domain-specific languages">
<meta name="author" content="Sven Efftinge">
<meta name="author" content="Miro Spoenemann">
<!-- styles -->
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<!-- Le fav and touch icons -->
<link rel="shortcut icon" href="/Xtext/images/favicon.png">
<link href="/Xtext/css/bootstrap.css" rel="stylesheet" type='text/css'>
<link href="/Xtext/css/bootstrap-responsive.css" rel="stylesheet" type='text/css'>
<link href="/Xtext/css/shield-responsive.css" rel="stylesheet" type='text/css'>
<link href='/Xtext/css/fonts.css' rel='stylesheet' type='text/css'>
<link href="/Xtext/css/prettyPhoto.css" rel="stylesheet" media="screen" type='text/css'>
<link href="/Xtext/css/prettify.css" type="text/css" rel="stylesheet"/>
<link href="/Xtext/css/style.css" rel="stylesheet" type='text/css'>
<!-- cover flow -->
<link href="/Xtext/css/coverflow.css" rel="stylesheet" type='text/css'>
<!--[if lt IE 9]>
<link href="/css/iebugs.css" rel="stylesheet" type='text/css'>
<![endif]-->
</head>
<body>
<!-- Google Tag Manager -->
<noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-TGDS5S"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-TGDS5S');</script>
<!-- End Google Tag Manager -->
<header class="site-header">
<!-- Navbar -->
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="btn btn-navbar" data-toggle="collapse"
data-target=".nav-collapse"> <span class="icon-bar"></span> <span
class="icon-bar"></span> <span class="icon-bar"></span>
</a> <a class="brand" href="/Xtext/index.html"></a>
<div class="nav-collapse collapse" style="height: 0px;">
<ul class="nav">
<!--li ><a href="/Xtext/news.html">News</a></li-->
<li ><a href="/Xtext/download.html">Download</a></li>
<li ><a href="/Xtext/documentation/index.html">Documentation</a></li>
<li ><a href="/Xtext/community.html">Community</a></li>
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">Support &amp; Trainings<span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="https://www.itemis.com/en/xtext/support-and-team/" target="_blank">itemis</a></li>
<li><a href="https://typefox.io/trainings-2" target="_blank">TypeFox</a></li>
</ul>
</li>
<li ><a href="http://xtend-lang.org">Xtend</a></li>
</ul>
<!--div class="nav pull-right">
<li ><a><iframe src="https://ghbtns.com/github-btn.html?user=eclipse&repo=xtext&type=star&count=true" frameborder="0" scrolling="0" width="170px" height="20px"></iframe></a></li>
</div-->
</div>
<!--/.nav-collapse -->
</div>
</div>
</div>
<!-- Navbar End -->
</header>
<div class="page-content">
<script>
function startSearch(event) {
if (event.keyCode == 13) {
var q = 'site:eclipse.org/Xtext/documentation+' + event.target.value;
window.open('https://www.google.com/search?q=' + q, "_self");
}
}
</script>
<div class="wrapper">
<div id="page">
<div class="inner">
<div id="maincontainer" class="container">
<span class="edit-on-github pull-right">
<a href="https://github.com/eclipse/xtext/edit/website-published/xtext-website/documentation/208_tortoise.md">Edit on Github</a>
</span>
<div class="span3" style="margin-left: 0px;">
<div class="search-bar">
<img src="/Xtext/images/search-gray.png"/>
<input type="search" id="google-search" onkeyup="startSearch(event);"/>
</div>
<ul id="nav-outline" style="margin-left: 0px;">
<li class="nav-part">Getting Started</li>
<li><a href="102_domainmodelwalkthrough.html">15 Minutes Tutorial</a></li>
<li><a href="103_domainmodelnextsteps.html">15 Minutes Tutorial - Extended</a></li>
<li><a href="104_jvmdomainmodel.html">Five simple steps to your JVM language</a></li>
<!--li class="nav-part">Seven JVM Languages Built With Xbase</li>
<li><a href="201_sevenlang_introduction.html">Introduction</a></li>
<li><a href="202_scripting.html">Scripting Language</a></li>
<li><a href="203_builddsl.html">Build Language</a></li>
<li><a href="204_mongodb.html">DSL for MongoDB</a></li>
<li><a href="205_guice.html">DSL for Guice</a></li>
<li><a href="206_httprouting.html">Http Routing Language</a></li>
<li><a href="207_template.html">Template Language</a></li>
<li><a href="208_tortoise.html">Little Tortoise</a></li-->
<li class="nav-part">Reference Documentation</li>
<li><a href="301_grammarlanguage.html">The Grammar Language</a></li>
<li><a href="302_configuration.html">Configuration</a></li>
<li><a href="303_runtime_concepts.html">Language Implementation</a></li>
<li><a href="305_xbase.html">Integration with Java</a></li>
<!--li><a href="306_mwe2.html">MWE2</a></li-->
<!--li><a href="307_special_languages.html">Typical Language Configurations</a></li-->
<li><a href="308_emf_integration.html">Integration with EMF</a></li>
<li><a href="310_eclipse_support.html">Eclipse Support</a></li>
<!--li><a href="320_idea_support.html">IntelliJ IDEA Support</a></li-->
<li><a href="330_web_support.html">Web Editor Support</a></li>
<li><a href="350_continuous_integration.html">Continuous Integration</a></li>
<!--li class="nav-part">Appendix</li>
<li><a href="401_migrating_from_1_0_x.html">Migrating from Xtext 1.0.x to 2.0</a></li>
<li><a href="402_migrating_from_0_7.html">Migrating from Xtext 0.7.x to 1.0</a></li-->
</ul>
</div>
<div class="span8 doc-contents">
<h1 id="tortoise">Little Tortoise</h1>
<p>Do you remember the programming language <a href="http://en.wikipedia.org/wiki/Logo_%28programming_language%29">Logo</a>? Logo was used in computer science classes to teach children how to program. In fact, it was a adaptation of LISP! But the remarkable part was the so-called <em>turtle</em>, a graphical cursor that can be given commands to move and turn, thereby drawing lines.</p>
<p><img src="images/tortoise_screenshot.png" alt="" /></p>
<p>The goal is a language to control a turtle drawing an image. Technically, this example will teach you how to adapt and use the <a href="https://github.com/eclipse/xtext-extras/blob/master/org.eclipse.xtext.xbase/src/org/eclipse/xtext/xbase/interpreter/impl/XbaseInterpreter.java">XbaseInterpreter</a> for your own languages.</p>
<h2 id="tortoise-solution">Overview</h2>
<p>We have built a language that allows to define <em>Programs</em> and <em>SubPrograms</em>. Each of these has a body, which can contain any number of expressions. In addition to the standard Xbase expressions, we are able to issue commands to the tortoise. Here is an example explaining the concepts in comments:</p>
<pre><code class="language-tortoise">// Program: Haus vom Nikolaus
begin
val length = 150 // local variable
val diagonal = length * sqrt(2) // all Math.* methods are available
lineWidth = 2 // assignment of a property
square(length) // call to a SubProgram
turnRight(45) // call to a command method
lineColor = blue // all ColorConstants.* are available
forward(diagonal)
turnLeft(90)
lineColor = red
forward(diagonal / 2)
turnLeft(90)
forward(diagonal / 2)
turnLeft(90)
lineColor = blue
forward(diagonal)
end // main program
sub square // a subprogram
int length // parameter
begin
for (i : 1..4) { // loop-expression from Xbase
forward(length)
turnRight(90)
}
end // sub square
</code></pre>
<p>The main trick about our solution is to <em>not</em> bake in the turtle commands into the language itself, but define it in the runtime library. This way, the language stays as slim as can be and additions can be easily added without the need to regenerate the whole language infrastructure.</p>
<p>The core of the runtime library is the class <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.tortoiseshell.lib/src/org/xtext/tortoiseshell/lib/Tortoise.java">Tortoise</a>. You can think of it as of our only domainmodel class: It keeps the current state of the tortoise and allows modifying it using methods. Here is an excerpt of its code:</p>
<pre><code class="language-xtend">class Tortoise {
double angle
double x
double y
@Accessors int delay = 200
boolean isPaint = true
@Accessors int lineWidth
@Accessors Color lineColor
List&lt;ITortoiseEvent.Listener&gt; listeners = newArrayList()
...
</code></pre>
<p>When a method changes the state of the tortoise, an event is thrown. These events are consumed by a GEF based view and turned into animations of a <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.tortoiseshell.lib/src/org/xtext/tortoiseshell/lib/view/TortoiseFigure.xtend">TortoiseFigure</a>. This loose coupling of model and view allows for easier testing.</p>
<h2 id="tortoise-running">Running the Example</h2>
<p>In the runtime Eclipse, open the <em>Tortoise View</em> (<em>Window → Show View → Other → Xtext → TortoiseView</em>). Then open one of the example files in <em>org.eclipse.xtext.tortoiseshell.examples</em>. The <em>Program</em> is interpreted on editor activation and on save. An additional toggle button <em>Step Mode</em> in the <em>Tortoise View</em> allows to execute the code live from the editor up to the caret’s current line.</p>
<p><img src="images/Pythagoras.png" alt="Tortoise takes a rest after running the Pythagoras example" /></p>
<h2 id="tortoise-grammar">Grammar</h2>
<p>The grammar is very short. Once again, we inherit from the Xbase language to have nice Java integration and rich expressions. A user can define a <em>Program</em>, which can have <em>SubPrograms</em> with parameters. The <em>Executable</em> rule is never called, but defines a common supertype for <em>Program</em> and <em>SubProgram</em> that will hold their common member <code>body</code>. A <em>Body</em> is an <em>XBlockExpression</em> from Xbase, but with the keywords <code>begin</code> and <code>end</code> instead of the curly braces.</p>
<pre><code class="language-xtext">grammar org.xtext.tortoiseshell.TortoiseShell
with org.eclipse.xtext.xbase.Xbase
import "http://www.eclipse.org/xtext/xbase/Xbase"
generate tortoiseShell "http://www.xtext.org/tortoiseshell/TortoiseShell"
Program :
body=Body
subPrograms+=SubProgram*;
SubProgram:
'sub' name=ValidID (':' returnType=JvmTypeReference)?
(parameters += FullJvmFormalParameter)*
body=Body;
Body returns XBlockExpression:
{XBlockExpression}
'begin'
(expressions+=XExpressionInsideBlock ';'?)*
'end';
Executable:
Program | SubProgram;
</code></pre>
<h2 id="tortoise-inferrer">Translation to Java</h2>
<p>With the tortoise commands defined as methods in the runtime library class <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.tortoiseshell.lib/src/org/xtext/tortoiseshell/lib/Tortoise.java">Tortoise</a>, we have to infer a Java class that inherits from this. Within this class, we create a method for each <em>Program</em> and <em>SubProgram</em>. The resulting code looks like this:</p>
<pre><code class="language-xtend">class TortoiseShellJvmModelInferrer extends AbstractModelInferrer {
public static val INFERRED_CLASS_NAME = 'MyTortoiseProgram'
@Inject extension JvmTypesBuilder
def dispatch void infer(Program program,
IJvmDeclaredTypeAcceptor acceptor,
boolean isPreIndexingPhase) {
acceptor.accept(program.toClass(INFERRED_CLASS_NAME))[
superTypes += typeRef(Tortoise)
if(program.body != null)
members += program.toMethod("main", typeRef(Void.TYPE)) [
body = program.body
]
for(subProgram: program.subPrograms)
members += subProgram.toMethod(subProgram.name,
subProgram.returnType ?: inferredType(subProgram.body)) [
for(subParameter: subProgram.parameters)
parameters += subParameter.toParameter(subParameter.name, subParameter.parameterType)
body = subProgram.body
]
]
}
}
</code></pre>
<h2 id="tortoise-interpreter">Interpreter</h2>
<p>The Xbase language library does not only provide a compiler that generates Java code, but also an <a href="https://github.com/eclipse/xtext-extras/blob/master/org.eclipse.xtext.xbase/src/org/eclipse/xtext/xbase/interpreter/impl/XbaseInterpreter.java">interpreter</a>. This has been adapted to execute our <em>Programs</em>.</p>
<p>After all an interpreter is just a big visitor. For each expression type, it has an evaluation method, that recursively calls the evaluation methods for the subexpressions for its arguments. The methods also pass an execution context storing all temporary state such as local variables.</p>
<p>The first thing we have to cope with is the mixture of existing Java methods (from the super class <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.tortoiseshell.lib/src/org/xtext/tortoiseshell/lib/Tortoise.xtend">Tortoise</a>) and inferred ones. While the former are evaluated via Java reflection, we need special treatment for the latter. The idea is to bind an instance of <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.tortoiseshell.lib/src/org/xtext/tortoiseshell/lib/Tortoise.xtend">Tortoise</a> to <code>this</code> and intercept calls to the inferred methods to execute them directly. This is accomplished by overriding the method <code>invokeOperation</code>:</p>
<pre><code class="language-xtend"> @Inject extension IJvmModelAssociations
override protected invokeOperation(JvmOperation operation,
Object receiver,
List&lt;Object&gt; argumentValues) {
val executable = operation.sourceElements.head
if (executable instanceof Executable) {
val context = createContext
context.newValue(QualifiedName.create("this"), tortoise)
operation.parameters.forEach[p, i|
context.newValue(QualifiedName.create(p.name), argumentValues.get(i))
]
val result = evaluate(executable.body, context, CancelIndicator.NullImpl)
if(result.exception != null)
throw result.exception
result.result
} else {
super.invokeOperation(operation, receiver, argumentValues)
}
}
</code></pre>
<p>One thing you have to know about the Java inferrence is that when creating Java elements using the <a href="https://github.com/eclipse/xtext-extras/blob/master/org.eclipse.xtext.xbase/src/org/eclipse/xtext/xbase/jvmmodel/JvmTypesBuilder.java">JvmTypesBuilder</a>, the infrastructure stores the information which elements have been inferred from which source elements. To navigate these traces, we use the Xbase service <a href="https://github.com/eclipse/xtext-extras/blob/master/org.eclipse.xtext.xbase/src/org/eclipse/xtext/xbase/jvmmodel/IJvmModelAssociations.java">IJvmModelAssociations</a>. So to detect whether a <a href="https://github.com/eclipse/xtext-extras/blob/master/org.eclipse.xtext.common.types/emf-gen/org/eclipse/xtext/common/types/JvmOperation.java">JvmOperation</a> is inferred, we check whether it has a source element. If so, we have to setup an execution context binding <code>this</code> and the parameters as local variables and then execute the method’s body using the interpreter.</p>
<p>To start the interpretation we have to do almost the same: Setup the execution context and then evaluate the <em>Program’s</em> body. The respective code is</p>
<pre><code class="language-xtend"> override run(Tortoise tortoise, EObject program, int stopAtLine) {
if(tortoise != null &amp;&amp; program != null) {
this.tortoise = tortoise
this.stopAtLine = stopAtLine
try {
program.jvmElements.filter(JvmOperation).head
?.invokeOperation(null, #[])
} catch (StopLineReachedException exc) {
// ignore
}
}
}
</code></pre>
<p>The <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.tortoiseshell/src/org/xtext/tortoiseshell/interpreter/TortoiseShellInterpeter.xtend#L74">StopLineReachedException</a> is part of the <em>Step Mode</em>. It is thrown when the execution reaches the line <code>stopAtLine</code>, thus terminating the current execution. The throwing code is</p>
<pre><code class="language-xtend"> override protected internalEvaluate(XExpression expression,
IEvaluationContext context,
CancelIndicator indicator) {
val line = NodeModelUtils.findActualNodeFor(expression)?.startLine
if(line-1 == stopAtLine)
throw new StopLineReachedException
super.internalEvaluate(expression, context, indicator)
}
</code></pre>
<h2 id="tortoise-additions">Literal Classes</h2>
<p>To make the static methods and fields of <a href="http://docs.oracle.com/javase/8/docs/api//java/lang/Math.html">Math</a> and <a href="https://github.com/eclipse/gef/blob/master/org.eclipse.draw2d/src/org/eclipse/draw2d/ColorConstants.java">ColorConstants</a> callable directly, we provided the <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.tortoiseshell/src/org/xtext/tortoiseshell/scoping/TortoiseShellImplicitlyImportedFeatures.xtend">TortoiseShellImplicitlyImportedFeatures</a>:</p>
<pre><code class="language-xtend">class TortoiseShellImplicitlyImportedFeatures extends ImplicitlyImportedFeatures {
override protected getStaticImportClasses() {
(super.getStaticImportClasses() + #[Math, ColorConstants])
.toList
}
}
}
</code></pre>
<p>To overcome a small issue in the interpreter we also had to implement the <a href="https://github.com/xtext/seven-languages-xtext/blob/master/languages/org.xtext.tortoiseshell/src/org/xtext/tortoiseshell/scoping/TortoiseShellIdentifiableSimpleNameProvider.xtend">TortoiseShellIdentifiableSimpleNameProvider</a>.</p>
<hr />
<p><strong><a href="301_grammarlanguage.html">Next Chapter: The Grammar Language</a></strong></p>
</div>
</div>
</div>
</div>
</div>
</div>
<footer class="site-footer">
<div id="extra">
<div class="inner">
<div class="container">
<div class="row">
<div class="span6">
<h3 class="footer-links-header">Quick Links</h3>
<ul class="footer-links clearfix">
<li><a href="http://www.eclipse.org/legal/privacy.php">Privacy Policy</a></li>
<li><a href="http://www.eclipse.org/legal/termsofuse.php">Terms of Use</a></li>
<li><a href="http://www.eclipse.org/legal/copyright.php">Copyright Agent</a></li>
<li><a href="http://www.eclipse.org/legal/">Legal</a></li>
</ul>
<ul class="footer-links clearfix">
<li><a href="http://www.eclipse.org">Eclipse Home</a></li>
<li><a href="http://marketplace.eclipse.org/">Market Place</a></li>
<li><a href="http://live.eclipse.org/">Eclipse Live</a></li>
<li><a href="http://www.planeteclipse.org/">Eclipse Planet</a></li>
</ul>
</div>
<div class="span6">
<h3 class="footer-links-header"><a href="https://twitter.com/xtext" style="color: white;">@Xtext</a> on Twitter</h3>
<a class="twitter-timeline" href="https://twitter.com/xtext" data-widget-id="346625441290928128"
data-chrome="noheader nofooter transparent"
data-theme="dark">Tweets by @xtext</a>
<script>
!function(d,s,id) {
var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';
if(!d.getElementById(id)) {
js=d.createElement(s);
js.id=id;
js.src=p+"://platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js,fjs);
}
}(document,"script","twitter-wjs");
</script>
</div>
</div>
</div>
</div>
</div>
<a href="#" class="scrollup fadeOutRight animated" style="display: none;">ScrollUp</a>
<!-- Le javascript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="/Xtext/js/jquery-1.11.3.min.js"></script>
<script src="/Xtext/js/bootstrap.min.js"></script>
<script src="/Xtext/js/jquery.easing.1.3.js" type="text/javascript"></script>
<script src="/Xtext/js/jquery.prettyPhoto.js" type="text/javascript"></script>
<script src="/Xtext/js/twitter.js" type="text/javascript"></script>
<script src="/Xtext/js/prettify.js" type="text/javascript"></script>
<script src="/Xtext/js/lang-xtend.js" type="text/javascript"></script>
<script src="/Xtext/js/lang-common.js" type="text/javascript"></script>
<script src="/Xtext/js/custom.js" type="text/javascript"></script>
<script src="https://apis.google.com/js/platform.js" async defer></script>
<!--script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push([ '_setAccount', 'UA-2429174-3' ]);
_gaq.push([ '_trackPageview' ]);
(function() {
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl'
: 'http://www')
+ '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();
</script-->
<script src="/Xtext/js/coverflow.min.js" type="text/javascript"></script>
<script>
$(function() {
$('#coverflow').coverflow({
active : 1,
visibleAside: 2,
overlap : 0.5,
scale : 0.9,
angle : 20,
trigger : {
"itemfocus" : true,
"swipe" : true,
"mousewheel" : false
}
});
$('#coverflow :hidden').toggle();
$(window).resize(function() {
$('#coverflow').coverflow();
});
});
</script>
</footer>
</body>
</html>