<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="http://witiko.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="http://witiko.github.io/" rel="alternate" type="text/html" /><updated>2026-02-16T12:26:15+00:00</updated><id>http://witiko.github.io/feed.xml</id><title type="html">Vít Starý Novotný</title><subtitle>A Pony from the Bo-hay-mia</subtitle><entry><title type="html">Static analysis of expl3 programs (11½): Chunks, edges, flow graphs, confidence, and reaching definitions</title><link href="http://witiko.github.io/Expl3-Linter-11.5/" rel="alternate" type="text/html" title="Static analysis of expl3 programs (11½): Chunks, edges, flow graphs, confidence, and reaching definitions" /><published>2025-11-24T00:00:00+00:00</published><updated>2026-01-27T00:00:00+00:00</updated><id>http://witiko.github.io/Expl3-Linter-11.5</id><content type="html" xml:base="http://witiko.github.io/Expl3-Linter-11.5/"><![CDATA[<p>Over the past two months, I released <a href="https://github.com/Witiko/expltools/releases/tag/2025-10-04">three</a> <a href="https://github.com/Witiko/expltools/releases/tag/2025-10-22">new</a> <a href="https://github.com/Witiko/expltools/releases/tag/2025-11-24">updates</a> of
<a href="https://ctan.org/pkg/expltools"><em>expltools</em></a>, the bundle that provides the <em>explcheck</em> static analyzer for
the expl3 programming language.</p>

<p>These updates include major improvements but did not yet advance the final
stage of the pipeline, <strong>flow analysis</strong>, which I teased in the <a href="/Expl3-Linter-11">previous
post</a>. That’s because our work on <strong>flow analysis</strong> so far has been
groundwork: figuring out how to adapt static-analysis techniques to expl3
before moving on to implementation.</p>

<p>In this post, I outline the flow graph structure currently used to represent
expl3 code and describe our adaptation of <a href="https://en.wikipedia.org/wiki/Reaching_definition#As_analysis">the reaching definitions
algorithm</a> for dynamically scoped languages like expl3.</p>

<!-- more -->

<h1 id="chunks-edges-and-flow-graphs">Chunks, edges, and flow graphs</h1>
<p>In the <a href="/Expl3-Linter-11">previous post</a>, we introduced <a href="/Expl3-Linter-11#segments"><em>segments</em></a> as the units of
top-level code in expl3 parts and nested code in T- and F-branches of
conditionals, function definitions, and others. Each segment contains zero or
more <a href="/Expl3-Linter-9/#what-is-semantic-analysis">statements</a> found during <strong>semantic analysis</strong>.</p>

<p><strong>Flow analysis</strong> links statements using directed <em>edges</em> to form a <em>flow
graph</em>. It also partitions each segment into <em>chunks</em>: sequences of recognized
statements separated by <code class="language-plaintext highlighter-rouge">OTHER_TOKENS_COMPLEX</code> statements, which contain
arbitrary TeX code that may have side effects.</p>

<figure>
  <a href="/images/er-diagram-2.svg" target="_blank">
    <img src="/images/thumbnails/er-diagram-2.png" alt="Entity relationship diagram of the analysis data model, highlighting our representation of input files." />
  </a>
  
    <figcaption>Image: <em>Entity relationship diagram of the analysis data model, highlighting our representation of input files.</em>
    </figcaption>
  
</figure>

<h1 id="static-edges-and-confidence">Static edges and confidence</h1>
<p>Some edges arise directly from the static structure of an expl3 program. These
are known without extra analysis and are called <em>static edges</em>.</p>

<h2 id="following-statements-and-chunks-conditionals">Following statements and chunks, conditionals</h2>
<p>Within a chunk, statements typically follow one another deterministically.
<em>Explcheck</em> does not record these next-statement edges explicitly, but the
analysis implicitly assumes them to be present.</p>

<p>The most common explicitly recorded static edge is <code class="language-plaintext highlighter-rouge">NEXT_CHUNK</code>, which
represents moving to the following chunk. We add <code class="language-plaintext highlighter-rouge">NEXT_CHUNK</code> edges as follows:</p>

<ol>
  <li>Connect the last statement of each chunk in an expl3 part to the first
statement of the first chunk in the next expl3 part.</li>
  <li>Connect the last statement of each chunk to the first statement of the next
chunk in the same segment.</li>
</ol>

<p>Another common static edge is <code class="language-plaintext highlighter-rouge">NEXT_FILE</code>, which represents inputting a different
file from the current <a href="/Expl3-Linter-11#inter-file-dependencies"><em>file group</em></a>:</p>

<ol>
  <li>Connect the last statement of every file to the first statement of every
other file.</li>
</ol>

<p>Other common static edges are <code class="language-plaintext highlighter-rouge">TF_BRANCH</code> and <code class="language-plaintext highlighter-rouge">TF_BRANCH_RETURN</code>:</p>

<ol>
  <li>For each conditional function, add a <code class="language-plaintext highlighter-rouge">TF_BRANCH</code> edge to the first statements
of the first chunks in its T- and F-branches.</li>
  <li>For each T- and F-branch, add a <code class="language-plaintext highlighter-rouge">TF_BRANCH_RETURN</code> edge from the last
statement of its last chunk to the statement following its conditional
function.</li>
</ol>

<h2 id="confidence">Confidence</h2>
<p>All edges carry varying degrees of <em>confidence</em>: statements do not always follow
one another deterministically, TeX code between chunks can alter the execution
state in arbitrary ways, and transitions between files can be speculative.</p>

<p>For implicit next-statement edges, the confidence is usually <code class="language-plaintext highlighter-rouge">DEFINITELY</code>. It is
weakened to <code class="language-plaintext highlighter-rouge">MAYBE</code> when the current statement may be a call to a user-defined
function. For confirmed calls, the edge is removed entirely, because control flow
enters the function and returns later rather than advancing directly to the
following statement. We will discuss how function calls are represented and
resolved in the following section.</p>

<p>For <code class="language-plaintext highlighter-rouge">NEXT_CHUNK</code> and <code class="language-plaintext highlighter-rouge">NEXT_FILE</code> edges, the confidence is usually <code class="language-plaintext highlighter-rouge">MAYBE</code>; it
becomes <code class="language-plaintext highlighter-rouge">DEFINITELY</code> only for <code class="language-plaintext highlighter-rouge">NEXT_CHUNK</code> edges between immediately adjacent
expl3 parts.</p>

<p>For <code class="language-plaintext highlighter-rouge">TF_BRANCH</code> and <code class="language-plaintext highlighter-rouge">TF_BRANCH_RETURN</code> edges, the confidence is always
<code class="language-plaintext highlighter-rouge">DEFINITELY</code>, because exactly one branch will execute for any given call and
control will always return from it.</p>

<h3 id="weakening-confidence">Weakening confidence</h3>

<p>Although <code class="language-plaintext highlighter-rouge">TF_BRANCH</code> and <code class="language-plaintext highlighter-rouge">TF_BRANCH_RETURN</code> edges are individually marked as
<code class="language-plaintext highlighter-rouge">DEFINITELY</code>, the combination of multiple incoming or outgoing edges can still
introduce ambiguity about which path was actually taken: if both <code class="language-plaintext highlighter-rouge">T</code>- and
<code class="language-plaintext highlighter-rouge">F</code>-branches are present, only one will execute, and if a conditional function is
called from multiple places, the origin of control flow becomes statically
ambiguous within the function body.</p>

<p>To address this, later analyses typically weaken confidence based on edge
multiplicity.</p>

<p>For forward analyses such as <a href="https://en.wikipedia.org/wiki/Reaching_definition#As_analysis">reaching definitions</a>, edges whose endpoint has
in-degree greater than one are weakened to <code class="language-plaintext highlighter-rouge">MAYBE</code>. For example, this ensures
that function definitions coming from only one of the <code class="language-plaintext highlighter-rouge">T</code>- or <code class="language-plaintext highlighter-rouge">F</code>-branches are
appropriately weakened before control returns to the caller, indicating that the
definition may not exist depending on which branch was taken.</p>

<p>For backward analyses such as <a href="https://en.wikipedia.org/wiki/Live-variable_analysis">live variable analysis</a>, edges whose
starting point has out-degree greater than one are weakened to <code class="language-plaintext highlighter-rouge">MAYBE</code>. For
example, this ensures that variable liveness is weakened before entering a
function with several possible definitions, reflecting the fact that only some
of those definitions may use the variable.</p>

<p>We may also skip the weakening if the definitions reach both the <code class="language-plaintext highlighter-rouge">T</code>- and
<code class="language-plaintext highlighter-rouge">F</code>-branches, or if the variable is used in all possible definitions of the
called function, even when the in- or out-degree is greater than one.</p>

<h1 id="dynamic-edges-and-reaching-definitions">Dynamic edges and reaching definitions</h1>
<p>Other edges depend on runtime behavior and dynamic scoping. These require
estimation and are called <em>dynamic edges</em>.</p>

<h2 id="function-definitions-and-calls">Function definitions and calls</h2>
<p>A key example is function calls and returns: <code class="language-plaintext highlighter-rouge">FUNCTION_CALL</code> and
<code class="language-plaintext highlighter-rouge">FUNCTION_CALL_RETURN</code>. Determining them requires knowing which function
definitions may reach each call.</p>

<p>If a function name has only one definition, we might assume that definition is
used. But that definition may not reach the call. And often, multiple
definitions share the same name, so we must determine which ones are used.</p>

<p>We start by building only the static edges. Then, we run <a href="https://en.wikipedia.org/wiki/Reaching_definition#As_analysis">the reaching
definitions algorithm</a>. Afterwards, we insert the dynamic edges:</p>

<ol>
  <li>For each function call, add a <code class="language-plaintext highlighter-rouge">FUNCTION_CALL</code> edge to the first statement of
the first chunk in every definition that reaches it.</li>
  <li>For each definition, add a <code class="language-plaintext highlighter-rouge">FUNCTION_CALL_RETURN</code> edge from the last
statement of its last chunk to the statement following any call it can reach.</li>
</ol>

<p>For <code class="language-plaintext highlighter-rouge">FUNCTION_CALL</code> and <code class="language-plaintext highlighter-rouge">FUNCTION_CALL_RETURN</code> edges, the confidence is the lower
of the minimum confidence along the path from the definition to the call, and the
confidence of the definition statement itself.</p>

<h2 id="dynamic-function-definitions">Dynamic function definitions</h2>
<p>The reaching definitions algorithm does not capture definitions created
dynamically inside function calls. Consider the following example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\cs_new:Nn
  \example_bar:
  { bar }
\cs_new:Nn
  \example_foo:
  {
    \cs_set:Nn
      \example_bar:
      { baz }
  }
\example_foo:
\example_bar:
</code></pre></div></div>

<p>The final call to <code class="language-plaintext highlighter-rouge">\example_bar:</code> should use the redefinition inside
<code class="language-plaintext highlighter-rouge">\example_foo:</code> (expands to <code class="language-plaintext highlighter-rouge">baz</code>), not the original definition on the first
three lines (expands to <code class="language-plaintext highlighter-rouge">bar</code>). A naive pass would get this wrong.</p>

<p>To handle cases like this, we iterate:</p>

