Shapes - an introductory MPS tutorial

Skip to end of metadata
Go to start of metadata

If you're new to MPS and want to try it out quickly, this is the right tutorial for you. Within two hours you'll get a new language and functional code that uses that language. In the tutorial you'll start from scratch and by walking along a safe and a convenient path you'll design the core elements of a new language. We'll avoid advanced concepts, complicated constructs and dark corners in order to get to the finish line quickly. At the end you'll know what MPS is all about and what principles it builds on.

So, please fasten your seat belts. We're in for a fast ride.

You may also watch this tutorial on-line as a screen-cast. Please check it out.

Prerequisities

We'll assume you've gone through the initial parts of the Fast Track to MPS Tutorial and so are familiar with the MPS environment, understand the concept of a language and a solution and can command the MPS projectional editor. If, not, please consider spending the first 30 minutes or so of your time to check it out. Especially these keyboard shortcuts are key to your survival:

  • Control + Space - to complete an incomplete word or to turn an invalid (red) identifier into a correct (black) one
  • Alt + Enter - to display a pop-up menu with handy options applicable at the current editor position
  • Control/Cmd + Up-Arrow - Expand the region of selected text
  • Tab - to navigate around editable elements in the editor
  • Control/Cmd + Z - Undo

We also assume you've installed MPS and you have it running in front of you. So now we can set off.

Cheating allowed
The result of this tutorial has been bundled as a sample project with MPS distributions. If you get stuck at any moment, feel free to open it from your home folder/MPSSamples or after downloading it (an MPS 3.0 compatible version here) in MPS and look at the implementation. Try to avoid copy-pasting, though, since you wouldn't learn much that way. (BTW, the sample also contains additional features explained in the Building an interpreter cookbook, which you can have some fun with.)

Goal

You're going to implement a sample language for specifying graphical shapes. The language will allow its users to lay out visual two-dimensional shapes on a flat canvas. The definition will be then translated into a Java Swing application, which will visualize the layout on the screen.


The language could enable non-programmers to build Java applications without any knowledge of Java, Swing or the 2D Graphics API. You, on the other hand, will play the role of a language designer, who will prepare such an easy-to-use language building on his or her knowledge of Java. You automate the work of a UI programmer by providing a language and a generator that cover some of the cases that a UI programmer currently has to solve manually.
If you are not versed in Java or Swing, do not worry too much too early. We assumed this possibility and made the tutorial to guide you carefully. You'll be able to pass easily, you'll see.

Create a new project

We'll start with a fresh project. On the welcome screen you click on Create New Project and then follow the wizard.


You'll get an empty project containing and empty Language Definition and an empty Solution.


Solutions hold your programs. To write these programs, you use languages, either defined in the same project or imported ones. In our tutorial will first define a language and than use it to write code that we can execute.

Languages and programs under the hood

First, here's a bit of background knowledge that you should know before going on.

The language that we're building must allow for painting definitions, which consist of individual commands, each on a separate line and each defining a single shape to draw. Our language needs to cover each such command with a Concept. Concepts define the abstract syntax of a language, i.e. the set of allowed language logical constructs. A program then consists of Abstract Syntax Trees, which hold instances of these Concepts.

The AST of the short program above shows the abstract syntax - it consists of Nodes, each Node is an instance of a Concept that the language defines. Concepts define properties, children and references and Nodes then give the concrete values.

To wrap up the little theory here:

  • Languages consist of Concepts
  • Concepts define the logical (abstract) elements with their properties, children and references
  • Programs (solutions) consist of ASTs, which consist of Nodes
  • Nodes are instances of Concepts giving concrete values to propertieschildren and references of their Concepts

Graphical shape

Our language is going to be pretty straightforward. We'll need only a few concepts:

  • Canvas - to define the top-most node representing the whole Painting definition and holding all Shapes
  • Shape - representing a command to draw a shape on the Canvas, it will serve as a common super-concept to all concrete shapes, such as a circle or a square
  • Circle - representing a command to draw a circle
  • Square - representing a command to draw a square
  • ColorReference - representing one of several pre-defined Colors defined in java.awt.Color

