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
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# The EOF reader cases need these files to end WITHOUT a newline, byte for byte
# (no CRLF or final-newline normalisation on checkout).
programs/eof-*.txt -text
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
# per-Lua-build files, .shen-kernel-cache.<hash>.bin, as of shen-lua#55).
.shen-kernel-cache*.bin
.shen-kernel-cache*.bin.tmp
# shen-lua on 42.2 also caches the standard library alongside it.
.shen-kernel-cache*.bin.stlib

# Generated KLambda artefacts (e.g. a stray shake/build output in programs/).
*.kl
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ go run . --heavy # also Yggdrasil stage-1 byte-identity
```

~70 cases in `cases/*.json` + `programs/*.shen`: arithmetic, lists, strings,
closures, Prolog, trap-error, and S42 kernel behavior any port should share
closures, Prolog, trap-error, and ShenOSKernel 42.2 behavior any port should share
(`integer?`, `=`, intern of `"true"`, hash agreeing with `=`, tuples/vectors,
`put`/`get`, freeze captured by a lambda).

Expand Down
75 changes: 44 additions & 31 deletions adapters.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions cases/cli.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,25 @@
"doc": "CLI parity: 'eval -e EXPR' prints the value of EXPR to stdout"
},
{
"name": "version-expr-42",
"name": "version-expr-42.2",
"mode": "eval",
"expr": "(version)",
"expect": "output",
"golden": "42",
"doc": "CLI parity: (version) returns runtime kernel version 42 uniformly (works on every impl, incl. shen-lua which has no --version flag)"
"golden": "42.2",
"doc": "CLI parity: (version) returns the runtime kernel version, ShenOSKernel 42.2 (set in init.kl), uniformly on every port (incl. shen-lua, which has no --version flag). Eval print is unquoted, so shen-c's 42.2 matches. Was version-expr-42 (Tarver S42.0) until every port moved to the community 42.2 kernel (pyrex41/bifrost#26)."
},
{
"name": "version-banner-contains",
"mode": "version",
"expect": "version",
"contains": "42",
"doc": "CLI parity: the --version banner contains runtime kernel version 42. shen-lua has no --version flag, so Bifrost falls back to eval of (version). The experimental shen-c banner is port-local; this case uses kernels/adapters that emit 42."
"contains": "42.2",
"doc": "CLI parity: the --version banner contains the runtime kernel version 42.2. The standard launcher banner (extension-launcher.kl) leads with it, e.g. `42.2 (port (\"Julia\" \"shen-julia\") implementation (...))`. shen-lua has no --version flag, so Bifrost falls back to eval of (version). KNOWN PORT GAP: experimental shen-c prints a port-local `Shen-C 0.2.3` banner and FAILS here (it already did on Tarver S42.0); `bifrost impls --versions` falls back to eval of (version) for it."
},
{
"name": "stdin-eof-clean-exit",
"mode": "repl-eof",
"expect": "clean-exit",
"doc": "DELIBERATE IMPROVEMENT OVER CANONICAL (not a spec conformance): starting the REPL and closing stdin (EOF) exits cleanly on every maintained impl. Canonical Shen LOOPS FOREVER here — the kernel shen.loop catches the reader's EOF error, prints it, and recurses (e.g. official shen-cl emits ~26MB of 'NIL is not of type CHARACTER' in 8s). All ports here detect EOF at the input layer and exit 0; this case guards that they keep doing so. (Earlier this passed spuriously for shen-cl because its repl adapter used a bare -q, which shen-cl rejects as an invalid arg and exits before the REPL even starts — now fixed to bare {bin}.)"
"doc": "DELIBERATE IMPROVEMENT OVER CANONICAL (not a spec conformance): starting the REPL and closing stdin (EOF) exits cleanly on every maintained impl. Canonical Shen LOOPS FOREVER here — the kernel shen.loop catches the reader's EOF error, prints it, and recurses (e.g. official shen-cl emits ~26MB of 'NIL is not of type CHARACTER' in 8s). All ports here detect EOF at the input layer and exit 0; this case guards that they keep doing so. (Earlier this passed spuriously for shen-cl because its repl adapter used a bare -q, which shen-cl rejects as an invalid arg and exits before the REPL even starts — now fixed to bare {bin}.) UPSTREAM shen-scheme 0.50 (tizoc release, the 42.2 reference) keeps the canonical loop and FAILS here by design (~5 MB/s of EOF errors until the timeout; Bifrost caps captured output at 8 MB). The pyrex41 shen-scheme fork reapplies its EOF exit on top of 0.50 (pyrex41/shen-scheme#9, `make test-repl-eof`)."
}
]
}
47 changes: 44 additions & 3 deletions cases/divergences.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,57 @@
"name": "hush-file-write",
"mode": "special-hush",
"expect": "agreement",
"doc": "RESOLVED (was a known divergence): under -q (*hush* = true), `pr` to a FILE stream now WRITES on every impl. *hush* only gates stdout, never stderr or an explicit file stream. Driven as `{bin} eval -q -l PROG`. S42 shen-c/shen-forth need host `pr` overrides (stock writer.kl returns without writing whenever *hush* for every stream) and eval -q/-l. Previously shen-lua/shen-rust SILENCED the write (zero-byte file) under -q while shen-cl/shen-go/ShenScript wrote. Hard agreement: every available impl must land in the same bucket (all 'written'); any impl that silences the write is a real FAIL."
"doc": "RESOLVED (was a known divergence): under -q (*hush* = true), `pr` to a FILE stream now WRITES on every impl. *hush* only gates stdout, never stderr or an explicit file stream. Driven as `{bin} eval -q -l PROG`. S42 shen-c/shen-forth need host `pr` overrides (stock writer.kl returns without writing whenever *hush* for every stream) and eval -q/-l. Previously shen-lua/shen-rust SILENCED the write (zero-byte file) under -q while shen-cl/shen-go/ShenScript wrote. Hard agreement: every available impl must land in the same bucket (all 'written'); any impl that silences the write is a real FAIL. UPSTREAM shen-scheme 0.50 (tizoc release) uses stock writer.kl, so `-q` SILENCES the file write there (zero bytes) and it FAILS this case by design; the pyrex41 fork reapplies the stdout-only *hush* override on top of 0.50 (pyrex41/shen-scheme#9)."
},
{
"name": "load-toplevel-echo",
"mode": "eval",
"expr": "(load \"programs/load-echo-probe.shen\")",
"cwd": ".",
"expect": "agreement",
"known_divergence": "s42-load-echo-format",
"doc": "RESOLVED (was a known divergence): `(load FILE)` now echoes each toplevel form's evaluated value on every impl, cache state notwithstanding. The canonical kernel load (klambda/load.kl -> shen.eval-and-print) PRINTS each toplevel result before returning `loaded` (the echo from `shen eval -l FILE` / `(load FILE)` at the REPL; NOT `script` mode, which is quiet everywhere -- so this case uses eval mode). shen-rust and shen-go always emitted it; shen-lua emitted it on a COLD fasl cache but DROPPED it on a WARM fasl-cache HIT (boot.lua returned `loaded` straight out of fasl_replay without re-running the kernel load), making `(load file)` stdout cache-state dependent -- a cross-port divergence AND an intra-port nondeterminism. Fixed in shen-lua by recording the per-form echo bytes as fasl \"e\" records on the miss and re-pr'ing them on the hit (pyrex41/shen-lua#40, PR #41). The cosmetic `run time:` banner stays dropped on a warm hit by design; the normaliser here strips the whole banner (text line + the blank line its leading newline produces) so that omission is not itself a divergence. Now asserted as a HARD agreement: every available impl must echo `\"PROBE\"` / `42` / `(fn p)` (the last stripped by the normaliser) then `loaded`; any impl that drops the value echo is a real FAIL."
"doc": "RESOLVED (was a known divergence): `(load FILE)` now echoes each toplevel form's evaluated value on every impl, cache state notwithstanding. The canonical kernel load (klambda/load.kl -> shen.eval-and-print) PRINTS each toplevel result before returning `loaded` (the echo from `shen eval -l FILE` / `(load FILE)` at the REPL; NOT `script` mode, which is quiet everywhere -- so this case uses eval mode). shen-rust and shen-go always emitted it; shen-lua emitted it on a COLD fasl cache but DROPPED it on a WARM fasl-cache HIT (boot.lua returned `loaded` straight out of fasl_replay without re-running the kernel load), making `(load file)` stdout cache-state dependent -- a cross-port divergence AND an intra-port nondeterminism. Fixed in shen-lua by recording the per-form echo bytes as fasl \"e\" records on the miss and re-pr'ing them on the hit (pyrex41/shen-lua#40, PR #41). The cosmetic `run time:` banner stays dropped on a warm hit by design; the normaliser strips the whole banner (text line + the blank line its leading newline produces; that blank-line drop was documented but missing until the 42.2 move, which left this case tagged as a known divergence) so that omission is not itself a divergence. Now asserted as a HARD agreement: every available impl must echo `\"PROBE\"` / `42` / `(fn p)` (the last stripped by the normaliser) then `loaded`; any impl that drops the value echo is a real FAIL."
},
{
"name": "bigint-2pow53-plus-1",
"mode": "eval",
"expr": "(+ 9007199254740993 0)",
"expect": "agreement",
"known_divergence": "platform-integer-precision",
"table": {
"shen-cl": "9007199254740993",
"shen-erl": "9007199254740993",
"shen-scheme": "9007199254740993",
"shen-julia": "9007199254740993",
"shen-c": "9007199254740993",
"shen-rust": "9007199254740993",
"shen-swift": "9007199254740993",
"shen-truffle": "9007199254740993",
"shen-go": "9007199254740992",
"ShenScript": "9007199254740992",
"shen-lua": "9007199254740992"
},
"doc": "DOCUMENTED DIVERGENCE (Shen leaves number precision to the platform). 2^53+1 is the first integer an IEEE double cannot hold. Ports with exact integers here print 9007199254740993: bignums (shen-cl on SBCL/ECL, shen-erl, shen-scheme) and 64-bit integers (shen-julia, shen-c, shen-rust, shen-swift, shen-truffle). Ports whose only number is a double (shen-go, ShenScript, shen-lua) print 9007199254740992. The table pins each port to its bucket: reported as DIVERGE, but a port that changes bucket FAILS."
},
{
"name": "bigint-2pow63-ordered",
"mode": "eval",
"expr": "(> (+ 9223372036854775807 1) 9223372036854775807)",
"expect": "agreement",
"known_divergence": "platform-integer-precision",
"table": {
"shen-cl": "true",
"shen-erl": "true",
"shen-scheme": "true",
"shen-julia": "false",
"shen-c": "false",
"shen-rust": "false",
"shen-swift": "false",
"shen-truffle": "false",
"shen-go": "false",
"ShenScript": "false",
"shen-lua": "false"
},
"doc": "DOCUMENTED DIVERGENCE (Shen leaves number precision to the platform). (+ (2^63-1) 1) is only greater than 2^63-1 with bignums: shen-cl, shen-erl and shen-scheme print true. Every other port prints false, for one of three reasons: 64-bit wraparound to -2^63 (shen-julia, shen-c); overflow promoted to a double equal to 2^63, which compares as not greater (shen-rust, shen-swift, shen-truffle); or doubles throughout (shen-go, ShenScript, shen-lua). The printed sum also differs by host float formatting (9223372036854775808 / -9223372036854775808 / 9223372036854776000 / 9.223372036854776e+18 / 9.223372036854776E18), which is why the case compares a boolean. The table pins each port to its bucket."
}
]
}
5 changes: 3 additions & 2 deletions cases/errors.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@
"name": "error-message-head",
"mode": "eval",
"expr": "(trap-error (head []) (lambda E (error-to-string E)))",
"expect": "agreement",
"doc": "the kernel error MESSAGE for (head []) should agree across impls on the same kernel. S41.2 is 'head expects a non-empty list'; S42 sys.kl includes a trailing newline in the kernel string. Mixing 41.2 and 42 ports may FAIL because of that kernel split. shen-go now uses kernel 42."
"expect": "output",
"doc": "HARD AGREEMENT again (was a kernel-split agreement case): every port runs ShenOSKernel 42.2, whose sys.kl message for (head []) is 'head expects a non-empty list' plus a trailing newline (dropped by the normaliser). Pinned as a golden so a port whose native head raises its own host message FAILS. Earlier, mixing Tarver S42.0 and S41.2 ports split on this message.",
"golden": "head expects a non-empty list"
},
{
"name": "int-div-zero",
Expand Down
39 changes: 39 additions & 0 deletions cases/kernel-42.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"group": "kernel-42.2",
"cases": [
{
"name": "lineread-eof-no-newline",
"mode": "eval",
"cwd": ".",
"expr": "(let S (open \"programs/eof-lineread.txt\" in) L1 (lineread S) L2 (lineread S) (do (close S) [L1 L2]))",
"expect": "output",
"golden": "[[hello [+ 1 2]] [last line no newline]]",
"doc": "42.1 reader fix: lineread returns the final line of a stream that ends at EOF WITHOUT a trailing newline (programs/eof-lineread.txt has none). Driven through a file stream, not REPL stdin: upstream shen-scheme 0.50's REPL loops at stdin EOF, so a stdin probe would test the REPL rather than the reader."
},
{
"name": "read-eof-no-newline",
"mode": "eval",
"cwd": ".",
"expr": "(let S (open \"programs/eof-read.txt\" in) A (read S) B (read S) (do (close S) [A B]))",
"expect": "output",
"golden": "[[+ 40 2] 42]",
"doc": "42.1 reader fix: read returns a final token that is terminated only by EOF (programs/eof-read.txt is `(+ 40 2) 42` with no trailing newline), so the bare 42 is not lost or left waiting for more input. File stream, not REPL stdin (see lineread-eof-no-newline)."
},
{
"name": "source-form-before-macros",
"mode": "script",
"program": "source-form-order.shen",
"expect": "output",
"golden": "[(@p handler-then-macro 1) (@p handler-then-macro 2)]",
"doc": "42.1 source-form handlers run BEFORE ordinary macros, and macros still expand what a handler produces. bifrost-probe has both a handler (-> bifrost-probe2) and a macro rule (-> macro-first); the forms come from read-from-string after registration (a script is read whole before it runs, so the handler cannot apply to its own file). A port that does not boot shen.register-source-form prints the BIFROST-SKIP capability marker (capability SKIP)."
},
{
"name": "namespace-externals-expression",
"mode": "script",
"program": "namespace-externals-expr.shen",
"expect": "output",
"golden": "[42 2]",
"doc": "42.2 namespaces: (externals EXPR) EVALUATES EXPR to a symbol list (here a call, (bifrost-ns-vocab)); 42.1 took only a literal list. bifrost-ns-ext stays global and local becomes bifrost.demo.local. Only ports that boot the experimental extension-namespaces.kl run it (shen-scheme, shen-rust at the time of writing); ports where (shen.x.namespaces.initialise) is unavailable print the BIFROST-SKIP capability marker (capability SKIP)."
}
]
}
13 changes: 9 additions & 4 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ bifrost install IMPL … # legacy; prefer env + Nix — docs/install.md
3. `~/.config/bifrost/impl` (`bifrost use X`; on Windows `%APPDATA%\bifrost\impl`)
4. `shen-cl` if present, else the first available port

`bifrost impls --versions` probes each port's `(version)` live. README badges
`bifrost impls --versions` probes each port's kernel live. It reads the
leading token of the standard `--version` banner (`42.2 (port ("Julia"
"shen-julia") …)`), and falls back to an eval of `(version)` when a banner is
port-local (shen-c prints `Shen-C 0.2.3`) or absent (shen-lua). README badges
drift; this does not.

## How a launcher is found
Expand All @@ -32,7 +35,8 @@ For each adapter in [`adapters.json`](../adapters.json):

1. the env-var override (e.g. `$BIFROST_SHEN_GO`) if it names an existing file
2. the first existing path in `default_paths`
3. for shen-lua, a `shen` command on `PATH` that reports `shen-lua` and kernel 42
3. for shen-lua, a `shen` command on `PATH` that reports `shen-lua` and the
adapter's kernel (`42.2`)

Missing ports are **skipped and reported**, never a hard error.

Expand All @@ -51,8 +55,9 @@ Missing ports are **skipped and reported**, never a hard error.
| `shen-truffle` | `BIFROST_SHEN_TRUFFLE` | `../shen-truffle/target/shen-truffle/bin/shen-truffle` |
| `shen-c` | `BIFROST_SHEN_C` | `../shen-c/bin/shen-c` (experimental) |

Production ports target ShenOSKernel **42**. `shen-joy` is a bounded S42-derived
subset. `shen-c` is experimental and self-locates its kernel (do not set
Production ports target ShenOSKernel **42.2** (community `shen-sources` tag
`shen-42.2`, the kernel of upstream shen-scheme 0.50). `shen-joy` is a bounded
first-order subset defined against 42.2. `shen-c` is experimental and self-locates its kernel (do not set
`$SHEN_C_HOME`). Unfinished `shen-forth`, `shen-inets`, `shen-ocaml`, and
`shen-odin` have flakes but are not Bifrost implementations.

Expand Down
Loading
Loading