<ol>
  <li>Add dynamic edges based on the current reaching definitions.</li>
  <li>Run reaching definitions again.</li>
  <li>Replace the old dynamic edges with the new ones.</li>
  <li>Repeat until nothing changes.</li>
</ol>

<p>Intuitively, the process converges because each round finishes some calls
(their reaching definitions no longer depend on other unfinished calls),
and the set of unfinished calls strictly decreases. When no edges change, we
have reached a fixed point.</p>

<p>In the above example, both calls start out as unfinished. After the first
iteration, the call to <code class="language-plaintext highlighter-rouge">\example_foo:</code> finishes, but the call to
<code class="language-plaintext highlighter-rouge">\example_bar:</code> remains unfinished. After the second iteration, the
<code class="language-plaintext highlighter-rouge">\example_bar:</code> call also finishes because its reaching definition
shifts to the redefinition inside <code class="language-plaintext highlighter-rouge">\example_foo:</code>. No unfinished calls remain,
no edges change, and the algorithm stabilizes.</p>

<h2 id="limitations">Limitations</h2>
<p>While the above algorithm works for straightforward expl3 programs, it cannot
detect function definitions or calls that arise only through dynamic expansion
and are not obvious from the surface text of the program. This limitation is
inherent to any static analyzer of a highly dynamic language such as TeX.</p>

<p>While some simple cases may become supported in the future, any attempt to
model complex dynamic behavior is constrained both by our incomplete knowledge
of the execution environment and by the requirement that the analysis must
always terminate in a predictable number of steps.</p>

<h1 id="get-involved">Get involved!</h1>

<p>Your feedback is invaluable. Feel free to contribute or share your thoughts by
visiting <a href="https://mirrors.ctan.org/support/expltools/doc/warnings-and-errors.pdf">the project repository</a>. More improvements are on the way as
<em>explcheck</em> continues to grow.</p>]]></content><author><name></name></author><category term="expl3" /><category term="LaTeX" /><category term="programming" /><category term="devlog" /><summary type="html"><![CDATA[Over the past two months, I released three new updates of expltools, the bundle that provides the explcheck static analyzer for the expl3 programming language. These updates include major improvements but did not yet advance the final stage of the pipeline, flow analysis, which I teased in the previous post. That’s because our work on flow analysis so far has been groundwork: figuring out how to adapt static-analysis techniques to expl3 before moving on to implementation. In this post, I outline the flow graph structure currently used to represent expl3 code and describe our adaptation of the reaching definitions algorithm for dynamically scoped languages like expl3.]]></summary></entry><entry><title type="html">Static analysis of expl3 programs (11): Inter-file dependencies, segments, and code coverage</title><link href="http://witiko.github.io/Expl3-Linter-11/" rel="alternate" type="text/html" title="Static analysis of expl3 programs (11): Inter-file dependencies, segments, and code coverage" /><published>2025-09-30T00:00:00+00:00</published><updated>2026-02-16T00:00:00+00:00</updated><id>http://witiko.github.io/Expl3-Linter-11</id><content type="html" xml:base="http://witiko.github.io/Expl3-Linter-11/"><![CDATA[<p>Today, I’m excited to release the next major <a href="https://github.com/Witiko/expltools/releases/tag/2025-09-29">update</a> to <a href="https://ctan.org/pkg/expltools"><em>expltools</em></a>, a bundle that includes the static analysis tool <em>explcheck</em> for the expl3 programming language. This update improves upon the <strong>semantic analysis</strong> processing step introduced in the <a href="/Expl3-Linter-10">previous post</a> and lays groundwork for the final processing step of <strong>flow analysis</strong>.</p>

<!-- more -->

<h1 id="inter-file-dependencies">Inter-file dependencies</h1>

<p>The original <a href="https://tug.org/tc/devfund/documents/2024-09-expltools.pdf">project proposal</a> assumed that files would be checked individually. In practice, this limited the <strong>semantic analysis</strong> of multi-file projects, where one file defines a symbol and another uses it. To address this, the new release introduces the concept of <em>file groups</em>: sets of files that are assumed to be used together.</p>

<p>For example, suppose we have a file <code class="language-plaintext highlighter-rouge">foo.tex</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\cs_new:Nn
  \__example_foo:n
  { foo }
\cs_generate_variant:Nn
  \__example_foo:n
  { V }
</code></pre></div></div>

<p>and another file <code class="language-plaintext highlighter-rouge">bar.tex</code>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\__example_foo:V
  \l_tmpa_tl
</code></pre></div></div>

<p>Previously, running <code class="language-plaintext highlighter-rouge">explcheck foo.tex bar.tex</code> produced:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Checking 2 files

Checking foo.tex                                         1 warning

    foo.tex:4:1:              W402 unused private function variant

Checking bar.tex                                           1 error

    bar.tex:1:1:                E408 calling an undefined function

Total: 1 error, 1 warning in 2 files
</code></pre></div></div>

<p>With the new release, files from the same directory are automatically grouped, and the result changes to:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Checking 2 files

Checking foo.tex                                                OK
Checking bar.tex                                                OK

Total: 0 errors, 0 warnings in 2 files
</code></pre></div></div>

<p>This greatly reduces false positives.</p>

<p>You can control this behavior with the <code class="language-plaintext highlighter-rouge">--group-files</code> option or by using <code class="language-plaintext highlighter-rouge">+</code> and <code class="language-plaintext highlighter-rouge">,</code> between filenames to group or separate them.</p>

<p>In addition, <em>explcheck</em> will report missing symbols from external packages. To suppress these, use the <code class="language-plaintext highlighter-rouge">imported_prefixes</code> option in your configuration.</p>

<h1 id="segments">Segments</h1>

<p>Earlier versions of <em>explcheck</em> had special-case support for only one kind of nested code: function replacement texts. For instance, consider the example from a <a href="/Expl3-Linter-8.5">previous post</a>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\cs_new:Nn
  \example_foo:n
  {
    \cs_new:Nn
      \example_bar:n
      {
        #1,~##1!
      }
  }
\example_foo:n { Hello }
\example_bar:n { world }
</code></pre></div></div>

<p>Here we have three top-level calls (<code class="language-plaintext highlighter-rouge">\cs_new:Nn</code>, <code class="language-plaintext highlighter-rouge">\example_foo:n</code>, <code class="language-plaintext highlighter-rouge">\example_bar:n</code>) and two replacement texts.</p>

<p>But expl3 contains many more forms of nested code: <code class="language-plaintext highlighter-rouge">T</code>- and <code class="language-plaintext highlighter-rouge">F</code>-branches of conditional functions, loop bodies (e.g., <code class="language-plaintext highlighter-rouge">\ior_map_inline:Nn</code>), key–value definitions, and more. Ad-hoc support for each would be brittle, and the upcoming <strong>flow analysis</strong> requires a uniform way to connect code across different nesting types and levels.</p>

<p>To address this, the new release introduces <em>segments</em>, a unified representation for both top-level and nested code. This simplifies implementation and makes future extensions easier.</p>

<figure>
  <a href="/images/er-diagram.svg" target="_blank">
    <img src="/images/thumbnails/er-diagram.png" alt="Entity relationship diagram of the analysis data model, highlighting our representation of input files." />
  </a>
  
    <figcaption>Image: <em>Entity relationship diagram of the analysis data model, highlighting our representation of input files.</em>
    </figcaption>
  
</figure>

<p>The first new segment type added is <a href="https://github.com/witiko/expltools/issues/92">support for <code class="language-plaintext highlighter-rouge">T</code>- and <code class="language-plaintext highlighter-rouge">F</code>-branches of conditional functions</a>, and future updates will continue expanding segment coverage.</p>

<h1 id="code-coverage">Code coverage</h1>

<p>To show how well <em>explcheck</em> understands a piece of code, the <code class="language-plaintext highlighter-rouge">--verbose</code> output now reports <strong>code coverage</strong>: the ratio of <em>well-understood</em> expl3 tokens to the total number of tokens. A token is well-understood if it is either simple text or part of an analyzed argument within a recognized statement in the most deeply nested segment containing it.</p>

<p>For example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\cs_new:Nn
  \example_baz:n
  { \unexpected }
some~text
</code></pre></div></div>

<ul>
  <li>The nine tokens in <code class="language-plaintext highlighter-rouge">some~text</code> are well-understood (simple text).</li>
  <li>The four tokens <code class="language-plaintext highlighter-rouge">\cs_new:Nn</code>, <code class="language-plaintext highlighter-rouge">\example_baz:n</code>, <code class="language-plaintext highlighter-rouge">{</code>, and <code class="language-plaintext highlighter-rouge">}</code> are well-understood (recognized statement).</li>
  <li>The token <code class="language-plaintext highlighter-rouge">\unexpected</code> is <em>not</em> well-understood, because there are no recognized statements in the replacement text.</li>
</ul>

<p>Thus, coverage = 13/14 tokens ≈ 93%.</p>

<p>For context, <em>explcheck</em> currently achieves ~21% code coverage across all expl3 code in TeX Live. Future releases aim to raise this figure by recognizing more statements, segment types, and plain TeX / LaTeX2e constructs.</p>

<h1 id="working-with-the-community">Working with the community</h1>

<p>Since March, I’ve been reaching out to package maintainers with reports of issues found by <em>explcheck</em>. Of 11 reported issues, 8 have already been fixed:</p>

<ol>
  <li><a href="https://github.com/BITNP/BIThesis/issues/604">BITNP/BIThesis#604</a>: fixed</li>
  <li><a href="https://github.com/jspitz/jslectureplanner/issues/7">jspitz/jslectureplanner#7</a>: fixed</li>
  <li><a href="https://github.com/dbitouze/nwejm/issues/5">dbitouze/nwejm#5</a></li>
  <li><a href="https://github.com/dbitouze/gzt/issues/56">dbitouze/gzt#56</a></li>
  <li><a href="https://github.com/michal-h21/responsive-latex/issues/1">michal-h21/responsive-latex#1</a>: fixed</li>
  <li><a href="https://github.com/fpantigny/nicematrix/issues/12">fpantigny/nicematrix#12</a>: fixed</li>
  <li><a href="https://github.com/josephwright/siunitx/issues/796">josephwright/siunitx#796</a>: wontfix</li>
  <li><a href="https://github.com/BITNP/BIThesis/issues/640">BITNP/BIThesis#640</a>: fixed</li>
  <li><a href="https://github.com/se2p/se2thesis/issues/23">se2p/se2thesis#23</a>: fixed</li>
  <li><a href="https://github.com/John02139/asmeconf/issues/11">John02139/asmeconf#11</a>: fixed</li>
  <li><a href="https://dickimaw-books.com/bugtracker.php?key=303">dickimaw-books.com#303</a>: fixed</li>
</ol>

<p>Following today’s update, at least 15 new potential issues were detected in public TeX Live repositories. Reports have been submitted, including:</p>

