📜 ⬆️ ⬇️

Creating language extensions in RASE. Part 2. Create an expression

image Our starting point will be the project, resulting in the writing of the last article .
So, we have at our disposal a small module written in AS (two strings framed in different quotes), the language myLanguages.escapedStrings , in which there are only two automation scripts related to the Intentions aspect: one processes the string in single quotes, and the other - string in double quotes.

Screenshot

Perhaps, the implementation of this code cannot be called ideal, but it is a working example of a simple and effective solution of the task set before us: using the Intentions language, we can easily add new functionality to the editor. The same way can be implemented slightly differently, but it is the Intentions that is the fastest and easiest.
')
With a slight sadness, we look at our experimental string, flavored with a large number of slashes. On the one hand, the problem is solved, but on the other - somehow ugly, wrong. Why not suggest some simple way in which our string would remain the same, but would not cause the error "Incorrect string literal"? There is nothing easier. Recall that in some other languages ​​there is a similar functionality - for example, in C # for such there is a convenient @ "..." construction, which would be quite suitable for us to port as a language extension in ActionScript.
Do not forget that in RASE, the source code of the application (taking into account all the extensions used) is first processed by the generator, which creates the source code that the ActionScript compiler understands. That is, we will be able to work with our line unchanged, but it will only undergo automatic screening at the time of generation.


Step 1. For the work, comrades!
You must create a new expression. To do this, go to our language extension and in the structure aspect create a new concept.

Screenshot

Enter the name of the concept - EscapedString .

Screenshot

Then the question arises, which entity will it expand? To answer it, you can try a little research. If our string is enclosed in double quotes, then it is of type StringLiteral , and if in single quotes, then StringApostropheLiteral . This assumption is easily confirmed by calling the context menu of our line and selecting Go to Concept Declaration (which is equivalent to the keyboard shortcut Ctrl-Shift-S or Cmd-Shift-S on the Mac).

Screenshot

Screenshot

In order to finally determine which concept will expand our EscapedString , you can again look at the context menu of the StringLiteralBase type and look at how it looks within the framework of the object hierarchy.

Screenshot

Screenshot

We see that StringLiteralBase , for which StringLiteral and StringApostropheLiteral are children , is itself inherited from Expression , implementing the IStringLiteral interface.

Screenshot

Having studied the structure and methods of the StringLiteralBase concept, we decide to inherit from Expression with the IStringLiteral interface and get up one level with the StringLiteralBase. Go back to our unfinished root and enter (note the spelling supported by the “smart” autocomplete by Ctrl-Space ):

Screenshot

In the window with the hierarchy, the change will immediately be reflected in the displayed tree:

Screenshot

Moving on. The next step is to add an alias in the same window with our concept. It will be used for autocomplete (to add a construction to the code, the user must enter the “@” character). For the same purpose, add a shortDescription (this is the description to the right of the alias in the auto-click menu).

Screenshot


Step 2. Creating an editor
We proceed to the creation of the editor (in the next tab).

Screenshot

We create in it a visual representation of our future construction - enclosed in quotes value , before the quotes there is the symbol @

Screenshot

Constant in our screenshot is a special cell in which the plain text is stored. “Const” and “ctrl + space” will soon become your frequent action when creating an editor.

Add another cell ( Enter ) and select from the list of autocomplex “{value}”

Screenshot

Add one more constant quotation.

At this stage, the new module makes sense to compile the first time. Press Ctrl-F9 ( Cmd + F9 ) or select Build> Make Module (s) in the main menu, then go to Main () to create an example with a new line.

We enter the text starting with the @ symbol (hooray! The autocompet is already running, which was set using the alias in the Structure section at the end of the first step), but notice that our design looks somehow unexpectedly strange.

Screenshot

Screenshot


Step 3. Restoring order
To remove extra spaces, go back to the tab in which the editor was created, and pay attention to the Inspector window, in which the styles of each element appear. You may notice that each element of our design has its own set of properties.

Screenshot

Remove the space between the @ and the opening quote. To do this, select the "doggy" symbol and add the following property to the basic style:

Screenshot

In order not to bother with the next invention of the bike when making quotes, we can look at the already working code and see how it is done in the similar tab in StringLiteralBase .

