<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Avyukt Soni</title>
    <description>The latest articles on DEV Community by Avyukt Soni (@avyuktsoni0731).</description>
    <link>https://dev.to/avyuktsoni0731</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1197826%2Fec7a71d6-0322-4120-a650-490503173a5d.png</url>
      <title>DEV Community: Avyukt Soni</title>
      <link>https://dev.to/avyuktsoni0731</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/avyuktsoni0731"/>
    <language>en</language>
    <item>
      <title>Porting a Python PEG parser to Rust in 72 hours, and actually proving it worked</title>
      <dc:creator>Avyukt Soni</dc:creator>
      <pubDate>Tue, 11 Aug 2026 05:17:39 +0000</pubDate>
      <link>https://dev.to/avyuktsoni0731/porting-a-python-peg-parser-to-rust-in-72-hours-and-actually-proving-it-worked-3jl2</link>
      <guid>https://dev.to/avyuktsoni0731/porting-a-python-peg-parser-to-rust-in-72-hours-and-actually-proving-it-worked-3jl2</guid>
      <description>&lt;p&gt;There's a joke that every Rust project starts with someone saying "we should rewrite this in Rust" and ends four weekends later with a half-finished repo and a lot of new opinions about lifetimes.&lt;/p&gt;

&lt;p&gt;We had 72 hours. So we skipped the opinions.&lt;/p&gt;

&lt;p&gt;This is the story of &lt;a href="https://github.com/avyuktsoni0731/rs-parsimonious" rel="noopener noreferrer"&gt;&lt;code&gt;rs-parsimonious&lt;/code&gt;&lt;/a&gt; — a complete Rust port of &lt;a href="https://github.com/erikrose/parsimonious" rel="noopener noreferrer"&gt;&lt;code&gt;erikrose/parsimonious&lt;/code&gt;&lt;/a&gt;, a pure-Python PEG packrat parser — built for &lt;a href="https://coderesurrection.com/2026/" rel="noopener noreferrer"&gt;Port Mortem 2026&lt;/a&gt;, a hackathon whose entire premise is that &lt;em&gt;writing&lt;/em&gt; a port is easy and &lt;em&gt;proving&lt;/em&gt; it works is the interesting part.&lt;/p&gt;

&lt;p&gt;Here's where we landed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Differential fuzz&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;20,041 cases, 0 divergences&lt;/strong&gt;, 600s continuous&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Upstream test suite&lt;/td&gt;
&lt;td&gt;84 passed, 2 skipped — running &lt;strong&gt;unmodified&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Port test suite&lt;/td&gt;
&lt;td&gt;54 Rust parity tests, all green&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;unsafe&lt;/code&gt; blocks&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0&lt;/strong&gt; — &lt;code&gt;#![forbid(unsafe_code)]&lt;/code&gt;, enforced in CI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold start&lt;/td&gt;
&lt;td&gt;23 ms → &lt;strong&gt;2 ms&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p99 latency&lt;/td&gt;
&lt;td&gt;0.068 ms → &lt;strong&gt;0.016 ms&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peak memory&lt;/td&gt;
&lt;td&gt;~13 MB → &lt;strong&gt;~6.7 MB&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ships as&lt;/td&gt;
&lt;td&gt;one &lt;strong&gt;2.1 MB static binary&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let me walk through how we got there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a parser was the right thing to port
&lt;/h2&gt;

&lt;p&gt;This was the most important decision we made, and we made it before writing a single line of Rust.&lt;/p&gt;

&lt;p&gt;A PEG parser has a beautiful property for this kind of work: its interface is &lt;code&gt;(grammar, input) -&amp;gt; parse_tree | error&lt;/code&gt;. That's it. No timezones. No locale handling. No floating-point tolerance you have to negotiate with yourself. Two implementations either produce the same tree or they don't, and there's never an argument about what "the same" means.&lt;/p&gt;

&lt;p&gt;That clarity is what made everything downstream possible. When your oracle is unambiguous, you can automate the proof. When it's fuzzy, you're hand-checking cases forever.&lt;/p&gt;

&lt;p&gt;We also confirmed nobody had already ported this specific project — a surprisingly involved check, since the obvious candidates in this space (&lt;code&gt;textdistance&lt;/code&gt;, &lt;code&gt;croniter&lt;/code&gt;, &lt;code&gt;wcwidth&lt;/code&gt;) all turned out to have Rust ports already published. &lt;code&gt;parsimonious&lt;/code&gt; came back clean. The Rust PEG libraries that exist (&lt;code&gt;pest&lt;/code&gt;, &lt;code&gt;rust-peg&lt;/code&gt;) are independent implementations, not translations of this codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing around the packrat cache
&lt;/h2&gt;

&lt;p&gt;The first real design decision was how to model expressions, and it's where the Rust version diverges most interestingly from the Python one.&lt;/p&gt;