<ol>
  <li>An email to Martin Hensel about <a href="https://koppor.github.io/explcheck-issues/texmf-dist/tex/latex/mhchem/mhchem.sty">the issues detected in package mhchem</a></li>
  <li>An email to Herbert Voß about <a href="https://koppor.github.io/explcheck-issues/texmf-dist/tex/latex/hvarabic/hvarabic.sty">the issues detected in package hvarabic</a></li>
  <li>An email to Andrew Parsloe about the issues detected in <a href="https://ctan.org/author/parsloe">their many packages</a></li>
  <li><a href="https://gitee.com/nwafu_nan/nwafuthesis-l3/issues/ID0M68">nwafu_nan/nwafuthesis-l3#ID0M68</a></li>
  <li><a href="https://gitee.com/nwafu_nan/chinesechess/issues/ID0MAM">nwafu_nan/chinesechess#ID0MAM</a></li>
  <li><a href="https://github.com/samcarter/beamertheme-tcolorbox/issues/5">samcarter/beamertheme-tcolorbox#5</a></li>
  <li><a href="https://github.com/hust-latex/hustthesis/issues/45">hust-latex/hustthesis#45</a></li>
  <li><a href="https://github.com/Zeta611/simplebnf/issues/10">Zeta611/simplebnf#10</a></li>
  <li><a href="https://github.com/irenier/sysuthesis/issues/1">irenier/sysuthesis#1</a></li>
  <li><a href="https://github.com/slatex/sTeX/issues/453">slatex/sTeX#453</a></li>
  <li><a href="https://github.com/dcpurton/scripture/issues/117">dcpurton/scripture#117</a></li>
  <li><a href="https://github.com/luatexja/luatexja/issues/35">luatexja/luatexja#35</a></li>
  <li><a href="https://github.com/leo-colisson/robust-externalize/issues/59">leo-colisson/robust-externalize#59</a></li>
  <li><a href="https://github.com/Vidabe/cooking-units/issues/32">Vidabe/cooking-units#32</a></li>
  <li><a href="https://github.com/dcpurton/scripture/issues/117">fpantigny/nicematrix#117</a></li>
</ol>

<p>Let’s see how many more we can fix before the next update! 😉</p>

<h1 id="get-involved">Get involved!</h1>

<p>Your feedback is invaluable. Feel free to contribute or share your thoughts by visiting <a href="https://github.com/Witiko/expltools/">the project repository</a>. More improvements are on the way as <em>explcheck</em> continues to grow.</p>]]></content><author><name></name></author><category term="expl3" /><category term="LaTeX" /><category term="programming" /><category term="devlog" /><summary type="html"><![CDATA[Today, I’m excited to release the next major update to expltools, a bundle that includes the static analysis tool explcheck for the expl3 programming language. This update improves upon the semantic analysis processing step introduced in the previous post and lays groundwork for the final processing step of flow analysis.]]></summary></entry><entry><title type="html">Static analysis of expl3 programs (9): Semantic analysis</title><link href="http://witiko.github.io/Expl3-Linter-9/" rel="alternate" type="text/html" title="Static analysis of expl3 programs (9): Semantic analysis" /><published>2025-08-18T00:00:00+00:00</published><updated>2025-08-18T00:00:00+00:00</updated><id>http://witiko.github.io/Expl3-Linter-9</id><content type="html" xml:base="http://witiko.github.io/Expl3-Linter-9/"><![CDATA[<p>Today, I’m excited to release the next major <a href="https://github.com/Witiko/expltools/releases/tag/2025-08-18">update</a> to <a href="https://ctan.org/pkg/expltools"><em>expltools</em></a>, a bundle that includes the static analysis tool <em>explcheck</em> for the expl3 programming language. This update finishes the <strong>semantic analysis</strong>, completing four out of five planned processing steps.</p>

<!-- more -->

<h1 id="what-is-semantic-analysis">What is semantic analysis?</h1>

<p>Syntactic analysis is the stage where expl3 function calls identified during <strong>syntactic analysis</strong> (as discussed in <a href="/Expl3-Linter-8">my previous blog post</a>) are classified as statements.</p>

<p>For example, consider the following expl3 code snippet:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\tl_new:N
  \l_example_tl
\tl_set:Nn
  \l_example_tl
  { foo~bar }
</code></pre></div></div>

<p>In this example, semantic analysis successfully recognizes two statements:</p>

<ol>
  <li>A variable <code class="language-plaintext highlighter-rouge">\l_example_tl</code> is declared.</li>
  <li>The variable <code class="language-plaintext highlighter-rouge">\l_example_tl</code> is defined as “foo bar”.</li>
</ol>

<p>Additionally, it warns that the variable <code class="language-plaintext highlighter-rouge">\l_example_tl</code> seems unused.</p>

<p>The semantic analysis is the most complex processing step thus far and took four months to complete, as detailed in two of my previous <a href="/Expl3-Linter-8.5">blog</a> <a href="/Expl3-Linter-8.75">posts</a>.</p>

<h1 id="whats-next-flow-analysis">What’s next? Flow analysis</h1>

<p>In the following months, I plan to implement <strong>flow analysis</strong>. This next step aims to determine the data and control flows in expl3 programs and answer advanced questions, such as:</p>

<ul>
  <li>“Is there any dead code that is never executed?”</li>
  <li>“Is a boolean expression fully-expandable?”</li>
  <li>“Do we always open a stream before accessing it and close it afterwards?”</li>
</ul>

<h2 id="im-in-over-my-head">“I’m in over my head!”</h2>

<p>Unlike the <strong>semantic analysis</strong>, which is mostly context-free and can be conducted even when large parts of the expl3 program are not understood, the <strong>flow analysis</strong> requires near-complete understanding of the program, which we usually only have for the simplest of programs at this point.</p>

<p>I foresaw this problem in the <a href="https://tug.org/tc/devfund/documents/2024-09-expltools.pdf">project proposal</a> and suggested the following approach:</p>

<blockquote>
  <p>The initial version of the linter may not have a sufficient understanding of expl3 code to support proper flow analysis. Instead, the initial version of the linter may need to use pseudo-flow-analysis that would check for simple cases of the warnings and errors from flow analysis. Future versions of the linter should improve their code understanding to the point where proper flow analysis can be performed.</p>
</blockquote>

<p>However, this solution would require building a separate processing step (<strong>pseudo-flow-analysis</strong>) that would need to be fully replaced later, which doesn’t seem economical.</p>

<p>Instead, the ticket <a href="https://github.com/Witiko/expltools/issues/124">witiko/expltools#124</a> introduced a new mechanism that allows a processing step to say “I’m in over my head!” and stop the processing early. This way, only code that is well-understood will be processed by the <strong>flow analysis</strong>, whereas more complex code will stop at the <strong>semantic analysis</strong>.</p>

<h1 id="get-involved">Get involved!</h1>

<p>Your feedback is invaluable. Feel free to contribute or share your thoughts by visiting <a href="https://github.com/Witiko/expltools/">the project repository</a>. Stay tuned for more updates as <em>explcheck</em> continues to evolve!</p>]]></content><author><name></name></author><category term="expl3" /><category term="LaTeX" /><category term="programming" /><category term="devlog" /><summary type="html"><![CDATA[Today, I’m excited to release the next major update to expltools, a bundle that includes the static analysis tool explcheck for the expl3 programming language. This update finishes the semantic analysis, completing four out of five planned processing steps.]]></summary></entry><entry><title type="html">Static analysis of expl3 programs (10): Expltools goes to India</title><link href="http://witiko.github.io/Expl3-Linter-10/" rel="alternate" type="text/html" title="Static analysis of expl3 programs (10): Expltools goes to India" /><published>2025-07-22T00:00:00+00:00</published><updated>2025-09-19T00:00:00+00:00</updated><id>http://witiko.github.io/Expl3-Linter-10</id><content type="html" xml:base="http://witiko.github.io/Expl3-Linter-10/"><![CDATA[<p>Yesterday I returned from TUG 2025 in Trivandrum, Kerala (July 18–20, 2025),
where I was thrilled to present <a href="https://youtu.be/ImCoKj_kLw4?t=50m23s">my talk</a> announcing the development of
<a href="https://ctan.org/pkg/expltools"><em>expltools</em></a> to the broader TeX community.</p>

<p>In this post, I will try to briefly summarize my experience in the form of a
travel diary while it is still fresh in my mind. I’ll concentrate on my own
experiences rather than the full program details. If you’d like to explore the
agenda itself, please consult <a href="https://tug.org/tug2025/program.html">the conference program</a> and watch the
YouTube recordings here: <a href="https://www.youtube.com/watch?v=Eu_qM53tInw">day one, part one</a> and <a href="https://www.youtube.com/watch?v=CRYYps0usiA">part two</a>, <a href="https://www.youtube.com/watch?v=ImCoKj_kLw4">day
two</a>, and <a href="https://www.youtube.com/watch?v=gR3F3GmK0lo">day three</a>.</p>

<!-- more -->

<h1 id="july-16-getting-from-brno-to-trivandrum">July 16: Getting from Brno to Trivandrum</h1>

<p>Early in the morning on July 16, I took a train from Brno to Prague.</p>

<figure>
  <a href="/images/20250716_054327.jpg" target="_blank">
    <img src="/images/thumbnails/20250716_054327.jpg" alt="Getting up-to-date on the current gaming news with the LEVEL magazine" />
  </a>
  
    <figcaption>Image: <em>Getting up-to-date on the current gaming news with the LEVEL magazine</em>
    </figcaption>
  
</figure>

<p>There, I boarded a flight from Prague to Abu Dhabi and then a connecting flight
to Trivandrum, where I arrived early in the morning of the next day.</p>

<p>After getting to the hotel, I slept until shortly before the noon.</p>

<figure>
  <a href="/images/20250717_061518.jpg" target="_blank">
    <img src="/images/thumbnails/20250717_061518.jpg" alt="My hotel room at the Hyatt Regency hotel just before taking a rest after the flight" />
  </a>
  
    <figcaption>Image: <em>My hotel room at the Hyatt Regency hotel just before taking a rest after the flight</em>
    </figcaption>
  
</figure>

<figure>
  <a href="/images/20250717_105906.jpg" target="_blank">
    <img src="/images/thumbnails/20250717_105906.jpg" alt="A view of the city from the east-facing window of my hotel room" />
  </a>
  
    <figcaption>Image: <em>A view of the city from the east-facing window of my hotel room</em>
    </figcaption>
  
</figure>

<h1 id="july-17-exploring-trivandrum">July 17: Exploring Trivandrum</h1>

<p>This was my first time visiting India. Therefore, after I woke up, I spent the
rest of the day walking around the city.</p>

<figure>
  <a href="/images/20250717_135331.jpg" target="_blank">
    <img src="/images/thumbnails/20250717_135331.jpg" alt="The Centre for Development of Advanced Computing" />
  </a>
  
    <figcaption>Image: <em>The Centre for Development of Advanced Computing</em>
    </figcaption>
  
</figure>

<figure>
  <a href="/images/20250717_140316.jpg" target="_blank">
    <img src="/images/thumbnails/20250717_140316.jpg" alt="The Kanakakunnu Palace" />
  </a>
  
    <figcaption>Image: <em>The Kanakakunnu Palace</em>
    </figcaption>
  
</figure>

<figure>
  <a href="/images/20250717_145607.jpg" target="_blank">
    <img src="/images/thumbnails/20250717_145607.jpg" alt="A clock tower at the entrance of the University of Kerala" />
  </a>
  
    <figcaption>Image: <em>A clock tower at the entrance of the University of Kerala</em>
    </figcaption>
  