Screenshot

Screenshot

There, these styles are called StringQuotationOpen and StringQuotationClose respectively . Copy them into your code. And for the properties of the value field in our source, the following structure is responsible:

Screenshot

Again we compile the language extension module and go to the Main () class. Quite another thing !.

Screenshot


Step 4. Code generation
Now it would be curious to look at the AS code in which our expression is generated. From the context menu of the editor, select Generate (obsolete)> Generate Text From Current Model (by the way, the same action is available at the bottom of the Build section of the main menu).

Screenshot

The generated code can be viewed in the lower left corner of the screen in the Output window. What do we see there?

Screenshot

The message “ TexGen not found ” means: the editor cannot find the generator corresponding to our language extension. Oh yes: we haven't written it yet.

Screenshot

Add a new generator in the same way that we created every new aspect of our expansion. Usually the name for the generator is chosen by the name of the target language, into which it will convert our code. In our case, this language will be ActionScript.

Screenshot

Obviously, the chosen name itself does not indicate its purpose, therefore, using the context menu, go to the properties of this aspect and in the lower window of the Dependencies tab (full path Generator Properties> Dependencies> Depends on Generator ) we specify its dependence on the ActionScript language.
Screenshot

In the third tab of the generator properties, we indicate at what stage in the chain of code generation of the entire project we will use it. In our case, before ActionScript.

Screenshot

The settings are set, you can proceed to the description of the generator logic. We return to the root EscapedString . Clicking on the empty field in the Generator tab, we call Template Declaration .

Screenshot

Two tabs open to our gaze: the first is called main , and the second is reduce_EscapedString . As you can guess, the first one contains the general rules for our design, and the second - direct operations on it.

Screenshot

Screenshot

Go to the reduce_EscapedString tab and insert the following construction:

Screenshot
We receive the code of a usual line.

Template Fragment (available by pressing the Ctrl-Shift-F key combination or via the Alt-Enter drop-down menu), this is a piece of code or text that we later insert into the generated code.

Screenshot

A generator template annotation appeared around our line.

Screenshot


Now we need to place the value in the generated string, for which we add the property macro to our line (we select the ready template from the intention list for node.value ):

Screenshot

Screenshot

As a result, if the module was compiled and the code was generated from Main (), we would get the following semi-finished product:

Screenshot

As we can see, the generator so far drives out normal unscreened text.

In order to add screening functionality, put the cursor on the “$” symbol and open the Inspector window. We start writing code that will change the value of our string when generating:

Screenshot
However, we will not describe the entire screening process in the property macro , since it is better to create a method at the EscapedString construction itself and call it from a macro. To create methods for concepts, the Behavior aspect is specifically designed, in which we will create the escape () method.

Step 5. The Behavior Aspect
Now we need to add the escape () method to our concept.

Using the familiar gesture through the context menu of our language myLanguages.escapedStrings , we add the Behavior aspect to the project .

Screenshot

Our task is to get a valid line at the output, with all the necessary slashes.

Screenshot

Select this. value , using the corresponding item from the refactor menu or using the keyboard shortcut Ctrl-Alt-V ( Cmd-Alt-V ), we turn it into the local variable result , which will be output by our method.

Screenshot

Screenshot

Screenshot

In order not to complicate the task, the working code will be posed from the example of Intentions given in the previous article. In the editor, for this there is a keyboard shortcut Ctrl-E ( Cmd-E ), calling the window with a list of recently opened files.

Screenshot

Without further ado, we copy lines of code from one root to another (when copying, RASE will ask us about import, but we will choose Import None ). If you created a project from scratch, and not based on a learning project, do not forget to import the Regexp language using the Ctrl-L shortcut ( Cmd-L ).

In the end, after several fixes, our source should look like this:

Screenshot

In order for the resulting code to work, it remains for us to return to the Generator aspect, where we should change the node in the Inspector . value on node. escape () .

Screenshot

Screenshot

Compile the module, generate the ActionScript and look at the result.

Screenshot


