ocaml: phase 1 sequence operator ; (+10 tests, 123 total)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 53s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 53s
Two-phase grammar: parse-expr-no-seq (prior entry) + parse-expr wraps it with ;-chaining. List bodies keep parse-expr-no-seq so ; remains a separator inside [...]. Match clause bodies use the seq variant and stop at | — real OCaml semantics. Trailing ; before end/)/|/in/then/else/eof permitted.
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
;; let let x = e in body (no rec, function shorthand)
|
;; let let x = e in body (no rec, function shorthand)
|
||||||
;; let rec f x = e in body
|
;; let rec f x = e in body
|
||||||
;; match match e with [|] p -> body | p -> body | ...
|
;; match match e with [|] p -> body | p -> body | ...
|
||||||
|
;; sequence e1 ; e2 → (:seq e1 e2 …) (lowest-precedence binary)
|
||||||
;;
|
;;
|
||||||
;; Pattern scope:
|
;; Pattern scope:
|
||||||
;; _ (wildcard), int/string/char/bool literals, ident (var binding),
|
;; _ (wildcard), int/string/char/bool literals, ident (var binding),
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
;; (:op OP LHS RHS)
|
;; (:op OP LHS RHS)
|
||||||
;; (:neg E) (:not E)
|
;; (:neg E) (:not E)
|
||||||
;; (:tuple ITEMS) (:list ITEMS)
|
;; (:tuple ITEMS) (:list ITEMS)
|
||||||
|
;; (:seq EXPRS)
|
||||||
;; (:if C T E)
|
;; (:if C T E)
|
||||||
;; (:fun PARAMS BODY)
|
;; (:fun PARAMS BODY)
|
||||||
;; (:let NAME PARAMS EXPR BODY) (:let-rec NAME PARAMS EXPR BODY)
|
;; (:let NAME PARAMS EXPR BODY) (:let-rec NAME PARAMS EXPR BODY)
|
||||||
@@ -280,6 +282,7 @@
|
|||||||
(else lhs)))))
|
(else lhs)))))
|
||||||
(set! parse-pattern (fn () (parse-pattern-cons)))
|
(set! parse-pattern (fn () (parse-pattern-cons)))
|
||||||
(define parse-expr (fn () nil))
|
(define parse-expr (fn () nil))
|
||||||
|
(define parse-expr-no-seq (fn () nil))
|
||||||
(define parse-tuple (fn () nil))
|
(define parse-tuple (fn () nil))
|
||||||
(define parse-binop-rhs (fn (lhs min-prec) lhs))
|
(define parse-binop-rhs (fn (lhs min-prec) lhs))
|
||||||
(define parse-prefix (fn () nil))
|
(define parse-prefix (fn () nil))
|
||||||
@@ -324,7 +327,7 @@
|
|||||||
(let
|
(let
|
||||||
((items (list)))
|
((items (list)))
|
||||||
(begin
|
(begin
|
||||||
(append! items (parse-expr))
|
(append! items (parse-expr-no-seq))
|
||||||
(define
|
(define
|
||||||
loop
|
loop
|
||||||
(fn
|
(fn
|
||||||
@@ -336,7 +339,7 @@
|
|||||||
(when
|
(when
|
||||||
(not (at-op? "]"))
|
(not (at-op? "]"))
|
||||||
(begin
|
(begin
|
||||||
(append! items (parse-expr))
|
(append! items (parse-expr-no-seq))
|
||||||
(loop)))))))
|
(loop)))))))
|
||||||
(loop)
|
(loop)
|
||||||
(consume! "op" "]")
|
(consume! "op" "]")
|
||||||
@@ -521,17 +524,17 @@
|
|||||||
(fn
|
(fn
|
||||||
()
|
()
|
||||||
(let
|
(let
|
||||||
((cond-expr (parse-expr)))
|
((cond-expr (parse-expr-no-seq)))
|
||||||
(begin
|
(begin
|
||||||
(consume! "keyword" "then")
|
(consume! "keyword" "then")
|
||||||
(let
|
(let
|
||||||
((then-expr (parse-expr)))
|
((then-expr (parse-expr-no-seq)))
|
||||||
(cond
|
(cond
|
||||||
((at-kw? "else")
|
((at-kw? "else")
|
||||||
(begin
|
(begin
|
||||||
(advance-tok!)
|
(advance-tok!)
|
||||||
(let
|
(let
|
||||||
((else-expr (parse-expr)))
|
((else-expr (parse-expr-no-seq)))
|
||||||
(list :if cond-expr then-expr else-expr))))
|
(list :if cond-expr then-expr else-expr))))
|
||||||
(else (list :if cond-expr then-expr (list :unit)))))))))
|
(else (list :if cond-expr then-expr (list :unit)))))))))
|
||||||
(define
|
(define
|
||||||
@@ -539,7 +542,7 @@
|
|||||||
(fn
|
(fn
|
||||||
()
|
()
|
||||||
(let
|
(let
|
||||||
((scrut (parse-expr)))
|
((scrut (parse-expr-no-seq)))
|
||||||
(begin
|
(begin
|
||||||
(consume! "keyword" "with")
|
(consume! "keyword" "with")
|
||||||
(when (at-op? "|") (advance-tok!))
|
(when (at-op? "|") (advance-tok!))
|
||||||
@@ -555,7 +558,7 @@
|
|||||||
(begin
|
(begin
|
||||||
(consume! "op" "->")
|
(consume! "op" "->")
|
||||||
(let
|
(let
|
||||||
((body (parse-match-body)))
|
((body (parse-expr)))
|
||||||
(append! cases (list :case p body)))))))
|
(append! cases (list :case p body)))))))
|
||||||
(one)
|
(one)
|
||||||
(define
|
(define
|
||||||
@@ -567,9 +570,8 @@
|
|||||||
(begin (advance-tok!) (one) (loop)))))
|
(begin (advance-tok!) (one) (loop)))))
|
||||||
(loop)
|
(loop)
|
||||||
(cons :match (cons scrut (list cases)))))))))
|
(cons :match (cons scrut (list cases)))))))))
|
||||||
(define parse-match-body (fn () (parse-expr)))
|
|
||||||
(set!
|
(set!
|
||||||
parse-expr
|
parse-expr-no-seq
|
||||||
(fn
|
(fn
|
||||||
()
|
()
|
||||||
(cond
|
(cond
|
||||||
@@ -578,6 +580,40 @@
|
|||||||
((at-kw? "if") (begin (advance-tok!) (parse-if)))
|
((at-kw? "if") (begin (advance-tok!) (parse-if)))
|
||||||
((at-kw? "match") (begin (advance-tok!) (parse-match)))
|
((at-kw? "match") (begin (advance-tok!) (parse-match)))
|
||||||
(else (parse-tuple)))))
|
(else (parse-tuple)))))
|
||||||
|
(set!
|
||||||
|
parse-expr
|
||||||
|
(fn
|
||||||
|
()
|
||||||
|
(let
|
||||||
|
((lhs (parse-expr-no-seq)))
|
||||||
|
(cond
|
||||||
|
((at-op? ";")
|
||||||
|
(let
|
||||||
|
((items (list lhs)))
|
||||||
|
(begin
|
||||||
|
(define
|
||||||
|
loop
|
||||||
|
(fn
|
||||||
|
()
|
||||||
|
(when
|
||||||
|
(at-op? ";")
|
||||||
|
(begin
|
||||||
|
(advance-tok!)
|
||||||
|
(cond
|
||||||
|
((at-kw? "end") nil)
|
||||||
|
((at-op? ")") nil)
|
||||||
|
((at-op? "|") nil)
|
||||||
|
((at-kw? "in") nil)
|
||||||
|
((at-kw? "then") nil)
|
||||||
|
((at-kw? "else") nil)
|
||||||
|
((= (ocaml-tok-type (peek-tok)) "eof") nil)
|
||||||
|
(else
|
||||||
|
(begin
|
||||||
|
(append! items (parse-expr-no-seq))
|
||||||
|
(loop))))))))
|
||||||
|
(loop)
|
||||||
|
(cons :seq items))))
|
||||||
|
(else lhs)))))
|
||||||
(let
|
(let
|
||||||
((result (parse-expr)))
|
((result (parse-expr)))
|
||||||
(begin
|
(begin
|
||||||
|
|||||||
@@ -300,6 +300,28 @@ cat > "$TMPFILE" << 'EPOCHS'
|
|||||||
(epoch 308)
|
(epoch 308)
|
||||||
(eval "(ocaml-parse \"match x with | () -> 0\")")
|
(eval "(ocaml-parse \"match x with | () -> 0\")")
|
||||||
|
|
||||||
|
;; ── Sequences (;) ──────────────────────────────────────────────
|
||||||
|
(epoch 320)
|
||||||
|
(eval "(ocaml-parse \"1; 2\")")
|
||||||
|
(epoch 321)
|
||||||
|
(eval "(ocaml-parse \"1; 2; 3\")")
|
||||||
|
(epoch 322)
|
||||||
|
(eval "(ocaml-parse \"(1; 2)\")")
|
||||||
|
(epoch 323)
|
||||||
|
(eval "(ocaml-parse \"begin a; b; c end\")")
|
||||||
|
(epoch 324)
|
||||||
|
(eval "(ocaml-parse \"let x = 1 in x; x\")")
|
||||||
|
(epoch 325)
|
||||||
|
(eval "(ocaml-parse \"if c then (a; b) else c\")")
|
||||||
|
(epoch 326)
|
||||||
|
(eval "(ocaml-parse \"[1; 2; 3]\")")
|
||||||
|
(epoch 327)
|
||||||
|
(eval "(ocaml-parse \"1; 2;\")")
|
||||||
|
(epoch 328)
|
||||||
|
(eval "(ocaml-parse \"begin a; end\")")
|
||||||
|
(epoch 329)
|
||||||
|
(eval "(ocaml-parse \"match x with | _ -> a; b\")")
|
||||||
|
|
||||||
EPOCHS
|
EPOCHS
|
||||||
|
|
||||||
OUTPUT=$(timeout 60 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
|
OUTPUT=$(timeout 60 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
|
||||||
@@ -476,6 +498,18 @@ check 306 "match ctor with tuple arg" '("pcon" "Pair" ("pvar" "a") ("pvar" "b
|
|||||||
check 307 "match string literal" '("plit" ("string" "hi"))'
|
check 307 "match string literal" '("plit" ("string" "hi"))'
|
||||||
check 308 "match unit pattern" '("plit" ("unit"))'
|
check 308 "match unit pattern" '("plit" ("unit"))'
|
||||||
|
|
||||||
|
# ── Sequences ───────────────────────────────────────────────────
|
||||||
|
check 320 "seq 1;2" '("seq" ("int" 1) ("int" 2))'
|
||||||
|
check 321 "seq 1;2;3" '("seq" ("int" 1) ("int" 2) ("int" 3))'
|
||||||
|
check 322 "seq in parens" '("seq" ("int" 1) ("int" 2))'
|
||||||
|
check 323 "seq in begin/end" '("seq" ("var" "a") ("var" "b") ("var" "c"))'
|
||||||
|
check 324 "let body absorbs seq" '("let" "x" () ("int" 1) ("seq" ("var" "x") ("var" "x")))'
|
||||||
|
check 325 "if-branch parens for seq" '("if" ("var" "c") ("seq" ("var" "a") ("var" "b"))'
|
||||||
|
check 326 "list ; is separator" '("list" ("int" 1) ("int" 2) ("int" 3))'
|
||||||
|
check 327 "trailing ; OK" '("seq" ("int" 1) ("int" 2))'
|
||||||
|
check 328 "begin a; end singleton seq" '("seq" ("var" "a"))'
|
||||||
|
check 329 "match clause body absorbs ;" '("case" ("pwild") ("seq" ("var" "a") ("var" "b")))'
|
||||||
|
|
||||||
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"
|
||||||
|
|||||||
@@ -128,11 +128,12 @@ SX CEK evaluator (both JS and OCaml hosts)
|
|||||||
- [~] **Parser:** expressions: literals, identifiers, constructor application,
|
- [~] **Parser:** expressions: literals, identifiers, constructor application,
|
||||||
lambda, application (left-assoc), binary ops with precedence (29 ops via
|
lambda, application (left-assoc), binary ops with precedence (29 ops via
|
||||||
`lib/guest/pratt.sx`), `if`/`then`/`else`, `let`/`in`, `let rec`,
|
`lib/guest/pratt.sx`), `if`/`then`/`else`, `let`/`in`, `let rec`,
|
||||||
`fun`/`->`, tuples, list literals, `begin`/`end`, unit `()`. Top-level
|
`fun`/`->`, `match`/`with`, tuples, list literals, sequences `;`,
|
||||||
decls: `let [rec] name params* = expr` and bare expressions, `;;`-separated
|
`begin`/`end`, unit `()`. Top-level decls: `let [rec] name params* = expr`
|
||||||
via `ocaml-parse-program`. _(Pending: `type`/`module`/`exception`/`open`/
|
and bare expressions, `;;`-separated via `ocaml-parse-program`. _(Pending:
|
||||||
`include` decls, `match`/`with`, `try`/`with`, `function`, record literals/
|
`type`/`module`/`exception`/`open`/`include` decls, `try`/`with`,
|
||||||
updates, field access, sequences `;`, `and` mutually-recursive bindings.)_
|
`function`, record literals/updates, field access, `and` mutually-recursive
|
||||||
|
bindings.)_
|
||||||
- [~] **Patterns:** constructor (nullary + with args, incl. flattened tuple
|
- [~] **Patterns:** constructor (nullary + with args, incl. flattened tuple
|
||||||
args `Pair (a, b)` → `(:pcon "Pair" PA PB)`), literal (int/string/char/
|
args `Pair (a, b)` → `(:pcon "Pair" PA PB)`), literal (int/string/char/
|
||||||
bool/unit), variable, wildcard `_`, tuple, list cons `::`, list literal.
|
bool/unit), variable, wildcard `_`, tuple, list cons `::`, list literal.
|
||||||
@@ -314,6 +315,14 @@ the "mother tongue" closure: OCaml → SX → OCaml. This means:
|
|||||||
|
|
||||||
_Newest first._
|
_Newest first._
|
||||||
|
|
||||||
|
- 2026-05-07 Phase 1 — sequence operator `;`. Lowest-precedence binary;
|
||||||
|
`e1; e2; e3` → `(:seq e1 e2 e3)`. Two-phase grammar: `parse-expr-no-seq`
|
||||||
|
is the prior expression entry point; new `parse-expr` wraps it with
|
||||||
|
`;` chaining. List-literal items still use `parse-expr-no-seq` so `;`
|
||||||
|
retains its separator role inside `[…]`. Match-clause bodies use the
|
||||||
|
seq variant and stop at `|`, matching real OCaml semantics. Trailing `;`
|
||||||
|
before `end`/`)`/`|`/`in`/`then`/`else`/eof is permitted. 123/123 tests
|
||||||
|
passing (+10).
|
||||||
- 2026-05-07 Phase 1 — `match`/`with` + pattern parser. Patterns: wildcard,
|
- 2026-05-07 Phase 1 — `match`/`with` + pattern parser. Patterns: wildcard,
|
||||||
literal, var, ctor (nullary + with arg, with tuple-arg flattening so
|
literal, var, ctor (nullary + with arg, with tuple-arg flattening so
|
||||||
`Pair (a, b)` → `(:pcon "Pair" PA PB)`), tuple, list literal, cons `::`
|
`Pair (a, b)` → `(:pcon "Pair" PA PB)`), tuple, list literal, cons `::`
|
||||||
|
|||||||
Reference in New Issue
Block a user