ocaml: phase 6 expanded stdlib (+15 tests, 319 total)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 54s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 54s
List: concat/flatten, init, find/find_opt, partition, mapi/iteri, assoc/assoc_opt. Option: iter/fold/to_list. Result: get_ok/get_error/ map_error/to_option. Fixed skip-to-boundary! in parser to track let..in / begin..end / struct..end / for/while..done nesting via a depth counter — without this, nested-let inside a top-level decl body trips over the decl-boundary detector. Stdlib functions like List.init / mapi / iteri use begin..end to make their nested-let intent explicit.
This commit is contained in:
@@ -895,22 +895,45 @@
|
||||
(fn
|
||||
()
|
||||
(let ((t (peek-tok))) (if (= t nil) (len src) (get t :pos)))))
|
||||
;; skip-to-boundary! advances `idx` to the next top-level decl
|
||||
;; boundary, tracking `let`/`begin`/`struct` etc. nesting so that
|
||||
;; an inner `let ... in ...` doesn't terminate a top-level decl
|
||||
;; body. Boundary tokens (when at depth 0):
|
||||
;; ;; let module open include and type exception
|
||||
;; Boundary at any depth: eof.
|
||||
(define
|
||||
skip-to-boundary!
|
||||
(fn
|
||||
()
|
||||
(cond
|
||||
((>= idx tok-len) nil)
|
||||
((= (ocaml-tok-type (peek-tok)) "eof") nil)
|
||||
((at-op? ";;") nil)
|
||||
((at-kw? "let") nil)
|
||||
((at-kw? "module") nil)
|
||||
((at-kw? "open") nil)
|
||||
((at-kw? "include") nil)
|
||||
((at-kw? "and") nil)
|
||||
((at-kw? "type") nil)
|
||||
((at-kw? "exception") nil)
|
||||
(else (begin (advance-tok!) (skip-to-boundary!))))))
|
||||
(fn ()
|
||||
(let ((depth 0))
|
||||
(begin
|
||||
(define step
|
||||
(fn ()
|
||||
(cond
|
||||
((>= idx tok-len) nil)
|
||||
((= (ocaml-tok-type (peek-tok)) "eof") nil)
|
||||
((and (= depth 0) (at-op? ";;")) nil)
|
||||
((and (= depth 0) (at-kw? "let")) nil)
|
||||
((and (= depth 0) (at-kw? "module")) nil)
|
||||
((and (= depth 0) (at-kw? "open")) nil)
|
||||
((and (= depth 0) (at-kw? "include")) nil)
|
||||
((and (= depth 0) (at-kw? "and")) nil)
|
||||
((and (= depth 0) (at-kw? "type")) nil)
|
||||
((and (= depth 0) (at-kw? "exception")) nil)
|
||||
;; Track nested blocks that have explicit closing
|
||||
;; tokens. let..in / begin..end / struct..end /
|
||||
;; sig..end / for..done / while..done. `if`/`match`/
|
||||
;; `try` don't have hard close tokens so we don't
|
||||
;; track them — their bodies are bounded by the
|
||||
;; surrounding expression structure.
|
||||
((or (at-kw? "let") (at-kw? "begin") (at-kw? "struct")
|
||||
(at-kw? "sig") (at-kw? "for") (at-kw? "while"))
|
||||
(begin (set! depth (+ depth 1)) (advance-tok!) (step)))
|
||||
((or (at-kw? "in") (at-kw? "end") (at-kw? "done"))
|
||||
(begin
|
||||
(when (> depth 0) (set! depth (- depth 1)))
|
||||
(advance-tok!) (step)))
|
||||
(else (begin (advance-tok!) (step))))))
|
||||
(step)))))
|
||||
(define
|
||||
parse-decl-let
|
||||
(fn ()
|
||||
|
||||
@@ -82,6 +82,67 @@
|
||||
match lst with
|
||||
| [] -> failwith \"List.nth: out of range\"
|
||||
| h :: t -> if n = 0 then h else nth t (n - 1)
|
||||
|
||||
let rec concat lst =
|
||||
match lst with
|
||||
| [] -> []
|
||||
| h :: t -> append h (concat t)
|
||||
|
||||
let flatten = concat
|
||||
|
||||
let rec init n f =
|
||||
if n = 0 then [] else
|
||||
begin
|
||||
let rec build i =
|
||||
if i = n then [] else f i :: build (i + 1)
|
||||
in build 0
|
||||
end
|
||||
|
||||
let rec find_opt p lst =
|
||||
match lst with
|
||||
| [] -> None
|
||||
| h :: t -> if p h then Some h else find_opt p t
|
||||
|
||||
let rec find p lst =
|
||||
match find_opt p lst with
|
||||
| None -> failwith \"List.find: not found\"
|
||||
| Some x -> x
|
||||
|
||||
let rec partition p lst =
|
||||
match lst with
|
||||
| [] -> ([], [])
|
||||
| h :: t ->
|
||||
(match partition p t with
|
||||
| (yes, no) ->
|
||||
if p h then (h :: yes, no) else (yes, h :: no))
|
||||
|
||||
let rec mapi f lst =
|
||||
begin
|
||||
let rec go i xs =
|
||||
match xs with
|
||||
| [] -> []
|
||||
| h :: t -> f i h :: go (i + 1) t
|
||||
in go 0 lst
|
||||
end
|
||||
|
||||
let rec iteri f lst =
|
||||
begin
|
||||
let rec go i xs =
|
||||
match xs with
|
||||
| [] -> ()
|
||||
| h :: t -> f i h; go (i + 1) t
|
||||
in go 0 lst
|
||||
end
|
||||
|
||||
let rec assoc k lst =
|
||||
match lst with
|
||||
| [] -> failwith \"List.assoc: not found\"
|
||||
| (k2, v) :: t -> if k = k2 then v else assoc k t
|
||||
|
||||
let rec assoc_opt k lst =
|
||||
match lst with
|
||||
| [] -> None
|
||||
| (k2, v) :: t -> if k = k2 then Some v else assoc_opt k t
|
||||
end ;;
|
||||
|
||||
module Option = struct
|
||||
@@ -114,6 +175,21 @@
|
||||
match o with
|
||||
| None -> false
|
||||
| Some _ -> true
|
||||
|
||||
let iter f o =
|
||||
match o with
|
||||
| None -> ()
|
||||
| Some x -> f x
|
||||
|
||||
let fold none_v f o =
|
||||
match o with
|
||||
| None -> none_v
|
||||
| Some x -> f x
|
||||
|
||||
let to_list o =
|
||||
match o with
|
||||
| None -> []
|
||||
| Some x -> [x]
|
||||
end ;;
|
||||
|
||||
module Result = struct
|
||||
@@ -136,6 +212,26 @@
|
||||
match r with
|
||||
| Ok _ -> false
|
||||
| Error _ -> true
|
||||
|
||||
let get_ok r =
|
||||
match r with
|
||||
| Ok x -> x
|
||||
| Error _ -> failwith \"Result.get_ok: Error\"
|
||||
|
||||
let get_error r =
|
||||
match r with
|
||||
| Ok _ -> failwith \"Result.get_error: Ok\"
|
||||
| Error e -> e
|
||||
|
||||
let map_error f r =
|
||||
match r with
|
||||
| Ok x -> Ok x
|
||||
| Error e -> Error (f e)
|
||||
|
||||
let to_option r =
|
||||
match r with
|
||||
| Ok x -> Some x
|
||||
| Error _ -> None
|
||||
end ;;
|
||||
|
||||
module String = struct
|
||||
|
||||
@@ -764,6 +764,40 @@ cat > "$TMPFILE" << 'EPOCHS'
|
||||
(epoch 1323)
|
||||
(eval "(ocaml-run-program \"exception E of string ;; try raise (E \\\"oops\\\") with | E s -> s\")")
|
||||
|
||||
;; ── Phase 6 expanded stdlib (List/Option/Result extensions) ───
|
||||
(epoch 1400)
|
||||
(eval "(ocaml-run \"List.concat [[1;2];[3];[4;5]]\")")
|
||||
(epoch 1401)
|
||||
(eval "(ocaml-run \"List.init 5 (fun i -> i * 10)\")")
|
||||
(epoch 1402)
|
||||
(eval "(ocaml-run \"List.find_opt (fun x -> x > 2) [1;2;3;4]\")")
|
||||
(epoch 1403)
|
||||
(eval "(ocaml-run \"List.find_opt (fun x -> x > 99) [1;2;3]\")")
|
||||
(epoch 1404)
|
||||
(eval "(ocaml-run \"List.mapi (fun i x -> i + x) [10;20;30]\")")
|
||||
(epoch 1405)
|
||||
(eval "(ocaml-run \"List.partition (fun x -> x > 2) [1;2;3;4]\")")
|
||||
(epoch 1406)
|
||||
(eval "(ocaml-run \"List.assoc 2 [(1, \\\"a\\\"); (2, \\\"b\\\"); (3, \\\"c\\\")]\")")
|
||||
(epoch 1407)
|
||||
(eval "(ocaml-run \"List.assoc_opt 99 [(1, \\\"a\\\")]\")")
|
||||
|
||||
(epoch 1410)
|
||||
(eval "(ocaml-run \"Option.fold 0 (fun x -> x * 10) (Some 7)\")")
|
||||
(epoch 1411)
|
||||
(eval "(ocaml-run \"Option.fold 0 (fun x -> x * 10) None\")")
|
||||
(epoch 1412)
|
||||
(eval "(ocaml-run \"Option.to_list (Some 7)\")")
|
||||
(epoch 1413)
|
||||
(eval "(ocaml-run \"Option.to_list None\")")
|
||||
|
||||
(epoch 1420)
|
||||
(eval "(ocaml-run \"Result.get_ok (Ok 42)\")")
|
||||
(epoch 1421)
|
||||
(eval "(ocaml-run \"Result.to_option (Ok 1)\")")
|
||||
(epoch 1422)
|
||||
(eval "(ocaml-run \"Result.map_error (fun e -> e + 1) (Error 5)\")")
|
||||
|
||||
EPOCHS
|
||||
|
||||
OUTPUT=$(timeout 180 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
|
||||
@@ -1210,6 +1244,25 @@ check 1321 "exception arg" '("exception-def" "MyExn"'
|
||||
check 1322 "raise+catch with arg" '5'
|
||||
check 1323 "raise+catch string arg" '"oops"'
|
||||
|
||||
# ── Phase 6 expanded stdlib ─────────────────────────────────────
|
||||
check 1400 "List.concat" '(1 2 3 4 5)'
|
||||
check 1401 "List.init" '(0 10 20 30 40)'
|
||||
check 1402 "List.find_opt found" '("Some" 3)'
|
||||
check 1403 "List.find_opt missing" '("None")'
|
||||
check 1404 "List.mapi" '(10 21 32)'
|
||||
check 1405 "List.partition" '("tuple" (3 4) (1 2))'
|
||||
check 1406 "List.assoc" '"b"'
|
||||
check 1407 "List.assoc_opt missing" '("None")'
|
||||
|
||||
check 1410 "Option.fold Some" '70'
|
||||
check 1411 "Option.fold None" '0'
|
||||
check 1412 "Option.to_list Some" '(7)'
|
||||
check 1413 "Option.to_list None" '()'
|
||||
|
||||
check 1420 "Result.get_ok" '42'
|
||||
check 1421 "Result.to_option Ok" '("Some" 1)'
|
||||
check 1422 "Result.map_error" '("Error" 6)'
|
||||
|
||||
TOTAL=$((PASS + FAIL))
|
||||
if [ $FAIL -eq 0 ]; then
|
||||
echo "ok $PASS/$TOTAL OCaml-on-SX tests passed"
|
||||
|
||||
@@ -240,12 +240,13 @@ SX CEK evaluator (both JS and OCaml hosts)
|
||||
|
||||
- [~] `List`: `map`, `filter`, `fold_left`, `fold_right`, `length`, `rev`,
|
||||
`append`, `iter`, `for_all`, `exists`, `mem`, `nth`, `hd`, `tl`,
|
||||
`rev_append`. _(Pending: concat/flatten, iteri/mapi, find/find_opt,
|
||||
assoc/assq, sort, init, combine, split, partition.)_
|
||||
- [~] `Option`: `map`, `bind`, `value`, `get`, `is_none`, `is_some`.
|
||||
_(Pending: fold/join/iter/to_list/to_result.)_
|
||||
- [~] `Result`: `map`, `bind`, `is_ok`, `is_error`. _(Pending:
|
||||
fold/get_ok/get_error/map_error/to_option.)_
|
||||
`rev_append`, `concat`/`flatten`, `init`, `iteri`, `mapi`, `find`,
|
||||
`find_opt`, `assoc`, `assoc_opt`, `partition`. _(Pending:
|
||||
sort/stable_sort, combine, split.)_
|
||||
- [~] `Option`: `map`, `bind`, `value`, `get`, `is_none`, `is_some`,
|
||||
`iter`, `fold`, `to_list`. _(Pending: join/to_result.)_
|
||||
- [~] `Result`: `map`, `bind`, `is_ok`, `is_error`, `get_ok`,
|
||||
`get_error`, `map_error`, `to_option`. _(Pending: fold/join.)_
|
||||
- [~] `String`: `length`, `get`, `sub`, `concat`, `uppercase_ascii`,
|
||||
`lowercase_ascii`, `starts_with`. _(Pending: split_on_char, trim,
|
||||
contains, ends_with, index_opt, replace_all.)_
|
||||
@@ -360,6 +361,15 @@ the "mother tongue" closure: OCaml → SX → OCaml. This means:
|
||||
|
||||
_Newest first._
|
||||
|
||||
- 2026-05-08 Phase 6 — expanded stdlib slice (+15 tests, 319 total).
|
||||
List: concat/flatten, init, find/find_opt, partition, mapi/iteri,
|
||||
assoc/assoc_opt. Option: iter, fold, to_list. Result: get_ok,
|
||||
get_error, map_error, to_option. Also fixed parser's
|
||||
skip-to-boundary! to track `let..in` / `begin..end` / `struct..end`
|
||||
/ `for/while..done` nesting via a depth counter so nested let
|
||||
expressions inside top-level decl bodies don't trip over the
|
||||
decl-boundary detector. Stdlib functions like `init` use `begin..end`
|
||||
to make nested-let intent explicit.
|
||||
- 2026-05-08 Phase 3 — `exception` declarations (+4 tests, 304 total).
|
||||
`exception NAME [of TYPE]` parses to `(:exception-def NAME [ARG-SRC])`.
|
||||
Runtime is a no-op: exception values are just tagged ctor values, so
|
||||
|
||||
Reference in New Issue
Block a user