Repository navigation
Add ROADMAP, a documentation code-block checker, and fix the docs it found broken - #80
Merged
Merged
Conversation
Nothing was checking that the ```panther snippets in docs/ are valid Panther, and most of them were not. doccheck extracts every block and runs it through the real pncs front end (lexer, parser, binder), so the docs cannot drift away from the language the compiler accepts. Blocks can be annotated in place with an HTML comment above the fence -- parse-only, expect-error, or skip reason="..." -- which markdown renderers drop from the output. A malformed directive is reported rather than silently ignored, so a typo cannot quietly disable checking. Known-broken blocks live in tools/doccheck/baseline.txt, which is picked up automatically so a normal run reports only what changed. The check fails in both directions: a passing snippet breaking, and a baselined snippet starting to pass, so the list cannot go stale. Wired into CI as a fourth job. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
157 of 215 panther blocks did not compile; four crashed the compiler
outright, including the second example on the Getting Started page.
The docs described an aspirational language, not the implemented one.
Systematic corrections across every page:
Array<Int> -> Array[int] (square brackets, lowercase)
Int/String/Bool -> int/string/bool
fun f(): T { .. } -> def f(): T = .. (no fun, no return)
for (x in xs) -> for (i <- 0 to n) (only numeric ranges exist)
match (x) { .. } -> x match { case .. }
"n: " + count -> "n: " + string(count)
Several sections documented features the language does not have and were
rewritten to teach what it does: array literals, float literals, compound
assignment, nested block comments, pattern guards and alternation, tuples,
break/continue, and readLine. basics/variables.md no longer claims val
reassignment is a compile error, because it is not.
functions/higher-order-functions.md was 275 lines teaching lambdas.
Neither lambdas nor function-typed parameters nor nested defs parse, so
higher-order functions do not exist in any form. It is now a short page
that says so and shows the workarounds: top-level named functions,
generic functions, and enum + match for behaviour dispatch. Its two
skip-marked blocks are the only skips in the tree.
Result: 200 blocks, 198 compiling, 0 failing, 2 skipped. The baseline is
empty, so CI now enforces a clean run rather than a ratchet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ROADMAP.md covers the three stated goals -- self-hosting, generics, and runnable sample programs -- with measured numbers and the command that reproduces each, rather than estimates. What the measurements turned up: sbt pnc/compile reports 1058 diagnostics, and 685 of them mention an unsolved type variable, which is why generic inference leads the plan. pnc/src/ast.pn and printer.pn are stale committed artifacts that transpile no longer regenerates; CI cannot see the drift because git diff --exit-code ignores the untracked Ast.pn it now emits. The compiler never exits non-zero, so pnc/compile "succeeds" with 1058 errors. Nothing is ever written to disk. Compilation.emit(output) ignores its argument and Emitter.emit() returns in-memory structures, so the documented pncs output.pnb source.pn produces no file, and there is no reader and no runner. This blocks the sample programs. The in/out variance keywords are inverted in both the transpiler and the binder relative to Variance.scala's own comments. The two inversions cancel, so nothing breaks until someone writes [out T]. Fixing the docs surfaced nine further compiler gaps, all verified and recorded under cross-cutting: val immutability is not enforced at all, break/continue panic the binder through boundErrorStatement, decimal literals do not lex, and there is no return, no compound assignment, no pattern guards, alternation, or tuples, and no nested functions. README corrections: the output path is not honoured, and pnc/compile exits zero despite failing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a ROADMAP grounded in measurements, a tool that checks the documentation against the compiler, and the documentation fixes that tool demanded.
Why
The three stated goals — self-hosting, generics, usable sample programs — needed a plan based on what the compiler actually does rather than on estimates. Measuring that turned up more than expected, so this PR is the roadmap plus the first item burned down.
What changed
tools/doccheck/— a new sbt project. Extracts every```pantherblock from the markdown and runs it through the realpncsfront end. Blocks take directives in an HTML comment above the fence (parse-only,expect-error,skip reason="..."), which renderers drop. A malformed directive is reported rather than ignored, so a typo cannot quietly disable checking.tools/doccheck/baseline.txtrecords known-broken blocks and is picked up automatically; the check fails both when a passing snippet breaks and when a baselined snippet starts passing, so the list cannot go stale. Wired in as a fourth CI job.The documentation now compiles: 157 failing → 0.
The baseline is empty, so CI enforces a clean run. Corrections were systematic, not scattered typos:
Array<Int>,Option<User>Array[int],Option[User]Int,String,Boolint,string,boolfun f(): T { return x }def f(): T = xfor (item in items)for (i <- 0 to n)match (x) { 1 => .. }x match { case 1 => .. }"n: " + count"n: " + string(count)ROADMAP.md— every number is measured and every command reproduces it.What the measurements found
Worth a reviewer's attention; these are the reason the roadmap is ordered the way it is.
sbt pnc/compilereports 1058 diagnostics, and 685 mention an unsolved type variable — which is why generic inference leads the plan over everything else.Compilation.emit(output)ignores its argument andEmitter.emit()returns in-memory structures. The documentedpncs output.pnb source.pnproduces no file, there is no.pnbreader, and no CLI path to the workingCompilation.exec(). This is what blocks sample programs.pnc/compile"succeeds" with 1058 errors.pnc/src/ast.pnandprinter.pnare stale; transpile now emitsAst.pn/AstPrinter.pninstead. CI cannot see this becausegit diff --exit-codeignores untracked files. Accounts for 32 of the duplicate-definition diagnostics.in/outvariance keywords are inverted in both the transpiler and the binder relative toVariance.scala's own comments. The two inversions cancel, so nothing breaks until someone hand-writes[out T].Fixing the docs surfaced nine further gaps, all verified against the compiler:
valis not enforced —val x = 10; x = 20compiles with zero diagnostics. The docs promised an error that never happens; that prose is now corrected.break/continuecrash the compiler — they parse, thenboundErrorStatementcallspanic().return, no compound assignment, no pattern guards/alternation/tuples, no nested functions; decimal literals do not lex; block comments do not nest.Reviewer notes
functions/higher-order-functions.mdwas rewritten, not fixed. It was 275 lines teaching lambdas. Lambdas, function-typed parameters, and nesteddefall fail to parse, so higher-order functions do not exist in any form. It is now a short page that says so and shows the workarounds. Its twoskipblocks are the only skips in the tree. This is a content decision worth a second opinion.guides/still duplicates the topic directories. I fixed it in place rather than deleting it — that call is yours. Noted in the roadmap.Option,Result,List, andDictionaryare not builtins (onlyint,string,bool,char,unit,Arrayare), so a single-block compilation cannot mentionOption. Blocks had to work around it. Compiling each block alongside the runtime.pnsources would fix it; it is the top item under ROADMAP §4.4 and deliberately not in this PR.pnc/src/needed no regeneration and thetranspile-syncinvariant is untouched.Verification
sbt test/test— 246/246sbt scalafmtCheckAll— cleansbt "doccheck/run docs/src/content/docs"— 0 failingcheck-primitives-map.mjs— 15 primitives, 4 invariants, all paths resolve🤖 Generated with Claude Code
System recap — adds a new primitive (high risk)
Mode: recap · Base:
main@1bdb118· Head:a2b1309Classification: adds — a new
doc-checkprimitive (the doccheck sbt project and its CI job) plus a full rewrite of the language docs. No compiler source is touched, so no pipeline stage changes behaviour.Primitives touched
doc-checkprimitives.yamllanguage-docsvisual-recap-toolingdoc-checkentrytranspilerbuild.sbtgains thedoccheckproject; transpile/bootstrap tasks untouchedSystem map
A new checker compiles documentation snippets through the existing compiler front end; the docs are the input, CI is the gate, and no compiler stage is modified.
Legend: green = composes (wiring only) · amber = extended by this PR · red = new primitive · gray = context (unchanged, included only when an edge crosses it).
Change flow
Before / after
CI gains a fourth job:
docs(new)sbt "doccheck/run docs/src/content/docs"Invariants
transpile-sync— not affected. No file underpncs/,runtime/,metadata/, ortext/changed, sopnc/src/needed no regeneration.pncs-is-canonical— respected;pnc/src/is untouched.diagnostics-not-exceptions— violations documented, not fixed."text " + intandbreak/continuestillpanic()instead of reporting. The checker now reports these asCRASHrather than a plain failure; ROADMAP §4.2 tracks the fix.