</figure>

<figure>
  <a href="/images/20250717_150815.jpg" target="_blank">
    <img src="/images/thumbnails/20250717_150815.jpg" alt="A hindu temple near the Connemara Market" />
  </a>
  
    <figcaption>Image: <em>A hindu temple near the Connemara Market</em>
    </figcaption>
  
</figure>

<figure>
  <a href="/images/20250717_161523.jpg" target="_blank">
    <img src="/images/thumbnails/20250717_161523.jpg" alt="Electrocution waiting to happen" />
  </a>
  
    <figcaption>Image: <em>Electrocution waiting to happen</em>
    </figcaption>
  
</figure>

<p>Before returning to the hotel, I grabbed a late dinner at the <a href="https://www.instagram.com/food_text_trivandrum/">FOOD TEXT</a>
restaurant in the Palayam district.</p>

<figure>
  <a href="/images/20250717_154253.jpg" target="_blank">
    <img src="/images/thumbnails/20250717_154253.jpg" alt="About to order chicken biriani with two chicken wings and rice" />
  </a>
  
    <figcaption>Image: <em>About to order chicken biriani with two chicken wings and rice</em>
    </figcaption>
  
</figure>

<p>The food was delicious and I spend just about 250 ₹ for the meal and a glass of
ginger limeade. In Prague, I can imagine paying 250 Kč for the same meal, so the
pricing seemed intuitive. At the same time, Kč (Czech koruna) currently trades
for about four ₹ (Indian rupees), so the meal was extremely inexpensive from my
vantage point.</p>

<p>I returned to the hotel in the evening and took close to 10 hours of sleep,
still feeling somewhat exhausted from the long flight in addition to today’s ca
15 km walk.</p>

<h1 id="july-18-the-first-day-of-the-conference">July 18: The first day of the conference</h1>

<p>The next day, I woke up early and had a breakfast at the hotel before heading
to the conference room and receiving my welcome package there.</p>

<figure>
  <a href="/images/20250720_081332.jpg" target="_blank">
    <img src="/images/thumbnails/20250720_081332.jpg" alt="Pazhankanji, a traditional South Indian dish made from leftover rice and water" />
  </a>
  
    <figcaption>Image: <em>Pazhankanji, a traditional South Indian dish made from leftover rice and water</em>
    </figcaption>
  
</figure>

<figure>
  <a href="/images/20250718_094628.jpg" target="_blank">
    <img src="/images/thumbnails/20250718_094628.jpg" alt="A welcome package, which also included a book for LaTeX beginners" />
  </a>
  
    <figcaption>Image: <em>A welcome package, which also included <a href="https://books.sayahna.org/en/pdf/primer-2.rc1.pdf">a book for LaTeX beginners</a></em>
    </figcaption>
  
</figure>

<p>During a coffee break after the morning session of talks, I introduced myself
to Ben and Mathias from Overleaf. On Sunday, Mathias was going to give a talk
titled <a href="https://www.tug.org/tug2025/abstracts/jakobsen-parsing-editing.txt"><em>Best-effort TeX parsing for interactive editing</em></a> (<a href="https://www.tug.org/tug2025/av/d3-t25-jakobsen-parsing-editing/d3-t25-jakobsen-parsing-editing-slides.pdf">slides</a>),
which I expected to be the most closely related talk to mine at this
conference.</p>

<p>During the lunch, I introduced myself to Honza Vaněk and Hàn Thế Thành, two out
of four founders of the Czech company <a href="https://trivic.io/">Trivic</a>. Trivic develops proprietary
XML-based systems for copyediting, proofreading, and typesetting manuscripts,
all powered by TeX as the backend. Indian firms such as <a href="https://stmdocs.in/">STMDocs</a> deploy
these systems to fulfill orders for major publishers like Elsevier. Both Honza
and Thành are alumni of the Masaryk University, like myself.</p>

<p>In the evening, several participants, me included, took a guided bus tour
around the city. We drove almost 40 km, visiting some places that I already saw
yesterday as well as more distant places such as the commercial district of
Pulimoodu, the Sree Padmanabha Swamy Temple, and the coastline.</p>

<figure>
  <a href="/images/20250718_193511.jpg" target="_blank">
    <img src="/images/thumbnails/20250718_193511.jpg" alt="The commercial district of Pulimoodu" />
  </a>
  
    <figcaption>Image: <em>The commercial district of Pulimoodu</em>
    </figcaption>
  
</figure>

<figure>
  <a href="/images/20250718_194855.jpg" target="_blank">
    <img src="/images/thumbnails/20250718_194855.jpg" alt="Puthen Kovil Sree Bhagavathy Temple" />
  </a>
  
    <figcaption>Image: <em>Puthen Kovil Sree Bhagavathy Temple</em>
    </figcaption>
  
</figure>

<p>There was a heavy monsoon rain during the second half of the tour, which made
most participants hide in the lower floor of the bus.</p>

<p>After returning from the tour, I went to bed early, so that I would be fresh
for my morning talk about <em>expltools</em>.</p>

<h1 id="july-19-the-second-day-of-the-conference">July 19: The second day of the conference</h1>

<p>After a light breakfast, I gave a talk titled <a href="https://www.tug.org/tug2025/abstracts/starynovotny-expltools.txt"><em>Expltools: Development tools
for expl3 programmers</em></a> (<a href="https://www.tug.org/tug2025/av/d2-t11-starynovotny-expltools/d2-t11-starynovotny-expltools-slides.pdf">slides</a>):</p>

<figure>
  <iframe width="720" height="405" src="https://www.youtube.com/embed/ImCoKj_kLw4?start=3023&amp;end=5615&amp;feature=oembed" frameborder="0" allowfullscreen=""></iframe>
  <figcaption>Video: <em>“TUG 2025&thinsp;—&thinsp;Day 2 Saturday Stream” at YouTube, my talk starts at 50:23</em></figcaption>
</figure>

<p>Despite my stage fright and the initial hiccups with the microphones, I felt
the talk went smoothly. During the Q&amp;A session, the questions asked
confirmed that the audience had a strong understanding of the material that I
covered in my talk.</p>

<p>During the following coffee break, CV Radhakrishnan — a
founding member of <a href="https://tug.org.in/">the Indian TeX Users Group</a>, <a href="https://rivervalley.io/">River Valley
Technologies</a>, and STMDocs  — offered to help secure additional
funding for <em>expltools</em> development. This is an encouraging vote of confidence
in both the tool’s usefulness and the success of my presentation!</p>

<p>During lunch, Mathias and I revisited topics from my talk. He showed me <a href="https://github.com/overleaf/overleaf/blob/0546fb72332a820dc5eb2754a2fd149aacc8fdc0/services/web/frontend/js/features/source-editor/lezer-latex/latex.grammar">the
grammar their (La)TeX parser used</a>, which struck me as more elegant than
<em>expltools</em>’ pipeline approach — tokenizing the text first, then
using a rule-based parser to assemble larger structures. I pointed out,
however, that while less elegant, <em>expltools</em>’ method is inherently more robust:
it retains partial understanding of complex expl3 code that can’t be fully
parsed, and uses this to reduce false positives. That exchange led me to <a href="https://github.com/Witiko/expltools-tug25-paper/commit/b9b4ff95af27bb6281d1828da31367348b0b9239">draft
a paragraph</a> in our work-in-progress paper on <em>expltools</em> to highlight
precisely this advantage.</p>

<p>After lunch, I took to the streets of Trivandrum once more, retracing on foot
some of the sights from yesterday’s bus-tour.</p>

<figure>
  <a href="/images/20250719_140207.jpg" target="_blank">
    <img src="/images/thumbnails/20250719_140207.jpg" alt="The Methan Mani clock tower (left) and the Sree Padmanabha Swamy Temple (right) over the Padmatheertha Pond" />
  </a>
  
    <figcaption>Image: <em>The Methan Mani clock tower (left) and the Sree Padmanabha Swamy Temple (right) over the Padmatheertha Pond</em>
    </figcaption>
  
</figure>

<figure>
  <a href="/images/20250719_140658.jpg" target="_blank">
    <img src="/images/thumbnails/20250719_140658.jpg" alt="A detail of the Methan Mani clock tower" />
  </a>
  
    <figcaption>Image: <em>A detail of the Methan Mani clock tower</em>
    </figcaption>
  
</figure>

<figure>
  <a href="/images/20250719_141740.jpg" target="_blank">
    <img src="/images/thumbnails/20250719_141740.jpg" alt="A Mahatma Gandhi statue in the Gandhi Park" />
  </a>
  
    <figcaption>Image: <em>A Mahatma Gandhi statue in the Gandhi Park</em>
    </figcaption>
  
</figure>

<figure>
  <a href="/images/20250719_142042.jpg" target="_blank">
    <img src="/images/thumbnails/20250719_142042.jpg" alt="The bustling bazaar along the Chalakkambalam Road" />
  </a>
  
    <figcaption>Image: <em>The bustling bazaar along the Chalakkambalam Road</em>
    </figcaption>
  
</figure>

<figure>
  <a href="/images/20250719_151832.jpg" target="_blank">
    <img src="/images/thumbnails/20250719_151832.jpg" alt="Butter chicken with rice and fresh mango juice at the Statue restaurant (Palayam)" />
  </a>
  
    <figcaption>Image: <em>Butter chicken with rice and fresh mango juice at the Statue restaurant (Palayam)</em>
    </figcaption>
  
</figure>

<p>That evening, a group of participants, including myself, attended a
sitar-and-drum recital at a nearby theatre, followed by a Kathakali dance
performance.</p>

<figure>
  <a href="/images/20250719_183545.jpg" target="_blank">
    <img src="/images/thumbnails/20250719_183545.jpg" alt="A concert featuring sitar and drums" />
  </a>
  
    <figcaption>Image: <em>A concert featuring sitar and drums</em>
    </figcaption>
  
</figure>

<figure>
  <audio controls="">
   <source src="/sounds/20250719_183545.m4a" type="audio/mp4" />
   Your browser does not support the audio tag.
  </audio>
  
    <figcaption>Audio: <em>A concert featuring sitar and drums</em></figcaption>
  
</figure>

<figure>
  <a href="/images/20250719_195454.jpg" target="_blank">
    <img src="/images/thumbnails/20250719_195454.jpg" alt="A Kathakali dance performance" />
  </a>
  
    <figcaption>Image: <em>A Kathakali dance performance</em>
    </figcaption>
  
</figure>

<figure>
   <video poster="/videos/thumbnails/20250719_192800.webm.jpg" controls="">
    <source src="/videos/20250719_192800.webm" type="video/webm" />
    Your browser does not support the video tag.
  </video> 
  
    <figcaption>Video: <em>A Kathakali dance performance</em></figcaption>
  
</figure>

<h1 id="july-20-the-third-and-last-day-of-the-conference">July 20: The third (and last) day of the conference</h1>

<p>I woke up early and packed all my belongings for the 12 PM check-out at the
hotel.</p>

