Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Entries that only restate a diff will be deleted.
- Consequence: the port depends on libc math; Zig-only freestanding
builds are out of scope.
- Evidence: sampled glibc vs `std.math` on the same inputs (specials,
dense grids, powers of two). Counts in `dev/baseline-artifacts/16-libm-vs-stdmath.txt`:
dense grids, powers of two). Counts in `docs/evidence/baseline/16-libm-vs-stdmath.txt`:
- sin 602117 samples, 0 differ
- cos/tan/exp/log/log10/sqrt/ceil/floor/fabs: 0 differ on their samples
- asin 26109 samples, 24441 differ (rate 0.936)
Expand All @@ -70,7 +70,7 @@ Entries that only restate a diff will be deleted.
generic over a builder interface (ENCRUST-style encapsulated substitution,
arXiv:2604.04527).
- Choice: shared recursive-descent parser parameterized by a builder
(`parser.zig`). `NativeBuilder` builds `ast.Expr`. Prompt 3 adds a
(`parser.zig`). `NativeBuilder` builds `ast.Expr`. A later pass adds a
C-layout builder in `c_abi.zig` without forking parse logic.
- Consequence: builder interface must be rich enough for both layouts;
some C-specific allocation tricks stay in the C builder.
Expand All @@ -92,7 +92,7 @@ Entries that only restate a diff will be deleted.
`-Dfix-fac-overflow` rejects non-integer inputs with NaN (C truncates
`fac(4.8)` to 24, which smoke expects). We do not use `*%` for default
fac, because that would diverge from measured C.
- Evidence: `dev/baseline-artifacts/14-fac-ncr-probe.txt`. smoke expects
- Evidence: `docs/evidence/baseline/14-fac-ncr-probe.txt`. smoke expects
`fac(4.8)==24`.

## D007 - ncr/npr DoS surface is small under the overflow guard
Expand All @@ -107,12 +107,13 @@ Entries that only restate a diff will be deleted.
another intentional divergence.
- Evidence: `ncr(40,20)` 0.000046s; `ncr(1e9,100)` returns INF in 0.000001s;
`ncr(UINT_MAX, 200000)` returns INF in 0.000001s
(`15c-ncr-dos.txt`).
(`docs/evidence/baseline/15c-ncr-dos.txt`).

## D008 - Nesting bound 2048 (intentional divergence)

