<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0"
     xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xml:base="https://specificlanguages.com/">
  <channel>
    <title>How To on Specific Languages</title>
    <link>https://specificlanguages.com/categories/how/</link>
    <description>Recent content in How To on Specific Languages</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <managingEditor>sergej@specificlanguages.com (Sergej Koščejev)</managingEditor>
    <webMaster>sergej@specificlanguages.com (Sergej Koščejev)</webMaster>
    <lastBuildDate>Tue, 25 Jan 2022 09:23:38 +0100</lastBuildDate>
    
    <atom:link href="https://specificlanguages.com/categories/how/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>Writing an importer: Error handling considerations</title>
      <link>https://specificlanguages.com/posts/2022-01/25-writing-an-importer-error-handling-considerations/</link>
      <pubDate>Tue, 25 Jan 2022 09:23:38 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/25-writing-an-importer-error-handling-considerations/</guid>
      <description>&lt;p&gt;What can possibly go wrong when importing a JSON document? Several problems come to mind:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The input file can be unparseable as JSON. For example, the user has chosen a .zip file by mistake instead of .json.&lt;/li&gt;
&lt;li&gt;A valid JSON file can have unexpected structure. For example, the user has chosen a .json file with a different,
unrelated data set.&lt;/li&gt;
&lt;li&gt;The data set is correct but contains incorrect values. For example, a reference mentions an object that does not
exist, a field value is out of expected range, or some references form an unexpected cycle.&lt;/li&gt;
&lt;li&gt;The file could be fully valid but unexpectedly large.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We often want to avoid leaving the model in an inconsistent state after a failed import. While we can sometimes assume
that users are using version control and know how to roll back unintended changes, it is nicer if they don&amp;rsquo;t have to.
Our current importer logic adds the objects straight into the target model as we go through the parsed structure, and
as a result it is prone to this issue.&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/25-writing-an-importer-error-handling-considerations/"><![CDATA[ <p>What can possibly go wrong when importing a JSON document? Several problems come to mind:</p>
<ol>
<li>The input file can be unparseable as JSON. For example, the user has chosen a .zip file by mistake instead of .json.</li>
<li>A valid JSON file can have unexpected structure. For example, the user has chosen a .json file with a different,
unrelated data set.</li>
<li>The data set is correct but contains incorrect values. For example, a reference mentions an object that does not
exist, a field value is out of expected range, or some references form an unexpected cycle.</li>
<li>The file could be fully valid but unexpectedly large.</li>
</ol>
<p>We often want to avoid leaving the model in an inconsistent state after a failed import. While we can sometimes assume
that users are using version control and know how to roll back unintended changes, it is nicer if they don&rsquo;t have to.
Our current importer logic adds the objects straight into the target model as we go through the parsed structure, and
as a result it is prone to this issue.</p>
<p>We can fix this by importing objects into a <em>temporary model</em> first, and only moving them to the target model once the
full import is complete. While in theory the move could partially fail as well, in practice this is unlikely as it all
happens in memory with data that has passed validation.</p>
<p>To create a temporary model we can make use of <code>jetbrains.mps.smodel.tempmodel.TemporaryModels</code> singleton:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>TemporaryModels temporaryModels <span style="color:#f92672">=</span> TemporaryModels.<span style="color:#a6e22e">getInstance</span>();
</span></span><span style="display:flex;"><span>model tempModel <span style="color:#f92672">=</span> temporaryModels.<span style="color:#a6e22e">createReadOnly</span>(TempModuleOptions.<span style="color:#a6e22e">forDefaultModule</span>());
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">try</span> {
</span></span><span style="display:flex;"><span>  importDataIntoModel(sourceFile, tempModel);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  foreach root in tempModel.<span style="color:#a6e22e">roots</span>(<span style="color:#f92672">&lt;</span>all<span style="color:#f92672">&gt;</span>) {
</span></span><span style="display:flex;"><span>    targetModel.<span style="color:#a6e22e">add</span> <span style="color:#a6e22e">root</span>(root);
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>} <span style="color:#66d9ef">finally</span> {
</span></span><span style="display:flex;"><span>  temporaryModels.<span style="color:#a6e22e">dispose</span>(tempModel);
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>I have moved the original code into <code>importDataIntoModel</code> method that we now call to import the data into the temporary
model. After the import is successful, we add all roots to the target model. Adding a root to another model removes it
from its original model.</p>
<p>When creating a temporary model we have to choose whether it will be read-only or editable. This pertains to the ability
of the user to edit it via an editor, it is possible to modify the model via code in any case. We do not show the model
to the user and in any case we don&rsquo;t intend it to be modifiable by the user, so we choose the read-only option.</p>
<p>In the next post we will make it possible to update already existing data via import, rather than adding new objects on
each import. In the meantime, the code is <a href="https://github.com/specificlanguages/sample-importer">on GitHub</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Changing the concept of a node</title>
      <link>https://specificlanguages.com/posts/2022-01/20-changing-node-concept/</link>
      <pubDate>Thu, 20 Jan 2022 09:53:44 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/20-changing-node-concept/</guid>
      <description>&lt;p&gt;Sometimes we need to change the concept of a node during a refactoring or migration. If we simply replace the node
with another node, we will break references to the original node. How do we change the concept of a node &amp;ldquo;in place&amp;rdquo;?&lt;/p&gt;
&lt;h1 id=&#34;we-cant&#34;&gt;We can&amp;rsquo;t!&lt;/h1&gt;
&lt;p&gt;We cannot change the concept of a node directly. There are no API methods for it, and in fact it is stored in a final
field in the &lt;code&gt;SNode&lt;/code&gt; class.&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/20-changing-node-concept/"><![CDATA[ <p>Sometimes we need to change the concept of a node during a refactoring or migration. If we simply replace the node
with another node, we will break references to the original node. How do we change the concept of a node &ldquo;in place&rdquo;?</p>
<h1 id="we-cant">We can&rsquo;t!</h1>
<p>We cannot change the concept of a node directly. There are no API methods for it, and in fact it is stored in a final
field in the <code>SNode</code> class.</p>
<p>Here is what we can do instead:</p>
<ol>
<li>Replace the node with another node <em>that has the same ID</em>.</li>
<li>Move properties, references and children from the original node to the new node as needed.</li>
<li>Fix any <em>direct</em> references that we know about.</li>
</ol>
<p>A <em>direct</em> reference is a reference that internally stores a Java object reference to the target node. An <em>indirect</em>
reference instead stores only the identifiers of the target node and its model and will look up the target node object
when needed.</p>
<p>Any direct references that we don&rsquo;t know about in step 3 will get broken, but it is usually not a problem because direct
references are used only in a few specific scenarios:</p>
<ul>
<li>In the generator, if a reference macro returns a string rather than a node.</li>
<li>When a node is detached (deleted), all references in the detached subtree will be made direct.</li>
</ul>
<p>So as long as we a) do not perform these tricks in the generator (and we most probably don&rsquo;t need to), and b) fix the
reference targets of all the new descendants added in step 2, we should be safe.</p>
<h1 id="the-easy-way">The easy way</h1>
<p>MPS provides a method that performs the three steps above, <code>RefactoringRuntime#replaceWithNewConcept(originalNode, newConcept)</code> (<a href="http://127.0.0.1:63320/node?ref=528ff3b9-5fc4-40dd-931f-c6ce3650640e%2Fr%3Af69c3fa1-0e30-4980-84e2-190ae44e4c3d%28jetbrains.mps.lang.migration.runtime%2Fjetbrains.mps.lang.migration.runtime.base%29%2F7217668918201205170">MPS
link</a>).</p>
<p>This method assumes that the existing node is fully compatible with the new concept and will copy over all the
properties, references and children of the old node to the new node without checking whether they are appropriate for
the new concept. This may or may not be what you want. It will also fix the references in the descendants that point to
the original node to point to the new one.</p>
<p>If this behavior does not suit you, you can manually create a node with the specified ID and copy over the necessary
features.</p>
<h2 id="setting-the-id-of-a-node-after-creation">Setting the ID of a node after creation</h2>
<p>We cannot set the ID of a node via Open API but we can cast the <code>org.jetbrains.mps.openapi.model.SNode</code> interface to
<code>jetbrains.mps.smodel.SNode</code> class and use its <code>setId()</code> method:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>((SNode) newNode).<span style="color:#a6e22e">setId</span>(oldNode<span style="color:#f92672">/</span>.<span style="color:#a6e22e">getNodeId</span>());
</span></span></code></pre></div><p>The node must not be part of any model at the moment of the call to <code>setId()</code>.</p>
<h2 id="setting-the-id-of-a-node-in-a-quotation">Setting the ID of a node in a quotation</h2>
<p>If we create nodes using a quotation (light or full), we can use a hidden feature to specify the ID of the newly created
node. Typing a left square bracket (<code>[</code>) after a quotation will pop up two extra fields where we can specify the model
to use when creating the node and the ID of the new node.</p>
<p>(The model is a technical parameter that can be simply set to null. Specifying a model here <em>does not</em> add the node to
the model after creation, it simply asks the model to create the node, but all current model implementations create
nodes in the same way anyway.)</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/20-changing-node-concept/node-id-in-quotation.png">
    <img src="https://specificlanguages.com/posts/2022-01/20-changing-node-concept/node-id-in-quotation.png" width="640"/> </a>
</figure>

<h2 id="fixing-the-references">Fixing the references</h2>
<p>The <code>replaceWithNewConcept()</code> method described previously uses this code to fix reference targets in the new node:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/20-changing-node-concept/fixing-references.png">
    <img src="https://specificlanguages.com/posts/2022-01/20-changing-node-concept/fixing-references.png" width="640"/> </a>
</figure>

<h1 id="conclusion">Conclusion</h1>
<p>We cannot change the concept of a node directly but there is a workaround that works. Just don&rsquo;t do it during
generation. (But you probably won&rsquo;t need to, anyway.)</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Writing an importer: Invoking from the editor</title>
      <link>https://specificlanguages.com/posts/2022-01/18-writing-an-importer-invoking-from-editor/</link>
      <pubDate>Tue, 18 Jan 2022 11:38:09 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/18-writing-an-importer-invoking-from-editor/</guid>
      <description>&lt;p&gt;In the last &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/&#34;&gt;couple&lt;/a&gt; &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/&#34;&gt;of&lt;/a&gt; &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/14-writing-an-importer-adding-action-to-menu/&#34;&gt;posts&lt;/a&gt; in this series we have looked at invoking the importer
via an action from a menu. This works pretty well for a simple case where we only had to choose the input file.
Sometimes, however, we want give our users an interface to provide additional configuration to the importer to customize
how it behaves.&lt;/p&gt;
&lt;p&gt;We could do it by creating and showing a Swing dialog, but we can also create a concept to store the configuration in
the model and invoke the importer right from the editor of that concept by clicking a button.&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/18-writing-an-importer-invoking-from-editor/"><![CDATA[ <p>In the last <a href="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/">couple</a> <a href="https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/">of</a> <a href="https://specificlanguages.com/posts/2022-01/14-writing-an-importer-adding-action-to-menu/">posts</a> in this series we have looked at invoking the importer
via an action from a menu. This works pretty well for a simple case where we only had to choose the input file.
Sometimes, however, we want give our users an interface to provide additional configuration to the importer to customize
how it behaves.</p>
<p>We could do it by creating and showing a Swing dialog, but we can also create a concept to store the configuration in
the model and invoke the importer right from the editor of that concept by clicking a button.</p>
<p>Here is how it could look like:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/18-writing-an-importer-invoking-from-editor/import-configuration-editor.png">
    <img src="https://specificlanguages.com/posts/2022-01/18-writing-an-importer-invoking-from-editor/import-configuration-editor.png" width="640"/> </a><figcaption>
            <h4>Import configuration editor</h4>
        </figcaption>
</figure>

<h1 id="why-editor">Why editor?</h1>
<p>Why use the editor instead of an action? By creating a separate configuration concept we can easily persist a
configuration so that the user does not need to re-enter it every time she wants to run an import. We can also store
multiple alternative configurations. An MPS editor is also easier to design than a Swing dialog.</p>
<h1 id="implementation">Implementation</h1>
<p>We create a separate language for import configuration, <code>game.importconfig</code>, to keep the core language clean of
infrastructure concerns. The configuration is represented by a single rootable concept, <code>ImportConfiguration</code>, with a
string property for the source file.</p>
<p>The editor of <code>ImportConfiguration</code> embeds a Swing button to invoke the import logic. The code for the button is shown
below:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/18-writing-an-importer-invoking-from-editor/component-provider.png">
    <img src="https://specificlanguages.com/posts/2022-01/18-writing-an-importer-invoking-from-editor/component-provider.png" width="640"/> </a><figcaption>
            <h4>Button implementation</h4>
        </figcaption>
</figure>

<p>The implementation is similar in spirit to the action implementation from the previous posts:</p>
<ul>
<li>We <a href="https://specificlanguages.com/posts/how-to-get-current-project-from-editor/">retrieve the current IDEA project</a> using the same
mechanism (DataManager and DataContext) that IDEA actions use internally.</li>
<li>The <code>actionPerformed</code> method will be invoked outside of any model access lock so we need to start a command manually
using the <code>command</code> statement from <code>jetbrains.mps.lang.access</code> language.</li>
<li>Any exception thrown is <a href="https://specificlanguages.com/posts/notification-balloons-instead-of-message-boxes/">shown using a notification</a>, same as in the action.</li>
</ul>
<p>Find the latest code on <a href="https://github.com/specificlanguages/sample-importer">GitHub</a>. The <code>game.sandbox.imported</code>
model contains a sample import configuration.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>mpsclean</title>
      <link>https://specificlanguages.com/posts/2022-01/17-mpsclean/</link>
      <pubDate>Mon, 17 Jan 2022 08:56:10 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/17-mpsclean/</guid>
      <description>&lt;p&gt;A few years ago I found that using &lt;code&gt;git clean&lt;/code&gt; with my MPS projects deleted too much. It deleted the MPS
workspace&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, the &lt;code&gt;.idea&lt;/code&gt; directory that I would often have in my projects to facilitate &lt;a href=&#34;https://specificlanguages.com/articles/remote-debugging/&#34;&gt;remote debugging with
IntelliJ IDEA&lt;/a&gt; and some &lt;code&gt;.properties&lt;/code&gt; files that contained local settings but
were ignored by &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can pass options to &lt;code&gt;git clean&lt;/code&gt; to exclude all of the above, but the command becomes cumbersome:&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/17-mpsclean/"><![CDATA[ <p>A few years ago I found that using <code>git clean</code> with my MPS projects deleted too much. It deleted the MPS
workspace<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, the <code>.idea</code> directory that I would often have in my projects to facilitate <a href="https://specificlanguages.com/articles/remote-debugging/">remote debugging with
IntelliJ IDEA</a> and some <code>.properties</code> files that contained local settings but
were ignored by <code>.gitignore</code>.</p>
<p>You can pass options to <code>git clean</code> to exclude all of the above, but the command becomes cumbersome:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>git clean -dx -e .idea -e .mps -e <span style="color:#e6db74">&#39;*.properties&#39;</span> -e <span style="color:#e6db74">&#39;*.log&#39;</span>
</span></span></code></pre></div><p>At some point I defined an alias for the above command:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>alias mpsclean<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;git clean -dx -e .idea -e .mps -e &#39;*.properties&#39; -e &#39;*.log&#39;&#34;</span>
</span></span></code></pre></div><p>For safety reasons running the command above will not actually do anything as it&rsquo;s missing a <code>-f</code> or <code>-n</code> flag that
would tell it to either delete the files (<code>-f</code>) or do a dry run, listing all the files it is going to delete (<code>-n</code>). The
flag needs to be provided explicitly. So I would use the alias like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>mpsclean -n <span style="color:#75715e"># do not delete anything yet</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e"># check the list of files and directories to be deleted</span>
</span></span><span style="display:flex;"><span>mpsclean -f <span style="color:#75715e"># actually delete the files</span>
</span></span></code></pre></div><div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Newer MPS versions put a <code>.gitignore</code> file under the <code>.mps</code> directory when creating a new project so <code>git clean</code> will no longer delete the MPS workspace by default.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    
    <item>
      <title>Writing an importer: Adding the action to a menu</title>
      <link>https://specificlanguages.com/posts/2022-01/14-writing-an-importer-adding-action-to-menu/</link>
      <pubDate>Fri, 14 Jan 2022 09:49:12 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/14-writing-an-importer-adding-action-to-menu/</guid>
      <description>&lt;p&gt;In our &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/&#34;&gt;last post&lt;/a&gt; working on the importer, we created an
action to invoke our importer. The action worked but wasn&amp;rsquo;t included in any menu. Today we will fix this.&lt;/p&gt;
&lt;p&gt;Each menu in MPS shows a particular &lt;em&gt;action group&lt;/em&gt;. Action groups are extensible, dynamic lists of actions whose
contents can be modified by plugins such as our importer plugin. They are also recursive: you can add an action group to
another action group.&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/14-writing-an-importer-adding-action-to-menu/"><![CDATA[ <p>In our <a href="https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/">last post</a> working on the importer, we created an
action to invoke our importer. The action worked but wasn&rsquo;t included in any menu. Today we will fix this.</p>
<p>Each menu in MPS shows a particular <em>action group</em>. Action groups are extensible, dynamic lists of actions whose
contents can be modified by plugins such as our importer plugin. They are also recursive: you can add an action group to
another action group.</p>
<p>In fact, you cannot add a single action directly to an action group but have to wrap it in an action group of its own
first.</p>
<h1 id="finding-the-right-group">Finding the right group</h1>
<p>The biggest stumbling block here is finding the right group to add the action to. Although the
<a href="https://www.jetbrains.com/help/mps/plugin.html#actionsandactiongroups">documentation</a> states that the predefined MPS
groups are stored in the <code>jetbrains.mps.ide.actions</code> model, it is not really helpful in this instance.</p>
<p>First, the documentation is incomplete. For example, the <code>EditorPopup</code> group that represents the context menu in an
editor is defined in <code>jetbrains.mps.ide.editor.actions</code> (note the <code>.editor.</code> in the name).</p>
<p>Second, <code>jetbrains.mps.ide.actions</code> model contains dozens of actions, and the documentation does not specify which group
corresponds to which menu.</p>
<p>We can try guessing the name or looking through the completion menu. In our case we look for the context menu related to
a model so we could try typing <code>Model</code> and see what comes up.</p>
<p>If that fails, we can look up an action that is already present in the menu, and use <em>Find Usages</em> to find which groups
contain it. Ideally the action should be something distinct that would be easy to track, as opposed to a generic action
like <em>New</em> or <em>Build Model</em>.</p>
<p>Let&rsquo;s say we look through the menu and find an action called <em>Analyze Model Dependencies</em>. This is the action&rsquo;s title
but we need to find the actual action node whose name is going to be a Java identifier. So we need to guess again.</p>
<p>In this case, <code>AnalyzeModelDependencies</code> doesn&rsquo;t bring up any results but searching for just <code>Analyze</code> finds
<code>AnalyzeDependencies</code> which turns out to be the action group we are looking for.</p>
<p>The next step is to search for usages of <code>AnalyzeDependencies</code>, globally. To do that we bring up the <em>Find Usages
Settings</em> dialog by pressing <code>Ctrl+Alt+Shift+F7</code>, change the <em>Scope</em> to <em>Global</em> and confirm.</p>
<p>This brings us to <code>ModelActions</code> group that is the group we are looking for. It mostly consists of <em>anchors</em> (items with
an arrow in front of their names) that represent sections that other action groups can add actions to, to keep related
actions together.</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/14-writing-an-importer-adding-action-to-menu/model-actions-group.png">
    <img src="https://specificlanguages.com/posts/2022-01/14-writing-an-importer-adding-action-to-menu/model-actions-group.png" width="640"/> </a>
</figure>

<h1 id="adding-the-action-to-the-group">Adding the action to the group</h1>
<p>After we have figured out the action group, adding the action is fairly straightforward. I have recorded a screencast of
the necessary steps:</p>

			<div
					style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
				<iframe
					src="https://player.vimeo.com/video/665929764?dnt=0"
						style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allow="fullscreen">
				</iframe>
			</div>

<h1 id="next-invoking-importer-from-an-editor">Next: invoking importer from an editor</h1>
<p>In the next post we will implement an alternative way of invoking importers: from an editor. Meanwhile, the latest code
is <a href="https://github.com/specificlanguages/sample-importer">on GitHub</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>MPS Coderules: Entering and exiting</title>
      <link>https://specificlanguages.com/posts/2022-01/13-coderules-entering-exiting/</link>
      <pubDate>Thu, 13 Jan 2022 08:51:56 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/13-coderules-entering-exiting/</guid>
      <description>&lt;p&gt;Today I am going to try out &lt;a href=&#34;https://en.wikipedia.org/wiki/Ward_Cunningham#%22Cunningham&#39;s_Law%22&#34;&gt;Cunningham&amp;rsquo;s Law&lt;/a&gt;:
&amp;ldquo;The best way to get the right answer on the Internet is not to ask a question: it&amp;rsquo;s to post the wrong answer.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I have started looking at the &lt;a href=&#34;https://github.com/JetBrains/mps-coderules/&#34;&gt;MPS Coderules&lt;/a&gt; plugin which implements an
alternative analysis engine for MPS based on &lt;a href=&#34;https://en.wikipedia.org/wiki/Constraint_Handling_Rules&#34;&gt;Constraint Handling Rules
(CHR)&lt;/a&gt;, a rule-based declarative language, roughly occupying
the same position in whatever programming language taxonomy as does Prolog.&lt;/p&gt;
&lt;p&gt;Coderules may be the future of MPS type checking now that the &lt;a href=&#34;https://github.com/JetBrains/mps-kotlin-typesystem&#34;&gt;Kotlin-for-MPS type
system&lt;/a&gt; is being written in it. That&amp;rsquo;s why I wanted to get a feeling
for how easy it would be to understand an existing type system or develop one.&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/13-coderules-entering-exiting/"><![CDATA[ <p>Today I am going to try out <a href="https://en.wikipedia.org/wiki/Ward_Cunningham#%22Cunningham's_Law%22">Cunningham&rsquo;s Law</a>:
&ldquo;The best way to get the right answer on the Internet is not to ask a question: it&rsquo;s to post the wrong answer.&rdquo;</p>
<p>I have started looking at the <a href="https://github.com/JetBrains/mps-coderules/">MPS Coderules</a> plugin which implements an
alternative analysis engine for MPS based on <a href="https://en.wikipedia.org/wiki/Constraint_Handling_Rules">Constraint Handling Rules
(CHR)</a>, a rule-based declarative language, roughly occupying
the same position in whatever programming language taxonomy as does Prolog.</p>
<p>Coderules may be the future of MPS type checking now that the <a href="https://github.com/JetBrains/mps-kotlin-typesystem">Kotlin-for-MPS type
system</a> is being written in it. That&rsquo;s why I wanted to get a feeling
for how easy it would be to understand an existing type system or develop one.</p>
<h1 id="the-shock">The shock</h1>
<figure><a href="https://specificlanguages.com/posts/2022-01/13-coderules-entering-exiting/rule.png">
    <img src="https://specificlanguages.com/posts/2022-01/13-coderules-entering-exiting/rule.png" width="640"/> </a><figcaption>
            <h4>A Coderules rule</h4>
        </figcaption>
</figure>

<p>My first reaction when I saw a code rules program was that of resistance and recoil: the documentation is full of
similar-sounding foreign terms like <em>rule</em>, <em>constraint</em>, <em>query</em>, or <em>macro</em>. It appears that the language does not
simply describe the rules of typechecking but rather rules that build constraints. (Which seemed to be other kinds of
rules. After all isn&rsquo;t a constraint a kind of rule that constrains something?)</p>
<p>This was all a bit too meta to me so I quit.</p>
<p>The next time I tried looking at Kotlin&rsquo;s typesystem linked above but was immediately lost and overwhelmed. So I quit a
second time.</p>
<p>After several such attempts I started recognizing some patterns and making some distinctions.</p>
<p>I realized that <em>rules</em> are programs but <em>constraints</em> are not. Constraints are simply special objects floating around
in the reactor (the working memory) of the engine. The engine is processing the constraints according to the rules: it
matches them to a rule, the rule tells which of the matched constraints to keep and which to remove, and which new
constraints to add.</p>
<p>To keep with the reactor analogy, constraints are molecules and rules are chemical reactions that transform some
molecules into other molecules. Some of the &ldquo;input&rdquo; molecules are consumed while other molecules work like catalysts,
enabling the reaction while not being consumed in the process.</p>
<h1 id="entry-and-exit-point">Entry and exit point</h1>
<p>Past the initial shock, my next immediate question was &ldquo;Where does this program start and how does it end?&rdquo; The entry
point seems to be a <em>query</em>, which the type checker calls when it wants to know the type of something. There are
multiple queries (<code>check</code> and <code>typeOf</code>) and I don&rsquo;t yet understand the difference between them.</p>
<p>You can define queries (or, rather, query <em>handlers</em>) in two ways: using a lower-level <code>QueryTable concept</code>, or, for the
type system, using <code>TypecheckingQueries</code> concept. The latter is where we finally get a little bit domain-specific, as
the underlying engine is so general that besides computing types it can apparently also compute control flow, data flow
and other kinds of analyses).</p>
<p>The exit point is probably when the reactor &ldquo;stabilizes&rdquo;, i.e. no new rules can be fired. I have expected that the
engine would extract the &ldquo;answer&rdquo;, i.e. the type, from the constraints that remain in the reactor somehow but this does
not seem to be the case. Instead, the query (the entry point) gets passed a <em>type collector</em> which collects types via
side effects:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/13-coderules-entering-exiting/side-effect.png">
    <img src="https://specificlanguages.com/posts/2022-01/13-coderules-entering-exiting/side-effect.png" width="640"/> </a><figcaption>
            <h4>Returning the type of a type via a side effect</h4>
        </figcaption>