&lt;p&gt;Python models expressions as a class hierarchy — &lt;code&gt;Literal&lt;/code&gt;, &lt;code&gt;Regex&lt;/code&gt;, &lt;code&gt;Sequence&lt;/code&gt;, &lt;code&gt;OneOf&lt;/code&gt;, &lt;code&gt;Lookahead&lt;/code&gt;, &lt;code&gt;Quantifier&lt;/code&gt;, all subclassing &lt;code&gt;Expression&lt;/code&gt;. The reflexive Rust translation would be &lt;code&gt;Box&amp;lt;dyn Expression&amp;gt;&lt;/code&gt; trait objects.&lt;/p&gt;

&lt;p&gt;We went a different way, and packrat memoization is the reason.&lt;/p&gt;

&lt;p&gt;Packrat parsing caches &lt;code&gt;(expression, position) -&amp;gt; result&lt;/code&gt; to turn what would be exponential backtracking into linear-time parsing. For that cache to work, two things have to be true: expressions need &lt;strong&gt;stable identity&lt;/strong&gt;, and subexpressions need to be genuinely &lt;strong&gt;shared&lt;/strong&gt;. A rule referenced from three places in a grammar has to be &lt;em&gt;the same object&lt;/em&gt; in all three, or you get three separate cache entries doing three separate redundant parses.&lt;/p&gt;

&lt;p&gt;So: one owned struct, a kind enum, shared through &lt;code&gt;Arc&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Expression&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ExprKind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Expr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Expression&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;ExprKind&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Literal&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;literal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;Regex&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;fancy_regex&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Regex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;flags_bits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;Sequence&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;members&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RwLock&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Expr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;OneOf&lt;/span&gt;      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;members&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RwLock&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Expr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;Lookahead&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;member&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;negative&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;Quantifier&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;member&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Expr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// TokenMatcher, LazyRef, AdHoc …&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cache keys on &lt;code&gt;Arc::as_ptr&lt;/code&gt;. That gives us exactly the object identity Python got for free from its object model — but explicitly and cheaply. No allocation, no hashing an entire expression tree, just a pointer comparison.&lt;/p&gt;

&lt;p&gt;Nice side effect: with &lt;code&gt;Arc&lt;/code&gt;, sharing a subexpression costs a refcount bump instead of a deep clone. In a grammar where rules reference each other heavily, that adds up fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;RwLock&lt;/code&gt; that earns its keep
&lt;/h2&gt;

&lt;p&gt;You'll have noticed &lt;code&gt;members: RwLock&amp;lt;Vec&amp;lt;Expr&amp;gt;&amp;gt;&lt;/code&gt; up there. A lock inside a parser structure looks like something went wrong. It's actually the thing that makes recursive grammars work.&lt;/p&gt;

&lt;p&gt;When you compile a grammar, rules reference each other by name &lt;em&gt;before&lt;/em&gt; those rules exist. &lt;code&gt;parsimonious&lt;/code&gt; handles this with lazy references resolved in a second pass. In Python, that resolution mutates in place — and because everything points at the same object, every parent automatically sees the resolved version.&lt;/p&gt;

&lt;p&gt;Getting that right in Rust meant matching the semantics, not just the shape: resolve in place, so a rule referenced from anywhere in the tree sees the fully-resolved version. Interior mutability is the honest way to express "this graph gets patched up once during construction, then never changes again."&lt;/p&gt;

&lt;p&gt;It's the kind of thing that looks like a smell until you understand why it's there, and then it looks obvious.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping the meta-grammar intact
&lt;/h2&gt;

&lt;p&gt;Here's a fun wrinkle: &lt;code&gt;parsimonious&lt;/code&gt; is &lt;strong&gt;self-hosting&lt;/strong&gt;. The grammar that parses grammar definitions is itself written in parsimonious grammar syntax. Which means the meta-grammar has to work before anything else does.&lt;/p&gt;

&lt;p&gt;And the meta-grammar uses negative lookahead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="s2"&gt;"[a-zA-Z_][a-zA-Z_0-9]*(?![\"'])"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rust's standard &lt;code&gt;regex&lt;/code&gt; crate doesn't support lookaround. That's deliberate on their part — it's how they guarantee linear-time matching.&lt;/p&gt;

&lt;p&gt;We could have simplified the meta-grammar to dodge the problem. We didn't, because the whole point of the exercise is fidelity to the original. Instead we reached for &lt;code&gt;fancy-regex&lt;/code&gt;, which supports lookaround, and kept the upstream grammar exactly as written.&lt;/p&gt;

&lt;p&gt;Small decision, but it's the one that lets us say the grammar is genuinely &lt;em&gt;the same grammar&lt;/em&gt; rather than "close enough."&lt;/p&gt;

&lt;h2&gt;
  
  
  Squeezing out the performance
&lt;/h2&gt;

