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
23 changes: 23 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: test

on:
push:
branches: [main]
pull_request:

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

# Python, Node.js, Perl, GCC/G++ 14 come preinstalled on the runner.
- name: Install toolchains not preinstalled on the runner
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends racket

- uses: oven-sh/setup-bun@v2

- name: Run all language tests
run: ./run-tests.sh
67 changes: 67 additions & 0 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Roadmap

Working agreement: every step is sized to be completed (and verified) in one
sitting. An implementation only gets an `expected-output.txt` captured from an
actual observed run — never written from reading the source. Anything that
can't be run locally gets verified on the CI runner before merge.

## Milestone 1 — CI backfill: every existing language tested on every PR

The harness: `run-tests.sh` loops over `languages/*/*/test.sh`, runs each, and
diffs stdout against that folder's `expected-output.txt`. The GitHub Actions
workflow (`.github/workflows/test.yml`) runs it on every PR and push to main.
Folders without a `test.sh` are skipped until backfilled.

- [x] **Step 1 — harness + locally verifiable languages.** Workflow file,
`run-tests.sh`, and test.sh/expected-output.txt for Python, JavaScript,
TypeScript (Bun), Perl, Racket, and C. All six verified locally.
- [ ] **Step 2 — C++ and Go.** C++ needs GCC 14 (`<print>` header) — not on
the dev machine, so capture its output from the CI runner log on the PR,
then commit it as the expected file. Go is preinstalled on runners.
- [ ] **Step 3 — Ruby, Lua, Haskell.** All installable on the runner via apt
(`ruby`, `lua5.4`, `ghc`); add installs to the workflow's toolchain step.
- [ ] **Step 4 — OCaml, Elixir, Clojure, F#.** apt/preinstalled on runners
(`ocaml`, `elixir`, `clojure`; F# via preinstalled dotnet). Clojure runs
through its own deps.edn test runner — wrap it in test.sh.
- [ ] **Step 5 — Java and Kotlin.** Java: PR #29 (the `java` branch) fixes the
numeral typing so PRED works; add test.sh + expected output to that
branch, let CI validate it, then merge #29. Kotlin: runner has a JDK;
install kotlinc in the workflow.
- [ ] **Step 6 — exotic languages: ArkScript, FatScript, bruijn, Language 84.**
Each needs its own toolchain acquisition (GitHub releases, cargo/stack
installs, or building from source). If one is genuinely unobtainable in
CI, document that in its folder instead of leaving it silently untested.
- [ ] **Step 7 — flip the default and update the front door.** Once all
folders have tests: make `run-tests.sh` fail on folders *missing* a
test.sh, update README contribution instructions to require
test.sh + expected-output.txt in new-language PRs, and enable branch
protection so PRs need a green check to merge.

## Milestone 2 — fill out the missing major languages

One language per step, each landing as its own PR with implementation, README,
test.sh, and expected output, CI-green before merge. Order roughly by reach:

- [ ] Rust
- [ ] C#
- [ ] Swift
- [ ] Scala
- [ ] Zig
- [ ] PHP
- [ ] Dart
- [ ] Gleam (reopens the slot from closed PR #28)

Keep a "wanted" list in the README for languages left open to contributors
(Prolog, Erlang, APL, Idris, …).

## Milestone 3 — housekeeping

- [ ] Remove the committed binary `languages/c/c/app` and add a root
`.gitignore` for build artifacts.
- [ ] TypeScript implementation only exercises Church numerals at runtime —
booleans exist at the type level only. Decide whether that satisfies the
spec or needs a runtime supplement.
- [ ] Comment on merged PR #6 noting the `Function<Term, Term>` fix, closing
the loop on the thread there.
- [ ] Java README still describes an Oracle JDK manual install — point it at
apt/Temurin and the test.sh instead.
2 changes: 2 additions & 0 deletions languages/c/c/expected-output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
1
1
5 changes: 5 additions & 0 deletions languages/c/c/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
gcc lambda-core.c -std=c89 -pedantic -o lambda-core.test || exit 1
./lambda-core.test
status=$?
rm -f lambda-core.test
exit $status
30 changes: 30 additions & 0 deletions languages/j/javascript/expected-output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
LOGIC
---------------
TRUE/FALSE
t
f
NOT
f
t
AND
t
f
f
f
OR
t
t
t
f

CHURCH NUMERALS
---------------
ZERO/SUCC
0
1
2
3
PRED
0
1
2
1 change: 1 addition & 0 deletions languages/j/javascript/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node lambda-core.js
24 changes: 24 additions & 0 deletions languages/p/perl/expected-output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
LOGIC
-------------
TRUE: true
FALSE: false
NOT TRUE: false
NOT FALSE: true
TRUE AND TRUE: true
TRUE AND FALSE: false
FALSE AND TRUE: false
FALSE AND FALSE: false
TRUE OR TRUE: true
TRUE OR FALSE: true
FALSE OR TRUE: true
FALSE OR FALSE: false

CHURCH NUMERALS
-------------
ZERO: 0
ONE: 1
SUCC ONE: 2
SUCC SUCC ONE: 3
PRED ONE: 0
PRED SUCC ONE: 1
PRED SUCC SUCC ONE: 2
1 change: 1 addition & 0 deletions languages/p/perl/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
perl lambda-core.pl
30 changes: 30 additions & 0 deletions languages/p/python/expected-output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
LOGIC
-------------
TRUE/FALSE
t
f
NOT
f
t
AND
t
f
f
f
OR
t
t
t
f

CHURCH NUMERALS
-------------
ZERO/SUCC
0
1
2
3
PRED
0
1
2
1 change: 1 addition & 0 deletions languages/p/python/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
python3 lambda-core.py
30 changes: 30 additions & 0 deletions languages/r/racket/expected-output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
LOGIC
--------------
TRUE/FALSE
t
f
NOT
f
t
AND
t
f
f
f
OR
t
t
t
f

CHURCH NUMERALS
--------------
ZERO/SUCC
0
1
2
3
PRED
0
1
2
1 change: 1 addition & 0 deletions languages/r/racket/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
racket lambda-core.rkt
8 changes: 8 additions & 0 deletions languages/t/typescript/expected-output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Zero: 0
One: 1
Two: 2
Three: 3
Pred Zero: 0
Pred One: 0
Pred Two: 1
Pred Three: 2
1 change: 1 addition & 0 deletions languages/t/typescript/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bun run lambda-core.ts
35 changes: 35 additions & 0 deletions run-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/bin/sh
# Runs every language folder that has a test.sh and compares its stdout
# against that folder's expected-output.txt. Exits nonzero if any differ.
# Folders without a test.sh are skipped (not yet backfilled).
set -u

root=$(cd "$(dirname "$0")" && pwd)
fail=0
ran=0

for script in "$root"/languages/*/*/test.sh; do
[ -e "$script" ] || continue
dir=$(dirname "$script")
name=${dir#"$root"/languages/}
ran=$((ran + 1))

actual=$( (cd "$dir" && sh test.sh 2>/dev/null) )
status=$?
expected=$(cat "$dir/expected-output.txt")

if [ "$status" -eq 0 ] && [ "$actual" = "$expected" ]; then
echo "PASS $name"
else
echo "FAIL $name (exit $status)"
echo "--- expected ---"
echo "$expected"
echo "--- actual ---"
echo "$actual"
echo "---"
fail=1
fi
done

echo "$ran folder(s) tested"
exit $fail
Loading