We'll start our practical exercise with the Shape concept. Just like in object-oriented programming, Concepts can extends one another and thus inherit the capabilities of their super-concept. The Shape concept will serve as such a common super-concept, holding the color property, since all shapes in our language will need the color property, so we can conveniently inherit it.

Right-click on the Structure aspect of the language and create a new Concept. You'll get a new Concept definition open in the editor.

We should give the Concept a descriptive name, Shape in our case will work fine.

We created the Shape concept as a common super-concept for all shapes in our language and by itself Shape will not be used directly in ASTs. We'll mark Shape abstract to indicate explicitly that no instances (nodes) of Shape can be created.

If you misspelled the name of the concept and decide to change it, MPS will prompt you, whether you want to do a proper rename re-factoring, in order to update all possible references and usages of the concept in other parts of your code. Since we've just started and have no references to Shape, just hit the "No" button.

It is the time to practice the Alt + Enter keyboard shortcut now.


When positioned on the name of the Concept, the Alt + Enter keyboard shortcut brings up a contextual pop-up menu, which gives you the option to apply the "Make Abstract" intention to the Concept. Once you choose that option, the concept is marked as abstract. Now we could add properties, children and references that should be shared by all sub-concepts of Shape, but we will leave that for later to make our learning curve flat.

That's our first concept! Hurray! Time to add another one. How about Circle? Sure, by right-clicking on the Structure aspect we create another Concept and give it a name - Circle.

Circle

Following the same steps, you need to right-click on the Structure aspect of the language and add a new concept. Name it Circle.

Circles should inherit capabilities from Shape, so we need to indicate than in the extends clause. Position the cursor at the beginning of the cell holding the "BaseConcept" text and hit the most useful keyboard shortcut in MPS - Control + Space - to invoke code completion.

MPS will show you a list of options applicable as replacements for the "BaseConcept" text. We need to select Shape here to make Circle extend Shape.

So this is our first concrete concept that will be used by users of our language. To give the concept a nice textual representation in code-completion dialogs and enable MPS to be smart about creating an instance of Circle whenever the user types "circle", you need to give the concept an alias 'circle'.

Notice the Tab key can help you navigate around editable elements in the editor. Use the Tab key frequently.

Each circle needs to specify its coordinates on the screen and its radius. We'll create properties that will hold these integer values. Navigate to the properties section, place the cursor on the "<< ... >>" symbol that represents an empty collection of values and press Enter. This will create an empty property.


You give the property a name "x", then hit Tab and provide the type of the property - "integer". While typing "integer" you may hit the Control + Space keyboard shortcut to have the name of the type completed by MPS.


Then add properties for "Y" and "radius" and you'll be done with your first concrete concept.

Square

Now you should try yourself to create a concept for square. Simply repeat the steps we did for Circle, just create different properties - upperLeftX and upperLeftY to hold the coordinates of the upper-left corner followed by size to specify the length of the sides of the square. Ultimately you should get to this:

Canvas

Having created two shapes we can move on defining a concept to hold them all in a painting. We'll create another concept, called Canvas, that will represent a scene composed of shapes. The user will be able to create multiple scenes (Canvasses), which will be mutually independent and will not share shapes. Each Canvas will hold a name and a list of shapes that it contains.

So yet again right-click on the Structure aspect of the language to create a new concept and give it a name Canvas.

Notice that unlike Circle or SquareCanvas does not extend Shape, but BaseConceptCanvas is not supposed to be drawn nor placed on other Canvasses, so it should not extend Shape.

Navigate (using Tab) to the "<none>" text in the implements section and specify INamedConcept and the name of the Concept Interface that Canvas will implement. Concept Interfaces can also add new capabilities to Concepts that implements them. INamedConcept in our case enriches Canvas with the name property, so Canvas instances (called Nodes) will have a name property so the user can easily distinguish between them.