<p>At breakfast, Erik Nijenhuis, the vice-president of TUG, taught me the Dutch
word <em>Kaaskop</em> (<em>cheese-head</em>). Apparently, some Dutch speakers, such as Erik,
use it to describe devoted cheese lovers, while non-Dutchers sometimes employ
it as a lighthearted insult toward the Dutch.</p>

<p>Before lunch, I snapped photos with several attendees, caught up with Honza
Vaněk and Thành about the latest news from our alma mater, and assisted
Jean-Michel Hufflen in porting LaTeX2e code to LaTeX3.</p>

<figure>
  <a href="/images/20250720_091308.jpg" target="_blank">
    <img src="/images/thumbnails/20250720_091308.jpg" alt="T. Rishikesan Nair speaks about beauty and purpose" />
  </a>
  
    <figcaption>Image: <em>T. Rishikesan Nair speaks about beauty and purpose</em>
    </figcaption>
  
</figure>

<p>During the lunch, I spoke with Vrajaraja Govinda from <a href="https://www.iskcon.org/">the International
Society for Krishna Consciousness (ISKCON)</a> in Belagavi about the adoption
of GNU/Linux and (La)TeX in India and what can be done to spread awareness.</p>

<p>After lunch, the TUG Annual General Meeting convened under the chairmanship of
former TUG president and current board member Boris Veytsman. Financially, TUG
remains in the black, with its largest expenses devoted to printing the issues
of TUGboat and to accounting salaries. However, membership has been steadily
declining, and this year it dropped below the 1,000-member mark.</p>

<p>After the last session of talks, there was a calligraphic workshop by <a href="https://www.instagram.com/nbhattathiri/">Narayana
Bhattathiri</a>.</p>

<figure>
  <a href="/images/20250720_164936.jpg" target="_blank">
    <img src="/images/thumbnails/20250720_164936.jpg" alt="Calligraphy by Narayana: “Letters are Beautiful” rendered in multiple scripts" />
  </a>
  
    <figcaption>Image: <em>Calligraphy by Narayana: “Letters are Beautiful” rendered in multiple scripts</em>
    </figcaption>
  
</figure>

<figure>
  <a href="/images/20250720_165904.jpg" target="_blank">
    <img src="/images/thumbnails/20250720_165904.jpg" alt="The name Frank Mittelbach, drawn by Narayana" />
  </a>
  
    <figcaption>Image: <em>The name Frank Mittelbach, drawn by Narayana</em>
    </figcaption>
  
</figure>

<figure>
  <a href="/images/20250720_171502.jpg" target="_blank">
    <img src="/images/thumbnails/20250720_171502.jpg" alt="My GitHub handle, “witiko”, drawn by Narayana" />
  </a>
  
    <figcaption>Image: <em>My GitHub handle, “witiko”, drawn by Narayana</em>
    </figcaption>
  
</figure>

<figure>
  <a href="/images/20250720_180055.jpg" target="_blank">
    <img src="/images/thumbnails/20250720_180055.jpg" alt="Thành's request: the Vietnamese text “Nhà khủng lồ” (“a giant house”)" />
  </a>
  
    <figcaption>Image: <em>Thành's request: the Vietnamese text “Nhà khủng lồ” (“a giant house”)</em>
    </figcaption>
  
</figure>

<p>Shortly after the workshop, I said my goodbyes and left for the Trivandrum
airport.</p>

<figure>
  <a href="/images/20250720_200822.jpg" target="_blank">
    <img src="/images/thumbnails/20250720_200822.jpg" alt="Statues of a Kathakali dancer (top) and Nandi, the sacred vehicle of Lord Shiva" />
  </a>
  
    <figcaption>Image: <em>Statues of a Kathakali dancer (top) and Nandi, the sacred vehicle of Lord Shiva</em>
    </figcaption>
  
</figure>

<p>I arrived in Prague early in the morning the next day.</p>

<h1 id="parting-thoughts">Parting thoughts</h1>

<p>Attending the conference was an unforgettable experience. Not only did I make
lasting connections with fellow TeX enthusiasts, but I also had the joy of
exploring India for the very first time. Along the way, I collected valuable
bug reports for <em>expltools</em>, see issues <a href="https://github.com/Witiko/expltools/issues/109">#109</a>, <a href="https://github.com/Witiko/expltools/issues/110">#110</a>, and <a href="https://github.com/Witiko/expltools/issues/111">#111</a>.</p>

<p>My heartfelt thanks go to the sponsors, organizers, and every participant whose
passion and hard work brought this event to life. I’m especially grateful to
Honza Vaněk, Erik Nijenhuis, Ben Davies, Mathias Jakobsen, and <a href="https://github.com/ju-sh">Julin S.</a>
for their camaraderie throughout the conference.</p>]]></content><author><name></name></author><category term="expl3" /><category term="LaTeX" /><category term="programming" /><category term="devlog" /><summary type="html"><![CDATA[Yesterday I returned from TUG 2025 in Trivandrum, Kerala (July 18–20, 2025), where I was thrilled to present my talk announcing the development of expltools to the broader TeX community. In this post, I will try to briefly summarize my experience in the form of a travel diary while it is still fresh in my mind. I’ll concentrate on my own experiences rather than the full program details. If you’d like to explore the agenda itself, please consult the conference program and watch the YouTube recordings here: day one, part one and part two, day two, and day three.]]></summary></entry><entry><title type="html">Review of The Legend of Zelda: Tears of the Kingdom after ~20 hours</title><link href="http://witiko.github.io/Zelda-TotK-review-2/" rel="alternate" type="text/html" title="Review of The Legend of Zelda: Tears of the Kingdom after ~20 hours" /><published>2025-06-04T00:00:00+00:00</published><updated>2025-06-04T00:00:00+00:00</updated><id>http://witiko.github.io/Zelda-TotK-review-2</id><content type="html" xml:base="http://witiko.github.io/Zelda-TotK-review-2/"><![CDATA[<p>I recently <a href="/Zelda-BotW-review">reviewed</a> <em>The Legend of Zelda: Breath of the Wild</em> (BotW, 2017) and <a href="/Zelda-TotK-review">shared my thoughts</a> after 10 hours with <em>Tears of the Kingdom</em> (TotK, 2023). After logging another 10 hours, I’m ready to revisit the topic. This time, I’ll expand on the emulation experience, share updated impressions, and explain why I don’t plan to finish the game anytime soon.</p>

<!-- more -->

<h2 id="on-emulation">On emulation</h2>

<p>In my previous post, I described poor framerates and graphical glitches when emulating TotK. Since then, I’ve tried several <a href="https://github.com/hoverbike1/TOTK-Mods-collection/releases/tag/v2.1">additional mods</a> that improved performance and resolved many of the visual issues. Unfortunately, new problems emerged: frequent freezes and extreme slowdowns — especially in the Temple of Fire, where framerates dropped into single digits, making the game nearly unplayable.</p>

<p>Emulation will likely improve over time — especially after the Switch 2 releases and Nintendo’s attention shifts from the current console. It’s also possible that <a href="https://github.com/hoverbike1/TOTK-Mods-collection/releases/tag/4.69">newer mods</a> or more powerful hardware than my Steam Deck OLED could make TotK playable today. Still, as someone fairly experienced with tinkering, I can confidently say: unlike BotW, TotK is not currently a good experience on the Steam Deck OLED. The technical barriers are high enough that I can’t recommend it in this form.</p>

<h2 id="second-impressions">Second impressions</h2>

<p>In my first review, I described TotK as a more linear, story-driven reimagining of BotW — targeted more at new players than returning ones, due to the extensive reuse of Hyrule’s terrain. It seemed to trade BotW’s open-ended freedom and grounded design for a more guided experience.</p>

<p>While TotK reuses BotW’s map, I now appreciate that it reflects the passage of time. Even for someone who played BotW recently, like me, the changes help make exploration feel fresh.</p>

<p>The game also adds a significant amount of new content: wells, caves, sky islands, and a vast underworld. But these additions are also where TotK starts to lose me. Two of BotW’s defining strengths — freedom of movement and a grounded world — feel diminished here.</p>

<h3 id="freedom-of-movement">Freedom of movement</h3>

<p>In BotW, most of the game played out on Hyrule’s open surface. Besides shrines and divine beasts, few areas were enclosed, and nearly every location was reachable without fast travel. You could just point your horse — or paraglider — and go.</p>

<p>TotK’s layered world (sky, surface, depths, caves) complicates that freedom. Navigation is often disorienting, and fast travel becomes a necessity rather than a convenience. The result is a less organic, more fragmented style of exploration that lacks the magic of BotW’s wide-open traversal.</p>

<h3 id="grounded-world">Grounded world</h3>

<p>Despite its fantasy elements, BotW felt grounded. Its terrain invited real-world instincts — climb that peak, follow that path, ride the wind.</p>

<p>TotK, by contrast, leans into surrealism: sky islands, glowing depths, floating structures. The world starts to feel artificial, like a dream or a puzzle box. That shift breaks immersion for me and makes it harder to engage intuitively. You’re no longer navigating a world — you’re playing a game with its own unfamiliar logic.</p>

<h2 id="on-device-building">On device-building</h2>

<p>I touched on TotK’s vehicle-building system in my earlier review. With more time, I still find the core idea clever. The physics engine is robust, the parts are versatile, and solving problems through creative engineering is genuinely satisfying. In some ways, this system restores the sense of freedom lost to the game’s more guided structure.</p>

<p>But building devices is tedious. The interface often misinterprets your intent, and fiddling with part alignment gets old fast. Yes, there’s an unlockable feature that lets you recreate previous builds — but that only highlights how long and frustrating the manual process can be.</p>

<h2 id="conclusion">Conclusion</h2>

<p>Between the performance issues on my hardware and a gameplay direction that doesn’t fully resonate with me, I’m putting TotK aside for now. Maybe I’ll return in a year or two — once emulation matures and I’m in the mood for something different. For now, I think I’ll stick with BotW.</p>]]></content><author><name></name></author><category term="videogames" /><category term="review" /><summary type="html"><![CDATA[I recently reviewed The Legend of Zelda: Breath of the Wild (BotW, 2017) and shared my thoughts after 10 hours with Tears of the Kingdom (TotK, 2023). After logging another 10 hours, I’m ready to revisit the topic. This time, I’ll expand on the emulation experience, share updated impressions, and explain why I don’t plan to finish the game anytime soon.]]></summary></entry><entry><title type="html">Static analysis of expl3 programs (8¾): Semantic analysis for functions and what’s taking so long</title><link href="http://witiko.github.io/Expl3-Linter-8.75/" rel="alternate" type="text/html" title="Static analysis of expl3 programs (8¾): Semantic analysis for functions and what’s taking so long" /><published>2025-05-29T00:00:00+00:00</published><updated>2025-05-29T00:00:00+00:00</updated><id>http://witiko.github.io/Expl3-Linter-8.75</id><content type="html" xml:base="http://witiko.github.io/Expl3-Linter-8.75/"><![CDATA[<p>Today, I’m excited to announce another <a href="https://github.com/Witiko/expltools/releases/tag/2025-05-29">update</a> to <a href="https://ctan.org/pkg/expltools"><em>expltools</em></a>, the bundle that includes the static analysis tool <em>explcheck</em> for the expl3 programming language. This release significantly extends support for the <strong>semantic analysis</strong> processing step.</p>