&lt;p&gt;A few things stacked here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Release profile tuning.&lt;/strong&gt; Link-time optimization plus a single codegen unit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[profile.release]&lt;/span&gt;
&lt;span class="py"&gt;lto&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;codegen-units&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trades compile time for runtime, which is exactly the right trade for a library people build once and then run constantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero-copy where it counts.&lt;/strong&gt; Nodes hold an &lt;code&gt;Arc&amp;lt;str&amp;gt;&lt;/code&gt; into the source text rather than owning a copy of every matched substring. Pulling the text out of a node is a slice, not an allocation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The packrat cache itself.&lt;/strong&gt; This is the algorithmic win — it's what stops pathological grammars from exploding. Getting the identity model right (above) is what makes it genuinely effective rather than nominally present.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No runtime to boot.&lt;/strong&gt; This turned out to be the biggest practical improvement of all. Python has to start an interpreter and import a module before it can parse a single character. Our binary is already running.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Python&lt;/th&gt;
&lt;th&gt;Rust&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cold start&lt;/td&gt;
&lt;td&gt;23 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~11×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p99 latency&lt;/td&gt;
&lt;td&gt;0.068 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;0.016 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~4×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peak RSS&lt;/td&gt;
&lt;td&gt;~13 MB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~6.7 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~2×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Parse throughput&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;1.5–3.4×&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That startup number is the one I'd actually put on a slide. Nobody adopts a parser library because it shaves microseconds off a 40-character string. They adopt it because it starts instantly, uses half the memory, and deploys as a single file with no interpreter and no virtualenv in sight.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proving it, which was most of the work
&lt;/h2&gt;

&lt;p&gt;Here's the thing about writing your own tests: you wrote them. You tested what you thought of. The interesting failures live in the space you &lt;em&gt;didn't&lt;/em&gt; think of.&lt;/p&gt;

&lt;p&gt;So we built a differential harness.&lt;/p&gt;

&lt;p&gt;The Rust CLI exposes a JSON-line mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s1"&gt;'{"grammar":"g = \"hi\"\n","input":"hi","mode":"parse"}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | ./target/release/parsimonious json-line
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A Python oracle script accepts the identical schema and drives the original library. Then a fuzzer generates random grammars and random inputs, sends the same request to both sides, and compares results.&lt;/p&gt;

&lt;p&gt;The comparison is normalized on the things that are actually semantics: the &lt;code&gt;ok&lt;/code&gt; flag, tree structure (node spans and expression names), and on failure, the error &lt;code&gt;kind&lt;/code&gt; and &lt;code&gt;pos&lt;/code&gt;. Error &lt;em&gt;message text&lt;/em&gt; is deliberately out of scope — where a parse fails and why is behavior; how that gets phrased for a human is presentation, and demanding byte-identical Python exception strings from a Rust program would mean writing un-Rusty code for zero behavioral gain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final run: 600.02 seconds. 20,041 cases. Zero divergences.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;On top of that, upstream's own pytest suite runs completely untouched from a pinned git submodule — 84 passed, 2 skipped — and 54 Rust parity tests map back to specific test classes in the original.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building tamper-evidence that actually works
&lt;/h2&gt;

&lt;p&gt;One requirement of the hackathon is hashing the original test suite at kickoff, so the commit timestamp proves you didn't quietly edit tests later to make your port look better. (The event exists partly because a very high-profile Rust port did exactly that.)&lt;/p&gt;

&lt;p&gt;We took this seriously enough to audit our own compliance late in the build — and found that while our hash was committed and our test files were provably untouched, &lt;strong&gt;the script that generated the hash had never been checked in.&lt;/strong&gt; The method was described in a comment, but a prose description isn't an implementation: "SHA-256 of sorted per-file SHA-256s" leaves open which files, sorted how, hex digests or raw bytes, joined with what separator.&lt;/p&gt;

&lt;p&gt;So we built &lt;code&gt;scripts/hash_original_tests.py&lt;/code&gt; properly. It pins down every ambiguity explicitly, prints per-file digests alongside the combined one so the whole thing is auditable, and produces a hash anyone can regenerate in one command and check against the pinned submodule.&lt;/p&gt;

&lt;p&gt;That's the version that shipped. A hash you can't reproduce is just a number — a hash with a committed script behind it is actual evidence. Easily worth the hour.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell someone starting one of these
&lt;/h2&gt;

&lt;p&gt;Build the thing that proves the port before you build the port.&lt;/p&gt;

&lt;p&gt;We stood up the differential harness on day two, and from that moment on it paid for itself continuously — every divergence it caught was a five-minute fix instead of a day-three archaeology dig. Same story with the hash tooling.&lt;/p&gt;

&lt;p&gt;In a project like this, the verification infrastructure isn't overhead wrapped around the real work. It &lt;em&gt;is&lt;/em&gt; the real work. The port is almost a byproduct.&lt;/p&gt;




&lt;p&gt;Repo: &lt;a href="https://github.com/avyuktsoni0731/rs-parsimonious" rel="noopener noreferrer"&gt;github.com/avyuktsoni0731/rs-parsimonious&lt;/a&gt; — MIT, same as upstream. Every number above is reproducible from the repo; methodology lives in &lt;code&gt;bench/methodology.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Built for &lt;a href="https://coderesurrection.com/2026/" rel="noopener noreferrer"&gt;Port Mortem 2026&lt;/a&gt; by Hackathon Raptors.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>python</category>
      <category>testing</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
