ocaml: phase 4 'fun (a, b) -> body' tuple-param destructuring (+4 tests, 553 total)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 28s

parse-fun's collect-params now detects '(IDENT, ...)' as a
tuple-pattern parameter (lookahead at peek-tok-at 1/2 distinguishes
from '(x : T)' and '()' cases that try-consume-param! already
handles). For each tuple param it:

  1. parse-pattern to get the full pattern AST
  2. generate a synthetic __pat_N name as the actual fun parameter
  3. push (synth_name, pattern) onto tuple-binds

After parsing the body, wraps it innermost-first with one
'match __pat_N with PAT -> ...' per tuple-param. The user-visible
result is a (:fun (params...) body) where params are all simple
names but the body destructures.

Also retroactively simplifies Hashtbl.keys/values from
'fun pair -> match pair with (k, _) -> k' to plain
'fun (k, _) -> k', closing the iteration-99 workaround.

  (fun (a, b) -> a + b) (3, 7)              = 10
  List.map (fun (a, b) -> a * b)
           [(1, 2); (3, 4); (5, 6)]         = [2; 12; 30]
  List.map (fun (k, _) -> k)
           [("a", 1); ("b", 2)]              = ["a"; "b"]
  (fun a (b, c) d -> a + b + c + d) 1 (2, 3) 4 = 10
This commit is contained in:
2026-05-09 04:25:18 +00:00
parent 8ca3ef342d
commit 64f4f10c32
4 changed files with 73 additions and 12 deletions

View File

@@ -718,20 +718,60 @@
(fn
()
(let
((params (list)))
((params (list))
(tuple-binds (list)))
(begin
(define
collect-params
(fn ()
(let ((nm (try-consume-param!)))
(when (not (= nm nil))
(begin (append! params nm) (collect-params))))))
(cond
;; `(IDENT, ...)` — tuple-pattern param. Generate a
;; synthetic name and remember the pattern so we
;; can wrap the body with a match.
((and (at-op? "(")
(= (ocaml-tok-type (peek-tok-at 1)) "ident")
(or (= (ocaml-tok-value (peek-tok-at 2)) ",")
(= (ocaml-tok-value (peek-tok-at 2)) ")")
(= (ocaml-tok-value (peek-tok-at 2)) ":")))
(cond
;; (x : T) is a typed simple param — let the
;; original try-consume-param! handle that.
((= (ocaml-tok-value (peek-tok-at 2)) ":")
(let ((nm (try-consume-param!)))
(begin (append! params nm) (collect-params))))
(else
(let ((pat (parse-pattern)))
(let ((nm (str "__pat_" (len tuple-binds))))
(begin
(append! tuple-binds (list nm pat))
(append! params nm)
(collect-params)))))))
(else
(let ((nm (try-consume-param!)))
(when (not (= nm nil))
(begin (append! params nm) (collect-params))))))))
(collect-params)
(when
(= (len params) 0)
(error "ocaml-parse: fun expects at least one parameter"))
(consume! "op" "->")
(let ((body (parse-expr))) (list :fun params body))))))
(let ((body (parse-expr)))
;; Wrap body with `match __pat_N with PAT -> ...` for
;; each tuple-param, innermost first.
(let ((wrapped body))
(begin
(define wrap-binds
(fn (xs)
(when (not (= xs (list)))
(begin
(let ((nm (nth (first xs) 0))
(pat (nth (first xs) 1)))
(set! wrapped
(list :match (list :var nm)
(list (list :case pat wrapped)))))
(wrap-binds (rest xs))))))
(wrap-binds (reverse tuple-binds))
(list :fun params wrapped))))))))
(define
parse-let
(fn ()