1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
;;; Copyright (c) 2026 Nicholas Vermeulen
;;; SPDX-License-Identifier: AGPL-3.0-or-later
;; parse.lisp — pure parser combinators with exhaustive language checks.
;; Pure Lisp, zero interpreter changes.
;;
;; Combinators (`parse-seq`, `parse-alt`, `parse-many`, `parse-map`,
;; `parse-lit`, `parse-sat`) consume a list of tokens and return a RESULT as
;; data: either `(ok value rest)` or `(err pos reason)`. Trees and error
;; positions are ordinary lists — never raises for ordinary parse failure.
;; Ambiguity is detected by collecting ALL successful parses of a string
;; under an alt-heavy grammar (two ok results ⇒ ambiguous on that input).
;;
;; LEFT-RECURSION POLICY: combinators do NOT detect left recursion. A
;; left-recursive grammar diverges (no fuel). Callers must write right-
;; recursive or iterative forms (`parse-many`). Documented, not refused at
;; runtime — there is no static grammar analysis here.
;;
;; CLAIM DISCIPLINE:
;; "language-equivalent to the reference table on A^≤k"
;; NEVER "correct parser for the language" in general, and NEVER that an
;; ambiguous grammar is "resolved". Exhaustive checks classify every string
;; in a finite A^≤k against a hand table.
;; ── Result helpers ────────────────────────────────────────────────────────
;; ── Primitive combinators ─────────────────────────────────────────────────
;; Token stream is a list; position is how many tokens already consumed
;; (tracked only in err results — ok results carry the remaining stream).
;; Sequence: run p then q on the rest; value is (list vp vq).
;; Position advances by tokens actually consumed (from rest length).
;; Alternation: try p, on err try q (same input/pos). First success wins
;; for ordinary parse; use parse-all-alt for ambiguity detection.
;; Map a pure function over a successful value.
;; Zero-or-more greedy: always succeeds (possibly with empty list).
;; One-or-more.
;; Run a parser; require full consumption for accept.
;; ── Ambiguity: collect every successful full parse under a list of alts ──
;; `alts` is a list of parsers; each is tried independently on the same
;; input. Returns the list of ok values (trees). Length ≥ 2 ⇒ ambiguous.
;; ── Exhaustive language classification on A^≤k ────────────────────────────
;; Generate all strings over alphabet A of length 0..k (lists of tokens).
;; accept?: string -> boolean. reference: same shape. Exhaustive agreement.
;; Wrong reference must be refused with a witness string.