ocaml: phase 1+3 or-patterns (P1 | P2 | ...) parens-only (+5 tests, 394 total)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 41s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 41s
Parser: when | follows a pattern inside parens, build (:por ALT1 ALT2 ...). Eval: try alternatives, succeed on first match. Top-level | remains the clause separator — parens-only avoids ambiguity without lookahead. Examples now work: match n with | (1 | 2 | 3) -> 100 | _ -> 0 match c with | (Red | Green) -> 1 | Blue -> 2
This commit is contained in:
@@ -259,6 +259,22 @@
|
|||||||
(= (len (rest val)) (len arg-pats)))
|
(= (len (rest val)) (len arg-pats)))
|
||||||
(ocaml-match-list arg-pats (rest val) env))
|
(ocaml-match-list arg-pats (rest val) env))
|
||||||
(else ocaml-match-fail))))
|
(else ocaml-match-fail))))
|
||||||
|
((= tag "por")
|
||||||
|
;; (:por ALT1 ALT2 ...) — try each alternative; succeed on the
|
||||||
|
;; first match. Note: for now, alternatives must bind the same
|
||||||
|
;; set of variables (OCaml constraint) — we don't enforce this.
|
||||||
|
(let ((alts (rest pat)) (result ocaml-match-fail) (done false))
|
||||||
|
(begin
|
||||||
|
(define try-alts
|
||||||
|
(fn (xs)
|
||||||
|
(when (and (not done) (not (= xs (list))))
|
||||||
|
(let ((env2 (ocaml-match-pat (first xs) val env)))
|
||||||
|
(cond
|
||||||
|
((not (= env2 ocaml-match-fail))
|
||||||
|
(begin (set! result env2) (set! done true)))
|
||||||
|
(else (try-alts (rest xs))))))))
|
||||||
|
(try-alts alts)
|
||||||
|
result)))
|
||||||
((= tag "pas")
|
((= tag "pas")
|
||||||
;; (:pas INNER NAME) — match inner pattern, also bind NAME → val.
|
;; (:pas INNER NAME) — match inner pattern, also bind NAME → val.
|
||||||
(let ((inner (nth pat 1)) (alias (nth pat 2)))
|
(let ((inner (nth pat 1)) (alias (nth pat 2)))
|
||||||
|
|||||||
@@ -223,6 +223,19 @@
|
|||||||
(loop)
|
(loop)
|
||||||
(consume! "op" ")")
|
(consume! "op" ")")
|
||||||
(cons :ptuple items))))
|
(cons :ptuple items))))
|
||||||
|
;; Parens-only or-pattern: (P1 | P2 | ...).
|
||||||
|
((at-op? "|")
|
||||||
|
(let ((alts (list first)))
|
||||||
|
(begin
|
||||||
|
(define loop-or
|
||||||
|
(fn ()
|
||||||
|
(when (at-op? "|")
|
||||||
|
(begin (advance-tok!)
|
||||||
|
(append! alts (parse-pattern))
|
||||||
|
(loop-or)))))
|
||||||
|
(loop-or)
|
||||||
|
(consume! "op" ")")
|
||||||
|
(cons :por alts))))
|
||||||
(else (begin (consume! "op" ")") first))))))))
|
(else (begin (consume! "op" ")") first))))))))
|
||||||
((and (= tt "op") (= tv "["))
|
((and (= tt "op") (= tv "["))
|
||||||
(begin
|
(begin
|
||||||
|
|||||||
@@ -964,6 +964,18 @@ cat > "$TMPFILE" << 'EPOCHS'
|
|||||||
(epoch 2702)
|
(epoch 2702)
|
||||||
(eval "(ocaml-parse-program \"module type EMPTY = sig end\")")
|
(eval "(ocaml-parse-program \"module type EMPTY = sig end\")")
|
||||||
|
|
||||||
|
;; ── or-patterns (parens-only) ─────────────────────────────────
|
||||||
|
(epoch 2800)
|
||||||
|
(eval "(ocaml-run \"match 1 with | (1 | 2 | 3) -> 100 | _ -> 0\")")
|
||||||
|
(epoch 2801)
|
||||||
|
(eval "(ocaml-run \"match 2 with | (1 | 2 | 3) -> 100 | _ -> 0\")")
|
||||||
|
(epoch 2802)
|
||||||
|
(eval "(ocaml-run \"match 5 with | (1 | 2 | 3) -> 100 | _ -> 0\")")
|
||||||
|
(epoch 2803)
|
||||||
|
(eval "(ocaml-run \"match Red with | (Red | Green) -> 1 | Blue -> 2\")")
|
||||||
|
(epoch 2804)
|
||||||
|
(eval "(ocaml-run \"match Blue with | (Red | Green) -> 1 | Blue -> 2\")")
|
||||||
|
|
||||||
EPOCHS
|
EPOCHS
|
||||||
|
|
||||||
OUTPUT=$(timeout 180 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
|
OUTPUT=$(timeout 180 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
|
||||||
@@ -1525,6 +1537,13 @@ check 2700 "module type S parses" '("module-type-def" "S")'
|
|||||||
check 2701 "module type then module" '42'
|
check 2701 "module type then module" '42'
|
||||||
check 2702 "module type EMPTY" '("module-type-def" "EMPTY")'
|
check 2702 "module type EMPTY" '("module-type-def" "EMPTY")'
|
||||||
|
|
||||||
|
# ── or-patterns (parens-only) ──────────────────────────────────
|
||||||
|
check 2800 "(1|2|3) match 1" '100'
|
||||||
|
check 2801 "(1|2|3) match 2" '100'
|
||||||
|
check 2802 "(1|2|3) match 5" '0'
|
||||||
|
check 2803 "(Red|Green) Red" '1'
|
||||||
|
check 2804 "(Red|Green) Blue" '2'
|
||||||
|
|
||||||
TOTAL=$((PASS + FAIL))
|
TOTAL=$((PASS + FAIL))
|
||||||
if [ $FAIL -eq 0 ]; then
|
if [ $FAIL -eq 0 ]; then
|
||||||
echo "ok $PASS/$TOTAL OCaml-on-SX tests passed"
|
echo "ok $PASS/$TOTAL OCaml-on-SX tests passed"
|
||||||
|
|||||||
@@ -137,9 +137,9 @@ SX CEK evaluator (both JS and OCaml hosts)
|
|||||||
- [x] **Patterns:** constructor (nullary + with args, incl. flattened tuple
|
- [x] **Patterns:** constructor (nullary + with args, incl. flattened tuple
|
||||||
args), literal (int/string/char/bool/unit), variable, wildcard `_`,
|
args), literal (int/string/char/bool/unit), variable, wildcard `_`,
|
||||||
tuple, list cons `::`, list literal, record `{ f = pat; … }`,
|
tuple, list cons `::`, list literal, record `{ f = pat; … }`,
|
||||||
`as` binding. Match clauses support `when` guard via
|
`as` binding, or-pattern `(P1 | P2 | …)` (parens-only — top-level
|
||||||
`(:case-when PAT GUARD BODY)`. _(Pending: or-pattern `P1 | P2` —
|
`|` is the clause separator). Match clauses support `when` guard
|
||||||
ambiguous with clause separator without lookahead.)_
|
via `(:case-when PAT GUARD BODY)`.
|
||||||
- [ ] OCaml is **not** indentation-sensitive — no layout algorithm needed.
|
- [ ] OCaml is **not** indentation-sensitive — no layout algorithm needed.
|
||||||
- [ ] Tests in `lib/ocaml/tests/parse.sx` — 50+ round-trip parse tests.
|
- [ ] Tests in `lib/ocaml/tests/parse.sx` — 50+ round-trip parse tests.
|
||||||
|
|
||||||
@@ -377,6 +377,11 @@ the "mother tongue" closure: OCaml → SX → OCaml. This means:
|
|||||||
|
|
||||||
_Newest first._
|
_Newest first._
|
||||||
|
|
||||||
|
- 2026-05-08 Phase 1+3 — or-patterns `(P1 | P2 | ...)` parens-only
|
||||||
|
(+5 tests, 394 total). Parser: when `|` follows a pattern inside
|
||||||
|
parens, build `(:por ALT1 ALT2 ...)`. Eval: try alternatives, succeed
|
||||||
|
on the first match. Top-level `|` remains the clause separator (no
|
||||||
|
lookahead needed). Examples: `(1 | 2 | 3) -> ...`, `(Red | Green) -> 1`.
|
||||||
- 2026-05-08 Phase 4 — `module type S = sig … end` parser (+3 tests,
|
- 2026-05-08 Phase 4 — `module type S = sig … end` parser (+3 tests,
|
||||||
389 total). Signatures are parsed-and-discarded — sig..end balanced
|
389 total). Signatures are parsed-and-discarded — sig..end balanced
|
||||||
skipping. AST: `(:module-type-def NAME)`. Runtime no-op (signatures
|
skipping. AST: `(:module-type-def NAME)`. Runtime no-op (signatures
|
||||||
|
|||||||
Reference in New Issue
Block a user