- Constraint: C recurses without a depth limit and SIGSEGVs near depth
20144 on an 8 MiB stack (issue #136). The port must not inherit that crash.
20144 on WSL x86_64 with an 8 MiB stack (issue #136; host-dependent). The
port must not inherit that crash.
- Options: raise stack; keep unbounded recursion; hard error at a bound;
iterative parser with a heap stack.
- Choice: hard error `error.NestingTooDeep` at depth 2048 (same number as
Expand All @@ -122,7 +123,8 @@ Entries that only restate a diff will be deleted.
- Consequence: differential harnesses must treat deep nests as an expected
divergence table entry, not a fuzz failure.
- Evidence: unit test builds 1e6 nested parens and expects NestingTooDeep;
C crashes at 20144 without ASan.
C crashes at 20144 without ASan (WSL x86_64, 8 MiB stack; Darwin arm64
measured 23764 in `docs/findings.md`).

## D009 - Two-layer public surface

Expand All @@ -136,8 +138,8 @@ Entries that only restate a diff will be deleted.

## D010 - Export the accidental globals

- Constraint: Prompt 1 `nm` showed `te_free_parameters` and `next_token` as
globally defined alongside the header API.
- Constraint: early `nm` on the upstream object showed `te_free_parameters`
and `next_token` as globally defined alongside the header API.
- Options: header API only; exact nm set.
- Choice: exact nm set (all seven symbols).
- Consequence: `State` is public as an extern struct so `next_token` can be
Expand Down
5 changes: 4 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -183,5 +183,8 @@ pub fn build(b: *std.Build) void {
// Also attach to the default test step so `zig build test --fuzz` sees Smith cases.
test_step.dependOn(&run_harness_tests.step);

_ = b.step("demo", "Prompt 9: demo assets");
const demo_cmd = b.addSystemCommand(&.{ "bash", "docs/demo/regen-svgs.sh" });
demo_cmd.setCwd(b.path("."));
const demo_step = b.step("demo", "Regenerate docs/demo/*.svg from segment transcripts");
demo_step.dependOn(&demo_cmd.step);
}
24 changes: 24 additions & 0 deletions docs/demo-script.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,30 @@ terminal captures of each segment are already committed under `docs/demo/`
**Outstanding human action:** record the MP4 from this script and upload it
with the submission form **before 2026-08-03 18:00 UTC**.

## How to record (do this)

1. Open a terminal at a large font (18–24 pt). Black or dark theme.
2. Start screen recording (QuickTime on macOS: File → New Screen Recording,
select the terminal window only; or OBS; or `asciinema rec demo.cast`).
3. `cd` into the repo root and run the commands in the shot list below, in
order. Pause 2 seconds on each success line the narration calls out.
4. Stop recording under five minutes. Export MP4 (QuickTime: File → Export).
5. Upload where the organizer form asks. Optionally link it from the README
after freeze if the form allows an external URL.

If you use `asciinema`:

```bash
cd /path/to/tinyexpr.zig
asciinema rec /tmp/tinyexpr-demo.cast
# ... run the commands ...
# Ctrl-D to stop
# Convert if you have agg: agg /tmp/tinyexpr-demo.cast /tmp/tinyexpr-demo.gif
```

The SVG segments under `docs/demo/` are already committed for the README; the
organizer still wants a live video of you running the suite.

## Assets already in the repo

| Segment | SVG | Transcript |
Expand Down
12 changes: 12 additions & 0 deletions docs/demo/regen-svgs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
# Regenerate docs/demo/*.svg from docs/demo/segments/*.txt
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
cd "$ROOT"
count=0
for f in docs/demo/segments/*.txt; do
stem="$(basename "$f" .txt)"
python3 docs/demo/render_svg.py "$f" "docs/demo/${stem}.svg"
count=$((count + 1))
done
echo "demo SVGs regenerated: ${count}"
52 changes: 52 additions & 0 deletions docs/evidence/baseline/14-fac-ncr-probe.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
=== fac(n) via te_interp ===
n= 0 te=0x1p+0 bits=3ff0000000000000 tgamma=0x1p+0 same=1 inf=0 nan=0
n= 1 te=0x1p+0 bits=3ff0000000000000 tgamma=0x1p+0 same=1 inf=0 nan=0
n= 2 te=0x1p+1 bits=4000000000000000 tgamma=0x1p+1 same=1 inf=0 nan=0
n= 3 te=0x1.8p+2 bits=4018000000000000 tgamma=0x1.8p+2 same=1 inf=0 nan=0
n= 4 te=0x1.8p+4 bits=4038000000000000 tgamma=0x1.8p+4 same=1 inf=0 nan=0
n= 5 te=0x1.ep+6 bits=405e000000000000 tgamma=0x1.ep+6 same=1 inf=0 nan=0
n= 6 te=0x1.68p+9 bits=4086800000000000 tgamma=0x1.68p+9 same=1 inf=0 nan=0
n= 7 te=0x1.3bp+12 bits=40b3b00000000000 tgamma=0x1.3bp+12 same=1 inf=0 nan=0
n= 8 te=0x1.3bp+15 bits=40e3b00000000000 tgamma=0x1.3bp+15 same=1 inf=0 nan=0
n= 9 te=0x1.626p+18 bits=4116260000000000 tgamma=0x1.626p+18 same=1 inf=0 nan=0
n=10 te=0x1.baf8p+21 bits=414baf8000000000 tgamma=0x1.baf8p+21 same=1 inf=0 nan=0
n=11 te=0x1.308a8p+25 bits=418308a800000000 tgamma=0x1.308a8p+25 same=1 inf=0 nan=0
n=12 te=0x1.c8cfcp+28 bits=41bc8cfc00000000 tgamma=0x1.c8cfbffffffffp+28 same=0 inf=0 nan=0
n=13 te=0x1.7328ccp+32 bits=41f7328cc0000000 tgamma=0x1.7328cc0000001p+32 same=0 inf=0 nan=0
n=14 te=0x1.44c3b28p+36 bits=42344c3b28000000 tgamma=0x1.44c3b27ffffffp+36 same=0 inf=0 nan=0
n=15 te=0x1.30777758p+40 bits=4273077775800000 tgamma=0x1.30777758p+40 same=1 inf=0 nan=0
n=16 te=0x1.30777758p+44 bits=42b3077775800000 tgamma=0x1.3077775800001p+44 same=0 inf=0 nan=0
n=17 te=0x1.437eeecd8p+48 bits=42f437eeecd80000 tgamma=0x1.437eeecd80002p+48 same=0 inf=0 nan=0
n=18 te=0x1.6beecca73p+52 bits=4336beecca730000 tgamma=0x1.6beecca730001p+52 same=0 inf=0 nan=0
n=19 te=0x1.b02b930689p+56 bits=437b02b930689000 tgamma=0x1.b02b930689001p+56 same=0 inf=0 nan=0
n=20 te=0x1.0e1b3be415ap+61 bits=43c0e1b3be415a00 tgamma=0x1.0e1b3be415ap+61 same=1 inf=0 nan=0
n=21 te=inf bits=7ff0000000000000 tgamma=0x1.6283be9b5c61fp+65 same=0 inf=1 nan=0
n=22 te=inf bits=7ff0000000000000 tgamma=0x1.e77526159f06bp+69 same=0 inf=1 nan=0
n=23 te=inf bits=7ff0000000000000 tgamma=0x1.5e5c335f8a4cep+74 same=0 inf=1 nan=0
n=24 te=inf bits=7ff0000000000000 tgamma=0x1.06c52687a7b9ap+79 same=0 inf=1 nan=0
n=25 te=inf bits=7ff0000000000000 tgamma=0x1.9a940c33f6123p+83 same=0 inf=1 nan=0
n=26 te=inf bits=7ff0000000000000 tgamma=0x1.4d9849ea37eecp+88 same=0 inf=1 nan=0
n=27 te=inf bits=7ff0000000000000 tgamma=0x1.19787e5d9f316p+93 same=0 inf=1 nan=0
n=28 te=inf bits=7ff0000000000000 tgamma=0x1.ec92dd23d6967p+97 same=0 inf=1 nan=0
n=29 te=inf bits=7ff0000000000000 tgamma=0x1.be6518687a785p+102 same=0 inf=1 nan=0
n=30 te=inf bits=7ff0000000000000 tgamma=0x1.a27ec6e1f2d0dp+107 same=0 inf=1 nan=0
=== fac fractional ===
fac(20.9) => 0x1.0e1b3be415ap+61 bits=43c0e1b3be415a00 inf=0 nan=0
fac(21.1) => inf bits=7ff0000000000000 inf=1 nan=0
fac(-1) => nan bits=7ff8000000000000 inf=0 nan=1
fac(1e20) => inf bits=7ff0000000000000 inf=1 nan=0
=== ncr overflow-ish ===
ncr(10,3) => 0x1.ep+6 bits=405e000000000000 inf=0 nan=0
ncr(66,33) => inf bits=7ff0000000000000 inf=1 nan=0
ncr(67,33) => inf bits=7ff0000000000000 inf=1 nan=0
ncr(100,50) => inf bits=7ff0000000000000 inf=1 nan=0
ncr(1000,2) => 0x1.e7cbp+18 bits=411e7cb000000000 inf=0 nan=0
ncr(1e9,1) => 0x1.dcd65p+29 bits=41cdcd6500000000 inf=0 nan=0
ncr(1e9,2) => 0x1.bc16d66d7926cp+58 bits=439bc16d66d7926c inf=0 nan=0
ncr(-1,1) => nan bits=7ff8000000000000 inf=0 nan=1
ncr(5,6) => nan bits=7ff8000000000000 inf=0 nan=1
=== npr ===
npr(10,3) => 0x1.68p+9 bits=4086800000000000 inf=0 nan=0
npr(20,5) => 0x1.c638p+20 bits=413c638000000000 inf=0 nan=0
npr(21,21) => inf bits=7ff0000000000000 inf=1 nan=0
npr(100,2) => 0x1.356p+13 bits=40c3560000000000 inf=0 nan=0
6 changes: 6 additions & 0 deletions docs/evidence/baseline/15c-ncr-dos.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ncr(40,20) 0x1.00c258d9ap+37 0.000046s
ncr(60,30) 0x1.a42902a5af0bfp+56 0.000002s
ncr(1000000000,100) inf 0.000001s
ncr(1000000000,10000) inf 0.000001s
ncr(4000000000,100000) inf 0.000001s
ncr(4294967295,200000) inf 0.000001s
20 changes: 20 additions & 0 deletions docs/evidence/baseline/16-libm-vs-stdmath.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
fn total differ rate worst_abs_delta
sin 602117 0 0.00000000 0.000000e+00
cos 600022 0 0.00000000 0.000000e+00
tan 600022 0 0.00000000 0.000000e+00
asin 26109 24441 0.93611398 inf
acos 26109 24494 0.93814394 inf
atan 600022 30803 0.05133645 2.220446e-16
sinh 26109 24 0.00091922 2.328306e-10
cosh 26109 60 0.00229806 2.220446e-16
tanh 26109 682 0.02612126 2.220446e-16
exp 602117 0 0.00000000 0.000000e+00
log 302116 0 0.00000000 0.000000e+00
log10 300021 0 0.00000000 0.000000e+00
sqrt 302116 0 0.00000000 0.000000e+00
ceil 26109 0 0.00000000 0.000000e+00
floor 26109 0 0.00000000 0.000000e+00
fabs 26109 0 0.00000000 0.000000e+00
pow 24831 8294 0.33401796 inf
atan2 24831 3606 0.14522170 4.440892e-16
fmod 24831 6000 0.24163344 7.500000e+00
12 changes: 12 additions & 0 deletions docs/evidence/consumability/zig-fetch-save.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# zig fetch --save of package tarball (measured 2026-08-02)
# From scratch project /tmp/te-user-consumer after:
# tar -czf /tmp/tinyexpr-pkg.tar.gz build.zig build.zig.zon src LICENSE README.md
# zig fetch --save file:///tmp/tinyexpr-pkg.tar.gz
#
# Resulting build.zig.zon dependency entry:
.tinyexpr = .{
.url = "file:///tmp/tinyexpr-pkg.tar.gz",
.hash = "tinyexpr-0.1.0-fv-tH2JQAQC8ICkMA05jq1R8UmuqAqglyWWub-G8lCKK",
}
# zig build run printed: external_fetch_ok value=10
# See also zig-fetch-external-run.txt
2 changes: 1 addition & 1 deletion docs/evidence/cross-compile.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"unit_tests": "pass",
"full_suite": "not_run",
"ran": true,
"run_notes": "zig build test -Dtarget=wasm32-wasi -fwasmtime passed; wasm32-wasi is a third independent execution environment for differential work (prompt 5 seam)",
"run_notes": "zig build test -Dtarget=wasm32-wasi -fwasmtime passed; wasm32-wasi is a third independent execution environment for differential work",
"artifact": "libtinyexpr.a"
}
}
2 changes: 1 addition & 1 deletion docs/evidence/scorecard.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@
"unit_tests": "pass",
"full_suite": "not_run",
"ran": true,
"run_notes": "zig build test -Dtarget=wasm32-wasi -fwasmtime passed; wasm32-wasi is a third independent execution environment for differential work (prompt 5 seam)",
"run_notes": "zig build test -Dtarget=wasm32-wasi -fwasmtime passed; wasm32-wasi is a third independent execution environment for differential work",
"artifact": "libtinyexpr.a"
}
},
Expand Down
3 changes: 2 additions & 1 deletion docs/evidence/self-audit.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

Adversarial review before code freeze. Host: Kavyas-MacBook-Air-2.
UTC start: 2026-08-02. Freeze deadline: 2026-08-03 18:00 UTC.
Final commit SHA filled at tag time.
Final commit SHA at tag `submission-v1`: `76dec4d0e7a8a47215416cc12af994a8f94ca98d`
(re-pointed after credibility cleanup if a newer commit becomes the tag tip).

Disposition legend: **fixed** / **ok** / **qualified** / **human-required**.

Expand Down
14 changes: 8 additions & 6 deletions docs/journal.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,10 @@ and is not run against C in-process.

## 2026-08-02 elmengo allocfault-fuzz-sanitizers

Code freeze is 2026-08-03 18:00 UTC. Long differential fuzz started first
(`bash scripts/run-fuzz.sh 14400 fuzz/log-long.txt long`) and continues in
the background while the other campaigns run.
Code freeze is 2026-08-03 18:00 UTC. A long differential fuzz was started
(`bash scripts/run-fuzz.sh 14400 fuzz/log-long.txt long`) alongside the other
campaigns; that host later crashed and `fuzz/log-long.txt` was never written.
The certified 60s log remains the published duration evidence.

Pillar 4 allocfault (`tools/allocfault`): one LD_PRELOAD interposer for C and
Zig ABI. Corpus 107 inputs, **271** allocation ordinals tested, **0** crashes
Expand Down Expand Up @@ -225,9 +226,10 @@ Regenerated `docs/evidence/scorecard.json` after fixing fragment routing so
`buglead-*` and `sanitizers` land under real keys instead of polluting the
root. Coverage / enumeration / fuzz / alloc_fault / bugleads / sanitizers are
`measured`. `docs/evidence/hash-verify.txt` rewritten; `upstream_hash_ok` is
true. Long differential fuzz (`scripts/run-fuzz.sh 14400`) is still running
with progress dumps under `.local/fuzz/progress-long.json`; `fuzz/log-long.txt`
will be committed when that campaign ends. Do not invent a total.
true. Long differential fuzz (`scripts/run-fuzz.sh 14400`) was started toward
`fuzz/log-long.txt` and died with a WSL host crash; that artifact was never
committed. The certified 60s run in `fuzz/log.txt` stands. Do not invent a
long-run total.

## 2026-08-02 Kavyas-MacBook-Air-2 independent-reproduction

Expand Down
8 changes: 6 additions & 2 deletions fuzz/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,20 @@ Artifacts:
- `corpus-seed/`: small tracked seed inputs.
- `logs/`: gitignored runtime logs from `scripts/run-logged.sh`.

## Differential fuzz (Prompt 5)
## Differential fuzz

```bash
bash scripts/run-fuzz.sh 60 fuzz/log.txt certified # required 60s log
bash scripts/run-fuzz.sh 14400 fuzz/log-long.txt long
bash scripts/run-fuzz.sh 14400 fuzz/log-long.txt long # optional long campaign
bash scripts/run-metamorphic.sh
bash scripts/run-libfuzzer.sh 30 fuzz/log-libfuzzer.txt
zig build test -Doptimize=ReleaseSafe --fuzz # Zig 0.16 built-in; not Debug
```

The 60s certified log is `fuzz/log.txt` (0 divergences). A multi-hour
`fuzz/log-long.txt` campaign was started on WSL and lost to a host crash;
that artifact was never committed. The certified 60s run stands.

`fuzz/harness.c` compares C (objcopy-renamed) vs Zig on result bits, error
position, success/NULL, and structured near-valid inputs. `fuzz/log.txt` is
the certified sixty-second run. Wall-clock is secondary; executions and
Expand Down
2 changes: 1 addition & 1 deletion scripts/cross-compile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ for t in targets:
if test_triple != t:
note += f" (artifact target remains {t}; musl used for qemu without glibc runtimes)"
if t == "wasm32-wasi":
note += "; wasm32-wasi is a third independent execution environment for differential work (prompt 5 seam)"
note += "; wasm32-wasi is a third independent execution environment for differential work"
entry["run_notes"] = note
else:
entry["unit_tests"] = "fail"
Expand Down
2 changes: 1 addition & 1 deletion scripts/run-sanitizer-matrix.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Sanitizer / safety matrix (Prompt 5 Task 3). Writes docs/evidence/sanitizers.md
# Sanitizer / safety matrix. Writes docs/evidence/sanitizers.md
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
WORK="$ROOT/.local/sanitizers"
Expand Down
2 changes: 1 addition & 1 deletion src/c_abi.zig
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub const State = extern struct {
};

comptime {
// Layout numbers were measured on x86_64 Linux (prompt 1). Other ABIs differ
// Layout numbers were measured on x86_64 Linux. Other ABIs differ
// in pointer width; keep the struct definitions and skip the size lock there.
if (@import("builtin").cpu.arch == .x86_64 and @import("builtin").os.tag == .linux) {
if (@sizeOf(TeExpr) != 24 or @alignOf(TeExpr) != 8 or
Expand Down
2 changes: 1 addition & 1 deletion src/parser.zig
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! AST builder interface for the shared recursive-descent parser.
//! NativeBuilder constructs tagged-union Expr nodes.
//! CAbiBuilder (prompt 3.5) constructs te_expr-shaped trees.
//! CAbiBuilder constructs te_expr-shaped trees.

const std = @import("std");
const ast = @import("ast.zig");
Expand Down
Loading