<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Arch Linux Dev Blog - parsers</title>
    <subtitle>A blog about Arch Linux projects</subtitle>
    <link rel="self" type="application/atom+xml" href="https://devblog.archlinux.page/tags/parsers/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://devblog.archlinux.page"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-10-08T15:24:40+00:00</updated>
    <id>https://devblog.archlinux.page/tags/parsers/atom.xml</id>
    <entry xml:lang="en">
        <title>Writing parsers is hard</title>
        <published>2026-10-08T15:24:40+00:00</published>
        <updated>2026-10-08T15:24:40+00:00</updated>
        
        <author>
          <name>archlinux-staff</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://devblog.archlinux.page/2026/writing-parsers-is-hard/"/>
        <id>https://devblog.archlinux.page/2026/writing-parsers-is-hard/</id>
        
        <content type="html" xml:base="https://devblog.archlinux.page/2026/writing-parsers-is-hard/">&lt;p&gt;This is a story of how we got from something like this:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Failed to deserialize BUILDINFO file:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Parser failed with the following error:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;unknown&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       ^&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;invalid makepkg build environment option&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;expected `buildflags`, `ccache`, `check`, `color`, `distcc`, `sign`, `makeflags`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To this:&lt;/p&gt;
&lt;img title=&quot;Screenshot a colored parser error message&quot; alt=&quot;A parser error message with &quot; src=&quot;srcinfo_error_1.png&quot; /&gt;
&lt;p&gt;When the ALPM project started, the goal was fairly simple: Better tooling to interact with the low-level plumbing of Arch Linux Package Management (ALPM).
This meant binaries to read/write individual file types, better error messages, extensive specifications and of course exports to common dataformats like JSON.&lt;/p&gt;
&lt;p&gt;The thing is, although some of us already had experience with low-level dataframe parsing, none of us had experience on how to parse &lt;strong&gt;whole files&lt;/strong&gt;.
So as can be expected, we had to learn some things the hard way.&lt;/p&gt;
&lt;p&gt;Although this is a very technical topic, I&#39;ll try to keep it short for you to have an enjoyable read! Let&#39;s get started.&lt;/p&gt;
&lt;h2 id=&quot;serde&quot;&gt;Serde&lt;/h2&gt;
&lt;p&gt;In the Rust ecosystem, there&#39;s this very convenient crate called &lt;code&gt;serde&lt;/code&gt;. It&#39;s a generic de-/serialization library, which allows effortless transformation between &quot;simple&quot; dataformats like JSON or YAML.&lt;/p&gt;
&lt;p&gt;The first data formats we wrote parsers for were &lt;a rel=&quot;external&quot; href=&quot;https://alpm.archlinux.page/specifications/PKGINFO.5.html&quot;&gt;PKGINFO&lt;/a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https://alpm.archlinux.page/specifications/BUILDINFO.5.html&quot;&gt;BUILDINFO&lt;/a&gt; files, which contain information about a package and its build environment, respectively.
Those are fairly straight forward and basically simplified INI-style data formats:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot; &gt;&lt;code data-lang=&quot;ini&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;format&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;pkgname&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; mypkg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;pkgbase&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; mupkg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;pkgver&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 1.2-1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;pkgarch&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; x86_64&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;installed&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; acl-2.3.2-1-x86_64&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;installed&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; archlinux-keyring-20241203-1-any&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;buildenv&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; unknown&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our first approach was to simply parse these values with the &lt;a rel=&quot;external&quot; href=&quot;https://github.com/winnow-rs/winnow&quot;&gt;winnow&lt;/a&gt; parser library to a key-value map and feed it into &lt;em&gt;serde&lt;/em&gt;&#39;s generic data types.
&lt;em&gt;serde&lt;/em&gt; then did the heavy lifting and mapped this generic data onto our &lt;a rel=&quot;external&quot; href=&quot;https://alpm.archlinux.page/alpm-types/index.html&quot;&gt;alpm-types&lt;/a&gt; via their &lt;code&gt;FromString&lt;/code&gt; implementations.
While this worked, the error messages were somewhat lacking:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Failed to deserialize BUILDINFO file:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Parser failed with the following error:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;unknown&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       ^&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;invalid makepkg build environment option&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;expected `buildflags`, `ccache`, `check`, `color`, `distcc`, `sign`, `makeflags`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is an inherent design flaw of the approach chosen by us: We deserialize the data into a generic &lt;em&gt;serde&lt;/em&gt;-compatible format, during which all information about the surrounding context (line/column number) is &lt;strong&gt;lost&lt;/strong&gt;.
Any errors that happen when mapping serde&#39;s data to our types are completely detached from the actual context of the file.&lt;/p&gt;
&lt;p&gt;So while this worked, we learned that &lt;em&gt;serde&lt;/em&gt; is just not designed to be used in a context-preserving way, which would allow for more helpful error messages.&lt;/p&gt;
&lt;h2 id=&quot;winnow&quot;&gt;Winnow&lt;/h2&gt;
&lt;p&gt;Our next attempt was to write the parser with the &lt;a rel=&quot;external&quot; href=&quot;https://github.com/winnow-rs/winnow&quot;&gt;winnow&lt;/a&gt; library from start to finish.&lt;/p&gt;
&lt;p&gt;We left the old parsers be and started work on a parser for the &lt;a rel=&quot;external&quot; href=&quot;https://alpm.archlinux.page/specifications/SRCINFO.5.html&quot;&gt;SRCINFO&lt;/a&gt; format.
This format is quite a bit more complex!
Although it&#39;s also an &lt;code&gt;INI&lt;/code&gt; style format, it uses the special keywords &lt;code&gt;pkgbase = mypkgname&lt;/code&gt; and &lt;code&gt;pkgname = mypkgname&lt;/code&gt; to start sections which span until either another &lt;code&gt;pkgname&lt;/code&gt; keyword or the EOF is hit.
It&#39;s not straight forward.&lt;/p&gt;
&lt;p&gt;So to summarize: This parser needs to be aware of the current section it&#39;s in, by keeping track of special keywords.&lt;/p&gt;
&lt;p&gt;All that said, writing the parser with &lt;em&gt;winnow&lt;/em&gt; worked surprisingly well and we managed to get parser errors with surrounding context:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;File parsing error:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;parse error at line 4, column 11&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;4 |  pkgrel = 1-nope&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  |           ^&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;expected end of package release value&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That was a big step in the right direction!
The parser became quite a bit more complex, as we no longer had &lt;em&gt;serde&lt;/em&gt; to do all of the &lt;a rel=&quot;external&quot; href=&quot;https://gitlab.archlinux.org/archlinux/alpm/alpm/-/blob/29a7e4209ba2317ccf92e035f87ecc55226eb64b/alpm-srcinfo/src/source_info/parser.rs#L540&quot;&gt;type mapping&lt;/a&gt; for us, but on the upside we had full control over everything.&lt;/p&gt;
&lt;p&gt;We continued to write parsers this way until the end of last year, when we increasingly noticed issues with the quality of error messages.
You can already see an artifact of this problem in the error message above: the caret should be on the &lt;code&gt;-&lt;/code&gt; and not the &lt;code&gt;1&lt;/code&gt;, as that&#39;s the actual position in which the parser failed.&lt;/p&gt;
&lt;h2 id=&quot;definitely-not-forward-parsing&quot;&gt;Definitely-not-forward-parsing&lt;/h2&gt;
&lt;p&gt;I&#39;m sure there&#39;s a well-known terminology for the type of parsing behavior I&#39;ll present to you in a bit, but I&#39;ll just call it &quot;forward-parsing&quot;.&lt;/p&gt;
&lt;p&gt;So what we did until now was &quot;definitely-not-forward-parsing&quot;.
Take a look at this code example, which can parse the line &lt;code&gt;pkgname = my_pkg_name&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot; &gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;pkgname = &lt;/span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;parse_next&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;?&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;till_line_end&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;and_then&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;Name&lt;/span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;/span&gt;&lt;span&gt;parser&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;context&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;The error message would go here&lt;/span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;parse_next&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;?&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first line simply consumes the slice &lt;code&gt;pkgname = &lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;till_line_end&lt;/code&gt; then takes all of the remaining content from the current cursor position up until the next newline or the end of file (EOF) (i.e. &lt;code&gt;my_pkg_name&lt;/code&gt;).
&lt;code&gt;.and_then(Name::parser)&lt;/code&gt; then calls the nested &lt;code&gt;Name::parser&lt;/code&gt;, on the literal slice of content &lt;code&gt;my_pkg_name&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Intuitively, I expected this to work out just fine, as &lt;code&gt;Name::parser&lt;/code&gt; is also a &lt;em&gt;winnow&lt;/em&gt; parser.
However, we run into a similar issue as we did with the &lt;em&gt;serde&lt;/em&gt; parsers: When using &lt;code&gt;and_then&lt;/code&gt;, the nested &lt;code&gt;Name::parser&lt;/code&gt; only operates on the &lt;code&gt;my_pkg_name&lt;/code&gt; slice without any context of its surroundings.&lt;/p&gt;
&lt;p&gt;Now, when an error occurs, the parent parser has no idea where exactly the error occurred and simply points to the start of the slice that we just tried to parse:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;File parsing error:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;parse error at line 120, column 11&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;120 | pkgname = qemu-common$$$&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    |           ^&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;invalid character in package name&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;expected ASCII alphanumeric character, `_`, `@`, `+`, `-`, `.`, the name of a package&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was a fundamental issue in most of our parsers.
The parsing approach for all our types was guided by the line-based nature of our file parsers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Parse until the end of a line&lt;/li&gt;
&lt;li&gt;Ensure that content can be mapped to the type while being fully consumed&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Turns out, what we had to do instead was:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Consume all valid tokens for the given type and ensure they are valid.&lt;/li&gt;
&lt;li&gt;Check if there&#39;s the expected newline/delimiter/EOF, if not throw an error about unexpected trailing content.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;forward-parsing&quot;&gt;Forward-parsing&lt;/h2&gt;
&lt;p&gt;To fix this issue, we had to refactor everything.
All of our parsers would need to be restructured to follow this new paradigm.&lt;/p&gt;
&lt;p&gt;The ideation for this happened in &lt;a rel=&quot;external&quot; href=&quot;https://gitlab.archlinux.org/archlinux/alpm/alpm/-/work_items/226#note_356334&quot;&gt;November 2025&lt;/a&gt;, with the first draft MR for the groundwork being opened &lt;a rel=&quot;external&quot; href=&quot;https://gitlab.archlinux.org/archlinux/alpm/alpm/-/merge_requests/463&quot;&gt;shortly after&lt;/a&gt;.
Several months of development and many headaches later, the &lt;a rel=&quot;external&quot; href=&quot;https://gitlab.archlinux.org/archlinux/alpm/alpm/-/merge_requests/595&quot;&gt;final MR&lt;/a&gt; was merged (that MR contains pretty much all rationale, examples and up-/downsides, if you&#39;re interested in further details).&lt;/p&gt;
&lt;p&gt;All parsers now attempted to consume the tokens their respective type expected, and, if valid, stopped afterwards.
This allowed much finer control when handling parsers and made filetype specific error handling of the consumer libraries much better.&lt;/p&gt;
&lt;p&gt;In practice, the call-sites now looked &lt;a rel=&quot;external&quot; href=&quot;https://gitlab.archlinux.org/archlinux/alpm/alpm/-/blob/4fdc2356c995d257188b1a9b81c89033cf7bfb52/alpm-srcinfo/src/source_info/parser.rs#L542&quot;&gt;like this&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot; &gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;name = &lt;/span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;parse_next&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;?&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;let&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Name&lt;/span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;/span&gt;&lt;span&gt;parser&lt;/span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;parse_next&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;?&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;//&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; Expect either the newline or the EOF&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;eof&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; newline&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;context&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;The error message would go here&lt;/span&gt;&lt;span style=&quot;color: light-dark(#032F62, #9ECBFF);&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;parse_next&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;?&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;//&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; We also introduced some helper functions, which allowed us&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt;//&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6A737D, #6A737D);&quot;&gt; to make the newline handling a bit more ergonomic:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;let&lt;/span&gt;&lt;span&gt; name&lt;/span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt; Name&lt;/span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;::&lt;/span&gt;&lt;span style=&quot;color: light-dark(#6F42C1, #B392F0);&quot;&gt;parser_until_line_ending_inclusive&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;input&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style=&quot;color: light-dark(#D73A49, #F97583);&quot;&gt;?&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Although this doesn&#39;t look like much of a difference, the error messages were &lt;strong&gt;finally&lt;/strong&gt; correct:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#24292E, #E1E4E8); background-color: light-dark(#FFFFFF, #24292E);&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;File parsing error:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;parse error at line 120, column 22&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    |&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;120 | pkgname = qemu-common$$$&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    |                      ^&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;invalid character in package name&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;expected ASCII alphanumeric character, `_`, `@`, `+`, `-`, `.`, the name of a package&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another great benefit of this refactoring is that all of our parsers now behave the exact same way.
Previously, there were small deviations in behavior, but since we restructured all parsers around proper traits (interfaces), we know exactly how each parser will behave.
They&#39;re just really really nice to use now.&lt;/p&gt;
&lt;h2 id=&quot;but-wait-there-s-more&quot;&gt;But wait, there&#39;s more&lt;/h2&gt;
&lt;p&gt;Now that we got proper error positioning, I finally decided to tackle the issue of missing context in error messages.&lt;/p&gt;
&lt;p&gt;While the errors are already pretty good now, they could be better! &lt;br /&gt;
There could be an error span, which highlights the specific sequence of characters that caused an issue.
When parsing nested types or data formats, it would be awesome to see the different layers... &lt;br /&gt;
There could be &lt;span style=&quot;color:#e40303&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:#ff8c00&quot;&gt;o&lt;/span&gt;&lt;span style=&quot;color:#c8a000&quot;&gt;l&lt;/span&gt;&lt;span style=&quot;color:#008026&quot;&gt;o&lt;/span&gt;&lt;span style=&quot;color:#0057e7&quot;&gt;r&lt;/span&gt;&lt;span style=&quot;color:#750787&quot;&gt;s&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Winnow&lt;/em&gt;&#39;s default error type is more of a debug type, and they explicitly state so in their documentation and tutorial.
It became clear that if we wanted to have custom-tailored contextual errors, we would have to write our own parser error library.&lt;/p&gt;
&lt;p&gt;After another two months of working on a new parser error library and refactoring almost all parsers &lt;em&gt;again&lt;/em&gt;, we merged the &lt;a rel=&quot;external&quot; href=&quot;https://gitlab.archlinux.org/archlinux/alpm/alpm/-/merge_requests/650&quot;&gt;last MR&lt;/a&gt; and we &lt;em&gt;finally&lt;/em&gt; have really fancy errors:&lt;/p&gt;
&lt;img title=&quot;Screenshot a colored parser error message&quot; alt=&quot;A parser error message for an invalid package name. Multiple colors are used to highlight specific sections and the different parsing layers are part of the error message.&quot; src=&quot;srcinfo_error_1.png&quot; /&gt;
&lt;p&gt;With support for layered context and error spans:&lt;/p&gt;
&lt;img title=&quot;Another screenshot of a parser error message&quot; alt=&quot;Another parser error message, which features an error span beneath the sequence of characters that caused the error in question.&quot; src=&quot;srcinfo_error_2.png&quot; /&gt;
&lt;p&gt;While the error messages may not yet be 100% perfect, we now have all the tooling to make them perfect.
It&#39;s just a matter of adjusting wordings and adding/removing layers of context. Small stuff.&lt;/p&gt;
&lt;p&gt;And not just that, the new error type also allows us to translate our parser error messages 🎉 , which simply wasn&#39;t an option before.&lt;/p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;/h2&gt;
&lt;p&gt;While most of the heavy lifting is now done, the two original &lt;em&gt;serde&lt;/em&gt;-based parsers for &lt;a rel=&quot;external&quot; href=&quot;https://alpm.archlinux.page/specifications/BUILDINFO.5.html&quot;&gt;BUILDINFO&lt;/a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https://alpm.archlinux.page/specifications/PKGINFO.5.html&quot;&gt;PKGINFO&lt;/a&gt; still need to be migrated.
However, at this point it should now be fairly straight-forward.
It&#39;s even a &lt;a rel=&quot;external&quot; href=&quot;https://gitlab.archlinux.org/archlinux/alpm/alpm/-/work_items?label_name%5B%5D=hint%3A%3Agood-first-issue&quot;&gt;good first issue&lt;/a&gt; if that&#39;s your kind of thing :D.&lt;/p&gt;
&lt;p&gt;Either way, the parsers in &lt;a rel=&quot;external&quot; href=&quot;https://alpm.archlinux.page/alpm-srcinfo/index.html&quot;&gt;alpm-srcinfo&lt;/a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https://alpm.archlinux.page/alpm-db/index.html&quot;&gt;alpm-db&lt;/a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https://alpm.archlinux.page/alpm-repo-db/index.html&quot;&gt;alpm-repo-db&lt;/a&gt; are released and ready.
And it would make a lot of sense to actually start &lt;strong&gt;using&lt;/strong&gt; them.&lt;/p&gt;
&lt;p&gt;For example, it would be super helpful to &lt;a rel=&quot;external&quot; href=&quot;https://gitlab.archlinux.org/archlinux/aurweb/-/blob/master/conf/config.defaults#L138&quot;&gt;run these parsers by default&lt;/a&gt; in the &lt;a rel=&quot;external&quot; href=&quot;https://gitlab.archlinux.org/archlinux/aurweb&quot;&gt;aurweb&lt;/a&gt; application, so that new AUR package maintainers get early feedback if they made any mistakes.&lt;/p&gt;
&lt;p&gt;We also regularly observe outdated or invalid &lt;a rel=&quot;external&quot; href=&quot;https://alpm.archlinux.page/specifications/SRCINFO.5.html&quot;&gt;SRCINFO&lt;/a&gt; files in the official package source repositories during our test runs.
As such it might even make sense to call &lt;a rel=&quot;external&quot; href=&quot;https://alpm.archlinux.page/alpm-srcinfo/index.html&quot;&gt;alpm-srcinfo&lt;/a&gt; or even &lt;a rel=&quot;external&quot; href=&quot;https://alpm.archlinux.page/lints/&quot;&gt;alpm-lint&lt;/a&gt; somewhere in the &lt;a rel=&quot;external&quot; href=&quot;https://gitlab.archlinux.org/archlinux/devtools&quot;&gt;devtools&lt;/a&gt; to catch such oversights early on.&lt;/p&gt;
&lt;p&gt;Anyhow, we&#39;re excited to already have removed a huge chunk of technical debt in a six month effort and we&#39;re looking forward to writing new stuff again! Soon™&lt;/p&gt;
</content>
        
    </entry>
</feed>
