Implementation Comparison
The shared comparison runner lives in scripts/compare-impls.mjs because this repo owns the corpus. It compares sibling implementation checkouts against the same .crv / .html pairs and reports default conformance, optional Tier-2 adapter coverage, rough CLI timing, and the extension hook surface each implementation exposes.
Snapshot (2026-05-31)
Historical run from 2026-05-31, when the corpus held 154 pairs. The corpus has since grown (now ~200 pairs); regenerate with
npm run compare:implsfor current figures. The numbers below are from that dated run.
| Implementation | Commit | Corpus | Mismatches | Errors | Avg CLI ms/file |
|---|---|---|---|---|---|
| Rust | 07ea8ea | 154 / 154 | 0 | 0 | 33.83 |
| JS | 48c45e0 | 154 / 154 | 0 | 0 | 58.92 |
| PHP | b1fa677 | 154 / 154 | 0 | 0 | 73.96 |
Spec commit: 840b64f
Optional Tier-2 Profile
The optional profile enables a shared adapter per feature where each implementation exposes one. Unsupported feature/implementation combinations are reported as skipped, not failures.
| Feature | Rust | JS | PHP |
|---|---|---|---|
| Social link templates | pass | pass | pass |
| Emoji map | pass | pass | skipped |
| German smart quotes | skipped | skipped | pass |
| Bare URL autolink | skipped | skipped | pass |
| Implementation | Optional pass | Skipped | Mismatches | Errors | Avg CLI ms/file |
|---|---|---|---|---|---|
| Rust | 2 / 2 | 2 | 0 | 0 | 63.93 |
| JS | 2 / 2 | 2 | 0 | 0 | 94.26 |
| PHP | 3 / 3 | 1 | 0 | 0 | 103.30 |
Optional cross-implementation diffs: 0
CLI Timing
These timings include process startup and should be read as smoke-level CLI performance, not parser microbenchmarks.
33.83 ms58.92 ms73.96 msExtension Surface
The comparison run is default/no-opt-in, so extension behavior is not yet exercised across every min/max profile. This matrix records the hook surface available in each implementation today.
| Capability | Rust | JS | PHP |
|---|---|---|---|
| Inline matcher | yes | no | yes |
| Block matcher | yes | no | yes |
| After-parse transform | yes | yes | yes |
| Before-render transform | yes | yes | yes |
| Inline extension renderer | yes | yes | yes |
| Block extension renderer / render listener | yes | no | yes |
| Converter-level registration | no | no | yes |
Running It
npm run compare:impls
npm run compare:impls -- --corpus=optional
npm run compare:impls -- --limit=20 --benchBy default the script expects sibling checkouts:
../carve-rs../carve-js../carve-php
Override those paths with CARVE_RS_DIR, CARVE_JS_DIR, and CARVE_PHP_DIR.
The documented snapshot used:
CARVE_RS_DIR=/media/mark/data/work/git/carve-rs \
CARVE_JS_DIR=/media/mark/data/work/git/carve-js \
CARVE_PHP_DIR=/media/mark/data/work/git/carve-php \
node scripts/compare-impls.mjsDefault raw output:
Implementation summary
profile=default/no-opt-in corpus=core corpus_pairs=154
rust: pass=154/154 mismatch=0 error=0 skipped=0 avg_ms=33.83
js: pass=154/154 mismatch=0 error=0 skipped=0 avg_ms=58.92
php: pass=154/154 mismatch=0 error=0 skipped=0 avg_ms=73.96
cross_impl_diffs=0
Extension capability matrix
rust: inline matcher, block matcher, after_parse, before_render, inline extension renderer, block extension renderer
js: afterParse, beforeRender, inline extension renderer
php: inline matcher, block matcher, parsed-document hook, before-render hook, render listeners, converter registration
extension_profile_note=this run compares default/no-opt-in output. Use --corpus=optional for Tier-2 opt-in adapters.Optional raw output:
Implementation summary
profile=optional/opt-in corpus=optional corpus_pairs=4
rust: pass=2/2 mismatch=0 error=0 skipped=2 avg_ms=63.93
js: pass=2/2 mismatch=0 error=0 skipped=2 avg_ms=94.26
php: pass=3/3 mismatch=0 error=0 skipped=1 avg_ms=103.30
cross_impl_diffs=0
Optional feature coverage
social-link-templates: rust, js, php
emoji-map: rust, js
smart-quotes-locale-de: php
bare-url-autolink: phpScope
The tool has two profiles:
- It runs the mandatory Tier-1 corpus in
tests/corpus. - It runs optional Tier-2 adapters in
tests/corpus-optionalwith--corpus=optional. - It compares byte-identical output after trimming.
- It reports CLI-level average time per corpus file.
- It reports extension system surface area.
Tier-3 app-extension max profiles still need language-specific adapter fixtures. That means a small runner per implementation that enables the same test extension in each language, then feeds those through the same comparison loop.