Since Canvas will represent the painting scene and will by itself not be part of any other concept, we mark it that instances can be root. This will allow Canvas instances to be the roots of ASTs.

To indicate that Canvasses may hold Shapes, we'll create a child collection of shapes. Again, hit Enter in the "<< ... >>" cell of the children section, type in the name of the child and the type of nodes it may hold. Do not forget about the* *Control + Space key shortcut to bring up the code-completion dialog.

You should end up with a concepts definition like this:

Now we've created enough concepts to have a minimalistic language ready for use. Let's give it a try.

An early test ride

We need to build the language before we can use it. In the future, after you've made a change to your language definition, please remember to repeat the process of "Rebuild" in so that the change could take effect. Right-click on the top-most node (the very most root, representing the whole project, including the Language and the Solution) in the Project View and choose Rebuild Project.

Once built, the language will be available for use in the model inside the sandbox Solution. Simply create a new Canvas by right-clicking on the model and choosing the root concept to instantiate. Notice, the root concepts are those that show up in this menu and can be instantiated at the top-most level within a model.

Now give the Canvas instance a name and you've got your first painting. You can start using the concepts defined in the language to place visual shapes on the canvas.





The editing experience of our language as well as the layout is at this stage using some defaults, which we will change soon to get more customized look of the code.

Again, Control + Space is the  keyboard shortcut to use heavily here. Use it each time you hesitate what to time. Notice that the code-completion dialog offers us the two types of shapes that we have created in the language.

You can insert additional shapes by pressing Enter at the end of the last shape in the list of shapes, which in our case is the closing "}" symbol for the "circle".

Cool! I hope you're celebrating your success properly. Now, how about tuning those editors to make the code look better on the screen?

Editors

MPS uses a projectional editor. You might have noticed that already by feeling that the editor behaves slightly differently from what you would have expected. Unlike text-based languages, MPS never represents code as plain text. Instead, code in MPS means AST - Abstract Syntax Tree. Always.

This comes with huge benefits for language design, language composability and non-parseable notations, which you can read more about in the MPS documentation.

Here we should focus on the editing aspect of projectional languages. Since plain-text editors cannot represent ASTs reliably and since editing ASTs directly would be highly inconvenient, projectional languages have to provide editors for all Concepts that are part of the language. Sometimes even multiple alternative editors for a single Concept, but that's not our goal here. MPS can do a good job in many cases to provide a default editor for Concepts that do not have one. This is very nice for language prototyping. For convenient use by an end-user we, however, should spend some time preparing an explicit editor.

Shape

The Shape concept does not need an editor, since it is an abstract concept. So we will leave this one untouched.

Circle

For Circle we could create an editor that nicely places all necessary properties on a single line. We'll open the Circle concept in the editor, click the green '+' button in the lower left corner and pick Editor -> Concept Editor.

You'll get an empty editor definition for the Circle concept. Remember, Control + Space will be needed heavily to edit things here.

The editor for a concept in MPS consists of visual cells, each of which represents some piece of information belonging to the underlying concept. As nodes are composed hierarchically in an AST, so are their editors composed on the screen with sub-nodes' editors nested inside their ancestors' ones.

For Circle, we'd like to show value of all the properties (x, y, radius) plus some arbitrary text around them, all on a single line.
So first we'll choose a layout for these cells - indent layout will work just fine here. So hit Control + Space in the red cell and pick it up fem the list. You may speed the search up by typing a square bracket followed by a minus symbol.

Now type "circle" to enter constant text that will be placed at the beginning of the line.

Hit Enter to create a new cell. Type "x:" to mark that the following cell contains the value of the x property.

Now you have to pick the x property from the code-completion menu to bind the cell to the right property.

Now you can continue on your own to insert cells holding constant text and well as values of the y and radius properties. Remember, Enter will insert new cells, Control + Space will bring up the code-completion menu. You should end up with an editor like this.

