diff --git a/README.md b/README.md index 435e761..5a22257 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ![Status](https://img.shields.io/badge/status-proof--of--concept-orange) ![Parser](https://img.shields.io/badge/parser-M0-brightgreen) -![Execution](https://img.shields.io/badge/execution-not--implemented-red) +![Execution](https://img.shields.io/badge/M0%20core-runs-brightgreen) ![License](https://img.shields.io/badge/license-AGPLv3-blue) **โˆ€ child โˆˆ world : canCode(child)** @@ -18,7 +18,7 @@ ## ๐Ÿšจ Project Status: Proof of Concept -**Important**: MPL is a research prototype. This repository contains the grammar, the parser, and the browser interpreter (`js/mpl.js`) that runs the M0 core today at [mpl.codes](https://mpl.codes). A native runtime beyond the browser core does not exist yet โ€” building it is the next milestone, and contributors are welcome. +**The M0 core runs โ€” with ratified semantics.** This repository contains the grammar, the parser, and the browser interpreter (`js/mpl.js`) that runs the M0 core today at [mpl.codes](https://mpl.codes). Its semantics are not folklore: every judgment call is recorded and ruled in [conformance/JUDGMENT_CALLS.md](conformance/JUDGMENT_CALLS.md), pinned by **89 ratified conformance tests** that gate CI, and a differential fuzzer holds the interpreter and the grammar to zero divergence. A native runtime beyond the browser core does not exist yet โ€” building it is the next milestone, and contributors are welcome. ### What Works Today โœ… @@ -28,6 +28,7 @@ Every item below is enforced by [CI](.github/workflows/ci.yml) on every push: - All 10 [example programs](examples/) parse (`./gradlew parseExamples`) - A test suite covering the lexer, the parser, the examples, and every ```` ```mpl ```` code block in this README (`./gradlew test`) - An ASCII escape sequence for every Unicode symbol ([glyph-escapes.md](glyph-escapes.md)) +- The M0 core executes with ratified semantics: 89 ratified conformance tests (`node conformance/harness/run.mjs --ratified`), exact rational arithmetic, and a recorded ruling for every semantic question ([conformance/JUDGMENT_CALLS.md](conformance/JUDGMENT_CALLS.md)) ### What Doesn't Work Yet ๐Ÿšง - **No native runtime** - The M0 core runs in the browser interpreter (`js/mpl.js`); everything beyond it parses but does not run yet @@ -203,14 +204,14 @@ Only ASCII escapes exist today; the rest is the tooling we want to build: ```mpl -- Level 1: Basic math (everyone knows this!) -x โ† 5 + 3; -y โ† x ร— 2; +x โ‰œ 5 + 3; +y โ‰œ x ร— 2; -- Level 2: Logic (learned in school) x > 10 โˆง y < 20 โŸน โœŽ"Success!"; -- Level 3: Advanced (natural progression) -squares โ† 0; +squares โ‰œ 0; โˆ€ n โˆˆ [1, 2, 3, 4, 5] : squares โ† squares + n ร— n; ``` @@ -265,10 +266,10 @@ We envision students could progress like this: **Growing skills**: Applying math knowledge to programming ```mpl -- Month 6: Using mathematical concepts they know -data โ† [23, 45, 67, 34, 89, 12]; -total โ† 0; +data โ‰œ [23, 45, 67, 34, 89, 12]; +total โ‰œ 0; โˆ€ x โˆˆ data : total โ† total + x; -average โ† total รท 6; +average โ‰œ total รท 6; โœŽ("Average: " + average) ``` @@ -291,13 +292,13 @@ Some notation you might expect from math class โ€” โˆ‘, โˆš, ยฒ, `%` (modulo), | ```mpl -- Store values (like math class!) -length โ† 5; -width โ† 3; -area โ† length ร— width; +length โ‰œ 5; +width โ‰œ 3; +area โ‰œ length ร— width; โœŽ("Area = " + area); -- Make decisions: (condition โŸน result) | fallback -age โ† 15; +age โ‰œ 15; (age โ‰ฅ 18 โŸน โœŽ"Adult") | โœŽ"Minor"; ``` @@ -309,7 +310,7 @@ age โ† 15; โˆ€ n โˆˆ [1, 2, 3, 4, 5] : โœŽ(n ร— n); -- Accumulate a running total -total โ† 0; +total โ‰œ 0; โˆ€ n โˆˆ [1, 2, 3, 4, 5] : total โ† total + n; โœŽ("Total: " + total); ``` @@ -319,14 +320,14 @@ total โ† 0; ```mpl -- Weather data analysis -temperatures โ† [28, 30, 27, 31, 29, 33, 28]; -total โ† 0; +temperatures โ‰œ [28, 30, 27, 31, 29, 33, 28]; +total โ‰œ 0; โˆ€ t โˆˆ temperatures : total โ† total + t; -ฮผ โ† total รท 7; +ฮผ โ‰œ total รท 7; โœŽ("Average: " + ฮผ + "ยฐC"); -- Parallel processing (โ€– = parallel) -results โ† analyzeNorth() โ€– analyzeSouth() โ€– analyzeEast(); +results โ‰œ analyzeNorth() โ€– analyzeSouth() โ€– analyzeEast(); ``` ### Level 4: Advanced concepts ๐Ÿš€ diff --git a/build.gradle b/build.gradle index 4ab62d1..5dc1109 100644 --- a/build.gradle +++ b/build.gradle @@ -41,6 +41,10 @@ sourceSets { } test { + // ConformanceCountTest reads these; declaring them makes the up-to-date + // check re-run tests when the README claim or the corpus changes. + inputs.file('README.md') + inputs.dir('conformance/corpus') testLogging { events "passed", "skipped", "failed" exceptionFormat "full" diff --git a/src/test/java/com/mpl/test/ConformanceCountTest.java b/src/test/java/com/mpl/test/ConformanceCountTest.java new file mode 100644 index 0000000..95bf8e0 --- /dev/null +++ b/src/test/java/com/mpl/test/ConformanceCountTest.java @@ -0,0 +1,32 @@ +package com.mpl.test; + +import org.junit.Test; +import static org.junit.Assert.*; +import java.nio.file.*; +import java.util.regex.*; + +/** + * The README's stated conformance-test count must equal the number of + * corpus entries, so the public claim can never silently rot: adding or + * removing an entry without updating the README fails CI (Stage 3, A8). + */ +public class ConformanceCountTest { + + @Test + public void readmeCountMatchesCorpus() throws Exception { + String readme = Files.readString(Paths.get("README.md")); + long dirs; + try (var stream = Files.list(Paths.get("conformance/corpus"))) { + dirs = stream.filter(Files::isDirectory).count(); + } + Matcher m = Pattern.compile("(\\d+) ratified conformance tests").matcher(readme); + int mentions = 0; + while (m.find()) { + mentions++; + assertEquals("README claims " + m.group(1) + + " ratified conformance tests, but conformance/corpus has " + dirs, + dirs, Long.parseLong(m.group(1))); + } + assertTrue("README must state the ratified conformance test count", mentions >= 1); + } +}