<?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: Fu liang</title>
    <description>The latest articles on DEV Community by Fu liang (@fu_liang_dfbec863ed6c21e3).</description>
    <link>https://dev.to/fu_liang_dfbec863ed6c21e3</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%2F4161477%2F3efd7948-1fb9-42a3-a107-3627794120b6.jpg</url>
      <title>DEV Community: Fu liang</title>
      <link>https://dev.to/fu_liang_dfbec863ed6c21e3</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fu_liang_dfbec863ed6c21e3"/>
    <language>en</language>
    <item>
      <title>Testing automata-theory exercises in the browser: a regex flavor for CS coursework</title>
      <dc:creator>Fu liang</dc:creator>
      <pubDate>Sun, 04 Oct 2026 12:19:22 +0000</pubDate>
      <link>https://dev.to/fu_liang_dfbec863ed6c21e3/testing-automata-theory-exercises-in-the-browser-a-regex-flavor-for-cs-coursework-3kio</link>
      <guid>https://dev.to/fu_liang_dfbec863ed6c21e3/testing-automata-theory-exercises-in-the-browser-a-regex-flavor-for-cs-coursework-3kio</guid>
      <description>&lt;p&gt;If you've ever taught or taken a formal languages course, you know the workflow: the exercise says &lt;em&gt;write a regular expression for all strings over {a,b} containing an even number of a's&lt;/em&gt;, you write &lt;code&gt;(b|ab)*&lt;/code&gt; on paper... and then you have no way to check it.&lt;/p&gt;

&lt;p&gt;Programming regex flavors can't validate it for you — they're full of conveniences the formal dialect doesn't have: character classes, quantifier shorthands, anchors. What you want is a checker that speaks the &lt;strong&gt;textbook dialect&lt;/strong&gt;: union &lt;code&gt;∪&lt;/code&gt; or &lt;code&gt;|&lt;/code&gt;, concatenation, Kleene star &lt;code&gt;*&lt;/code&gt;, plus &lt;code&gt;+&lt;/code&gt;, parentheses. Nothing more.&lt;/p&gt;

