Jekyll2023-10-08T07:32:46+00:00/feed.xmlngeor.comA blog by Nikolaos GeorgiouNikolaos GeorgiouMy Maven release workflow v22022-12-03T07:53:17+00:002022-12-03T07:53:17+00:00/2022/12/03/my-maven-release-workflow-v2<p>An update on my Maven release workflow, in other words, how I release
Maven libraries into the Maven central repository.</p>
<p>This is a followup from the <a href="/2022/02/05/my-maven-release-workflow.html">previous post</a>.</p>
<p>I haven’t updated the blog a lot lately but also I haven’t been coding much in something new, so
there isn’t much to update. The most coding I did at home was in Rust, and that was trying to
refactor some parts in <a href="https://github.com/ngeor/rusty-basic">rusty-basic</a>, no new features.</p>
<p>However, I did make some changes in how I (seldom) release my Java libraries into the Maven
central repository.</p>
<ul>
<li>Instead of using a specially named branch as the CI pipeline trigger, I use a tag.
So instead of a branch named <code class="language-plaintext highlighter-rouge">release-x.y.z</code>, the release starts with a tag <code class="language-plaintext highlighter-rouge">vx.y.z</code>.</li>
<li>Not using the Maven release plugin anymore. I find it a bit difficult to work with,
so I’ve stopped using it. I wrote a <a href="https://github.com/ngeor/krt">custom tool</a> to
replace it, which switches from snapshot to release version, generates the changelog
with <a href="https://github.com/orhun/git-cliff">git-cliff</a>, pushes the tag, and finally
switches back to the snapshot version. The tool supports also npm and python projects
(then again, like I said, I haven’t been using the tool a lot lately).
When deploying to the Maven central, I just run <code class="language-plaintext highlighter-rouge">mvn deploy</code>.</li>
</ul>
<p>The previous approach with the release branch would run all the release ceremony on the CI server,
while with this the release is kicked off on a local laptop.</p>
<p>In terms of running commands, in the previous approach it would be something like:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git checkout -b release-1.2.3
git push -u origin HEAD
</code></pre></div></div>
<p>and then wait until the release is magically done on the CI server.</p>
<p>In the new approach, using the custom tool I mentioned before:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>krt --type=maven minor
</code></pre></div></div>
<p>which will prepare the release (changelog, changing the <code class="language-plaintext highlighter-rouge">pom.xml</code>, creating the git tag) locally and then
push to the remote where the CI pipeline on the tag will deploy to Maven central.</p>
<p>The downside is that it requires some tooling to be present on the local machine (git-cliff and the krt tool)
but on the positive side the CI pipeline is less complicated.</p>
<p>I still have a Python script copy pasted across every single Java library project which performs
the release on the tag pipeline, which I would like one day to move to a central point to avoid duplication.</p>Nikolaos GeorgiouAn update on my Maven release workflow, in other words, how I release Maven libraries into the Maven central repository.My Maven release workflow2022-02-05T07:33:40+00:002022-02-05T07:33:40+00:00/2022/02/05/my-maven-release-workflow<p>In this post I’m describing my current setup regarding releasing
a versioned library into Maven central repository.</p>
<p><strong>Update: see latest in <a href="/2022/12/03/my-maven-release-workflow-v2.html">v2</a></strong></p>
<h2 id="initiating-the-release">Initiating the release</h2>
<p>The release starts from the default branch. There shouldn’t be any
pending changes. The version in the <code class="language-plaintext highlighter-rouge">pom.xml</code> must be a snapshot version.
To trigger the release, I <strong>create and push</strong> a new branch named <code class="language-plaintext highlighter-rouge">release-x.y.z</code>,
where <code class="language-plaintext highlighter-rouge">x.y.z</code> is the version that I want to release. This branch pattern
is picked up by a dedicate GitHub Actions workflow which does the work.</p>
<p>To create and push the branch, I use a Python script (<code class="language-plaintext highlighter-rouge">release.py</code>) which
I also to perform and finalize the release. So for this first step, I run
something like <code class="language-plaintext highlighter-rouge">./scripts/release.py initialize --version x.y.z</code>.</p>
<h2 id="performing-the-release">Performing the release</h2>
<p>The release is done through a GitHub Action. The custom <code class="language-plaintext highlighter-rouge">release.py</code> script
does all the work, which is a lot:</p>
<ul>
<li><strong>Determine the release version</strong>. It checks the environment variables
<code class="language-plaintext highlighter-rouge">GITHUB_REF_TYPE</code>, which must be set to <code class="language-plaintext highlighter-rouge">branch</code>, and <code class="language-plaintext highlighter-rouge">GITHUB_REF_NAME</code>,
which must follow the pattern <code class="language-plaintext highlighter-rouge">release-x.y.z</code>.</li>
<li><strong>Import a GPG key</strong>. In order to publish to the central Maven repository,
there’s a process which I have long forgotten (not as easy as publishing
to npm), which involves having your own GPG key. I store the key as a file
in the repo in a secure way. To import and use the key, I need to configure
two secrets in GitHub: <code class="language-plaintext highlighter-rouge">GPG_KEY</code> and <code class="language-plaintext highlighter-rouge">GPG_PASSPHRASE</code>. This also needs the
<code class="language-plaintext highlighter-rouge">gpg</code> binary to be present on the system.</li>
<li><strong>Configure git identity</strong>. Since we’re going to be making a few commits,
<code class="language-plaintext highlighter-rouge">git</code> needs to know who we are. The script just runs <code class="language-plaintext highlighter-rouge">git config user.name "My name"</code>
and <code class="language-plaintext highlighter-rouge">git config user.email "My email"</code>.</li>
<li><strong>Preparing the release</strong>. This uses the Maven release plugin and runs the <code class="language-plaintext highlighter-rouge">release:prepare</code>
goal. I am passing <code class="language-plaintext highlighter-rouge">-DreleaseVersion=x.y.z</code> to use the version I indicated when I created
the release branch. Also, <code class="language-plaintext highlighter-rouge">-DpushChanges=false</code>, because the plugin has some difficulty
pushing when running through GitHub Actions which I didn’t want to dive into. When this step
finishes, there are new commits on the current branch and a new git tag. The first commit
switches from snapshot to release version (e.g. from <code class="language-plaintext highlighter-rouge">1.2.3-SNAPSHOT</code> to <code class="language-plaintext highlighter-rouge">1.2.3</code>) and then
the next switches to the snapshot version of the next iteration (e.g. <code class="language-plaintext highlighter-rouge">1.3.0-SNAPSHOT</code>).</li>
<li><strong>Update the changelog</strong>. I use <a href="https://github.com/orhun/git-cliff">git cliff</a> to automatically generate a changelog stored in the
git repo as <code class="language-plaintext highlighter-rouge">CHANGELOG.md</code>. This is a good opportunity to do so, as we just got a fresh tag
and we haven’t pushed the changes yet. I install git cliff in an eariler step of the
GitHub Action by curling the latest release and extracting it somewhere in the <code class="language-plaintext highlighter-rouge">PATH</code>. Another
important point, for git cliff to work, the checkout action needs to fetch all commits
with <code class="language-plaintext highlighter-rouge">fetch-depth: 0</code>, otherwise the changelog will be empty.</li>
<li><strong>Push changes</strong> to the current branch. This is apparently supported out of the box in GitHub Actions,
a pleasant surprise. It also doesn’t result in an some infinite loop of workflows being executed.
Simply run <code class="language-plaintext highlighter-rouge">git push --follow-tags</code> (to also push the tag created by the Maven release plugin).</li>
<li><strong>Perform the release</strong>. This uses again the Maven release plugin but this time runs the <code class="language-plaintext highlighter-rouge">release:perform</code> goal. The script creates dynamically a Maven <code class="language-plaintext highlighter-rouge">settings.xml</code> file which
contains my credentials for the Maven central repository server (stored also as GitHub secrets)
and the GPG secrets as properties <code class="language-plaintext highlighter-rouge">gpg.keyname</code> and <code class="language-plaintext highlighter-rouge">gpg.passphrase</code>. The server id in the <code class="language-plaintext highlighter-rouge">settings.xml</code> needs to match the server id in my <code class="language-plaintext highlighter-rouge">pom.xml</code>’s <code class="language-plaintext highlighter-rouge">distributionManagement</code> section,
as well as the <code class="language-plaintext highlighter-rouge">nexus-staging-maven-plugin</code> <code class="language-plaintext highlighter-rouge">serverId</code> property. I pass <code class="language-plaintext highlighter-rouge">-DlocalCheckout=true</code>
to the <code class="language-plaintext highlighter-rouge">release</code> plugin so that it doesn’t attempt to clone anything from GitHub.</li>
<li><strong>Clean up</strong>: delete the GPG key.</li>
</ul>
<h2 id="finalizing-the-release">Finalizing the release</h2>
<p>Meanwhile, on my computer, I just wait for the pipeline to finish. I get
an e-mail from Sonatype saying that it has analyzed my release and hasn’t
found any vulnerabilities. At this point, my release is done. I need to
merge the release branch and delete it. I use the same <code class="language-plaintext highlighter-rouge">release.py</code> script
for it and run <code class="language-plaintext highlighter-rouge">./scripts/release.py finalize</code>.</p>
<h2 id="show-me-the-code">Show me the code</h2>
<ul>
<li><a href="https://github.com/ngeor/java/blob/v3.1.1/scripts/release.py">release.py</a>
The custom Python script to orchestrate all the above.</li>
<li><a href="https://github.com/ngeor/java/blob/v3.1.1/.github/workflows/release.yml">release.yml</a>
The GitHub Action workflow.</li>
<li><a href="https://github.com/ngeor/java/blob/v3.1.1/pom.xml">pom.xml</a></li>
</ul>
<h2 id="painpoints">Painpoints</h2>
<p>Some of the complexity probably comes from Maven’s notion of snapshot and release
versions, but I wanted to follow that convention.</p>
<p>One obvious problem is that it’s very easy to make a mistake in the version, e.g. push a branch like <code class="language-plaintext highlighter-rouge">release-30.0.0</code> instead of <code class="language-plaintext highlighter-rouge">release-3.0.0</code>.</p>
<p>Furthermore, this probably only works for a team of one. It’s fine for me and my hobby
project, but if you have two people, it doesn’t offer any protection from two people
accidentally trying to create a release (one person thinking they should be pushing version <code class="language-plaintext highlighter-rouge">1.2.3</code>
as a hotfix and another person thinking they should be pushing version <code class="language-plaintext highlighter-rouge">1.3.0</code> as a minor version).
Essentially, the choice of the version number is outside the approval process. The person who initiates the release can do so if they have the right to push a branch, without double checking
with someone else.</p>Nikolaos GeorgiouIn this post I’m describing my current setup regarding releasing a versioned library into Maven central repository.Maven and monorepo2021-09-26T07:47:00+00:002021-09-26T07:47:00+00:00/2021/09/26/maven-and-monorepo<p>In this post, I’m playing with releasing a subset of libraries
from a Maven monorepo.</p>
<p>Nothing simpler that a git repo that contains just one Maven project without Maven modules.
Everything is simple: versioning, releasing, creating the automatic CI/CD pipeline…
until the time when the second project comes.</p>
<p>If the second project is totally unrelated
to the first one, you might as well create a second git repository. Arguably, some copy pasting
will be annoying, e.g. copy pasting the CI/CD pipeline, setting up again the credentials
required to authenticate against the remote Maven repository. But it’s not the end of the world.
Maybe with some tooling, you can even coordinate things like upgrading common non-functional
stuff like checkstyle.</p>
<p>On the other hand, if the second project depends on the first one, things are a bit more
difficult. The change cycle is longer. You need to first implement the change on the first project, publish it to the Maven repository, consume it on the second project. If a mistake
was made, you need to make a patch release on the first project. There’s more ceremony.</p>
<p>A monorepo starts to sound like a better solution. Setup one and only one git repository
where all your Maven projects live, under a common parent Maven project. Life is good again,
all source code is at your fingertips and you can change multiple libraries in a single commit.</p>
<p>But then you need to publish your libraries to a remote Maven repository (because they’re so awesome).
And now versioning becomes a bit more complicated.</p>
<p>Let’s see what happens if we use the <a href="https://maven.apache.org/maven-release/maven-release-plugin/">maven release plugin</a>. Preparing the release with <code class="language-plaintext highlighter-rouge">mvn release:prepare</code> will
do the following:</p>
<ul>
<li>interactively ask for the next release version for each library</li>
<li>interactively ask for the next snapshot version for each library</li>
<li>interactively ask for the git tag</li>
<li>create a commit with the release versions and tag it</li>
<li>create a follow-up commit setting the snapshot versions</li>
</ul>
<p>Performing the release with <code class="language-plaintext highlighter-rouge">mvn release:perform</code> will deploy the release versions to the
remote Maven repository.</p>
<p>The thing is, this will deploy <em>all</em> of the libraries. If your monorepo contains
multiple libraries, which are all related (think of Spring for example, or Jackson), then
maybe this is perfect for you. Your libraries all form a logical product,
so it makes sense to release them all in one go, even if there haven’t been any changes
to some of them. The versioning will probably be simple as well, one version number
for everything in the monorepo.</p>
<p>But in a monorepo where libraries are not necessarily related to each other,
this doesn’t scale well.
You’ll have to answer a lot of questions if you want to have control over the versions. You can also skip
the excessive interrogation with <code class="language-plaintext highlighter-rouge">-B</code> for batch mode, but then all versions will be
automatically chosen (if using SemVer policy, it will bump a minor version).</p>
<p>Deploying all of your libraries, even if they had no changes since their
last release, might be confusing to your users, who might be wondering why they
need to upgrade at all.</p>
<p>In my search for a better approach, I found two nice links:</p>
<ul>
<li><a href="https://paulhammant.com/2017/01/27/maven-in-a-google-style-monorepo/">Maven In A Google Style Monorepo</a></li>
<li><a href="https://opensourcelibs.com/lib/logiball-monorepo">Open Source Libs - Maven - Monorepo</a></li>
</ul>
<p>The motivation behind the first article is sparse checkouts and git performance when you’re dealing with a huge monorepo
that tests the boundaries of git. But the trick we’re using is the same: modify the parent pom file, keeping only a subset of
the modules we are interested in.</p>
<p>An interesting detail is that they don’t commit <code class="language-plaintext highlighter-rouge">pom.xml</code> files at all into the monorepo, to avoid git noise.
They commit <code class="language-plaintext highlighter-rouge">pom.xml.template</code> files, which are used to auto-generate <code class="language-plaintext highlighter-rouge">pom.xml</code> files based on the desired
Maven modules and version numbers.</p>
<p>In <a href="https://github.com/ngeor/java/tree/v1.10.0">my repo</a>, I already have <code class="language-plaintext highlighter-rouge">pom.xml</code> files and I didn’t want to remove them for now, but I think
the template approach they’re taking is better.</p>
<p>I hacked together <a href="https://github.com/ngeor/java/tree/v1.10.0/apps/yak4j-cli">some code</a> to automate this (works on my machine), but the principle is the following:</p>
<ul>
<li>Make a backup of the parent <code class="language-plaintext highlighter-rouge">pom.xml</code> in case things go wrong</li>
<li>Make sure we’re on the default branch and there aren’t any pending changes</li>
<li>Create a branch for the release</li>
<li>Modify the parent <code class="language-plaintext highlighter-rouge">pom.xml</code>, keeping only the modules we want to release</li>
<li>Commit the <code class="language-plaintext highlighter-rouge">pom.xml</code></li>
<li>Prepare the release with <code class="language-plaintext highlighter-rouge">mvn release:prepare</code>. This will create two commits, one with the release versions
and one with the next snapshot versions. The first commit will be tagged and the command will
push the tag to the remote git repository.</li>
<li>Have a CI job pick up the pushed tag from the previous step and deploy to the remote Maven repository.
When the job finishes, you have published your Maven artifacts.</li>
<li>Modify again the parent <code class="language-plaintext highlighter-rouge">pom.xml</code>, putting back all the modules.</li>
<li>Because the version of the parent <code class="language-plaintext highlighter-rouge">pom.xml</code> has changed, we need to fix that version in all
child modules that were previously excluded from the parent pom (you might want to write
some tooling for that).</li>
<li>Commit the modified <code class="language-plaintext highlighter-rouge">pom.xml</code> files, merge the release branch into the default branch and delete it</li>
</ul>
<p>Bonus: in the era of <code class="language-plaintext highlighter-rouge">gofmt</code>, <code class="language-plaintext highlighter-rouge">rustfmt</code>, etc, I like formatters that are opinionated and are <em>not</em> configurable.
I keep <code class="language-plaintext highlighter-rouge">pom.xml</code> files sorted with a Maven plugin:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mvn com.github.ekryd.sortpom:sortpom-maven-plugin:sort <span class="se">\</span>
<span class="nt">-Dsort</span>.createBackupFile<span class="o">=</span><span class="nb">false</span>
</code></pre></div></div>
<p>How can this be improved further?</p>
<ul>
<li>First of all, dependencies. If a library has changed, it is probably a good idea
to automatically publish any libraries that depend on it.</li>
<li>Second, I shouldn’t even have to tell it which libraries have changed. It should
go through git history, since the previous release, and figure out what paths
have changed.</li>
<li>Finally, changelog. It should be possible to go over the git history and
create a somewhat organized changelog, dividing the changes by affected library.</li>
</ul>Nikolaos GeorgiouIn this post, I’m playing with releasing a subset of libraries from a Maven monorepo.CI for Visual C++ 62021-09-18T06:02:01+00:002021-09-18T06:02:01+00:00/2021/09/18/ci-for-visual-c++-6<p>Some time ago, I thought of hacking on some really old
C/C++ code I had written. I have a VirtualBox image running
Windows 2000 and Visual Studio 6 (yes, that old). However,
I also wanted to be able to run the code in modern
Visual Studio 2019. Verifying that I haven’t broken anything
means I would have to manually build my code on two different
IDEs. That’s just boring. Instead, I hacked together a way of running
my build for both Visual Studio versions.</p>
<p>Normally, this would have been an easy task. Install TeamCity server.
Install one TeamCity agent on a machine that has Visual Studio 2019
and one agent on a machine that has Visual Studio 6. The problem is
that Windows 2000 is rather old, so you can’t install TeamCity there
(or anything else for that matter, I’ve tried to install various
programming languages to total failure).</p>
<p>So the Visual Studio 2019 part is easy. I install TeamCity (both server and agent) on my laptop, define a job, done. For the Visual Studio 6 part, I needed to somehow mimic the work of a build agent, which is more or less the following:</p>
<ul>
<li>start the VM that has Visual Studio 6</li>
<li>get the source code into the machine</li>
<li>run the build</li>
<li>get the build artifacts out of the machine</li>
<li>stop the VM</li>
</ul>
<p>You can go with variations regarding the lifecycle management
of the VM, e.g. just leave it running instead of shutting it down,
clone a new VM instead of reusing the same to achieve parallel
builds, etc. In my case, I just start and stop it per job,
as it’s not too time consuming. I rely on TeamCity’s shared resources
feature to ensure only one job can use that VM at any time.</p>
<p>Luckily, VirtualBox has a CLI (<code class="language-plaintext highlighter-rouge">VBoxManage.exe</code>) which supports
all needed operations:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">startvm --type headless</code> to start the VM without having it pop up in my screen while I’m working.</li>
<li><code class="language-plaintext highlighter-rouge">controlvm poweroff</code> to turn off the machine and <code class="language-plaintext highlighter-rouge">snapshot restorecurrent</code> to fall back to the VirtualBox snapshot that brings the VM to the pristine state.</li>
<li><code class="language-plaintext highlighter-rouge">copyto</code> to copy files into the VM (the source code)</li>
<li><code class="language-plaintext highlighter-rouge">run</code> to run the build</li>
<li><code class="language-plaintext highlighter-rouge">copyfrom</code> to copy files out of the VM (the interesting files are the artifacts, exe and dll files, and the build log)</li>
</ul>
<p>Of course, in practice I encountered more problems. First of all,
the <code class="language-plaintext highlighter-rouge">startvm</code> command returns immediately (if this was a real
machine, it returns as soon as you press the power button to turn
the machine on). I can’t just start copying files into the VM yet,
Windows 2000 hasn’t loaded. As a workaround, I used the <code class="language-plaintext highlighter-rouge">guestcontrol stat C:\</code> command in a wait loop. This command checks if the C:\ drive
exists in the VM. If it succeeds it means not only that Windows 2000
has loaded, but also the VirtualBox Guest Extensions are ready (which is what enables file transfers).</p>
<p>The second problem was that file tranfers were unpredicatable: sometimes they would fail, or time out. In the end, I figured out that
this had to do with how VirtualBox logs in into the VM. There is an
added cost for that log in operation. To solve that, I configured
Windows 2000 to automatically log in with my username and make sure I
use that same user for the file transfers. This way, it seems VirtualBox is smart enough to reuse the same session and everything goes smooth.</p>
<p>Running the build itself was a bit tricky, but it worked.
The command is something like <code class="language-plaintext highlighter-rouge">$MSDEV $SlnInGuest /OUT $BuildLogAbsolute /MAKE ALL /REBUILD</code>, where:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">MSDEV</code> points to Visual Studio 6’s CLI <code class="language-plaintext highlighter-rouge">"C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin\MSDEV.COM"</code> (I think I had to use the .com instead of the .exe file…)</li>
<li><code class="language-plaintext highlighter-rouge">SlnInGuest</code> points to the solution file</li>
<li><code class="language-plaintext highlighter-rouge">BuildLogAbsolute</code> points to the build log file</li>
</ul>
<p>The reason for capturing the log file is that I couldn’t get the build
output to show directly in TeamCity. So instead I redirect it to a file, copy it out of the VM, and print it manually.</p>
<p>The exit code of MSDEV is fortunately returned by VirtualBox so I can indicate
if the build was successful or not, without having to do some more hacky stuff like searching for “ERROR” in the build log.</p>
<p>Overall, the approach worked and I never ended up in some unexpected
intermediate state where I had to go manually into the VM. I even configured TeamCity to publish the build status on GitHub. It is
kind of cool to see on GitHub “VC6 build passed” in 2021.</p>Nikolaos GeorgiouSome time ago, I thought of hacking on some really old C/C++ code I had written. I have a VirtualBox image running Windows 2000 and Visual Studio 6 (yes, that old). However, I also wanted to be able to run the code in modern Visual Studio 2019. Verifying that I haven’t broken anything means I would have to manually build my code on two different IDEs. That’s just boring. Instead, I hacked together a way of running my build for both Visual Studio versions.Git repo juggling2021-05-28T12:02:59+00:002021-05-28T12:02:59+00:00/2021/05/28/git-repo-juggling<p>This post shows how to use <code class="language-plaintext highlighter-rouge">git subtree</code> commands to move git repositories around.
Perfect if you’re indecisive about monorepo vs multirepo.</p>
<h2 id="move-a-repo-in-a-different-repo-as-a-subfolder">Move a repo in a different repo as a subfolder</h2>
<p>Use case: you want to move a library into a monorepo (because you read a post that says it’s cool).</p>
<p>Before:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Source repo Destination repo
| |
|-- src |-- packages
| \-- index.ts | \-- bar
|-- package.json \-- README.md
|-- package-lock.json
\-- README.md
</code></pre></div></div>
<p>After:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Destination repo
|
|-- packages
| |-- bar
| \-- foo (**moved here from source repo**)
| |-- src
| | \-- index.ts
| |-- package.json
| |-- package-lock.json
| \-- README.md
\-- README.md
</code></pre></div></div>
<p>Steps:</p>
<ul>
<li>Clone the source repo in a folder named <code class="language-plaintext highlighter-rouge">source</code></li>
<li>Clone the destination repo in a sibling folder named <code class="language-plaintext highlighter-rouge">destination</code></li>
<li>In the destination repo, run: <code class="language-plaintext highlighter-rouge">git subtree add -P packages/foo ../source master</code> (assuming the main branch is called <code class="language-plaintext highlighter-rouge">master</code>)</li>
<li>Delete the source repo (I’m sure you won’t regret it)</li>
</ul>
<h2 id="move-a-subfolder-to-a-new-repo">Move a subfolder to a new repo</h2>
<p>Use case: you want to move a library out of a monorepo into its own dedicated repo
(because you read a different post that says monorepos are actually not cool).</p>
<p>Before:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Source repo
|
|-- packages
| |-- bar
| \-- foo
| |-- package.json
| \-- README.md
\-- README.md
</code></pre></div></div>
<p>After:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Destination repo (**keeping only the contents of foo**)
|-- package.json
\-- README.md
</code></pre></div></div>
<p>Steps:</p>
<ul>
<li>Clone the source repo in a folder named <code class="language-plaintext highlighter-rouge">source</code></li>
<li>Prepare a new empty <strong>bare</strong> repo in a folder named <code class="language-plaintext highlighter-rouge">destination</code> (with <code class="language-plaintext highlighter-rouge">git init --bare</code>)</li>
<li>In the source folder, run <code class="language-plaintext highlighter-rouge">git subtree push -P packages/foo ../destination master</code></li>
<li>To double check everything went fine, you can clone the bare repo into a regular repo and inspect
its contents.</li>
<li>Delete the library from the source repo</li>
</ul>
<h2 id="move-a-subfolder-to-an-existing-repo">Move a subfolder to an existing repo</h2>
<p>Use case: you have two monorepos and you want to move one library from one to the other
(because the oxymoron of having multiple monorepos escapes you).</p>
<p>Before:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Source repo Destination repo
| |
|-- packages |-- libs
|-- bar \-- bar
\-- foo
</code></pre></div></div>
<p>After:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Source repo Destination repo
| |
|-- packages |-- libs
\-- foo |-- bar
\-- bar_v2 (** moved here from source repo where it was called bar **)
</code></pre></div></div>
<ul>
<li>Clone the source repository into a folder named <code class="language-plaintext highlighter-rouge">source</code></li>
<li>Clone the destination repository into a sibling folder named <code class="language-plaintext highlighter-rouge">destination</code></li>
<li>In the source repo, keep only the desired library with <code class="language-plaintext highlighter-rouge">git subtree split -P packages/bar -b temp</code> (temp is the name of a branch we’ll use next)</li>
<li>Checkout the <code class="language-plaintext highlighter-rouge">temp</code> branch and double check the source repo folder only has the desired library</li>
<li>In the destination repo, add the library with <code class="language-plaintext highlighter-rouge">git subtree add -P libs/bar_v2 ../source temp</code></li>
<li>Delete the library from the source repo</li>
</ul>
<h2 id="update-2021-10-03">Update: 2021-10-03</h2>
<p>I have a put together <a href="https://github.com/ngeor/dotfiles/blob/trunk/bin/git-juggle.sh">a script</a> that handles some of these cases
(especially the moving back to multirepo part).</p>Nikolaos GeorgiouThis post shows how to use git subtree commands to move git repositories around. Perfect if you’re indecisive about monorepo vs multirepo.Using Lerna for TypeScript and npm2021-05-13T07:08:32+00:002021-05-13T07:08:32+00:00/2021/05/13/using-lerna-for-typescript-and-npm<p>In this post, I’m showing a way to setup a monorepo with Lerna, taking into
account some pitfalls when publishing to npm.</p>
<p><a href="https://github.com/lerna/lerna">Lerna</a> is a tool for managing JavaScript
projects with multiple packages. In my very simple example, I have a project
with 3 packages: a library, a CLI, and a VS Code Extension. The library package
is used by both the CLI and the VS Code Extension packages.</p>
<p>Lerna makes the following things easy in my workflow:</p>
<ul>
<li>Linking dependent packages together for local development. Normally, you’d
have to use the <code class="language-plaintext highlighter-rouge">npm link</code> command (multiple times, depending on how many
packages you need to link). With Lerna, <code class="language-plaintext highlighter-rouge">lerna bootstrap</code> takes care of that
when you first clone a project (together with doing <code class="language-plaintext highlighter-rouge">npm install</code> for you).</li>
<li>Version management. Conceptually, my packages are all part of the same
project, so I would like all of them to have the same version. Lerna takes
care of that easily, the version is declared in the <code class="language-plaintext highlighter-rouge">lerna.json</code> file and
controls the version for all packages. Bumping the version is also easily done
with the <code class="language-plaintext highlighter-rouge">lerna version</code> command.</li>
<li>Publishing to npm. For my workflow, I like to publish all packages to npm,
even if they didn’t have any changes since the previous release. I’ll dive
into npm publishing in more details later, but this is done in two steps. On
my local machine, I use the <code class="language-plaintext highlighter-rouge">lerna version</code> command to bump the version. This
creates and pushes a git tag. On the CI side, I run the <code class="language-plaintext highlighter-rouge">lerna publish</code>
command whenever a new tag is pushed.</li>
</ul>
<h2 id="repo-structure">Repo structure</h2>
<p>Things get a bit more complicated because I use TypeScript instead of
JavaScript. I’m going to show a little bit the structure of my repo:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[packages]
|- [html-fmt-cli]
| |- [src]
| | |- index.ts
| | \- index.test.ts
| |- package.json
| |- tsconfig.json
|- [html-fmt-core]
\- [html-fmt-vscode]
README.md
lerna.json
package.json
</code></pre></div></div>
<p>On the top level, there’s the root <code class="language-plaintext highlighter-rouge">package.json</code> and <code class="language-plaintext highlighter-rouge">lerna.json</code> files. The
root <code class="language-plaintext highlighter-rouge">package.json</code> just lists Lerna as a dev dependency. It’s flagged as
private to prevent accidentally publishing this to npm. <code class="language-plaintext highlighter-rouge">lerna.json</code> holds the
version of the packages and indicates that packages are to be found under the
<code class="language-plaintext highlighter-rouge">packages</code> folder.</p>
<p>The three packages underneath the <code class="language-plaintext highlighter-rouge">packages</code> folder have a similar structure.
Inside you’ll find a <code class="language-plaintext highlighter-rouge">package.json</code> and a <code class="language-plaintext highlighter-rouge">tsconfig.json</code>. To avoid mixing the
code with other files, code is kept in a separate sub-directory, <code class="language-plaintext highlighter-rouge">src</code>. This
will make life a bit more complicated when publishing to npm, which I’ll discuss
later. Tests are kept side by side with the code they’re testing (e.g.
<code class="language-plaintext highlighter-rouge">index.test.ts</code> is the unit test file for <code class="language-plaintext highlighter-rouge">index.ts</code>). I like this option more
compared to keeping tests on a separate directory because it’s easy to locate
and easier to write the <code class="language-plaintext highlighter-rouge">import</code> statement without trying to figure out how many
<code class="language-plaintext highlighter-rouge">../</code> to add to match the directory structure.</p>
<h2 id="tsconfig">tsconfig</h2>
<p>Moving further to TypeScript, this is how my <code class="language-plaintext highlighter-rouge">tsconfig.json</code> looks like for the
library project, <code class="language-plaintext highlighter-rouge">html-fmt-core</code>:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"compilerOptions"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"declaration"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"outDir"</span><span class="p">:</span><span class="w"> </span><span class="s2">"./out"</span><span class="p">,</span><span class="w">
</span><span class="nl">"allowJs"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w">
</span><span class="nl">"target"</span><span class="p">:</span><span class="w"> </span><span class="s2">"es6"</span><span class="p">,</span><span class="w">
</span><span class="nl">"module"</span><span class="p">:</span><span class="w"> </span><span class="s2">"commonjs"</span><span class="p">,</span><span class="w">
</span><span class="nl">"moduleResolution"</span><span class="p">:</span><span class="w"> </span><span class="s2">"node"</span><span class="p">,</span><span class="w">
</span><span class="nl">"sourceMap"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w">
</span><span class="nl">"strict"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"include"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"src/**/*.ts"</span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>The key elements here are:</p>
<ul>
<li>Source code is in <code class="language-plaintext highlighter-rouge">src</code> folder</li>
<li>Output JavaScript code will be in <code class="language-plaintext highlighter-rouge">out</code> folder (sibling of <code class="language-plaintext highlighter-rouge">src</code>)</li>
<li>Generate declaration files (<code class="language-plaintext highlighter-rouge">"declaration": true</code>), this makes VS Code happy</li>
</ul>
<h2 id="main-script-and-subdirectories">Main script and subdirectories</h2>
<p>This brings us to the next point, specifying the entry point in <code class="language-plaintext highlighter-rouge">package.json</code>:</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
</span><span class="nl">"main"</span><span class="p">:</span><span class="w"> </span><span class="s2">"out/index.js"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>The complications start from the fact that the main file is in a subdirectory.
Let’s say that <code class="language-plaintext highlighter-rouge">index.js</code> exports a function named <code class="language-plaintext highlighter-rouge">hello</code>. You can use it in a
different package like you would expect:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">hello</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">html-fmt-core</span><span class="dl">"</span><span class="p">;</span>
</code></pre></div></div>
<p>Let’s say now that we have a file named <code class="language-plaintext highlighter-rouge">Formatter.ts</code> (compiled into
<code class="language-plaintext highlighter-rouge">Formatter.js</code>).</p>
<p>You would expect this might work:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">whatever</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">html-fmt-core/Formatter</span><span class="dl">"</span><span class="p">;</span>
</code></pre></div></div>
<p>but unfortunately it doesn’t. What does work is specifying the <code class="language-plaintext highlighter-rouge">out</code> directory:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">whatever</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">html-fmt-core/out/Formatter</span><span class="dl">"</span><span class="p">;</span>
</code></pre></div></div>
<p>To avoid this altogether, what I did is to re-export what I want in the
<code class="language-plaintext highlighter-rouge">index.ts</code> file like this:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="o">*</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./Formatter</span><span class="dl">"</span><span class="p">;</span>
</code></pre></div></div>
<p>and use it as if it was part of the index file:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">hello</span><span class="p">,</span> <span class="nx">whatever</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">html-fmt-core</span><span class="dl">"</span><span class="p">;</span>
</code></pre></div></div>
<p>Note that this complication is happening because I’ve got the source code in the
<code class="language-plaintext highlighter-rouge">src</code> folder and the generated code in the <code class="language-plaintext highlighter-rouge">out</code> folder. It is also possible to
avoid this pain by simply having the code at the root level of the package (side
by side with <code class="language-plaintext highlighter-rouge">package.json</code> and <code class="language-plaintext highlighter-rouge">tsconfig.json</code>) and output the generated code
also there (with some rules in <code class="language-plaintext highlighter-rouge">.gitignore</code> to avoid committing it to git
accidentally).</p>
<h2 id="publishing-to-npm">Publishing to npm</h2>
<p>As I said in the beginning, it’s easy to publish all lerna packages with one
command. With my current setup, I will have some problems:</p>
<ul>
<li>It will publish not only the JavaScript code but also the TypeScript code. I
would like it to publish only the JavaScript code (together with the
declaration files for TypeScript users). This can be solved with some
<code class="language-plaintext highlighter-rouge">.npmignore</code> lines but I’ll do it a bit differently.</li>
<li>It will publish not only the code, but also the tests.</li>
<li>My <code class="language-plaintext highlighter-rouge">README</code> file will be missing, because it’s at the root folder of the
<em>project</em> and I don’t have (and don’t want to have) a <code class="language-plaintext highlighter-rouge">README</code> per <em>package</em>.</li>
</ul>
<p>The thing is, my JavaScript code is already nicely contained in the <code class="language-plaintext highlighter-rouge">out</code>
folder, so I’d like to publish just that subdirectory. Lerna
<a href="https://github.com/lerna/lerna/tree/main/commands/publish#--contents-dir">supports this</a>,
with a very spot on description:</p>
<blockquote>
<p>If you’re into unnecessarily complicated publishing, this will give you joy.</p>
</blockquote>
<p>So as per the docs, I need to write a custom script that will run in the
<code class="language-plaintext highlighter-rouge">prepack</code> step and:</p>
<ul>
<li>create an artificial <code class="language-plaintext highlighter-rouge">package.json</code></li>
<li>copy the <code class="language-plaintext highlighter-rouge">README.md</code> from the project root into the generated package root</li>
</ul>
<p>My custom prepack script looks like this:</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">fs</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">"</span><span class="s2">fs</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">fs</span><span class="p">.</span><span class="nx">copyFileSync</span><span class="p">(</span><span class="dl">"</span><span class="s2">../../README.md</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">out/README.md</span><span class="dl">"</span><span class="p">);</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">fs</span><span class="p">.</span><span class="nx">existsSync</span><span class="p">(</span><span class="dl">"</span><span class="s2">.npmignore</span><span class="dl">"</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">fs</span><span class="p">.</span><span class="nx">copyFileSync</span><span class="p">(</span><span class="dl">"</span><span class="s2">.npmignore</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">out/.npmignore</span><span class="dl">"</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">let</span> <span class="nx">packageJson</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span>
<span class="nx">fs</span><span class="p">.</span><span class="nx">readFileSync</span><span class="p">(</span><span class="dl">"</span><span class="s2">package.json</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="na">encoding</span><span class="p">:</span> <span class="dl">"</span><span class="s2">utf8</span><span class="dl">"</span> <span class="p">})</span>
<span class="p">);</span>
<span class="nx">packageJson</span><span class="p">.</span><span class="nx">main</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">index.js</span><span class="dl">"</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">packageJson</span><span class="p">.</span><span class="nx">bin</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">packageJson</span><span class="p">.</span><span class="nx">bin</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">index.js</span><span class="dl">"</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">packageJson</span><span class="p">.</span><span class="nx">scripts</span> <span class="o">=</span> <span class="p">{};</span>
<span class="nx">fs</span><span class="p">.</span><span class="nx">writeFileSync</span><span class="p">(</span><span class="dl">"</span><span class="s2">out/package.json</span><span class="dl">"</span><span class="p">,</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">packageJson</span><span class="p">));</span>
</code></pre></div></div>
<ul>
<li>It copies over the README file from the project root into the output folder of
the package</li>
<li>If an <code class="language-plaintext highlighter-rouge">.npmignore</code> file exists on the package root, copy it over to the out
folder (I use the <code class="language-plaintext highlighter-rouge">.npmignore</code> file to ignore the tests)</li>
<li>Create an <code class="language-plaintext highlighter-rouge">out/package.json</code> based on the original with the following
alterations:
<ul>
<li>the main entry point will be <code class="language-plaintext highlighter-rouge">index.js</code> instead of <code class="language-plaintext highlighter-rouge">out/index.js</code></li>
<li>same for the <code class="language-plaintext highlighter-rouge">bin</code> script, if one exists (it exists for the CLI project)</li>
<li>clear out the <code class="language-plaintext highlighter-rouge">scripts</code> because why not</li>
</ul>
</li>
</ul>
<p>The publish command for this is <code class="language-plaintext highlighter-rouge">lerna publish -y --contents out from-package</code>.</p>
<h2 id="summary">Summary</h2>
<p>Looking back at this, perhaps the easiest way is to leave the TypeScript code at
the package root and let the output JavaScript code live there too. However, if
you like to have a separate source subdirectory and a separate output
subdirectory, it’s definitely possible, with a bit of extra work.</p>Nikolaos GeorgiouIn this post, I’m showing a way to setup a monorepo with Lerna, taking into account some pitfalls when publishing to npm.Learning QBasic part 22020-11-30T06:50:25+00:002020-11-30T06:50:25+00:00/2020/11/30/learning-qbasic-part-2<p>This is the 2nd part of learning QBasic, still figuring out how QBasic works in
order to write an interpreter for it.</p>
<p>Quick-ish summary of what was covered in the <a href="/2020/08/08/learning-qbasic.html">previous post</a>:</p>
<ul>
<li><strong>5 types</strong>. There are 5 built-in types: integer, long, single, double, string
(so 4 numeric types and one type for strings).</li>
<li><strong>Implicit & explicit variables</strong>. A variable can be explicitly declared with
the <code class="language-plaintext highlighter-rouge">DIM</code> statement, but it can also be implicitly declared simply by using it
(e.g. <code class="language-plaintext highlighter-rouge">A = 42</code>).</li>
<li><strong>Compact and extended DIM</strong>. A variable declaration can specify the type with
the <code class="language-plaintext highlighter-rouge">AS type</code> clause, in which case I call it <em>extended</em>. Example:
<code class="language-plaintext highlighter-rouge">DIM A AS STRING</code>. If it’s without the <code class="language-plaintext highlighter-rouge">AS type</code> clause, I call it <em>compact</em>.
Example: <code class="language-plaintext highlighter-rouge">DIM A</code>, <code class="language-plaintext highlighter-rouge">DIM A$</code>. Implicit variables are by definition compact.</li>
<li><strong>Bare and qualified variables</strong>. A variable name can be optionally followed
by a <em>type qualifier</em> to indicate its type. The qualifiers are <code class="language-plaintext highlighter-rouge">%</code> for
integer, <code class="language-plaintext highlighter-rouge">&</code> for long, <code class="language-plaintext highlighter-rouge">!</code> for single, <code class="language-plaintext highlighter-rouge">#</code> for double, and <code class="language-plaintext highlighter-rouge">$</code> for string.
Example: <code class="language-plaintext highlighter-rouge">Msg$ = "Hello, world!"</code>.</li>
<li><strong>Resolving type</strong>. The type of a bare compact variable is determined by its
name (in all other cases the type is known). The default type is single. The
<code class="language-plaintext highlighter-rouge">DEFxxx</code> statements can change the default, based on the first letter of the
variable name (e.g. a common idiom is <code class="language-plaintext highlighter-rouge">DEFINT A-Z</code> to make all bare compact
variables integers).</li>
</ul>
<p>You can read the <a href="/2020/08/08/learning-qbasic.html">previous post</a> for a bit more context on how all these behave
on edge cases.</p>
<h2 id="more-on-the-numeric-types">More on the numeric types</h2>
<p>The <code class="language-plaintext highlighter-rouge">LEN</code> built-in function can be used to measure the size of a variable. For
the built-in numeric types, it shows how many bytes they use. For the string
type, it shows the length of the string. Let’s use it to see how big the
built-in types are:</p>
<div class="language-vb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PRINT</span> <span class="s">"Size of integer is:"</span><span class="p">,</span> <span class="n">LEN</span><span class="p">(</span><span class="n">A</span><span class="err">%</span><span class="p">)</span>
<span class="n">PRINT</span> <span class="s">"Size of long is:"</span><span class="p">,</span> <span class="n">LEN</span><span class="p">(</span><span class="n">B</span><span class="o">&</span><span class="p">)</span>
<span class="n">PRINT</span> <span class="s">"Size of single is:"</span><span class="p">,</span> <span class="n">LEN</span><span class="p">(</span><span class="n">C</span><span class="p">!)</span>
<span class="n">PRINT</span> <span class="s">"Size of double is:"</span><span class="p">,</span> <span class="n">LEN</span><span class="p">(</span><span class="n">D</span><span class="p">#)</span>
</code></pre></div></div>
<p>This will print out 2 for integer (16 bits), 4 for long (32 bits), 4 for single
and 8 for double (64 bits). For the two integer types, the minimum and maximum
represented values are:</p>
<table>
<thead>
<tr>
<th>Type</th>
<th style="text-align: right">Min</th>
<th style="text-align: right">Max</th>
</tr>
</thead>
<tbody>
<tr>
<td>Integer</td>
<td style="text-align: right">-32,768</td>
<td style="text-align: right">32,767</td>
</tr>
<tr>
<td>Long</td>
<td style="text-align: right">-2,147,483,648</td>
<td style="text-align: right">2,147,483,647</td>
</tr>
</tbody>
</table>
<p>Side note: In the current implementation of rusty basic, I use 32 and 64 bit
types internally (<a href="https://doc.rust-lang.org/std/primitive.i32.html">i32</a> and
<a href="https://doc.rust-lang.org/std/primitive.i64.html">i64</a>) and the min-max limits
for each type is enforced programmatically. This might change in the future.</p>
<h3 id="literal-types">Literal types</h3>
<p>Given the min-max values described above, consider the following program:</p>
<div class="language-vb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PRINT</span> <span class="mi">32767</span> <span class="c1">' max int</span>
<span class="n">PRINT</span> <span class="mi">32768</span>
<span class="n">PRINT</span> <span class="mi">32767</span> <span class="o">+</span> <span class="mi">1</span> <span class="c1">' oops!</span>
<span class="n">PRINT</span> <span class="mi">32768</span> <span class="o">+</span> <span class="mi">1</span>
</code></pre></div></div>
<p>The program gives an overflow error on the third line. This might be
counterintuitive. If it can print the value 32,768 on the second line, why can’t
it print 32,767 + 1? It’s discoveries like these that shed some light on how
QBasic evaluates expressions under the hood (and help me with re-implementing
them in rusty basic). What (probably) happens is that QBasic tries to use the
<strong>smaller type that can hold the literal expression</strong>. On the first line, it
sees 32,767 and it stores it in a integer as it fits. On the second line, it
uses a long type. On the third line, there are two values which both fit in the
integer type on their own. Then, at runtime, it tries to add two integer values
together, leading to an overflow as the result doesn’t fit anymore.</p>
<p>It is possible to qualify a number with a type qualifier to be explicit about
its intended type (I haven’t fully implemented this in rusty basic yet):</p>
<div class="language-vb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">'PRINT 32767 + 1 ' overflow error!</span>
<span class="n">PRINT</span> <span class="mi">32767</span><span class="o">&</span> <span class="o">+</span> <span class="mi">1</span> <span class="c1">' works</span>
</code></pre></div></div>
<h2 id="constants">Constants</h2>
<p>QBasic supports constants with the <code class="language-plaintext highlighter-rouge">CONST</code> statement, e.g. <code class="language-plaintext highlighter-rouge">CONST Answer = 42</code>.
What we’ve discussed so far regarding variable names and types goes a bit
different for constants.</p>
<h3 id="type-comes-from-the-right-side">Type comes from the right side</h3>
<p>For bare constants, i.e. where the name isn’t followed by a type qualifier, it
is the constant value that determines the type of the constant. In the statement
<code class="language-plaintext highlighter-rouge">CONST A = 42</code>, the type of A will be an integer, despite the fact that we
haven’t added <code class="language-plaintext highlighter-rouge">DEFINT A-Z</code> in the program. We can even do <code class="language-plaintext highlighter-rouge">CONST A = "hello"</code>.
It is the type of the right hand expression that determines the type of the
constant.</p>
<p>Let’s see this with an example:</p>
<div class="language-vb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">DEFINT</span> <span class="n">A</span><span class="o">-</span><span class="n">Z</span> <span class="c1">' bare variables are integers</span>
<span class="n">A</span> <span class="o">=</span> <span class="mi">32768</span> <span class="c1">' overflow!</span>
<span class="n">CONST</span> <span class="n">B</span> <span class="o">=</span> <span class="mi">32768</span> <span class="c1">' allowed, B is long</span>
</code></pre></div></div>
<p>In the variable <code class="language-plaintext highlighter-rouge">A</code>, the type is derived from the name. We have a <code class="language-plaintext highlighter-rouge">DEFINT A-Z</code>
statement, which means any unqualified variable starting with a letter A-Z (so
all variables) is an integer. Since A is an integer, assigning the value 32768
to it results to an overflow error, as it doesn’t fit the integer type. For <code class="language-plaintext highlighter-rouge">B</code>
however, the type is determined by the const value. The smallest type that can
accommodate 32768 is long, so B becomes a long constant.</p>
<h3 id="constants-always-follow-extended-naming-rules">Constants always follow extended naming rules</h3>
<p>We saw in the <a href="/2020/08/08/learning-qbasic.html">previous post</a> that it’s possible to do this with compact style
variables:</p>
<div class="language-vb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">DIM</span> <span class="n">A</span><span class="err">$</span>
<span class="n">DIM</span> <span class="n">A</span><span class="err">%</span>
<span class="n">A</span><span class="err">$</span> <span class="o">=</span> <span class="s">"The answer is"</span>
<span class="n">A</span><span class="err">%</span> <span class="o">=</span> <span class="mi">42</span>
<span class="n">PRINT</span> <span class="n">A</span><span class="err">$</span><span class="p">,</span> <span class="n">A</span><span class="err">%</span>
</code></pre></div></div>
<p>but not with extended style variables:</p>
<div class="language-vb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">DIM</span> <span class="n">A</span> <span class="n">AS</span> <span class="n">STRING</span>
<span class="n">DIM</span> <span class="n">A</span><span class="err">%</span> <span class="c1">' duplicate definition!</span>
</code></pre></div></div>
<p>Constants behave always like extended style variables in this case, even though
they can only be declared in the compact style (there is no such thing as
<code class="language-plaintext highlighter-rouge">CONST A AS STRING = "hello"</code>).</p>
<p>So if we have a constant with <code class="language-plaintext highlighter-rouge">CONST Msg = "Hello"</code> (or even
<code class="language-plaintext highlighter-rouge">CONST Msg$ = "Hello"</code>):</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">Msg</code> and <code class="language-plaintext highlighter-rouge">Msg$</code> are both valid ways to reference that constant</li>
<li>It is not possible to reference <code class="language-plaintext highlighter-rouge">Msg</code> with any other qualifier, e.g. <code class="language-plaintext highlighter-rouge">Msg%</code>
gives a <em>duplicate definition</em> error</li>
<li>It is not possible to declare a variable named <code class="language-plaintext highlighter-rouge">Msg</code> (with or without
qualifier)</li>
<li>
<p>Within the same sub-program, it is not possible to declare another constant
named <code class="language-plaintext highlighter-rouge">Msg</code> (with or without qualifier). It is possible however to re-define a
constant within a sub-program (because why not):</p>
<div class="language-vb highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">CONST</span> <span class="n">Msg</span> <span class="o">=</span> <span class="s">"hi"</span>
<span class="n">PRINT</span> <span class="n">Msg</span> <span class="c1">' prints hi</span>
<span class="n">Greetings</span> <span class="c1">' prints 42</span>
<span class="n">SUB</span> <span class="n">Greetings</span>
<span class="c1">' shadow global constant with a local constant</span>
<span class="n">CONST</span> <span class="n">Msg</span> <span class="o">=</span> <span class="mi">42</span>
<span class="n">PRINT</span> <span class="n">Msg</span>
<span class="n">END</span> <span class="n">SUB</span>
<span class="n">SUB</span> <span class="n">Oops</span>
<span class="c1">' error: cannot shadow global constant with a variable</span>
<span class="n">DIM</span> <span class="n">Msg</span><span class="err">$</span>
<span class="n">END</span> <span class="n">SUB</span>
</code></pre></div> </div>
</li>
</ul>
<h2 id="wrap-up">Wrap up</h2>
<p>While writing rusty basic, one of the most challenging things was to identify
what a name is, and do it in the same way QBasic does. This is what we have so far:</p>
<h3 id="defining-a-constant">Defining a constant</h3>
<ul>
<li>Type determined by value, not from name</li>
<li>Cannot co-exist with constant or variable of the same name, qualified or unqualified</li>
<li>Can shadow global constant inside subprogram (<code class="language-plaintext highlighter-rouge">FUNCTION</code> or <code class="language-plaintext highlighter-rouge">SUB</code>)</li>
</ul>
<div class="mermaid">
graph TD
A[CONST A = 42] --> B{constant exists in scope?}
B --> |Yes| DD[Duplicate definition]
B --> |No| C{variable exists in scope?}
C --> |Yes| DD
C --> |No| S[Success]
</div>
<h3 id="defining-an-extended-variable">Defining an extended variable</h3>
<ul>
<li>Type provided by <code class="language-plaintext highlighter-rouge">AS type</code> clause</li>
<li>Cannot co-exist with constant of the same name, qualified or unqualified, local or global</li>
<li>Cannot co-exist with variable of the same name, qualified or unqualified</li>
</ul>
<div class="mermaid">
graph TD
A[DIM A AS STRING] --> B{const exists in *any* scope?}
B --> |Yes| DD[Duplicate definition]
B --> |No| C{variable exists in scope?}
C --> |Yes| DD
C --> |No| S[Success]
</div>
<h3 id="defining-a-compact-variable">Defining a compact variable</h3>
<ul>
<li>Type provided by qualifier if qualified, resolved by name if bare (affected by <code class="language-plaintext highlighter-rouge">DEFxxx</code> statements)</li>
<li>Cannot co-exist with constant of the same name, qualified or unqualified, local or global</li>
<li>Cannot co-exist with extended variable of the same name</li>
<li>Can co-exist with compact variables of the same name and different type</li>
</ul>
<div class="mermaid">
graph TD
A[DIM A$] --> B{const exists in *any* scope?}
B --> |Yes| DD[Duplicate definition]
B --> |No| C{extended variable exists in scope?}
C --> |Yes| DD
C --> |No| E{compact variable A$ exists in scope?}
E --> |Yes| DD
E --> |No| S[Success]
</div>Nikolaos GeorgiouThis is the 2nd part of learning QBasic, still figuring out how QBasic works in order to write an interpreter for it.Adding mermaid diagrams2020-11-27T20:42:47+00:002020-11-27T20:42:47+00:00/2020/11/27/mermaid<p>I noticed at work the other day that GitLab renders mermaid diagrams
automatically in README files. I wanted to implement the same for my blog.</p>
<p>In case you don’t know <a href="https://mermaid-js.github.io/mermaid/#/">mermaid</a>, it’s
a text base approach to creating diagrams such as flow charts, sequence diagrams,
etc. It’s similar to PlantUML, but this one can run in the browser too, which
makes it very easy to integrate. Here’s an example:</p>
<p>This is some text before the graph.</p>
<div class="mermaid">
graph TD
A[Client] --> B[Load Balancer]
B --> C[Server1]
B --> D[Server2]
</div>
<p>This is some text after the graph.</p>
<p>This is the code that renders the graph:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"mermaid"</span><span class="nt">></span>
graph TD
A[Client] --> B[Load Balancer]
B --> C[Server1]
B --> D[Server2]
<span class="nt"></div></span>
</code></pre></div></div>
<p>And enabling it on my blog is as simple as adding these lines before the closing <code class="language-plaintext highlighter-rouge"></body></code> tag:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><script </span><span class="na">src=</span><span class="s">"https://unpkg.com/mermaid@8.8.0/dist/mermaid.min.js"</span><span class="nt">></script></span>
<span class="nt"><script></span><span class="nx">mermaid</span><span class="p">.</span><span class="nx">initialize</span><span class="p">({</span><span class="na">startOnLoad</span><span class="p">:</span><span class="kc">true</span><span class="p">});</span><span class="nt"></script></span>
</code></pre></div></div>
<p><strong>Update 2020-11-30</strong>: I changed the last line to:</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><script></span><span class="nx">mermaid</span><span class="p">.</span><span class="nx">initialize</span><span class="p">({</span><span class="na">startOnLoad</span><span class="p">:</span><span class="kc">true</span><span class="p">,</span> <span class="na">flowchart</span><span class="p">:</span> <span class="p">{</span> <span class="na">useMaxWidth</span><span class="p">:</span> <span class="kc">true</span> <span class="p">}});</span><span class="nt"></script></span>
</code></pre></div></div>
<p>so that the size of the diagram fits mobile devices too.</p>Nikolaos GeorgiouI noticed at work the other day that GitLab renders mermaid diagrams automatically in README files. I wanted to implement the same for my blog.Dropwizard with Immutables2020-10-11T12:19:06+00:002020-10-11T12:19:06+00:00/2020/10/11/dropwizard-with-immutables<p>In this post, I’m using <a href="https://www.dropwizard.io/en/latest/">Dropwizard</a>
together with the <a href="https://immutables.github.io/">Immutables</a> library and
sharing some thoughts on OOP.</p>
<p>Dropwizard is a Java framework for developing RESTful web services. I’ve been
using it at work for almost a year now. It’s not a mega framework like Spring,
but it gets the job done.</p>
<p>Immutables is a Java library for creating immutable value objects.</p>
<p>Let’s imagine we have a Dropwizard resource that deals with books and has two
methods: one for returning a list of books and one for adding a new book:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@GET</span>
<span class="kd">public</span> <span class="nc">List</span><span class="o"><</span><span class="nc">Book</span><span class="o">></span> <span class="nf">getBooks</span><span class="o">()</span> <span class="o">{</span>
<span class="c1">// get the books</span>
<span class="o">}</span>
<span class="nd">@POST</span>
<span class="kd">public</span> <span class="nc">Response</span> <span class="nf">addBook</span><span class="o">(</span><span class="nd">@NotNull</span> <span class="nd">@Valid</span> <span class="nc">Book</span> <span class="n">book</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// add a book</span>
<span class="o">}</span>
</code></pre></div></div>
<p>The most common way of implementing <code class="language-plaintext highlighter-rouge">Book</code> is to create a POJO class, with its
getters and setters, in 20 lines of traditionally verbose Java code:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Book</span> <span class="o">{</span>
<span class="kd">private</span> <span class="nc">String</span> <span class="n">isbn</span><span class="o">;</span>
<span class="kd">private</span> <span class="nc">String</span> <span class="n">title</span><span class="o">;</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">getIsbn</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">isbn</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setIsbn</span><span class="o">(</span><span class="nc">String</span> <span class="n">isbn</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">isbn</span> <span class="o">=</span> <span class="n">isbn</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="nf">getTitle</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">title</span><span class="o">;</span>
<span class="o">}</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setTitle</span><span class="o">(</span><span class="nc">String</span> <span class="n">title</span><span class="o">)</span> <span class="o">{</span>
<span class="k">this</span><span class="o">.</span><span class="na">title</span> <span class="o">=</span> <span class="n">title</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>You could also skip the getters and setters and go for public fields:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Book</span> <span class="o">{</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="n">isbn</span><span class="o">;</span>
<span class="kd">public</span> <span class="nc">String</span> <span class="n">title</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></div>
<p>In OOP, the above is frowned upon (and that’s putting it mildly). Exposing
public fields is a violation of one of the basic principles of OOP. So why would
you go for this?</p>
<p>In my mind, OOP design is about classes that have state and behaviour (fields
and methods). That’s all totally valid. But, when you’re programming methods
that receive or return JSON objects, these classes are supposed to be nothing
more than that. I think the fact that Java is adding
<a href="https://blogs.oracle.com/javamagazine/records-come-to-java">records</a>
(<a href="https://devblogs.microsoft.com/dotnet/welcome-to-c-9-0/#records">C# too by the way</a>)
is a recognition to the fact that data transfer objects deserve to have a
first-class support without having to write unnecessary boilerplate just to
avoid the stigma of the heretic.</p>
<p>When I think of practices like immutability, or favoring composition over
inheritance, I wonder if perhaps there ought to be a book “OOP - the good
parts”.</p>
<p>Back to the Immutables library: I like to use Immutables instead of the two
above approaches (especially when it comes to responses):</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Value</span><span class="o">.</span><span class="na">Immutable</span>
<span class="nd">@JsonSerialize</span><span class="o">(</span><span class="n">as</span> <span class="o">=</span> <span class="nc">ImmutableBook</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="nd">@JsonDeserialize</span><span class="o">(</span><span class="n">as</span> <span class="o">=</span> <span class="nc">ImmutableBook</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">interface</span> <span class="nc">Book</span> <span class="o">{</span>
<span class="nc">String</span> <span class="nf">getIsbn</span><span class="o">();</span>
<span class="nc">String</span> <span class="nf">getTitle</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Validation works as expected (e.g. let’s demand that both ISBN and title are not
blank):</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">Book</span> <span class="o">{</span>
<span class="nd">@NotBlank</span>
<span class="nc">String</span> <span class="nf">getIsbn</span><span class="o">();</span>
<span class="nd">@NotBlank</span>
<span class="nc">String</span> <span class="nf">getTitle</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Caveat: it only works when using getter style methods. It is not unusual for
Immutables to use a different naming convention for the methods like
<code class="language-plaintext highlighter-rouge">String isbn()</code>. In that case, validation doesn’t work.</p>
<p>Creating the responses is done with a builder object:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nc">List</span><span class="o"><</span><span class="nc">Book</span><span class="o">></span> <span class="nf">getBooks</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="nc">List</span><span class="o">.</span><span class="na">of</span><span class="o">(</span>
<span class="nc">ImmutableBook</span><span class="o">.</span><span class="na">builder</span><span class="o">()</span>
<span class="o">.</span><span class="na">isbn</span><span class="o">(</span><span class="s">"978-0078817038"</span><span class="o">)</span>
<span class="o">.</span><span class="na">title</span><span class="o">(</span><span class="s">"Turbo PASCAL 6: The Complete Reference (Programming series)"</span><span class="o">)</span>
<span class="o">.</span><span class="na">build</span><span class="o">()</span>
<span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>I personally like this approach more, especially because of the builder object.</p>Nikolaos GeorgiouIn this post, I’m using Dropwizard together with the Immutables library and sharing some thoughts on OOP.Learning QBasic part 12020-08-08T05:20:58+00:002020-08-08T05:20:58+00:00/2020/08/08/learning-qbasic<p>I’ve been writing an interpreter for QBasic for quite some time now, since March
28th to be precise. This was something I always wanted to do. In the process, I
am learning Rust, which I like a lot. But, as I implement feature after feature,
I am learning QBasic as well, especially things a developer normally never has
to worry about.</p>
<h2 id="dynamic-typing-nope">Dynamic Typing? Nope</h2>
<p>The first misconception I had was that QBasic is a dynamic typed language. In
Ruby, you can do the following:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">i</span> <span class="o">=</span> <span class="mi">42</span>
<span class="nb">puts</span> <span class="n">i</span>
<span class="n">i</span> <span class="o">=</span> <span class="s2">"is the answer"</span>
<span class="nb">puts</span> <span class="n">i</span>
</code></pre></div></div>
<p>The variable <code class="language-plaintext highlighter-rouge">i</code> gets first the numeric value 42 and then the string value “is
the answer”.</p>
<p>In QBasic, that doesn’t work. You can’t re-assign a variable to a different
type. In fact, it goes a bit beyond that. The name of the variable dictates the
type of values it can hold.</p>
<pre><code class="language-basic">A = 42 ' works
A = "oops" ' does not work
</code></pre>
<p>In my interpreter, <a href="https://github.com/ngeor/rusty-basic">rusty basic</a>, I refer
to these variable names as <strong>bare</strong>. They consist of just letters and optionally
numbers, e.g. <code class="language-plaintext highlighter-rouge">A</code>, <code class="language-plaintext highlighter-rouge">B</code>, <code class="language-plaintext highlighter-rouge">Age</code>, <code class="language-plaintext highlighter-rouge">MysteriousVariable42</code>.</p>
<p>Side note: I figured out variable names in QBasic can also contain dots for
crying out loud, which I haven’t supported yet.</p>
<p>By default, <em>bare</em> names hold float values of single precision. So the
assignment <code class="language-plaintext highlighter-rouge">A = 42</code> is actually creating a single value. To create a different
variable, we need to use what I call in rusty basic a <strong>qualified</strong> name: a
variable name followed by a <strong>type qualifier</strong>. There are five such characters:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">%</code> declares an integer</li>
<li><code class="language-plaintext highlighter-rouge">&</code> declares a long</li>
<li><code class="language-plaintext highlighter-rouge">!</code> declares a single (which is the default)</li>
<li><code class="language-plaintext highlighter-rouge">#</code> declares a double</li>
<li><code class="language-plaintext highlighter-rouge">$</code> declares a string</li>
</ul>
<p>Some examples:</p>
<pre><code class="language-basic">A% = 42
B& = 5653376574648
C! = 3.14
D# = 45644564.3353
E$ = "hello world"
</code></pre>
<p>When coding the interpreter, I often worry about edge cases. Things that a
developer wouldn’t normally do, but out of curiosity I dive into. An example is,
what would happen if I define the same variable name with different type
qualifiers:</p>
<pre><code class="language-basic">A% = 42
A$ = "the answer"
PRINT A%, A$
</code></pre>
<p>Will the above program work? Turns out it works just fine, as far as QBasic is
concerned, <code class="language-plaintext highlighter-rouge">A%</code> and <code class="language-plaintext highlighter-rouge">A$</code> are two different variables.</p>
<h2 id="default-type-resolution">Default type resolution</h2>
<p>I mentioned that a bare variable holds a single float value by default. Consider
the following program:</p>
<pre><code class="language-basic">A = 41
A! = A! + 1
PRINT A
</code></pre>
<p>It prints 42. As the <code class="language-plaintext highlighter-rouge">A</code> variable is a single, it is resolved to <code class="language-plaintext highlighter-rouge">A!</code>. <code class="language-plaintext highlighter-rouge">A</code> and
<code class="language-plaintext highlighter-rouge">A!</code> are the same variable.</p>
<p>It is possible to change the default type to one of the other 4 types with the
keywords <code class="language-plaintext highlighter-rouge">DEFINT</code> (for integers), <code class="language-plaintext highlighter-rouge">DEFLNG</code> (for longs), <code class="language-plaintext highlighter-rouge">DEFDBL</code> (for doubles)
and <code class="language-plaintext highlighter-rouge">DEFSTR</code> (for strings). There’s also of course <code class="language-plaintext highlighter-rouge">DEFSNG</code> for singles. In
fact, in the games that came with QBasic, Gorillas and Nibbles, the first
statement in the game is:</p>
<pre><code class="language-basic">' Set default data type to integer for faster game play
DEFINT A-Z
</code></pre>
<p>The above statement says that any variable that starts with a letter between A
and Z (so all variables) is an integer. I don’t know why integers aren’t the
default to begin with, might be some backwards compatibility issue with an even
older BASIC flavor.</p>
<p>If we use this statement in the above program:</p>
<pre><code class="language-basic">DEFINT A-Z
A = 41
A! = A! + 1
PRINT A
</code></pre>
<p>It will print 41 instead. Now we have two variables <code class="language-plaintext highlighter-rouge">A</code> (which is now an
integer, so <code class="language-plaintext highlighter-rouge">A%</code>) and <code class="language-plaintext highlighter-rouge">A!</code>.</p>
<p>In rusty-basic, I have a layer called “linting” which sits between getting the
parse tree and generating instructions. Among other things, the linter makes
sure that all bare names are resolved to qualified names and that there are no
mismatch types or duplicate definitions.</p>
<h2 id="making-it-more-complicated-dim-a-as-string">Making it more complicated: DIM A AS STRING</h2>
<p>I’m currently in the process of implementing the <code class="language-plaintext highlighter-rouge">DIM</code> statement. With <code class="language-plaintext highlighter-rouge">DIM</code>,
you can declare a variable without an assignment. It’s even possible to declare
arrays and user defined types, but I haven’t gone that far in implementation.</p>
<p>There’s two ways you can declare a variable with <code class="language-plaintext highlighter-rouge">DIM</code>. The one is exactly the
same as seen so far:</p>
<pre><code class="language-basic">DIM A
DIM A$
A = 42
PRINT A!
A$ = "the answer"
PRINT A$
</code></pre>
<p>The program works fine. The variable <code class="language-plaintext highlighter-rouge">A</code> is a bare variable resolved to a single
(so it’s the same as <code class="language-plaintext highlighter-rouge">A!</code>) and <code class="language-plaintext highlighter-rouge">A$</code> is a different variable.</p>
<p>It gets complicated when using the second way of declaring a variable:</p>
<pre><code class="language-basic">DIM A AS INTEGER
A = 42
PRINT A
</code></pre>
<p>The keywords that match the five data types are <code class="language-plaintext highlighter-rouge">INTEGER</code>, <code class="language-plaintext highlighter-rouge">LONG</code>, <code class="language-plaintext highlighter-rouge">SINGLE</code>,
<code class="language-plaintext highlighter-rouge">DOUBLE</code> and <code class="language-plaintext highlighter-rouge">STRING</code>.</p>
<p>For the lack of a better name, I call this an <strong>extended</strong> variable (naming
things is hard) while I call the variables seen earlier as <strong>compact</strong>. It’s
important to flag this as something special in the interpreter because it
follows different naming resolution rules.</p>
<p>An extended variable cannot coexist with other variables of the same bare name.
The following throw a <em>duplicate definition</em> error:</p>
<p>Trying to use the same extended variable with different type (this one makes
sense):</p>
<pre><code class="language-basic">DIM A AS INTEGER
DIM A AS STRING
</code></pre>
<p>Trying to use an extended integer with a bare compact (which should be single):</p>
<pre><code class="language-basic">DIM A AS INTEGER
DIM A
</code></pre>
<p>Trying to use an extended integer with a compact string:</p>
<pre><code class="language-basic">DIM A AS INTEGER
DIM A$
</code></pre>
<p>Trying to use an extended integer with an implicit compact string on assignment:</p>
<pre><code class="language-basic">DIM A AS INTEGER
A$ = "hello"
</code></pre>
<p>Side note: every such edge case discovery has become a unit test in rusty basic.</p>
<p>You could argue at least the latter might be something the interpreter should
allow, given it allows it for compact style variables, but it doesn’t.</p>
<p>The next difference is that <code class="language-plaintext highlighter-rouge">A</code> now is resolved to <code class="language-plaintext highlighter-rouge">A%</code> (as it declared as an
integer), bypassing the default type resolution rules:</p>
<pre><code class="language-basic">DIM A AS INTEGER
A% = 42
PRINT A
</code></pre>
<p>So it’s still possible to use a type qualifier, as long as it matches the type
declared at the <code class="language-plaintext highlighter-rouge">DIM</code> statement.</p>
<p>Again, these are all probably things that a developer wouldn’t do. It would be
also tempting on my side to just say “a variable must be unique, throw an error
otherwise” and call it a day. But I’m curious to discover how it is supposed to
work.</p>Nikolaos GeorgiouI’ve been writing an interpreter for QBasic for quite some time now, since March 28th to be precise. This was something I always wanted to do. In the process, I am learning Rust, which I like a lot. But, as I implement feature after feature, I am learning QBasic as well, especially things a developer normally never has to worry about.