Square

Square also needs an editor. Open the Square concept in the editor, hit the '+' symbol in the lower left corner and create a new Concept Editor. Following the instructions in the previous section, enter the editor definition as follows:


You can certainly do this on your own.

Canvas

The editor for the Canvas concept will be slightly different, since it spreads across multiple lines and holds a collection of child nodes. You, however, start in the same way as before, open the Canvas concept, hit the '+' symbol to create a new Concept Editor, insert an indent layout and enter some text to get the following:


Now bind the red cell to the name property. Since the property is inherited from the _INamedConcept _concept interface, it will be further down in the list, but it is surely there.


The following cell will hold a collection of shapes that have been added to the Canvas. Note how we compose editors here - Canvas only marks an area, in which individual shapes should be edited, and leaves that up to the shapes how they use that area.
Since the shapes should be organized vertically, each on a single line, you should pick vertical collection layout from the completion menu.


You need to bind the red cell to the shapes child collection of Canvas.



Now, to place the collection below the name of the Canvas, you should use the Alt + Enter shortcut the bring up the intention pop up and pick "Add On New Line".


You'll get the final editor definition:

Second run

Now you can rebuild the project again (right-click on the very top node in the Project View) and look at the sandbox code in  MyDrawing.


Look how the code layout changed:

It is the same code (AST), but it is organized on the screen differently.

Coloring

Now we should come back to Shape and add support for colors. Since both Circle and Square extend Shape, they will both inherit the color.

If for any reason you need to jump ahead to the generator, now is a good time to do so. You'll just need to ignore all the places where we refer to colors in the generator chapter.

Since we would like to allow the users of our language to pick the color for the shape from a list of pre-defined colors, we can't just use a textual property to hold the color value. Instead we'll add a reference to Shape and have this reference point to one of the pre-defined color constants defined in Java's java.awt.Color class. I picked the java.awt.Color class, because it saves us the effort of defining color constants ourselves. We simply reuse the already existing constants defined in the class.

New concept

First, we'll create a new concept to represent a color in our code. Now you most likely know how to do this:

ColorReference keeps a reference (a pointer going across the AST hierarchy) pointing to a single static field declaration. Why pointing to a static field declaration? Because the color constants on java.awt.Color are declared as StaticFieldDeclarations.

You may try yourself opening the java.awt.Color (Control/Cmd + N, type "Color", maybe also check the "Include stubs and non-project models" checkbox) class and check that, for example, the "white" constant is a StaticFieldDeclaration.

We'll specify in the constraints later that these static field declarations should be only the static field declarations defined in the java.awt.Color class.

In order to display and edit the colors we also need an editor for ColorReference.


The reference should simply display the name of the constant that it refers to, so we pick color from the code-completion menu and specify that the name property of the color constant is what we want to show to the user.

You should get an editor definition as follows:

Constraints

The ColorReference concepts needs to constrain the list of possible targets, so that it only points to the color constants (static field declarations) of the java.awt.Color class. We need to create the Constraints aspect for ColorReference now.



In the "reference constraints" we need to place our constraints on scope of the possible reference targets.

Pick the "reference scope" option from the code-completion menu:



Now, you'll need to type in code that returns a Scope containing all the possible targets for the color reference. Only the targets in the Scope will be allowed to be entered and will be used to populate the code-completion menu for possible colors. For our concrete example, we need to obtain all static fields of the java.awt.Color class and wrap them with a Scope instance, ListScope in our case.

First, however, let's sort out the imports, because in MPS your models have to explicitly import models that they want to use. The set of imports, or Dependencies, for a model or module is displayed by Alt + Enter on the model or module in the Project View on the left-hand side.

Dependencies

Display the dependencies of our Language:


Hit Alt + Enter and you'll get a dialog with the properties of the Language. Now switch to the Dependencies tab. It is empty, but needs to contain the following dependencies:


You need to add them using the '+' button. In the small search dialog type a few characters of the name of the desired dependency to narrow down the search and hit Enter whet you locate the correct one. Also, note the Extends type of dependency on jetbrains.mps.baseLanguage.


Now we'll set dependencies for the Constraints aspect of the language:


Again, hit Alt + Enter and ensure it contains all the following dependencies:


Finally, we'll set dependencies of our Solution, so we can safely refer to Java color constants from within our code:


You'll need to add a dependency on the JDK module:

Finalizing the scope constraint

Now with all the dependencies in scope, we can enter the code that retrieves all the color constants and stores them in the scope. Get back to the Constraints definition for ColorReference and enter the code:


Create a new instance of ListScope. ListScope is a very straightforward scope implementation that builds a scope for the supplied collection of nodes. Our scope will hold all the StaticFieldDeclarations found in the java.awt.Color class.


Implement its getName method so that it casts the child parameter to StaticFieldDeclaration and returns its name.


Now specify the parameter to the constructor of ListScope. The parameter must be a collections of nodes to build the scope for. The node/Color/ construct represents the model element (a node) that stands for the java.awt.Color class.

If you cannot type the code that we propose in the screen-shots, most likely you have not properly set the dependencies in the section above. Please, revisit those.


The code above obtains a Node representing the declaration of the java.awt.Color class and calls its staticFields() method, which returns all static fields of the class. This is then wrapped by ListScope and returned from the link-scope function. Now ColorReference can only point to color constants.

Updating Shape

The Shape concept is a good place to put our new ColorReference, since both Circle and Square will inherit it.


Shape may also define an editor component to define the editor for the color and Circle as well as Square will be able to reuse that editor component in their editors thus avoiding duplication.

Hit the '+' symbol and create a new Editor Component.



The editor component definition a language that you are already familiar with and so you will be easily able to specify an indent layout, define a constant text cell followed by a cell bound to the color property.

Embedding the editor component

The editor component defined for Shape should now be added into the editors for Circle and Square.




The editor component becomes just another cell in the editor's layout.

A third run

Now it is the best time to rebuild the language. Right-click on the Language node in the Project View and select "Rebuild Language".

If you rebuilt the whole project instead of just the Language here, you'd get a couple of errors, since the Solution now misses the newly defined color properties. We'll add those instantly.

Opening the MyDrawing program should show you the empty cells for colors in red. Try Control + Space in them and you will get a list of colors that you can pick from.



Now our language is fully defined. We can create a Canvas and add Circles and Squares to it, specifying their positions, sizes and colors. That's quite an achievement for such a short time.

What we're missing yet is the translation of these programs into Java, so that we could run them and see the shapes nicely drawn on the screen. If you continue you'll soon realize that we're almost there.

Generator

Our language now needs a generator so that we could generate Java code and run it. Our generator will be very straightforward and will only need a few rules and a single mapping configuration.

Your language already contains a skeleton of an empty generator. You can open the mapping configuration, that will specifying what rule to apply when. We will be adding configuration entries here gradually.

Here's the idea behind the generator that we will implement:

  • A Canvas gets translated into a Java class, which will extend Java's JFrame class and hold a JPanel that all the shapes will be drawn on
  • Each Shape gets translated into a method call on the Graphics object to draw the shape on the JPanel
  • A ColorReference gets translated into a reference to the appropriate color constant in the java.awt.Color class.

Let's start with the class for Cancas. You need to add a new entry in the root mapping roles, since Canvas is a root concept.

This will indicate that Canvas nodes should be replaces with a Java class. You need to use Alt + Enter the be able to select "New Root Template" from the pop-up menu.

The root template should be generating a Java class, so you need to pick "class".

This is the finishes root mapping rule. The map_Canvas is a name of the root template that was created in the generator. You should open it up so we could make changes to it.

First we need to set the dependencies of the generator module and the generator model as specified below:


Remember, Alt + Enter will bring up the properties of the node selected in the left-hand Project View.

With the dependencies you can start typing the Java code that will be part of the generated Java class. We will then parametrize the code with values from the Canvas, to make it reflect the user's intent.

The class needs to extend JFrame.

Now you'll add the main method to get a runnable Java class. You can use the "psvm" live template to enter the method quickly.

Inside the method, we'll need to instantiate map_Canvas.

We'll also need a method to initialize the frame. Type "method" and use Control + Space to complete the method definition:

Call the method Initialize() and make sure that the method is called from main.

Notice the canvas.initialize() call in the main() method. Have you added it?

All the shapes will be drawn on a JPanel, so we now need to add one as a field.

Notice, we use an anonymous inner class to be able to customize the JPanel a bit.

Important: To create an anonymous inner class in BaseLanguage, position your cursor right after new JPanel() and before the ending semicolon. Then hit the "{" (left curly brace) key and MPS will add the ending "}" symbol. Now keep the cursor between these "{" and "}" symbols to add methods to the panel's anonymous inner class.

We'll override the paintComponent method of JPanel, because this is the method where Java allows us to easily draw the shapes on the JPanel. Hit Control/Cmd + O, while the cursor is inside the JPanel's anonymous class body between the "{" and "}" symbols, to invoke the Override method dialog for the JPanel and select the paintComponent method.

Make sure your paintComponent() method is correctly nested inside the JPanel's anonymous inner class as displayed in the screen-shots. Also make sure it is called paintComponent, not paintComponents.

Enter the following code into the method:

Now, please, fill in the initialize method and we have a template ready:

Parametrizing the template

The template currently holds no values specified by the user. The properties and children of Canvas should be inserted into the template through macros. MPS gives you three types of macros*:*

  • property macros - to insert properties from the input model
  • node macros - to replace nodes in the template with nodes from the input model
  • reference macros - to adjust references in the template to point to nodes in the input model

We'll gradually use all of these.

To start with, we'll customize the name of the generated class and the Title of the frame with the name of the Canvas. Place the cursor on the name of the class - map_Canvas and hit Alt + Enter.



Now select the node.name property macro from the pop-up menu.

The "map_Canvas" text is now wrapped (annotated) with a property macro, which changes the name property to the name of the Canvas. The Inspector panel (Alt + 2) can be used to enter or modify the property macro, as well. Currently it returns the value of node.name, which is the name of the current Canvas.

Now you can wrap the "Title" text to customize the title of the frame. Using the Control/Cmd + Up Arrow key shortcut select the text "Title" without the surrounding " characters and with Alt + Enter insert the correct property macro:

The code should now look like this:

Drawing shapes

Our template assumes the code that draws shapes should be placed inside the paintComponent method of the JPanel field. The statement "System.out.println("Draw here");" serves as a placeholder for the real code that will draw all shapes. We will use the COPY_SRC macro to replace the placeholder statement with a statement that draws a single shape and we'll leverage the LOOP macro to repeat that for all shapes defined in the current Canvas.

Now, please select the placeholder statement including its closing semicolon using the Control/Cmd + Up Arrow key shortcut, hit Alt + Enter and choose the appropriate Node macro option to insert a LOOP macro to loop through all the child Shapes of the current Canvas.

Again, the Inspector (Alt + 2) shows the binding code.


The LOOP macro will repeat the "System.out.println("Draw here");" statement for each shape listed in node.shapes. We, however, need to have the "System.out.println("Draw here");" statements replaced with code that draws each of these shapes. The COPY_SRC macro will do just that. Please, select the statement again, hit Alt + Enter and choose Node macro.

Type in COPY_SRC (Control + Space) and you get a macro that will replace "System.out.println("Draw here");" with the current shape for all shapes that the LOOP macro provides.


Generating circles

