Weeknotes 2025 W42: Properly sick

October 13​–​19, 2025

Quick bits:


I’ve been properly sick for the first time since I got COVID-19, back in 2023. Ugh.

It started with a throat so sore I could barely swallow. At least I’ve got my medication. Paracetamol + Ibuprofen worked remarkably well, and gave me a good night of sleep.

But my head is still completely clogged up, I am low on energy, and I’m sleeping a ton. This sickness — whatever it is, at least not COVID-19 — better wrap up its business and fuck off soon.

Also: I am so happy that food and grocery delivery services exist. The idea of leaving my house2 fills me with dread at the moment.

I’ve had to miss one acting class. This is annoying, because there aren’t that many classes left before the public performance in December. I’ve also had to miss two TTRPG sessions, and it has now been over a month since we last played.


Okay, so I know I wrote “low on energy” but I think I still achieved a lot this week. Not that I needed to — I just did.

First, I released a new Nanoc version Nanoc 4.14, which adds support for TOML frontmatter and TOML configuration. Here’s a snippet from my updated web site:

+++
title = "Week­notes"
layout = "/prime.*"
+++

<p>Every Sunday, I publish my week­notes. […]</p>

YAML is a mess, and I dislike it the more I use it. Parsing it is a nightmare, too! My Zig SSG doesn’t support YAML frontmatter (yet) because I couldn’t find a reasonable YAML parser. TOML, on the other hand, proved to be much simpler, both to parse and to read with human eyes.

Did I add TOML support to Nanoc so that I could migrate off it to my new Zig SSG? Ha! Don’t be silly. But erm, maybe? That’s not going to happen for a long time, though.


I added a simple benchmark for Deng, and the results are sweet:

Benchmarking evaluator... done
  times (warmup):   10000
  times (real):     100000
  average duration: 8306ns

8.3µs — and that is for a debug build. A build with --release=fast is more impressive:

Benchmarking evaluator...
Benchmarking evaluator... done
  times (warmup):   1000
  times (real):     10000
  average duration: 551ns

0.6µs to evaluate a template once. That sure is fast!

I haven’t benchmarked the compiler, just the virtual machine. The compiler doesn’t need to be that fast, because there will typically be only few templates, and they’re all only compiled once.3


Deng also has whitespace removal now. Replacing {{ with {{- will remove the whitespace that occurs before that {{- token in the template; the same also works for -}}, {%- and -%}. Here is an example:

  <ul>
    {%- for page.tags as tag -%}
      <li>
        {{- author -}}
      </li>
    {%- endfor -%}
  </ul>

The result is nicely compact:

  <ul><li>Denis</li><li>Henry</li></ul>

The whitespace removal is done at compilation time, so it does not impact evaluation performance-wise.


Deng is now in a good-enough spot that I picked up work on my Zig static-site generator (SSG) again. The SSG now integrates properly with Deng, and of course the entire reason for creating Deng was to use it in this SSG.

Writing a library (Deng) versus using it (in the SSG) can be a world of a difference. I found many sharp edges in Deng that I’ve been working towards sanding off.

It has occurred to me that a lot of the spare-time/open-source work that I do is related to writing and publishing. I’ve built static-site generators (Nanoc and this unnamed Zig one), a templating language (Deng), markup languages (D★Mark and TomatenMark), and other tools (Deniki).


One of the differences between this new SSG and Nanoc is that the new SSG has filters that can act on metadata, not just content. In Nanoc, a filter has the following signature:4

f(content,metadata)=content f(\mathrm{content}, \mathrm{metadata}) = \mathrm{content}^\prime

In this new SSG, the signature instead is this:

f(content,metadata)=(content,metadata) f(\mathrm{content}, \mathrm{metadata}) = (\mathrm{content}^\prime, \mathrm{metadata}^\prime)

This is nice for two reasons:

In addition, I am toying with the idea of having content not be limited to just text or binary data. Imagine being able to store a parsed AST so that subsequent filters can operate on it, a bit like this (in pseudocode):

match "/**/*.md" {
  filter :parse_md
  filter :add_header_ids
  filter :serialize_md_to_html
}

This would open up the possibility of operating on in-memory binary data, too.


Entertainment:


Links:

Tech links:


  1. Police operation/deployment. ↩︎

  2. Which I would do with a face mask, of course. Not wearing one would only lead to actively infecting the people around me. I’m surely not that inconsiderate! ↩︎

  3. As long as the template doesn’t change, the compiled bytecode can remain cached and thus be reused. ↩︎

  4. Am I writing it like this just to show off the math support I implemented? Ehh, why would you even ask that? You sound envious. ↩︎

  5. Nanoc’s preprocess block is a pain. It’s a hack that I could never get rid of. The preprocess block is always executed, so it doesn’t benefit from Nanoc’s support for incremental builds. My new SSG will not have anything like a preprocess block. ↩︎

  6. Wolfenstein: The New Order (MachineGames, 2014), published by Bethesda Softworks. ↩︎

  7. Wolfenstein II: The New Colossus (MachineGames, 2017), published by Bethesda Softworks. ↩︎

You can reply to this weeknotes entry by email. I’d love to hear your thoughts!
If you like what I write, stick your email address below and subscribe. I send out my weeknotes every Sunday morning. Alternatively, subscribe to the web feed.