flan/test/programs/slices.flan
Joseph Ferano 26c53e0a19 Every defn in the tree states its return type, and Unit is written ()
The mechanical half, ahead of the parser change that needs it. tools/unit-return.py
fills the empty slot with () and rewrites Unit as () wherever a type is spelled --
(Fn [i32] Unit), (Map i32 Unit), a return type written out.

Deciding whether a defn already had a return type is the whole difficulty, and
the script does it the way parse.ml did: is_type_form is transcribed rather than
improved, because being identical to the parser it replaces is what makes the
sweep meaning-preserving. It is re-runnable, so the lanes that branched before
this can have the same pass at merge:

    python3 tools/unit-return.py .
    python3 tools/unit-return.py --in-strings test/test_flan.ml test/test_acceptance.ml \
        test/test_session.ml emacs/test-flan-dev.el emacs/test-flan-mode.el
    python3 tools/unit-return.py --raw-ml lib/prelude.ml
    python3 tools/unit-return.py --in-html web/index.html

-v logs every defn it saw and what it decided, which is how a sweep of 440 sites
gets reviewed at all. Embedded modes pool a file's type declarations across all
its fragments, because a snippet split across concatenation -- decls ^ "(defn f
[s [u8]] Cursor ...)" -- cannot see the names the other half declared; pooled
names count only in bare-symbol position, for the same reason the prelude's do.
A fragment that cuts off mid-form is skipped rather than guessed at. Five sites
in test_flan.ml still needed a hand, and they are in this commit.

Two things ride along because the sweep needs them: parse.ml reads a lone () as
the return type of a function with no body, which was not a shape the old
optional slot could produce; and the map refusals name () rather than Unit, since
that is now the spelling a caller wrote.
2026-09-12 23:06:40 +07:00

83 lines
3.4 KiB
Plaintext

;;;; The prelude's in-place slice algorithms.
;;;;
;;;; Every input here is chosen so that a wrong implementation passes nothing.
;;;; The sort input is unsorted, has duplicates, has negatives and has an odd
;;;; length, so a comparison with the wrong sense, an off-by-one that drops the
;;;; last element, and a swap that loses an equal key all show up. The second
;;;; sort is reverse-sorted, which is the worst case for insertion sort and the
;;;; case a no-op comparison would pass. The third sorts a *subslice* and then
;;;; prints the whole owning array: a slice is ptr+len into its owner, so the
;;;; five elements inside the range must be sorted and the three outside it
;;;; must be untouched. That last one is the property that dies silently if a
;;;; slice parameter ever starts being copied.
(defvar xs [7 i32])
(defvar ys [5 i32])
(defvar zs [8 i32])
(defn show [s [i32]] ()
(dotimes [i (len s)]
(when (> i 0) (print " "))
(print (at s i)))
(println ""))
(defn load-xs [] ()
(set (at xs 0) 5)
(set (at xs 1) -3)
(set (at xs 2) 5)
(set (at xs 3) 0)
(set (at xs 4) 12)
(set (at xs 5) -3)
(set (at xs 6) 7))
(defn main [] i32
(load-xs)
(show (slice xs 0 (len xs))) ; 5 -3 5 0 12 -3 7
;; Reading the whole slice, before anything reorders it.
(print (sum-i32 (slice xs 0 (len xs)))) (println "") ; 23
(print (match (min-i32 (slice xs 0 (len xs))) (Some v) v None 99))
(println "") ; -3
(print (match (max-i32 (slice xs 0 (len xs))) (Some v) v None 99))
(println "") ; 12
;; First index, not the last: 5 appears at 0 and at 2.
(print (match (index-of-i32 (slice xs 0 (len xs)) 5) (Some v) v None -1))
(println "") ; 0
(print (match (index-of-i32 (slice xs 0 (len xs)) 4) (Some v) v None -1))
(println "") ; -1
;; An empty slice has no least element, and None is the answer.
(print (match (min-i32 (slice xs 3 3)) (Some v) v None 99))
(println "") ; 99
;; Reverse of an odd-length slice: the middle element stays put.
(reverse-i32! (slice xs 0 (len xs)))
(show (slice xs 0 (len xs))) ; 7 -3 12 0 5 -3 5
;; And of a two-element one, the smallest case that can actually move.
(reverse-i32! (slice xs 0 2))
(show (slice xs 0 (len xs))) ; -3 7 12 0 5 -3 5
(load-xs)
(sort-i32! (slice xs 0 (len xs)))
(show (slice xs 0 (len xs))) ; -3 -3 0 5 5 7 12
;; Reverse-sorted: the case a comparison that never fires would pass.
(set (at ys 0) 5) (set (at ys 1) 4) (set (at ys 2) 3)
(set (at ys 3) 2) (set (at ys 4) 1)
(sort-i32! (slice ys 0 (len ys)))
(show (slice ys 0 (len ys))) ; 1 2 3 4 5
;; A subslice, with the elements on both sides left alone.
(set (at zs 0) 100) (set (at zs 1) 9) (set (at zs 2) -1)
(set (at zs 3) 9) (set (at zs 4) 4) (set (at zs 5) 0)
(set (at zs 6) 200) (set (at zs 7) 300)
(sort-i32! (slice zs 1 6))
(show (slice zs 0 (len zs))) ; 100 -1 0 4 9 9 200 300
;; Degenerate lengths must do nothing rather than run off an end.
(sort-i32! (slice zs 0 0))
(reverse-i32! (slice zs 0 0))
(sort-i32! (slice zs 2 3))
(reverse-i32! (slice zs 2 3))
(show (slice zs 0 (len zs))) ; 100 -1 0 4 9 9 200 300
0)