smirk-0.1.0.0: README.md
# Smirk (;-P / ;-) — Pure Native Resumable PCRE Engine in Haskell
**Smirk** is a 100% native Haskell Perl-Compatible Regular Expression (PCRE) engine. It provides high-assurance, pure functional regular expression matching, step-bounded ReDoS immunity, pausable/resumable continuations, sequence polymorphism, and first-class algebraic effect handlers without C dependencies, buffer allocations, or FFI overhead.
---
## The Name & Emoticon Grounding
The library's name and symbol—**Smirk** (**;-P** / **;-)**)—express cheeky, playful defiance:
- In the security and system reliability world, regular expression engines are notorious for disastrous catastrophic backtracking—the dreaded **ReDoS** (Regular Expression Denial of Service) attack, where a pathological string like `aaaaaaaaaaaaaaaaaaaaaaaaaaaaa!` against `(a+)+$` locks a CPU core in exponential $\mathcal{O}(2^N)$ backtracking for hours or days.
- In mainstream Haskell, developers were traditionally forced to link against foreign C libraries (`libpcre` / `libpcre2`) via FFI bindings, compromising memory safety, complicating static linking and cross-compilation, and inviting segmentation faults on unbounded recursion.
- **Smirk** smirks at both traps: by executing bytecode over an explicit, heap-allocated Pike-style backtracking state machine under a strict **Fuel / Step Budget**, Smirk guarantees that execution never locks a runtime thread, never overflows the C call stack, and can pause, yield an intact continuation, and resume on demand.
---
## Core Motivation & Architecture
### The Problem with C FFI and Unbounded Backtracking
1. **Foreign Function Interface (FFI) Vulnerabilities:** Traditional Haskell bindings to C PCRE libraries introduce native build dependencies, cross-compilation obstacles, and potential memory leaks across C string marshalling boundaries.
2. **Catastrophic Backtracking (ReDoS):** When processing untrusted user input (e.g. web forms, query filters, search strings), standard regex engines can enter exponential search loops that freeze application threads.
3. **Container Inflexibility:** Most regex libraries force data into strict `ByteString` or `Text`, forcing expensive allocations when scanning `[Char]`, finger-tree `Seq Char`, or `Vector Char`.
4. **Effect Monolithic Coupling:** Extensible effect users often find regex libraries tightly coupled to a single effect framework, pulling in heavy dependencies.
### The Smirk Solution
`smirk` resolves these challenges through five architectural pillars:
1. **100% Pure Haskell Pike Virtual Machine:** Patterns are compiled into linear bytecode vectors (`Opcode`), evaluated by a step-stepped virtual machine with an explicit heap-allocated backtracking stack.
2. **Fuel-Bounded Execution & ReDoS Defense:** Every instruction execution consumes 1 unit of `Fuel` (`StepBudget !Int`). If the budget is exhausted, the engine yields `OutOfFuel` along with a `MatchContinuation`.
3. **Pausable & Resumable Continuations:** Match evaluations can be paused, inspected, serialized, and resumed via `resumeMatch` with an replenished fuel budget.
4. **Sequence Polymorphism:** Through the `RegexTarget` abstraction, matching operates zero-copy across `Text`, `ByteString`, `[Char]`, `Seq Char`, and `Vector Char`.
5. **Decoupled 3-Package Architecture:** The core engine is completely independent of effect libraries; dedicated packages provide zero-overhead bindings for `effectful` and `polysemy`.
---
## Multi-Package Catalog
The repository is structured into four independent, modular Cabal packages:
| Package | Directory | Description | Hackage Status |
| :--- | :--- | :--- | :--- |
| **`smirk`** | [`./`](.) | Core engine: Megaparsec PCRE parser, AST, bytecode compiler, Pike VM, sequence polymorphism, glob engine, and QuasiQuoters (`[re|...|]`, `[ed|...|]`, `[glob|...|]`). Zero effect dependencies. | Ready (0.1.0.0) |
| **`smirk-effectful`** | [`../smirk-effectful/`](../smirk-effectful/) | First-class algebraic effect (`RegexEngine`) for the `effectful` ecosystem, featuring pure and fuel-bounded interpreters. Depends only on `smirk` and `effectful`. | Ready (0.1.0.0) |
| **`smirk-polysemy`** | [`../smirk-polysemy/`](../smirk-polysemy/) | Extensible effect (`RegexEngine`) for the `polysemy` ecosystem, featuring pure and fuel-bounded interpreters. Depends only on `smirk` and `polysemy`. | Ready (0.1.0.0) |
| **`smirk-penelope`** | [`../smirk-penelope/`](../smirk-penelope/) | Algebraic effect façade (`RegexEngine`) for the `penelope` effect system, featuring zero-cost vector-dispatched interpreters over flat Polytropos row vectors. | Ready (0.1.0.0) |
---
## Key Subsystems & Worked Examples
### 1. Basic PCRE Matching & Capture Groups
```haskell
{-# LANGUAGE OverloadedStrings #-}
import Smirk
main :: IO ()
main = do
case compileRegex "(?<protocol>https?)://(?<domain>[a-z.]+)" of
Left err -> putStrLn ("Compile error: " ++ show err)
Right reg -> do
case execMatch reg ("Visit https://haskell.org today!" :: Text) of
Nothing -> putStrLn "No match found"
Just res -> do
print (matchedSlice res) -- "https://haskell.org"
print (matchOffset res, matchLength res) -- (6, 19)
print (namedGroups res) -- fromList [("domain","haskell.org"),("protocol","https")]
```
### 2. Compile-Time QuasiQuoters (`[re|...|]` & `[ed|...|]`)
QuasiQuoters validate regex syntax and stream editor templates at compile time:
```haskell
{-# LANGUAGE QuasiQuotes #-}
import Smirk
-- 1. Compile-time verified pattern:
let pattern = [re|\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b|]
-- 2. Stream editor find-and-replace:
let editor = [ed|foo([0-9]+)///bar${1}|]
let replaced = applyEditor editor "foo42 and foo99"
-- replaced == "bar42 and bar99"
```
### 3. Step-Bounded Execution & ReDoS Defense
Defend against pathological catastrophic backtracking patterns by enforcing a step budget:
```haskell
import Smirk
let pathological = compileRegex "(a+)+$"
let target = "aaaaaaaaaaaaaaaaaaaaaaaaaaaa!" :: Text
case pathological of
Right reg ->
-- Restrict execution to at most 10,000 VM instruction steps:
case execMatchWithFuel reg target (StepBudget 10000) of
Matched _ -> putStrLn "Matched"
NoMatch -> putStrLn "Did not match"
OutOfFuel cont -> do
putStrLn "ReDoS detected! Step budget exceeded without locking CPU."
-- Optionally resume later with additional fuel:
case resumeMatch cont (StepBudget 5000) of
OutOfFuel _ -> putStrLn "Still out of fuel; safely aborting."
_ -> putStrLn "Finished."
```
### 4. Sequence Polymorphism across Haskell Containers
Match uniformly across different sequence representations without converting to `Text`:
```haskell
import qualified Data.ByteString.Char8 as BS
import qualified Data.Sequence as Seq
import qualified Data.Vector as V
import Smirk
let reg = [re|[0-9]+|]
-- Strict ByteString:
testMatch reg (BS.pack "port 8080") -- True
-- Standard String ([Char]):
testMatch reg "port 8080" -- True
-- Data.Sequence.Seq Char:
testMatch reg (Seq.fromList "port 8080") -- True
-- Data.Vector.Vector Char:
testMatch reg (V.fromList "port 8080") -- True
```
### 5. Effectful Integration (`smirk-effectful`)
```haskell
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeApplications #-}
import Effectful
import Smirk (re)
import Smirk.Effect.Effectful
lookupPort :: (RegexEngine :> es) => Text -> Eff es (Maybe Text)
lookupPort input = do
mMatch <- matchRegex [re|port:([0-9]+)|] input
pure (fmap matchedSlice mMatch)
```
### 6. Polysemy Integration (`smirk-polysemy`)
```haskell
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeApplications #-}
import Polysemy
import Smirk (re)
import Smirk.Effect.Polysemy
lookupPort :: (Member RegexEngine r) => Text -> Sem r (Maybe Text)
lookupPort input = do
mMatch <- matchRegex [re|port:([0-9]+)|] input
pure (fmap matchedSlice mMatch)
```
### 7. Native Filesystem Globbing (`[glob|...|]`)
```haskell
{-# LANGUAGE QuasiQuotes #-}
import Smirk
let g = [glob|src/**/*.{hs,lhs}|]
matchGlob g "src/Smirk/Core/AST.hs" -- True
matchGlob g "test/Spec.hs" -- False
```
---
## Documentation Corpus & Reading Order
The technical documentation corpus is structured for progressive disclosure:
1. [`docs/INDEX.md`](../../docs/INDEX.md) — Comprehensive corpus inventory, audience reading paths, and package catalog.
2. [`docs/TUTORIAL.md`](../../docs/TUTORIAL.md) — Detailed hands-on tutorial with worked examples across all 7 subsystems.
3. [`docs/NEWBIE_GUIDE.md`](../../docs/NEWBIE_GUIDE.md) — Fast symbol navigation table and 15-minute quickstart.
4. [`docs/MAINTAINER_GUIDE.md`](../../docs/MAINTAINER_GUIDE.md) — Architectural invariants (I1–I8), verification gates, and triage runbooks.
5. [`docs/notes/SMIRK_DESIGN.md`](../../docs/notes/SMIRK_DESIGN.md) — Canonical design monograph detailing decisions D1–D10.
6. [`docs/notes/DOC_STRATEGY.md`](../../docs/notes/DOC_STRATEGY.md) — Documentation strategy, precedence rule, and lifecycle.
7. [`docs/notes/COMMIT_ATTRIBUTION.md`](../../docs/notes/COMMIT_ATTRIBUTION.md) — Linux kernel RFC 2822 trailer format standards.
8. [`AGENTS.md`](../../AGENTS.md) — Advisory operational rules and navigation funnels for AI coding assistants.
---
## Physical Verification Discipline
Before any commit or release, all 5 physical verification gates must pass cleanly:
```bash
./bin/smirk-verify
```
1. **Gate 1: HLint Static Analysis:** Zero hints allowed across all source packages (`.hlint.yaml`).
2. **Gate 2: Full Cabal Test Suite:** All test suites pass across all packages (`cabal test all`).
3. **Gate 3: Tagref Cross-Reference Gate:** Zero dangling references and zero duplicate tags (`bin/smirk-tagref`).
4. **Gate 4: Markdown Link Integrity Gate:** Every relative link and heading anchor resolves (`bin/smirk-linkcheck`).
5. **Gate 5: CLOC Comment Density Gate:** Comment density satisfies documentation thresholds (`bin/smirk-cloc`).
---
## License & Attribution
- Licensed under the **Apache License 2.0** (`LICENSE`).
- Copyright (c) 2026 Nadia Yvette Chambers.
- AI assistant contributions follow the RFC 2822 trailer format (`Assisted-by:`). See [`docs/notes/COMMIT_ATTRIBUTION.md`](../../docs/notes/COMMIT_ATTRIBUTION.md).