<p>In this post, I’ll explain what this means for expl3 programmers, what functionality is still missing, and provide a realistic estimate for when full support for <strong>semantic</strong> (and <strong>flow</strong>) <strong>analysis</strong> can be expected. I’ll also discuss how this impacts the project roadmap.</p>

<p>Additionally, I’d like to tease a <a href="https://github.com/Witiko/expltools-tug25-paper">work-in-progress article</a> titled <em>Expltools: Development tools for expl3 programmers</em>, forthcoming in TUGboat 46(2) or (3), along with my upcoming <a href="https://www.tug.org/tug2025/abstracts/starynovotny-expltools.txt">talk</a> this July at <a href="https://www.tug.org/tug2025/">TUG 2025</a> in India.</p>

<p>Finally, as a follow-up to the previous <a href="/Expl3-Linter-8">two</a> <a href="/Expl3-Linter-8.5">posts</a>, in which I described how I collaborated with package authors to fix issues detected by <em>explcheck</em>, I’ll provide updates on the status of those tickets.</p>

<!-- more -->

<h1 id="what-are-the-new-features">What are the new features?</h1>

<p>As discussed in <a href="/Expl3-Linter-8.5">my previous post</a>, the initial <strong>semantic analysis</strong> implementation laid the foundation but did not yet report any new issues. The latest release changes that, introducing support for six issues from Section 4.1 of <a href="https://github.com/witiko/expltools/releases/download/2025-05-29/warnings-and-errors.pdf"><em>Warnings and errors for the expl3 analysis tool</em></a>:</p>

<ol>
  <li><strong>W401</strong>: Unused private function</li>
  <li><strong>W402</strong>: Unused private function variant</li>
  <li><strong>T403</strong>: Function variant of incompatible type</li>
  <li><strong>E404</strong>: Protected predicate function</li>
  <li><strong>E405</strong>: Function variant for an undefined function</li>
  <li><strong>W407</strong>: Multiply defined function variant</li>
</ol>

<p>This accounts for roughly one quarter of all issues planned for the <strong>semantic analysis</strong>.</p>

<h1 id="what-is-taking-so-long">What is taking so long?</h1>

<p>The original <a href="https://tug.org/tc/devfund/documents/2024-09-expltools.pdf">project proposal</a> assumed that each of the five processing steps would take about two months to implement, with a two-month buffer for unforeseen work. This assumption held for the first three steps:</p>

<ul>
  <li><strong>Preprocessing</strong>: 2 months (Oct – Nov 2024)</li>
  <li><strong>Lexical analysis</strong>: 1.5 months (Dec – mid Jan 2025)</li>
  <li><em><strong>Housekeeping and bugfixes</strong>: 1.5 months (mid Jan – Feb)</em></li>
  <li><strong>Syntactic analysis</strong>: 1 month (March)</li>
</ul>

<p>By contrast, <strong>semantic analysis</strong> has already taken two full months, and we’re only a quarter of the way through its planned issues — despite the fact that I’ve spent significantly more time on the project this month than in any of the previous seven.</p>

<p>Why the delay? The short answer is: <strong>semantic analysis</strong> is hard. This step is responsible for approximately 36% of all planned issues — nearly double what would be expected if the issues were evenly distributed.</p>

<p>Moreover, it’s the first step where all accumulated knowledge must be combined to detect higher-level problems. While previous steps focused on parsing and data collection, <strong>semantic analysis</strong> is where the real analysis begins.</p>

<p>It may even be more challenging than the final <strong>flow analysis</strong> step, where full implementation may not be feasible due to gaps in knowledge, as discussed in Section 4.3 of <a href="https://tug.org/tc/devfund/documents/2024-09-expltools.pdf">the proposal</a>.</p>

<p>If we weight the remaining steps by the number of planned issues (halving the expected effort for <strong>flow analysis</strong>) and fit the work into the original 10-month timeframe, we arrive at this estimate:</p>

<ul>
  <li><strong>Semantic analysis</strong>: 4 months (April – July, concluding around <a href="https://www.tug.org/tug2025/">TUG 2025</a>)</li>
  <li><strong>Flow analysis</strong>: 2 months (Aug – Sep)</li>
</ul>

<p>However, this may still be optimistic. If it takes two months to complete just one quarter of <strong>semantic analysis</strong> issues, a pessimistic projection would suggest a total of eight months — pushing the final release of <em>explcheck</em> to January 2026.</p>

<h1 id="working-with-the-community">Working with the community</h1>

<p>Since March, I’ve been actively reaching out to package maintainers and developers to report issues identified by <em>explcheck</em>. Out of eleven reported issues, seven have been resolved:</p>

<ol>
  <li><a href="https://github.com/BITNP/BIThesis/issues/604">BITNP/BIThesis#604</a>: fixed</li>
  <li><a href="https://github.com/jspitz/jslectureplanner/issues/7">jspitz/jslectureplanner#7</a>: fixed</li>
  <li><a href="https://github.com/dbitouze/nwejm/issues/5">dbitouze/nwejm#5</a></li>
  <li><a href="https://github.com/dbitouze/gzt/issues/56">dbitouze/gzt#56</a></li>
  <li><a href="https://github.com/michal-h21/responsive-latex/issues/1">michal-h21/responsive-latex#1</a>: fixed</li>
  <li><a href="https://github.com/fpantigny/nicematrix/issues/12">fpantigny/nicematrix#12</a>: fixed</li>
  <li><a href="https://github.com/josephwright/siunitx/issues/796">josephwright/siunitx#796</a>: wontfix</li>
  <li><a href="https://github.com/BITNP/BIThesis/issues/640">BITNP/BIThesis#640</a>: fixed</li>
  <li><a href="https://github.com/se2p/se2thesis/issues/23">se2p/se2thesis#23</a></li>
  <li><a href="https://github.com/John02139/asmeconf/issues/11">John02139/asmeconf#11</a>: fixed</li>
  <li><a href="https://dickimaw-books.com/bugtracker.php?key=303">dickimaw-books.com#303</a>: fixed</li>
</ol>

<p>As of today’s release, no additional errors have been found in TeX Live packages with public repositories, only warnings.</p>

<p>Thank you for your attention and stay tuned for more updates as <em>explcheck</em> continues to evolve!</p>]]></content><author><name></name></author><category term="expl3" /><category term="LaTeX" /><category term="programming" /><category term="devlog" /><summary type="html"><![CDATA[Today, I’m excited to announce another update to expltools, the bundle that includes the static analysis tool explcheck for the expl3 programming language. This release significantly extends support for the semantic analysis processing step. In this post, I’ll explain what this means for expl3 programmers, what functionality is still missing, and provide a realistic estimate for when full support for semantic (and flow) analysis can be expected. I’ll also discuss how this impacts the project roadmap. Additionally, I’d like to tease a work-in-progress article titled Expltools: Development tools for expl3 programmers, forthcoming in TUGboat 46(2) or (3), along with my upcoming talk this July at TUG 2025 in India. Finally, as a follow-up to the previous two posts, in which I described how I collaborated with package authors to fix issues detected by explcheck, I’ll provide updates on the status of those tickets.]]></summary></entry><entry><title type="html">Review of The Legend of Zelda: Tears of the Kingdom after ~10 hours</title><link href="http://witiko.github.io/Zelda-TotK-review/" rel="alternate" type="text/html" title="Review of The Legend of Zelda: Tears of the Kingdom after ~10 hours" /><published>2025-05-19T00:00:00+00:00</published><updated>2025-05-19T00:00:00+00:00</updated><id>http://witiko.github.io/Zelda-TotK-review</id><content type="html" xml:base="http://witiko.github.io/Zelda-TotK-review/"><![CDATA[<p>I recently <a href="/Zelda-BotW-review">reviewed</a> <em>The Legend of Zelda: Breath of the Wild</em> (BotW, 2017). In this article, I’ll share my early thoughts after about 10 hours spent with its sequel, <em>The Legend of Zelda: Tears of the Kingdom</em> (TotK, 2023).</p>

<!-- more -->

<h2 id="on-emulation">On emulation</h2>

<p>Like BotW, I’m playing TotK on my Steam Deck OLED via emulation, despite owning the original Switch version. However, this time around, the experience was less straightforward. Unlike BotW, which I played using the Ryujinx emulator without much trouble (though it’s generally better to emulate the Wii U version using CEMU), TotK initially ran poorly, often dipping below 10 FPS and freezing every few minutes once I left the intro area.</p>

<p>After several hours of troubleshooting, I landed on a more stable setup: Yuzu, game version 1.1.2, and a carefully chosen set of mods. This configuration now gives me a mostly stable 30 FPS, though I still encounter <a href="https://www.reddit.com/r/yuzu/comments/1gbd95g/totk_bug_black_screen_on_weapon_food_shields_and/">minor graphical glitches</a> that I haven’t been able to resolve. If you’re considering a similar setup, <a href="https://www.reddit.com/r/128bitbay/comments/14fjbje/totk_yuzu_steam_deck_almost_stable_30fps_setup/">these</a> <a href="https://www.reddit.com/r/SteamDeck/comments/169y9p9/how_to_setup_yuzu_motion_controls_on_the_steam/">guides</a> might save you some time.</p>

<h2 id="first-impressions">First impressions</h2>

<p>BotW set a bold new direction for the series, prioritizing player freedom above all else. Once the Great Plateau tutorial is complete, the player is handed the quest “Destroy Ganon” and given the freedom to rush straight to the final boss. While most players will choose to get lost in an open world full of optional side content, <a href="https://www.speedrun.com/botw">the fastest BotW speedruns</a> can finish the game in about 20 minutes, underscoring just how little of its world is strictly necessary.</p>

<p>By contrast, TotK feels like a return to a more structured, story-driven formula. The intro sequence is longer and more guided, and the game seems less interested in respecting the player’s time. Unlike BotW, there is no final boss to rush — only a long quest line that you must follow to see the conclusion to the story. This might be a deliberate response to players who found BotW too directionless, but it’s a noticeable shift in tone and design.</p>

<p>While TotK technically continues the story of BotW, the connection feels tenuous, not least because BotW itself had only a loose narrative. Worse, TotK reuses much of BotW’s world, which strips away a significant portion of the intrinsic motivation for returning players who have already spent hundreds of hours exploring Hyrule. In this sense, it feels less like a direct sequel and more like an alternative take on the same concept, aimed at players who weren’t sold on the radical shift of its predecessor. If only Nintendo marketed the game as such.</p>