Step 6. Type for Node window
We have achieved some success in creating educational language expansion, but it is impossible not to notice that the result was a bit strange. Why? Because from our line so far a little practical use, and even if you take a closer look, in the current intermediate result, we did not get a string (type String ), but in general it is not clear what . Do not believe - see for yourself by trying to call some action first for the adjacent "normal" lines ...

Screenshot

... and then for ours:

Screenshot

You can confirm the unnatural nature of our line by calling the type system window ( Ctrl-Shift-T or Cmd-Shift-T ), informing you that our expression has no type:

Screenshot

The type system is not an easy area, especially for novice programmers. The type system creates a semantic layer that defines the rules for the interaction and mutual definition of elements. By analogy with the classification of the animal world, in which there are different entities with certain relationships, for example, "elephants", "dogs", "animals", etc. (at the same time both “elephant” and “dog” are both “animals”, but “elephant” is not a “dog”).

In order for the behavior of our string in different contexts to become more predictable (and useful), we need to bring it to the appropriate type. In order not to complicate your life, we will again go to the trick and try to peek at the properties of the typesystem aspect of the “real” line.

Opening the tab with the code of our favorite StringLiteralBase , you can find the following constructions:

Screenshot

Yeah, before us is the Intention rule , as if to say that the entity being represented is a type of String . It uses a special language Quotation ( "Citation" ), allowing you to insert, for example, code into ActionScript or MXML, thereby achieving compactness and semanticity.
We create in our educational language aspect Typesystem .

Screenshot

We import with the help of Ctrl-L (or Cmd-L ) the Quotation language into our project and reproduce the same exact logic in our language extension.

Screenshot

In the body of the method, by analogy with StringLiteralBase, we reproduce the following:

Screenshot

Then — this is important — insert the ClassifierType ...

Screenshot

By the key combination Ctrl-R ( Cmd-R ) we call import of the model.

Screenshot

Screenshot

And finally, you can add a String .

Screenshot

For reference: something like this looks like a similar code without using the Quotation extension.

Screenshot

The module can be compiled - now our entity has found its place in the ActionScript type system.

Screenshot


Step 7. Left Transformations
Meanwhile, there are still a few things that should be mentioned in this article. At the end of Step 1, we asked an alias for our expression:

Screenshot

The editor has two more important behaviors - these are left and right transformations (you can learn more about them in the introductory article about RASE). In simple terms, we must teach the editor to turn a regular line into a screened one.

Add a new aspect of Action to the language with the command transform menu actions .

Screenshot

We give this entity the name makeStringEscaped and start making edits. We are interested in the left transformation of StringLiteralBase to EscapedString .

Screenshot

Screenshot

We add a simple item and start filling out the following template:

Screenshot

As a result, we should get something like this code:

Screenshot

The project can be compiled. Now when you add the @ character to any string, it will instantly turn into an EscapedString .

Screenshot

Screenshot

Screenshot

Screenshot

It remains to add the opposite action - transformation of the EscapedString entity into the source string when deleting @ .


Step 8. Improving Editor Behavior
Go to the Editor, where we already have a certain structure:

Screenshot

It is required to add a certain keyword to it, if it were deleted, the reverse transformation would occur. Select Create new> cell action map .

Screenshot

Now you should learn how to handle the event of deleting the @ symbol. There are two ways - difficult (interception of input from the keyboard) and simple ( action map ).

Screenshot

Our choice is small, select DELETE and enter the code that would replace EscapedString with StringLiteral.

Screenshot

In the adjacent tab, select the field containing the @ symbol in order to assign an action map to it in the Inspector window.

Screenshot

To make our transformation fully workable, we add the editable: false parameter to the properties of the neighboring field (the opening quote).

Screenshot

Learning language extension is completely ready.

Screenshot

Screenshot

Screenshot

Screenshot

9. Conclusion
We hope that after reading this article, the creation of language extensions has ceased to be something unrealistic for a regular developer. You can start small and automate your work through the intenions without creating language constructs. And when there is confidence and real need, you can start writing your own languages ​​and DSL. It is only important to learn to understand simple principles of work in the MPS platform, to navigate it and find ready-made solutions in other languages. In the following articles we will try to open this topic even deeper.

Source: https://habr.com/ru/post/122650/


All Articles