Now we get Canvas to be translated into a Java class and we also made a place for Shapes to add the code that will draw them. The time is up for us to define the actual translation rules for Shapes themselves, so that we get a "graphics.drawCircle()" method inserted in the generated code as a replacement for the Circle shape. You need to open the main mapping configuration and add a new entry to the "reduction rules" section:


Alt + Enter to create a new template:

The new reduct_Circle template will show up in the left-hand Project View.


You may also create a reduction rule for Square:


Open the reduce_Circle template. We now need to specify the Java code that will replace Circles. Remember, that the Java code will be placed in map_Canvas inside the paintComponent method.

First we'll enter a BlockStatement, that will wrap our template:

We will need a local variable of the Graphics type as a place holder for the paintComponent parameter of the same name. And yet again a BlockStatement.


To draw a circle in Java, we'll use the Graphics object to set the color first and that invoke its drawOval method. Please enter the code below:



Then use Control/Cmd / Up Arraw to select the inner BlockStatement, hit Alt + Enter and pick "Create Template Fragment" from the menu. This will mark the selected fragment of the code as the actual template, which will eventually be placed into map_Canvas

Parametrization

The code now needs to be parametrized with actual values from the Circle node.

The first value "10" should be replaced with the x coordinate of the Circle node. A property macro will do that. Similarly the second value "10" should be replaced with the y coordinate.

The third and fourth values "10" should both be replaced with the radius value of the circle node.

The drawOval() method's third and fourth parameters specify the oval's width and height, respectively. Since we want to draw a circle, we need to provide the same values for both arguments. This is why the property macros for the third and fourth parameters both specify node.radius.

Finally, the "Color.red" color placeholder reference should be replaced with the actual target of the color reference of the Circle node. We will use a reference macro to replace references. Please put the cursor on the "red" word and hit Alt + Enter.

Now specify the target StaticFieldDeclaration that the reference should point to: node.color.color in the Inspector (Alt + 2).

  • node - the currently generated Circle
  • color - the color child reference of the current Circle node (a ColorReference node)
  • color - the color reference defined in the ColorReference node, pointing to the a concrete static field declaration within the java.awt.Color class. This reference is the one that will replace the placeholder "red" value.

Reducing Squares

Identically provide the following code for the reduce_Square template.

The values passed into "drawRect" should be replaced with property macros with the upperLeftX, upperLeftY and size properties of the Square.

The drawRect() method's third and fourth parameters specify the rectangle's width and height, respectively. Since we want to draw a square, we need to provide the same values for both arguments. This is why the property macros for the third and fourth parameters both specify size.


Generating code

Now we're done defining the generator. If you rebuild the language, open MyDrawing, right-click it and choose "Preview Generated Text",

 
you'll get a nicely structured Java code that properly initializes a JFrame and draws all the shapes:

Running the code

It is nice to see generated code, but you might actually prefer seeing it running. MPS can compile and run generated Java code easily. We only need to indicate that Canvas is generated into a runnable Java class and thus Canvas itself should be treated as runnable, or as a "main" class. We only need to make Canvas implement the IMainClass interface and MPS will take care of the rest. The IMainClass interface comes from the jet brains.mps.execution.util language and so we need to add it to the list of dependencies of our language:

Use the Alt + Enter to get the properties dialogs. Notice that the language needs to be marked as Extends.

The Convas concept can now have the IMainClass interface added to the implements section.


Rebuild the language and then right-click on MyDrawing in the Project View and click "Run".


You will get a running Java application with your drawing on it as a reward for your efforts.

What to do next

Congratulations! You've just completed your introductory tutorial into MPS.

Now you can continue on your own adding more shapes to the language. Point, Line, Triangle, Rectangle or shapes with colorful fills might be nice additions to our little language.

If you want to understand MPS more thoroughly, it might be a good time to try the in-depth Calculator Tutorial, which explores many of the advanced concepts and will teach you much more about code generation, type-system and scoping.

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Jun 01, 2014

    Anonymous

    Thanks for this very nice tutorial! :-)

Add Comment