&lt;p&gt;RegexTester.dev now ships a &lt;strong&gt;Formal Automata Theory flavor&lt;/strong&gt; exactly for this.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it accepts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Symbols from your chosen alphabet (typically &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;b&lt;/code&gt;, or &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Union: &lt;code&gt;∪&lt;/code&gt; or &lt;code&gt;|&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Concatenation: juxtaposition or &lt;code&gt;.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Kleene star: &lt;code&gt;*&lt;/code&gt;, and one-or-more: &lt;code&gt;+&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Grouping with parentheses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything else — &lt;code&gt;\d&lt;/code&gt;, &lt;code&gt;[a-z]&lt;/code&gt;, lookahead, backreferences — is rejected with an explanation, because the point is to exercise exactly the constructs the course covers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instant feedback
&lt;/h2&gt;

&lt;p&gt;Type the expression and test strings in, and you immediately see which strings match and which don't — so you can iterate on the exercise instead of waiting for a grader.&lt;/p&gt;

&lt;p&gt;The flavor runs on the same engine infrastructure as the other eight dialects (PCRE2, RE2, .NET, Python, Java, Go, Rust, JavaScript), so the matching behavior is well-defined and consistent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a browser tool for coursework?
&lt;/h2&gt;

&lt;p&gt;Because the feedback loop is everything when learning. The gap between writing &lt;code&gt;(b|ab)*&lt;/code&gt; on paper and knowing whether it's right is where most of the confusion in a formal languages course lives. Close that gap to seconds and students experiment their way to understanding: they can &lt;em&gt;see&lt;/em&gt; that &lt;code&gt;(b|ab)*&lt;/code&gt; matches &lt;code&gt;babb&lt;/code&gt; but not &lt;code&gt;abab&lt;/code&gt;, and fix their mental model immediately.&lt;/p&gt;

&lt;p&gt;It also works the other direction: instructors can build exercise sets with permalinks — every regex and test string combination in the tester has a shareable URL, so "check your answer against this" becomes a link.&lt;/p&gt;




&lt;p&gt;Try it: open &lt;a href="https://regextester.dev" rel="noopener noreferrer"&gt;regextester.dev&lt;/a&gt;, pick the &lt;strong&gt;Formal Automata&lt;/strong&gt; flavor from the selector, and test that even-number-of-a's exercise.&lt;/p&gt;

&lt;p&gt;Full write-up: &lt;a href="https://regextester.dev/blog/Formal-automata-theory-flavor" rel="noopener noreferrer"&gt;regextester.dev/blog/Formal-automata-theory-flavor&lt;/a&gt;. Free, no signup — built for students and educators.&lt;/p&gt;

</description>
      <category>regex</category>
      <category>computerscience</category>
      <category>education</category>
      <category>theory</category>
    </item>
    <item>
      <title>Benchmarking regex patterns properly: P95/P99, equivalence checks, real engines</title>
      <dc:creator>Fu liang</dc:creator>
      <pubDate>Sun, 04 Oct 2026 12:08:30 +0000</pubDate>
      <link>https://dev.to/fu_liang_dfbec863ed6c21e3/benchmarking-regex-patterns-properly-p95p99-equivalence-checks-real-engines-1cij</link>
      <guid>https://dev.to/fu_liang_dfbec863ed6c21e3/benchmarking-regex-patterns-properly-p95p99-equivalence-checks-real-engines-1cij</guid>
      <description>&lt;p&gt;Every developer has been here: two regex patterns that both work. One is a dense one-liner, the other reads like prose. Which do you ship?&lt;/p&gt;

&lt;p&gt;"Use the simpler one" is a fine heuristic until the pattern runs over a 50 MB log file or every request in a hot path. Then you want data. The problem is that regex benchmarks done casually lie to you three ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The engine problem&lt;/strong&gt; — your JS benchmark measures JavaScript's RegExp, but the pattern ships to a PCRE2-based backend. Different engine, different behavior, different speed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The clock problem&lt;/strong&gt; — a fast regex finishes in microseconds, far below &lt;code&gt;performance.now()&lt;/code&gt; resolution. You're measuring timer noise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The equivalence problem&lt;/strong&gt; — the faster pattern is faster &lt;em&gt;because it matches less&lt;/em&gt;. You optimized a bug.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;RegexTester.dev's built-in &lt;a href="https://regextester.dev/bench" rel="noopener noreferrer"&gt;Benchmark Suite&lt;/a&gt; was designed against those three traps. Here's how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real engines, identical conditions
&lt;/h2&gt;

&lt;p&gt;Up to 5 alternative patterns run sequentially under identical input, flags and engine — the same engines the tester itself uses: PCRE2 and RE2 as WebAssembly in a dedicated worker, .NET's &lt;code&gt;System.Text.RegularExpressions&lt;/code&gt; as a real interpreter, and native JS RegExp (including the normalize-to-JS path used for Python, Java and other flavors).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnqqf6buhrj9rgna7uzxn.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnqqf6buhrj9rgna7uzxn.webp" alt="Benchmark setup" width="800" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sub-clock-precision timing
&lt;/h2&gt;

&lt;p&gt;Each sample times &lt;strong&gt;K consecutive executions&lt;/strong&gt; and records time ÷ K, with K auto-calibrated so every sample spans between 0.35 ms and 12 ms. This batch amplification resolves sub-microsecond patterns with sub-nanosecond effective resolution — far below browser clock precision.&lt;/p&gt;

&lt;p&gt;You get median, mean, P95, P99, ops/sec and the relative speedup of the fastest pattern, with a ±RSE column that tells you when a number is too noisy to trust.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frtubrg6maeddkcplsjkd.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frtubrg6maeddkcplsjkd.webp" alt="Results table" width="800" height="945"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Equivalence checking
&lt;/h2&gt;

&lt;p&gt;Speed means nothing if the output changed. The suite records each pattern's match count on the benchmark input and raises an &lt;strong&gt;"Output differs"&lt;/strong&gt; warning when counts diverge — a pattern that's faster because it matches less gets flagged before you ship it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading the charts
&lt;/h2&gt;

&lt;p&gt;Latency histograms and timelines make the distribution shape visible: a tight spike means stable performance; a long tail means some inputs hit pathological backtracking even when the median looks fine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0j0c8rbmtcseze59lwem.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0j0c8rbmtcseze59lwem.webp" alt="Charts" width="800" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Export
&lt;/h2&gt;

&lt;p&gt;Results export as Markdown (for PRs and docs) or JSON (for your own tooling), with the environment (UA, cores, timestamp) baked in so numbers stay comparable.&lt;/p&gt;




&lt;p&gt;Try it: open &lt;a href="https://regextester.dev/bench" rel="noopener noreferrer"&gt;regextester.dev/bench&lt;/a&gt;, paste two alternatives, press Run. Sixty seconds later you'll know which one to ship — and whether they really do the same thing.&lt;/p&gt;

&lt;p&gt;Full write-up: &lt;a href="https://regextester.dev/blog/regex-benchmark-suite" rel="noopener noreferrer"&gt;regextester.dev/blog/regex-benchmark-suite&lt;/a&gt;. Free, no signup, everything runs locally.&lt;/p&gt;

</description>
      <category>regex</category>
      <category>benchmarking</category>
      <category>performance</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I built a regex debugger that shows every backtrack (flame graph included)</title>
      <dc:creator>Fu liang</dc:creator>
      <pubDate>Sun, 04 Oct 2026 12:08:29 +0000</pubDate>
      <link>https://dev.to/fu_liang_dfbec863ed6c21e3/i-built-a-regex-debugger-that-shows-every-backtrack-flame-graph-included-g5e</link>
      <guid>https://dev.to/fu_liang_dfbec863ed6c21e3/i-built-a-regex-debugger-that-shows-every-backtrack-flame-graph-included-g5e</guid>
      <description>&lt;p&gt;Testers tell you &lt;em&gt;what&lt;/em&gt; a regex matches. A debugger tells you &lt;em&gt;why&lt;/em&gt; — and &lt;em&gt;where it burns time&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Every regex tester shows the same three things: matches, groups, and an explanation of the pattern. That covers the happy path. It stops helping the moment a regex is &lt;em&gt;almost&lt;/em&gt; right — matching one tag too many, missing a case you can't reproduce, or taking 400 ms on an input that looks identical to the one that took 4.&lt;/p&gt;

&lt;p&gt;The failure modes of a backtracking engine are invisible from the outside. A pattern doesn't slowly get slower — it tries a prefix, gives characters back, retries the inner quantifier at a different split, fails, and repeats that a few thousand times. The match result looks the same as the fast version's. Only the &lt;strong&gt;trace&lt;/strong&gt; of what the engine actually did tells them apart.&lt;/p&gt;

&lt;p&gt;So I built a full debugger into &lt;a href="https://regextester.dev" rel="noopener noreferrer"&gt;RegexTester.dev&lt;/a&gt;. Here's what it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  One click from the tester
&lt;/h2&gt;

&lt;p&gt;The entry point is the &lt;strong&gt;Debug&lt;/strong&gt; button on the tester toolbar. Hit it, and the debugger opens with the trace already recorded. The demo pattern &lt;code&gt;&amp;lt;([a-z]+)([^&amp;lt;]+)*(?:&amp;gt;(.*)&amp;lt;\/\1&amp;gt;|\s+/&amp;gt;)&lt;/code&gt; against &lt;code&gt;&amp;lt;div&amp;gt;Content&amp;lt;/div&amp;gt;&lt;/code&gt; generates &lt;strong&gt;7,530 execution steps&lt;/strong&gt; for 18 characters of input. That gap between "looks simple" and "7,530 steps" is exactly what the debugger makes visible.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzg3jdbik9gqut0a891sl.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzg3jdbik9gqut0a891sl.webp" alt="Regex debugger overview" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The execution graph
&lt;/h2&gt;

&lt;p&gt;The graph is a flame-chart of the engine's work. Each bar is one node attempt — a TRY that ended in OK (green), FAIL (red), or was undone by backtracking (thin blue give-back marks). Rows are stack depth; vertical bands separate match attempts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4q1ajn78wzzd16rxq2ai.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4q1ajn78wzzd16rxq2ai.webp" alt="Execution graph" width="797" height="115"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It behaves like a proper profiler view: scroll-wheel zoom, drag to pan, double-click to zoom to a block, click any block to jump there — the amber marker follows, and every other panel updates in lockstep. A healthy trace reads as a calm staircase. A sick one shows the signature immediately: one region of the graph orders of magnitude wider than everything else, striped with red and blue.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hot spots: where the steps go
&lt;/h2&gt;

&lt;p&gt;The Hot spots table is the profiler summary: every node of your pattern, ranked, with its operation count, give-backs, failures, and share of total steps.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fshmofmilggsj5bfenba5.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fshmofmilggsj5bfenba5.webp" alt="Hot spots table" width="800" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the same workflow you'd use on slow code: profile, find the hot function, fix, re-profile. It just didn't exist for regular expressions in a browser before.&lt;/p&gt;

&lt;h2&gt;
  
  
  From diagnosis to fix in two clicks
&lt;/h2&gt;

&lt;p&gt;Diagnosis without a fix is only half a tool. The &lt;strong&gt;Suggestions&lt;/strong&gt; panel combines static analysis with trace statistics to propose concrete rewrites — dialect-aware, so a PCRE2 pattern gets atomic groups and possessive quantifiers while a JavaScript pattern gets the constructs ES2025 actually supports.&lt;/p&gt;

&lt;p&gt;The classic case: &lt;code&gt;(a+)+b&lt;/code&gt; on &lt;code&gt;aaaaaaaaaaX&lt;/code&gt; — the textbook catastrophic backtracking example. The debugger names the disease and prescribes the cure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbgqilqkba4s8qu7zxlle.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbgqilqkba4s8qu7zxlle.webp" alt="Suggestions panel" width="800" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two things make this safe to trust:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Equivalence checking&lt;/strong&gt; — both patterns run against the real engine for your dialect on your input; only identical match lists earn the "same matches on this input" badge. A faster regex that matches less is a bug, not an optimization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apply&lt;/strong&gt; writes the rewrite into the tester and re-traces immediately: same input, Healthy verdict, 404 steps instead of 50,000+.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjgcr6bx5h1q95zdk2wvn.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjgcr6bx5h1q95zdk2wvn.webp" alt="After applying the rewrite" width="797" height="91"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A tracer kept honest
&lt;/h2&gt;

&lt;p&gt;The trace comes from an instrumented backtracking engine — a continuation-passing matcher written for this purpose, speaking the semantics of nine flavor dialects. Every trace is &lt;strong&gt;cross-checked against the real thing&lt;/strong&gt;: the match outcome is compared with the actual engine for the selected flavor (native RegExp, or PCRE2/.NET compiled to WebAssembly), and a "trace verified" badge only appears when they agree.&lt;/p&gt;

&lt;p&gt;Try it: &lt;a href="https://regextester.dev/debug" rel="noopener noreferrer"&gt;regextester.dev/debug&lt;/a&gt; — or read the full write-up at &lt;a href="https://regextester.dev/blog/regex-debugger" rel="noopener noreferrer"&gt;regextester.dev/blog/regex-debugger&lt;/a&gt;. Free, no signup, everything runs locally.&lt;/p&gt;

</description>
      <category>regex</category>
      <category>debugging</category>
      <category>javascript</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