<p>That said, TotK introduces some genuinely creative new powers and device-building mechanics, which my three-year-old found endlessly entertaining as we built a car loaded with explosives and drove it into a monster camp. So, while I’m skeptical that this new direction will sustain my interest over a 100+ hour playthrough like BotW did, I’m willing to keep playing for now, if only to see what other surprises it has in store.</p>]]></content><author><name></name></author><category term="videogames" /><category term="review" /><summary type="html"><![CDATA[I recently reviewed The Legend of Zelda: Breath of the Wild (BotW, 2017). In this article, I’ll share my early thoughts after about 10 hours spent with its sequel, The Legend of Zelda: Tears of the Kingdom (TotK, 2023).]]></summary></entry><entry><title type="html">Review of The Legend of Zelda: Breath of the Wild after ~100 hours of blind playthrough</title><link href="http://witiko.github.io/Zelda-BotW-review/" rel="alternate" type="text/html" title="Review of The Legend of Zelda: Breath of the Wild after ~100 hours of blind playthrough" /><published>2025-05-12T00:00:00+00:00</published><updated>2025-05-12T00:00:00+00:00</updated><id>http://witiko.github.io/Zelda-BotW-review</id><content type="html" xml:base="http://witiko.github.io/Zelda-BotW-review/"><![CDATA[<p>In this article, I will share my thoughts after spending around 100 hours playing <em>The Legend of Zelda: Breath of the Wild</em> (BotW, 2017) for the first time.</p>

<!-- more -->

<p>My playthrough was on a Steam Deck OLED using the open-source emulator <em>Ryujinx</em>, as opposed to the original Switch, for reasons I’ll explain shortly.</p>

<h2 id="preliminaries">Preliminaries</h2>
<p>To start off, let me provide some context on my background with the Zelda series, my choice to play on a Steam Deck OLED, and why I opted for emulation over the original hardware.</p>

<h3 id="my-background">My background</h3>
<p>At the time of writing, I’ve spent roughly 100 hours with BotW over the past two months. I have completed all the main story content that can be reasonably expected of a non-completionist run before facing the final boss, so I feel I have a solid grasp of what the game has to offer.</p>

<p>Before BotW, my Zelda experience included the following titles:</p>

<ul>
  <li><em>The Legend of Zelda: Link’s Awakening</em> (1993, Game Boy Color)</li>
  <li><em>The Legend of Zelda: Oracle of Seasons</em> and <em>Oracle of Ages</em> (2001, Game Boy Color)</li>
  <li><em>The Legend of Zelda: A Link to the Past</em> (2002, Game Boy Advance)</li>
  <li><em>The Legend of Zelda: The Minish Cap</em> (2004, Game Boy Advance)</li>
  <li><em>The Legend of Zelda: Phantom Hourglass</em> (2007, Nintendo DS)</li>
  <li><em>The Legend of Zelda: Spirit Tracks</em> (2009, Nintendo DS)</li>
  <li><em>The Legend of Zelda: Ocarina of Time</em> (2011, Nintendo 3DS)</li>
</ul>

<p>Of these, <em>The Minish Cap</em> was my introduction to the series, and the only other titles I’ve fully completed are <em>Oracle of Seasons</em> and <em>Oracle of Ages</em>. I nearly finished <em>Phantom Hourglass</em>, <em>Spirit Tracks</em>, and <em>Ocarina of Time</em>, but I put <em>Link’s Awakening</em> and <em>A Link to the Past</em> down quite early. I still hope to revisit these games someday and perhaps try <em>Majora’s Mask</em> (2000) and <em>Wind Waker</em> (2002) if time permits.</p>

<p>Besides Zelda, my experience with open-world games is limited. Aside from <em>Baldur’s Gate</em> (1998), <em>Diablo II</em> (2000), <em>Gothic</em> (2001), <em>World of Warcraft</em> (2004), and <em>Minecraft</em> (2011), I haven’t really delved into the genre. I haven’t played the <em>Elder Scrolls</em>, <em>Witcher</em>, or <em>FromSoftware</em> titles, so my perspective may differ from that of more seasoned open-world gamers.</p>

<h3 id="why-emulate-on-steam-deck">Why emulate on Steam Deck?</h3>
<p>I own the original Switch version of BotW, but I chose to play it on Ryujinx for several reasons.</p>

<p>First, the game runs beautifully on my Steam Deck OLED, including full gyroscope support via <a href="https://github.com/kmicki/SteamDeckGyroDSU">SteamDeckGyroDSU</a>, with next to no tinkering — a technical feat I find genuinely impressive and worth highlighting.</p>

<figure>
  <a href="/images/20250411_134208.jpg" target="_blank">
    <img src="/images/thumbnails/20250411_134208.jpg" alt="Exploring Hyrule on my Steam Deck OLED" />
  </a>
  
    <figcaption>Image: <em>Exploring Hyrule on my Steam Deck OLED</em>
    </figcaption>
  
</figure>

<p>Second, while I could have opted for the remastered version on the new Switch 2, I have a strong aversion to Nintendo’s business practices, including <a href="https://www.tweaktown.com/news/104575/nintendo-confirms-90-price-for-full-breath-of-the-wild-experience-on-switch-2/index.html">selling remasters at nearly double the original price</a>, <a href="https://www.theverge.com/news/663210/palworld-updates-feature-removed-nintendo-lawsuit">patent-bullying smaller studios</a>, and enforcing hardware exclusivity by <a href="https://www.theverge.com/2024/10/1/24259791/nintendo-ryujinx-switch-emulator-gdkchan-removed-downloads-github">shutting down fan projects like Ryujinx</a>, despite <a href="https://fandomwire.com/rules-for-thee-not-for-me-nintendos-museum-uncovers-major-hypocrisy-turns-out-emulating-games-is-fine-as-long-as-you-arent-doing-it/">relying on emulation themselves</a>. Given all this, I am in no rush to buy a Switch 2, even if it might offer some appealing exclusives.</p>

<p>With these preliminaries out of the way, let’s dive into the review.</p>

<h2 id="review">Review</h2>
<p>In the following sections, I’ll start by discussing the game’s core design, then highlight its strongest elements, and finally touch on a few of its weaknesses.</p>

<h3 id="core-design">Core design</h3>
<p>Most classic Zelda games were thinly veiled linear adventures, where progress was gated by items found in dungeons. BotW, however, takes a fundamentally different approach, presenting a truly open world where players can theoretically rush straight to the final boss after the initial tutorial area. This shift in design philosophy is a radical departure and deserves special attention.</p>

<p>In BotW, players start on the Great Plateau, a small but varied region where they acquire all the core abilities needed to explore the entire game world. Unlike previous titles, which might have required a specific item to progress (e.g., a hookshot to cross a chasm), BotW encourages creative problem-solving. For instance, to survive a cold area early in the game, you can:</p>

<ul>
  <li>Cook a meal that grants cold resistance</li>
  <li>Carry a torch or light a campfire for warmth</li>
  <li>Simply sprint through and heal as needed</li>
</ul>

<p>This flexibility is central to the game’s design, allowing for a wide range of player strategies and approaches.</p>

<figure>
  <a href="/images/16297153_20250512120907_1.jpg" target="_blank">
    <img src="/images/thumbnails/16297153_20250512120907_1.jpg" alt="Cooking a spicy meal for cold resistance near the Temple of Time" />
  </a>
  
    <figcaption>Image: <em>Cooking a spicy meal for cold resistance near the Temple of Time</em>
    </figcaption>
  
</figure>

<p>Once players leave the Great Plateau, they receive the game’s signature item, the paraglider, and are free to explore the massive world of Hyrule at their own pace.</p>

<h3 id="strong-points">Strong points</h3>
<p>BotW’s strength lies in its deceptively simple core loop: climb towers to reveal parts of the world map, spot interesting landmarks from those high vantage points, and glide down to explore them, discovering even more points of interest along the way. This creates a nearly endless feedback loop of exploration and discovery, driven by curiosity rather than explicit markers or objectives.</p>

<p>What’s remarkable is just how much this simplicity accomplishes. The game reveals nearly all of its major systems in the first hour and then relies on them for the rest of its 100+ hour playtime. There are no intricate skill trees, endless collectible lists, or sprawling quest lines — just an expectation that players will find satisfaction in the sheer act of discovery. For a big-budget title, this is a strikingly bold choice.</p>

<figure>
  <a href="/images/20250512105917_1.jpg" target="_blank">
    <img src="/images/thumbnails/20250512105917_1.jpg" alt="Mining an ore deposit near the Dueling Peaks Stable&thinsp;—&thinsp;because the economy of Hyrule runs on shiny rocks" />
  </a>
  
    <figcaption>Image: <em>Mining an ore deposit near the Dueling Peaks Stable&thinsp;—&thinsp;because the economy of Hyrule runs on shiny rocks</em>
    </figcaption>
  
</figure>

<p>The climbing system, while seemingly a small addition, fundamentally changes how players move through the world. It allows for truly free-form exploration, making every mountain peak a potential vantage point and every cliff a potential shortcut. Few other games have captured this sense of freedom, and it’s hard to overstate its impact on the overall experience.</p>

<p>This freedom is further enhanced by the paraglider, which, while not entirely new to the series — having appeared in earlier forms in <em>The Wind Waker</em> (2002) and <em>Skyward Sword</em> (2011) — synergizes perfectly with the climbing mechanic. The ability to climb to a high vantage point and then glide across vast distances gives players an unparalleled sense of agency, making every climb a meaningful choice and every peak a potential launchpad for further exploration.</p>

<figure>
  <a href="/images/20250512105244_1.jpg" target="_blank">
    <img src="/images/thumbnails/20250512105244_1.jpg" alt="Dodging a Guardian's laser" />
  </a>
  
    <figcaption>Image: <em>Dodging a Guardian's laser</em>
    </figcaption>
  
</figure>

<p>This approach has clearly resonated with other developers as well. Games like <em>Genshin Impact</em> (2020) have adopted similar traversal mechanics, further validating BotW’s influence on the genre. Here’s hoping these games won’t also find themselves on the wrong side of a Nintendo cease and desist.</p>

<h3 id="weak-points">Weak points</h3>
<p>That said, BotW is not without its flaws. The weapon durability system, for instance, can be frustrating. While the idea of forcing players to constantly switch up their gear is interesting in theory, it often feels punishing, especially early on, where every encounter risks breaking your best weapons. This can discourage combat altogether, as players may feel the cost is too high.</p>

<figure>
  <a href="/images/20250512105146_1.jpg" target="_blank">
    <img src="/images/thumbnails/20250512105146_1.jpg" alt="Bravely (or perhaps foolishly) engaging a Guardian in melee combat" />
  </a>
  
    <figcaption>Image: <em>Bravely (or perhaps foolishly) engaging a Guardian in melee combat</em>
    </figcaption>
  
</figure>

<p>The game’s few more linear sections, like the Lost Woods trials, also feel at odds with the otherwise open-ended design. These moments can feel like playing a completely different, lesser game, which is a stark contrast to the freedom offered elsewhere and highlights the difficulty of integrating tightly scripted sequences into an otherwise open-ended game world. It’s as if the designers had to briefly put the brakes on player agency to tell a particular story beat or guide the player through a specific experience, which can feel jarring given the game’s otherwise freeform approach.</p>

<p>This tension extends to the story as a whole. While BotW makes the best of its open-world structure by presenting an insomniac protagonist piecing together their fragmented past through beautifully shot cinematics and some of the best writing the series has ever had, the open-world nature of the game inherently undermines a strong, cohesive narrative. Without the ability to control the pacing and order in which the player experiences key plot points, the game struggles to build a sense of urgency or momentum, leaving the player free to ignore the main quest entirely. This can make the stakes feel less pressing, reducing the incentive to confront the final boss rather than simply wandering the world indefinitely. In this sense, a fragmented, less focused story might be an unavoidable trade-off for the kind of freedom that defines BotW.</p>