</figure>

<p>(I am still a bit confused here because the method to retrieve the type collector from a query is deprecated, yet I see
no indication of there being a replacement.)</p>
<h1 id="to-be-continued">To be continued&hellip;</h1>
<p>This is all that I have time to write today but I want to continue my investigation of coderules in the future. I still
have lots of open questions, both low and high level, but I am curious what questions you might have about Coderules.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Writing an importer: Action code</title>
      <link>https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/</link>
      <pubDate>Tue, 11 Jan 2022 09:00:00 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/</guid>
      <description>&lt;p&gt;Continuing where we left off &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/&#34;&gt;last&lt;/a&gt;, today we are ready to write some code in our action. We need to choose a file and
then invoke the importer logic on the file and the model.&lt;/p&gt;
&lt;h1 id=&#34;file-chooser&#34;&gt;File Chooser&lt;/h1&gt;
&lt;p&gt;Since MPS builds upon the IDEA Platform which builds upon Swing, we can use the usual Swing components as well as the
platform-provided facilities. I prefer to use classes from the platform when available because they often provide a more
polished look-and-feel. In this case the platform provides a class called &lt;code&gt;FileChooser&lt;/code&gt; for opening a &amp;ldquo;choose file&amp;rdquo;
dialog, documented &lt;a href=&#34;https://plugins.jetbrains.com/docs/intellij/file-and-class-choosers.html&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/"><![CDATA[ <p>Continuing where we left off <a href="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/">last</a>, today we are ready to write some code in our action. We need to choose a file and
then invoke the importer logic on the file and the model.</p>
<h1 id="file-chooser">File Chooser</h1>
<p>Since MPS builds upon the IDEA Platform which builds upon Swing, we can use the usual Swing components as well as the
platform-provided facilities. I prefer to use classes from the platform when available because they often provide a more
polished look-and-feel. In this case the platform provides a class called <code>FileChooser</code> for opening a &ldquo;choose file&rdquo;
dialog, documented <a href="https://plugins.jetbrains.com/docs/intellij/file-and-class-choosers.html">here</a>.</p>
<h1 id="projects">Projects</h1>
<p>To invoke the <code>FileChooser</code> we will need to pass to it the current project instance. There are in fact several different
project classes in MPS, one coming from IDEA (<code>com.intellij.openapi.project.Project</code>) and another one coming from MPS
(<code>jetbrains.mps.project.MPSProject</code>). In fact, there are other project classes in MPS but we will not need to concern
ourselves with them in this tutorial.</p>
<p>To obtain an instance of the IDEA project we add a context parameter of type Project to our action. I like to call it
<code>ideaProject</code> to disambiguate it from a MPS project.</p>
<p>Here is what the <code>FileChooser</code> invocation looks like:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-java" data-lang="java"><span style="display:flex;"><span>VirtualFile chosenFile <span style="color:#f92672">=</span> FileChooser.<span style="color:#a6e22e">chooseFile</span>(
</span></span><span style="display:flex;"><span>  FileChooserDescriptorFactory.<span style="color:#a6e22e">createSingleFileDescriptor</span>(<span style="color:#e6db74">&#34;json&#34;</span>), <span style="color:#66d9ef">this</span>.<span style="color:#a6e22e">ideaProject</span>, <span style="color:#66d9ef">null</span>);
</span></span></code></pre></div><h1 id="virtual-file-system">Virtual file system</h1>
<p>The result we get from the dialog is a <code>VirtualFile</code> object. Virtual files are abstractions provided by the IDEA
platform&rsquo;s virtual file system (VFS). We are not going to use VFS in our code though, so we will escape the abstraction
immediately by calling <code>getPath()</code> on it.</p>
<p>Next, we need to make sure that we are not calling the file chooser while holding a model lock.</p>
<h1 id="model-access-reading-writing-and-commanding">Model access: reading, writing, and commanding</h1>
<p>All model access in MPS takes place under a read-write lock. The rules are documented in the <a href="https://www.jetbrains.com/help/mps/open-api-accessing-models-from-code.html#usingcommands">MPS
help</a>. In our case, the
importer modifies the model so it will need to be invoked from within a <em>command</em>.</p>
<p>By default, an action&rsquo;s <code>execute</code> block is wrapped in a command automatically and we don&rsquo;t have to think of this. On the
other hand, UI calls are <em>disallowed</em> when under a read-write lock to prevent UI freezes and deadlocks. This forces us
to manage model locking explicitly in our code so we need to disable the automatic command by changing <code>required access</code>
from <code>command</code> to <code>none</code>.</p>
<p>If we now need to execute a piece of code in a command, we do it explicitly by using the <code>command</code> statement from
<code>jetbrains.mps.lang.access</code> language that was helpfully added to the model for us by the wizard that created the plugin
solution.</p>
<p>We need to pass a <em>repository</em> to the command, which we can obtain from the current MPS project, which we get from the
context like the IDEA project above.</p>
<h1 id="show-error-messages-as-balloons">Show error messages as balloons</h1>
<p>Our importer logic can throw exceptions that we should show to the user. If we don&rsquo;t do anything and simply rethrow the
exception, it will cause a flashing exclamation mark to appear in the bottom right corner. If we catch the exception we
can use a plain Swing message box to show it, or, even better, a notification. I have written previously about
notifications <a href="https://specificlanguages.com/posts/notification-balloons-instead-of-message-boxes/">here</a>.</p>
<h1 id="the-complete-code">The complete code</h1>
<p>Here is what the action looks like after all the modifications:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/final-code.png">
    <img src="https://specificlanguages.com/posts/2022-01/11-writing-an-importer-action-code/final-code.png" width="640"/> </a>
</figure>

<h1 id="next-adding-the-action-to-a-menu">Next: adding the action to a menu</h1>
<p>The action works now but can still only be invoked via the <em>Find Action</em> popup (<code>Ctrl/Cmd+Shift+A</code>). In the next post we
will add it to a menu.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Writing an importer: Actions</title>
      <link>https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/</link>
      <pubDate>Mon, 10 Jan 2022 10:55:11 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/</guid>
      <description>&lt;p&gt;In our &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/&#34;&gt;last post&lt;/a&gt; we have created a simple importer that we
could invoke from the MPS Console. Today we&amp;rsquo;ll start implementing a way for our users to invoke the importer.&lt;/p&gt;
&lt;p&gt;A common way of extending MPS is via &lt;a href=&#34;https://www.jetbrains.com/help/mps/plugin.html&#34;&gt;plugins&lt;/a&gt; that provide &lt;em&gt;actions&lt;/em&gt;.
An action is a piece of code that can be invoked via a menu, such as the main menu or a context menu, or a key
combination. In the context of MPS it is important to distinguish these so called &lt;em&gt;plugin actions&lt;/em&gt; from &lt;em&gt;editor actions&lt;/em&gt;
which are invoked by the MPS editor to manipulate the node being edited. Both types of actions can be provided by the
language developer but plugin actions can perform any task and have access to the entire IDE, whereas editor actions
only have access to the current editor and the node being edited.&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/"><![CDATA[ <p>In our <a href="https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/">last post</a> we have created a simple importer that we
could invoke from the MPS Console. Today we&rsquo;ll start implementing a way for our users to invoke the importer.</p>
<p>A common way of extending MPS is via <a href="https://www.jetbrains.com/help/mps/plugin.html">plugins</a> that provide <em>actions</em>.
An action is a piece of code that can be invoked via a menu, such as the main menu or a context menu, or a key
combination. In the context of MPS it is important to distinguish these so called <em>plugin actions</em> from <em>editor actions</em>
which are invoked by the MPS editor to manipulate the node being edited. Both types of actions can be provided by the
language developer but plugin actions can perform any task and have access to the entire IDE, whereas editor actions
only have access to the current editor and the node being edited.</p>
<p>To launch the importer our action will need two pieces of input from the user: the source file and the target model. Our
plan will be to take the target model from the context and use the built-in file chooser dialog to ask for the source
file.</p>
<h1 id="plugin-solution">Plugin solution</h1>
<p>Whereas we were free to put our Java code (almost) anywhere we like, we are more constrained with plugins and actions.
They must reside in a specially configured solution in a specially named model. MPS provides a wizard that can create
both the solution and the model for us, found in the context menu of the project (<em>New → Plugin Solution</em>):</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/new-plugin-solution.png">
    <img src="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/new-plugin-solution.png" width="640"/> </a>
</figure>

<p>The wizard will perform three tasks for us:</p>
<ol>
<li>
<p>Create a solution and set its <em>Solution Kind</em> set to <em>Other</em>. You can also manually set the solution kind on the Java
tab in the solution properties dialog:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/solution-kind.png">
       <img src="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/solution-kind.png" width="640"/> </a>
   </figure>

</li>
<li>
<p>Create a model named <code>&lt;solution-name&gt;.plugin</code> with a few useful languages already imported for use.</p>
</li>
<li>
<p>Create a root node of concept <code>StandalonePluginDescriptor</code>.</p>
</li>
</ol>
<p>These three arcane bits of configuration are required for MPS to load actions and other plugin elements from the
solution. More details about MPS classloading and solution kinds can be found on a helpful <a href="https://github.com/mbeddr/mbeddr.core/wiki/MPS:-Deps-and-Classpath">Wiki
page</a> of the mbeddr project.</p>
<h1 id="creating-an-action">Creating an action</h1>
<p>We can now create our first action in the model (<em>right click on the model → New → j.m.lang.plugin → Action</em>). Let&rsquo;s
call it <code>ImportLocationsFromJson</code> and set the caption to &ldquo;Import Locations from JSON&hellip;&rdquo;. The ellipsis at the end is a
convention to indicate that the action will show a dialog.</p>
<p>As a quick check that things so far have been configured properly, after we build the action we should be able to use
the &ldquo;Find Action&rdquo; popup (<code>Ctrl/Cmd+Shift+A</code>) to look it up:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/find-action.png">
    <img src="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/find-action.png" width="640"/> </a>
</figure>

<p>You can even try invoking it from that popup (press <code>Enter</code>): it should do nothing at all (including throwing no
errors).</p>
<p>NOTE: Actions are documented in the <a href="https://www.jetbrains.com/help/mps/plugin.html#actionsandactiongroups">JetBrains MPS
Help</a> and I suggest you read the documentation to
gain a better understanding of what we are going to do.</p>
<h1 id="getting-the-target-model-context-parameters">Getting the target model: context parameters</h1>
<p>Our action will be placed in a context menu of a model. It can determine the model that it is invoked on via a context
parameter with key <code>MODEL</code>. To use this key we need to import the model that contains <code>MPSCommonDataKeys</code> class,
<code>jetbrains.mps.ide.actions@java_stub</code>:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/mps-common-data-keys.png">
    <img src="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/mps-common-data-keys.png" width="640"/> </a>
</figure>

<p>If MPS does not give the name to the parameter automatically, name it <code>model</code>. Leave it required.</p>
<p>Adding a required parameter serves two purposes: we will be able to use it in the <code>execute</code> block, and the action will
be disabled if invoked from a context where no model is present. If you now try looking up the action again via
<code>Cmd/Ctrl+Shift+A</code>, you will find that the action is disabled if the editor is focused and enabled if the focus is on a
model in the Logical View (project tree).</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/import-locations-disabled.png">
    <img src="https://specificlanguages.com/posts/2022-01/10-writing-an-importer-actions/import-locations-disabled.png" width="640"/> </a><figcaption>
            <h4>&#34;Import Locations...&#34; action is disabled when the focus is currently in the editor</h4>
        </figcaption>
</figure>

<h1 id="next-code">Next: code!</h1>
<p>Having the model means we have one half of our inputs without writing any code yet. In our next part we will have no
such luxury and will get our hands dirty with Java code to let the user pick the input file and perform the action.
Meanwhile, the code is <a href="https://github.com/specificlanguages/sample-importer">on GitHub</a> and I&rsquo;m eager to hear your
reactions and thoughts about the tutorial so far.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Writing an importer: The basics</title>
      <link>https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/</link>
      <pubDate>Fri, 07 Jan 2022 12:00:00 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/</guid>
      <description>&lt;p&gt;&lt;em&gt;NOTE: Due to an error, only a single paragraph was sent by email from the &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/06-writing-an-importer-introduction/&#34;&gt;first post&lt;/a&gt; in this series. I apologize for any inconvenience caused and
hope it will work better this time.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Continuing from our &lt;a href=&#34;https://specificlanguages.com/posts/2022-01/06-writing-an-importer-introduction/&#34;&gt;last post&lt;/a&gt;, let&amp;rsquo;s have a brief look
at the data and the language we are going to work with and set things up. Remember, the code is &lt;a href=&#34;https://github.com/specificlanguages/sample-importer&#34;&gt;available on
GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;the-language&#34;&gt;The language&lt;/h1&gt;
&lt;p&gt;I have created a very small language for this tutorial, called &lt;code&gt;game.core&lt;/code&gt;. It consists of a single concept named
&lt;code&gt;Location&lt;/code&gt;, representing a location in a game:&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/"><![CDATA[ <p><em>NOTE: Due to an error, only a single paragraph was sent by email from the <a href="https://specificlanguages.com/posts/2022-01/06-writing-an-importer-introduction/">first post</a> in this series. I apologize for any inconvenience caused and
hope it will work better this time.</em></p>
<p>Continuing from our <a href="https://specificlanguages.com/posts/2022-01/06-writing-an-importer-introduction/">last post</a>, let&rsquo;s have a brief look
at the data and the language we are going to work with and set things up. Remember, the code is <a href="https://github.com/specificlanguages/sample-importer">available on
GitHub</a>.</p>
<h1 id="the-language">The language</h1>
<p>I have created a very small language for this tutorial, called <code>game.core</code>. It consists of a single concept named
<code>Location</code>, representing a location in a game:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/location-structure.png">
    <img src="https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/location-structure.png" width="640"/> </a>
</figure>

<p>In text-based adventures each location is connected to other locations, for example via compass directions (north, east,
south, west). To keep things simple but still interesting in our case, each location has just one optional <code>next</code>
reference pointing to the next location in the adventure.</p>
<p>Each location also has a <code>name</code> (inherited from <code>INamedConcept</code>) and a <code>description</code>. A location is uniquely identified
by its name. The <code>description</code> field holds additional information about the location.</p>
<h1 id="the-input">The input</h1>
<p>We will be importing JSON data such as this file (available in the GitHub repository as
<a href="https://github.com/specificlanguages/sample-importer/blob/main/input/adventure.json"><code>input/adventure.json</code></a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&#34;locations&#34;</span>: [
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;Street&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;description&#34;</span>: <span style="color:#e6db74">&#34;You stand in front of an old house that looks just like you remembered it in the past. The entrance door is ajar.&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;next&#34;</span>: <span style="color:#e6db74">&#34;House Entrance&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;House Entrance&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;description&#34;</span>: <span style="color:#e6db74">&#34;You enter the house. Everything here is covered with dust: the floor, the hanger, the shelves. Except for one dust-free door with a label on it that says \&#34;Cellar\&#34;.&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;next&#34;</span>: <span style="color:#e6db74">&#34;Cellar&#34;</span>
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        {
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;name&#34;</span>: <span style="color:#e6db74">&#34;Cellar&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#f92672">&#34;description&#34;</span>: <span style="color:#e6db74">&#34;The cellar is dark and you can&#39;t see anything. Your right hand rests on the wall and senses a light switch.&#34;</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Note that a location refers to its next location by its (unique) name. Also note that we can refer to locations that
appear later in the file (i.e. the importer must support <em>forward references</em>).</p>
<h1 id="gson">Gson</h1>
<p>To parse JSON data we will use the <a href="https://github.com/google/gson">Gson</a> library from Google. To do that we will need
to add the Gson JAR to our project. The process of doing so is
<a href="https://www.jetbrains.com/help/mps/getting-the-dependencies-right.html#addingexternaljavaclassesandjarstoaproject-runtimesolutions">well</a>
<a href="https://tomassetti.me/how-to-add-jars-and-resources-in-a-jetbrains-mps-project/">documented</a>
<a href="https://www.youtube.com/watch?v=P7NvsMxMNPM">elsewhere</a> and I will not cover it here. In the linked project</p>
<p>Now we can test our parser from the <a href="https://www.jetbrains.com/help/mps/mps-console.html">MPS Console</a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#f92672">&gt;</span> {
</span></span><span style="display:flex;"><span>    JsonElement element; 
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">try</span> (Reader reader <span style="color:#f92672">=</span> Files.<span style="color:#a6e22e">newBufferedReader</span>(Path.<span style="color:#a6e22e">of</span>(<span style="color:#e6db74">&#34;/path/to/sample-importer/input/adventure.json&#34;</span>))) { 
</span></span><span style="display:flex;"><span>      element <span style="color:#f92672">=</span> JsonParser.<span style="color:#a6e22e">parseReader</span>(reader); 
</span></span><span style="display:flex;"><span>    } 
</span></span><span style="display:flex;"><span>    <span style="color:#960050;background-color:#1e0010">#</span>print element;
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// output:</span>
</span></span><span style="display:flex;"><span>  <span style="color:#75715e">// {&#34;locations&#34;:[{&#34;name&#34;:&#34;Street&#34;,&#34;description&#34;:&#34;You stand in front of an old house ...</span>
</span></span></code></pre></div><p>Since it can be a bit difficult for newcomers to MPS to figure out how to type things properly, here is a screencast of me typing this code into MPS Console and running it:</p>

			<div
					style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
				<iframe
					src="https://player.vimeo.com/video/663267049?dnt=0"
						style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allow="fullscreen">
				</iframe>
			</div>

<h1 id="where-to-put-the-code">Where to put the code</h1>
<p>The code of the importer will live in a Java class but where do we put the class? We have several options, ordered by
the amount of effort required:</p>
<ol>
<li>Put the class in one of the existing aspects of the <code>game.core</code> language.</li>
<li>Create a separate model in the <code>game.core</code> and put the class there. Such a model would be called &ldquo;utility model&rdquo; in
MPS parlance.</li>
<li>Create a separate solution for the importer logic, create a model there and put the class into it.</li>
</ol>
<p>We don&rsquo;t need to agonize too much about the decision because we can always move classes between models and modules.
Since none of the existing language aspects feel appropriate, we create a new model, name it <code>game.core.importer</code>, and
a new class <code>ImporterLogic</code> inside:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/import-logic-created.png">
    <img src="https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/import-logic-created.png" width="640"/> </a>
</figure>

<h1 id="initial-code">Initial code</h1>
<p>We are now finally ready to write some code! Since our input data contains forward references, we store all nodes in a
hash map, keyed by their names. If we encounter a reference to a node we haven&rsquo;t seen yet, we create it but only
initialize its name. Later when we encounter the full data for the target location, we take the previously created node
from the map and fill it in.</p>
<p>Here is the full code:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-java" data-lang="java"><span style="display:flex;"><span><span style="color:#66d9ef">public</span> <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">ImportLogic</span> {
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">public</span> <span style="color:#66d9ef">static</span> <span style="color:#66d9ef">void</span> <span style="color:#a6e22e">importData</span>(string sourceFile, model targetModel) <span style="color:#66d9ef">throws</span> IOException {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">final</span> JsonElement element;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">try</span> (Reader reader <span style="color:#f92672">=</span> Files.<span style="color:#a6e22e">newBufferedReader</span>(Path.<span style="color:#a6e22e">of</span>(sourceFile))) {
</span></span><span style="display:flex;"><span>      element <span style="color:#f92672">=</span> JsonParser.<span style="color:#a6e22e">parseReader</span>(reader);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    JsonArray locationsArray <span style="color:#f92672">=</span> element.<span style="color:#a6e22e">getAsJsonObject</span>().<span style="color:#a6e22e">get</span>(<span style="color:#e6db74">&#34;locations&#34;</span>).<span style="color:#a6e22e">getAsJsonArray</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Store locations in a map to resolve forward references</span>
</span></span><span style="display:flex;"><span>    Map<span style="color:#f92672">&lt;</span>string, node<span style="color:#f92672">&lt;</span>Location<span style="color:#f92672">&gt;&gt;</span> locationsByName <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> HashMap<span style="color:#f92672">&lt;</span>string, node<span style="color:#f92672">&lt;</span>Location<span style="color:#f92672">&gt;&gt;</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    foreach locationElement in locationsArray {
</span></span><span style="display:flex;"><span>      JsonObject locationObject <span style="color:#f92672">=</span> locationElement.<span style="color:#a6e22e">getAsJsonObject</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      string name <span style="color:#f92672">=</span> locationObject.<span style="color:#a6e22e">get</span>(<span style="color:#e6db74">&#34;name&#34;</span>).<span style="color:#a6e22e">getAsString</span>();
</span></span><span style="display:flex;"><span>      string description <span style="color:#f92672">=</span> locationObject.<span style="color:#a6e22e">get</span>(<span style="color:#e6db74">&#34;description&#34;</span>).<span style="color:#a6e22e">getAsString</span>();
</span></span><span style="display:flex;"><span>      JsonElement nextElement <span style="color:#f92672">=</span> locationObject.<span style="color:#a6e22e">get</span>(<span style="color:#e6db74">&#34;next&#34;</span>);
</span></span><span style="display:flex;"><span>      string nextName <span style="color:#f92672">=</span> nextElement <span style="color:#f92672">==</span> <span style="color:#66d9ef">null</span> <span style="color:#f92672">?</span> <span style="color:#66d9ef">null</span> : nextElement.<span style="color:#a6e22e">getAsString</span>();
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      node<span style="color:#f92672">&lt;</span>Location<span style="color:#f92672">&gt;</span> location <span style="color:#f92672">=</span> locationsByName.<span style="color:#a6e22e">computeIfAbsent</span>(name, {string name <span style="color:#f92672">=&gt;</span> <span style="color:#f92672">&lt;</span>Location(name: name)<span style="color:#f92672">&gt;</span>; });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      node<span style="color:#f92672">&lt;</span>Location<span style="color:#f92672">&gt;</span> nextLocation <span style="color:#f92672">=</span> nextName <span style="color:#f92672">==</span> <span style="color:#66d9ef">null</span> <span style="color:#f92672">?</span> <span style="color:#66d9ef">null</span> : locationsByName.<span style="color:#a6e22e">computeIfAbsent</span>(nextName, {string name <span style="color:#f92672">=&gt;</span> <span style="color:#f92672">&lt;</span>Location(name: name)<span style="color:#f92672">&gt;</span>; });
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      location.<span style="color:#a6e22e">description</span> <span style="color:#f92672">=</span> description;
</span></span><span style="display:flex;"><span>      location.<span style="color:#a6e22e">next</span> <span style="color:#f92672">=</span> nextLocation;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>      targetModel.<span style="color:#a6e22e">add</span> <span style="color:#a6e22e">root</span>(location);
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h1 id="running-the-importer">Running the importer</h1>
<p>We don&rsquo;t have any user-friendly way to run the importer yet (I will cover it in a later post) but we can already run it
from the MPS Console. Before we do that we need to create a target model for the imported nodes. Let&rsquo;s put it into the
<code>game.sandbox</code> solution and name it <code>game.sandbox.imported</code>. Next, we can invoke the importer from the console as
follows:</p>
<figure><a href="https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/import-from-mps-console.png">
    <img src="https://specificlanguages.com/posts/2022-01/07-writing-an-importer-basics/import-from-mps-console.png" width="640"/> </a>
</figure>

<p>The console shows an error because we don&rsquo;t catch IOException but the code will compile and run. As a result, you should
see the three locations created in the <code>game.sandbox.imported</code> model.</p>
<p>Note that since the code in <code>ImportLogic#importData()</code> manipulates the target model, it must run in the context of a
<a href="https://www.jetbrains.com/help/mps/open-api-accessing-models-from-code.html#usingcommands"><em>command</em></a>. MPS Console
establishes a command for us automatically, in other cases we may need to start a command manually.</p>
<p>In the next post we will look at invoking the importer in a user-friendly way. Until then, I would be happy to get any
feedback and questions you have.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Writing an importer: Introduction</title>
      <link>https://specificlanguages.com/posts/2022-01/06-writing-an-importer-introduction/</link>
      <pubDate>Thu, 06 Jan 2022 11:18:00 +0100</pubDate>
      <author>sergej@specificlanguages.com (Sergej Koščejev)</author>
      <guid>https://specificlanguages.com/posts/2022-01/06-writing-an-importer-introduction/</guid>
      <description>&lt;p&gt;What do you do if you have some data that you want to import into MPS? There is no built-in importer in MPS, nor is
there a ready-to-use framework, so it can be overwhelming for a beginning MPS language developer to piece together all
the required knowledge of MPS subsystems.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s why I want to start a series of beginner-level posts to cover the process of writing an importer. I will assume
you have experience programming in Java but are just starting with MPS.&lt;/p&gt;</description>
      <content:encoded xml:base="https://specificlanguages.com/posts/2022-01/06-writing-an-importer-introduction/"><![CDATA[ <p>What do you do if you have some data that you want to import into MPS? There is no built-in importer in MPS, nor is
there a ready-to-use framework, so it can be overwhelming for a beginning MPS language developer to piece together all
the required knowledge of MPS subsystems.</p>
<p>That&rsquo;s why I want to start a series of beginner-level posts to cover the process of writing an importer. I will assume
you have experience programming in Java but are just starting with MPS.</p>
<p>Our domain is going to be text-based adventure games, also known as <a href="https://en.wikipedia.org/wiki/Interactive_fiction">interactive
fiction</a>. We will use a very simple language for describing a very
simple adventure game, and import game data from a JSON file into MPS. I am not going to cover the development of the
language itself in this series and will only focus on the importing process.</p>
<p>I will be using the latest currently released version of MPS, 2021.2.3, and I will publish my code <a href="https://github.com/specificlanguages/sample-importer">on
GitHub</a> as we go, so you can follow along. The linked repository
already contains our simple language, a sandbox where I have manually created a few game locations, and a sample JSON
input file with the map data. I will talk more about the language in the next post.</p>
<p>In the upcoming posts I am going to cover these topics:</p>
<ul>
<li><strong>The basics.</strong> Using Google Gson from MPS to read a JSON file and using the MPS SModel API to create MPS nodes from
the data.</li>
<li><strong>Invoking the importer.</strong> Various ways in which you can launch the import from within MPS.</li>
<li><strong>Supporting subsequent reimports.</strong> How to update the model when the input data changes.</li>
<li><strong>Error handling.</strong> Letting the user know if there was a problem with the input data.</li>
</ul>
<p>The list of topics isn&rsquo;t set in stone and I may come up with additional topics as we go. But if there&rsquo;s an interesting
aspect related to importers that you want me to cover, let me know. You can hit Reply if you are reading this as an
email or email me directly at <a href="mailto:sergej@specificlanguages.com">sergej@specificlanguages.com</a>).</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