<figure>
  <a href="/images/pict_014.jpg" target="_blank">
    <img src="/images/thumbnails/pict_014.jpg" alt="Taking selfies with stabled horses instead of rescuing Princess Zelda" />
  </a>
  
    <figcaption>Image: <em>Taking selfies with stabled horses instead of rescuing Princess Zelda</em>
    </figcaption>
  
</figure>

<h2 id="conclusion">Conclusion</h2>
<p>BotW marks a bold and largely successful shift from linear adventure to open-world exploration, stripping back the formula to its most essential elements while still providing hundreds of hours of compelling gameplay. Despite a few rough edges, it’s a landmark title that deserves its praise, and I look forward to seeing how its successor, <em>Tears of the Kingdom</em>, builds on this foundation and if this direction will become the new standard for the series.</p>]]></content><author><name></name></author><category term="videogames" /><category term="review" /><summary type="html"><![CDATA[In this article, I will share my thoughts after spending around 100 hours playing The Legend of Zelda: Breath of the Wild (BotW, 2017) for the first time.]]></summary></entry><entry><title type="html">Static analysis of expl3 programs (8½): First stages of semantic analysis and working with the community</title><link href="http://witiko.github.io/Expl3-Linter-8.5/" rel="alternate" type="text/html" title="Static analysis of expl3 programs (8½): First stages of semantic analysis and working with the community" /><published>2025-04-25T00:00:00+00:00</published><updated>2025-05-29T00:00:00+00:00</updated><id>http://witiko.github.io/Expl3-Linter-8.5</id><content type="html" xml:base="http://witiko.github.io/Expl3-Linter-8.5/"><![CDATA[<p>Today, I’m excited to announce another <a href="https://github.com/Witiko/expltools/releases/tag/2025-04-25">update</a> to <a href="https://ctan.org/pkg/expltools"><em>expltools</em></a>, the bundle that includes the static analysis tool <em>explcheck</em> for the expl3 programming language. This update introduces initial support for <strong>semantic analysis</strong>. In this post, I’ll explain what this means for expl3 programmers, what’s still missing, and where we’re headed next.</p>

<p>In <a href="/Expl3-Linter-7">a previous post</a>, I introduced <a href="https://koppor.github.io/explcheck-issues/">a public website</a> listing issues in current TeX Live packages detected by <em>explcheck</em>. This time, I’ll also talk about how I’ve been using that resource to collaborate with package authors and help fix real-world issues.</p>

<!-- more -->

<h1 id="what-is-semantic-analysis">What is semantic analysis?</h1>

<p>Semantic analysis builds on the results of <strong>syntactic analysis</strong> (covered in <a href="/Expl3-Linter-8">my last post</a>) by interpreting recognized function calls as high-level statements and performing additional processing based on their type.</p>

<p>For example, consider this expl3 snippet, which prints “Hello, world!” when executed:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\cs_new:Nn
  \example_foo:n
  {
    \cs_new:Nn
      \example_bar:n
      {
        #1,~##1!
      }
  }
\example_foo:n { Hello }
\example_bar:n { world }
</code></pre></div></div>

<p>Semantic analysis identifies three top-level statements here:</p>

<ol>
  <li>The definition of the function <code class="language-plaintext highlighter-rouge">\example_foo:n</code>.</li>
  <li>A call to <code class="language-plaintext highlighter-rouge">\example_foo:n</code> with the argument “Hello”.</li>
  <li>A call to another user-defined function, <code class="language-plaintext highlighter-rouge">\example_bar:n</code>, with the argument “world”.</li>
</ol>

<p>Let’s take a closer look at the first one.</p>

<p>The definition of <code class="language-plaintext highlighter-rouge">\example_foo:n</code> contains parameter tokens like <code class="language-plaintext highlighter-rouge">#1</code>. During semantic analysis, these are replaced with ⟨argument⟩ pseudo-tokens to represent the values inserted during a function call. Doubled parameter characters (<code class="language-plaintext highlighter-rouge">##</code>) are simplified to single ones (<code class="language-plaintext highlighter-rouge">#</code>). After this transformation, the inner function definition becomes:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>\cs_new:Nn
  \example_bar:n
  {
    ⟨argument⟩,~#1!
  }
</code></pre></div></div>

<p>This replacement text is then re-analyzed syntactically and semantically, producing one nested statement:</p>

<ol>
  <li>The definition of the function <code class="language-plaintext highlighter-rouge">\example_bar:n</code>.</li>
</ol>

<p>This nested definition is processed again, yielding the fully transformed replacement text:</p>

<div class="language-latex highlighter-rouge"><div class="highlight"><pre class="highlight"><code>⟨argument⟩,~⟨argument⟩!
</code></pre></div></div>

<p>However, no further nested statements are found.</p>

<h1 id="what-does-this-mean-for-expl3-programmers">What does this mean for expl3 programmers?</h1>

<p>Until now, <em>explcheck</em> only analyzed top-level function calls. With this update, it also parses replacement texts inside function definitions. This allows issues from Section 3 of <a href="https://github.com/witiko/expltools/releases/download/latest/warnings-and-errors.pdf">Warnings and errors for the expl3 analysis tool</a> to be detected not just at the top level, but also within functions.</p>

<p>That said, semantic analysis currently recognizes only function definitions. Other constructs — such as variable declarations, function variant generation, or key-value assignments — are not yet interpreted. As a result, issues described in Section 4 of the same document are still out of scope. Expect support for (some of) these in the next release.</p>

<h1 id="working-with-the-community">Working with the community</h1>

<p>In March, I reached out to the maintainers of five packages with issues listed on <a href="https://koppor.github.io/explcheck-issues/">our website</a>:</p>

<ol>
  <li><a href="https://github.com/BITNP/BIThesis/issues/604">BITNP/BIThesis#604</a></li>
  <li><a href="https://github.com/jspitz/jslectureplanner/issues/7">jspitz/jslectureplanner#7</a></li>
  <li><a href="https://github.com/dbitouze/nwejm/issues/5">dbitouze/nwejm#5</a></li>
  <li><a href="https://github.com/dbitouze/gzt/issues/56">dbitouze/gzt#56</a></li>
  <li><a href="https://github.com/michal-h21/responsive-latex/issues/1">michal-h21/responsive-latex#1</a></li>
</ol>

<p>Thanks to the maintainers’ quick responses, three of these issues have already been resolved!</p>

<p>Following today’s update, six more issues were found in TeX Live packages with public repositories. I’ve submitted reports to their maintainers as well:</p>

<ol>
  <li><a href="https://github.com/fpantigny/nicematrix/issues/12">fpantigny/nicematrix#12</a></li>
  <li><a href="https://github.com/josephwright/siunitx/issues/796">josephwright/siunitx#796</a></li>
  <li><a href="https://github.com/BITNP/BIThesis/issues/640">BITNP/BIThesis#640</a></li>
  <li><a href="https://github.com/se2p/se2thesis/issues/23">se2p/se2thesis#23</a></li>
  <li><a href="https://github.com/John02139/asmeconf/issues/11">John02139/asmeconf#11</a></li>
  <li><a href="https://dickimaw-books.com/bugtracker.php?key=303">dickimaw-books.com#303</a></li>
</ol>

<p>One of these has already been fixed — before I even finished writing this post. Let’s see how many more we can fix before the next update! 😉</p>]]></content><author><name></name></author><category term="expl3" /><category term="LaTeX" /><category term="programming" /><category term="devlog" /><summary type="html"><![CDATA[Today, I’m excited to announce another update to expltools, the bundle that includes the static analysis tool explcheck for the expl3 programming language. This update introduces initial support for semantic analysis. In this post, I’ll explain what this means for expl3 programmers, what’s still missing, and where we’re headed next. In a previous post, I introduced a public website listing issues in current TeX Live packages detected by explcheck. This time, I’ll also talk about how I’ve been using that resource to collaborate with package authors and help fix real-world issues.]]></summary></entry><entry><title type="html">Snake (3): Interplay between different systems</title><link href="http://witiko.github.io/Snake-3/" rel="alternate" type="text/html" title="Snake (3): Interplay between different systems" /><published>2025-03-31T00:00:00+00:00</published><updated>2025-03-31T00:00:00+00:00</updated><id>http://witiko.github.io/Snake-3</id><content type="html" xml:base="http://witiko.github.io/Snake-3/"><![CDATA[<p>Three weeks ago, I <a href="https://github.com/witiko/snake">released</a> my classic browser-based snake game — a project
I started 15 years ago — under a free and open-source license.</p>

<p>In my <a href="/Snake-2">previous post</a>, I introduced the various systems that bring the game
to life. In this post, I’ll explore how these systems interact to create a balanced
and engaging gameplay experience.</p>

<!-- more -->

<h1 id="adaptive-game-board">Adaptive game board</h1>

<p>The game board has a minimum size of 30 × 20 squares, but
it dynamically adjusts to accommodate various aspect ratios.</p>

<p>The final board size directly influences other systems. For instance, the snake
can only ever consume up to 30 × 20 food pieces. On boards
with larger aspect ratios, each food item contributes additional squares to the
snake’s tail, resulting in faster growth.</p>

<p>More examples of this interplay are discussed below.</p>

<h1 id="movement">Movement</h1>

<p>The snake’s speed is calibrated so that the average of the board’s width and
height is traversed within a specific time frame. This duration is determined
by the initial speed setting chosen by the player — ranging from one second at
the highest speed to six seconds at the lowest speed.</p>

<h1 id="food">Food</h1>

<p>Consuming gray food temporarily boosts the snake’s speed and triggers earlier
spawning of colorful food. Meanwhile, eating colorful food provides an even
greater temporary speed boost. The duration of these boosts is calculated based
on the average board dimensions, ensuring that players can cover a consistent
distance along the board regardless of its aspect ratio.</p>

<h1 id="balancing-risk-and-reward">Balancing risk and reward</h1>

<p>At the start of the game, speed boosts from food consumption create a fun,
fast-paced experience. However, as the snake grows and covers more of the
board, these boosts can become a liability. To mitigate this:</p>

<ul>
  <li>
    <p><strong>Growth-based reduction:</strong> The boost effect is gradually reduced as the
snake’s length increases, helping to prevent accidental collisions in the
crowded mid- and end-game.</p>
  </li>
  <li>
    <p><strong>Collision prevention:</strong> When boosted, the snake will pause before any
collision for the duration it would take an unboosted snake to move one cell.
This pause gives players a brief moment to adjust their direction and avoid
mistakes.</p>
  </li>
</ul>

<p>To encourage food-seeking even during the mid- and end-game — when collision risks are higher and players are more cautious — the game increases the spawn probability of rare food items as the board becomes more crowded. Skilled players who successfully consume every colorful food further boost their chances of encountering exceedingly rare items.</p>]]></content><author><name></name></author><category term="programming" /><category term="videogames" /><summary type="html"><![CDATA[Three weeks ago, I released my classic browser-based snake game — a project I started 15 years ago — under a free and open-source license. In my previous post, I introduced the various systems that bring the game to life. In this post, I’ll explore how these systems interact to create a balanced and engaging gameplay experience.]]></summary></entry></feed>