flan/test/programs/text.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

84 lines
3.5 KiB
Plaintext

;;;; The prelude's byte predicates, parse-i64, and the two number helpers.
;;;;
;;;; The cases are chosen so a wrong implementation fails one: a prefix longer
;;;; than the string (which must answer false, not trap), the empty prefix and
;;;; the whole string as its own prefix, a prefix that matches at the wrong
;;;; end, and for parse-i64 every shape strtoll answers 0 for — "", "abc",
;;;; "12x", "-" — each of which a caller could not tell from a real 0.
(defn show-bool [b bool] ()
(print (if b "t" "f")))
(defn main [] i32
(show-bool (bytes=? (bytes "abc") (bytes "abc"))) ; t
(show-bool (bytes=? (bytes "abc") (bytes "abd"))) ; f same length
(show-bool (bytes=? (bytes "abc") (bytes "ab"))) ; f prefix, not equal
(show-bool (bytes=? (bytes "") (bytes ""))) ; t
(println "")
(show-bool (starts-with? (bytes "hello") (bytes "hel"))) ; t
(show-bool (starts-with? (bytes "hello") (bytes "llo"))) ; f matches the end
(show-bool (starts-with? (bytes "hi") (bytes "hiya"))) ; f longer, no trap
(show-bool (starts-with? (bytes "hello") (bytes ""))) ; t
(show-bool (starts-with? (bytes "hello") (bytes "hello"))) ; t
(println "")
(show-bool (ends-with? (bytes "hello") (bytes "llo"))) ; t
(show-bool (ends-with? (bytes "hello") (bytes "hel"))) ; f matches the start
(show-bool (ends-with? (bytes "hi") (bytes "hiya"))) ; f longer, no trap
(show-bool (ends-with? (bytes "hello") (bytes ""))) ; t
(show-bool (ends-with? (bytes "hello") (bytes "hello"))) ; t
(println "")
;; First occurrence, and None for a byte that is not there.
(print (match (index-of-byte (bytes "banana") \a) (Some i) i None -1))
(print " ")
(print (match (index-of-byte (bytes "banana") \z) (Some i) i None -1))
(print " ")
(print (match (index-of-byte (bytes "") \a) (Some i) i None -1))
(println "")
;; Accepted.
(print (match (parse-i64 (bytes "0")) (Some v) v None -999)) (print " ")
(print (match (parse-i64 (bytes "42")) (Some v) v None -999)) (print " ")
(print (match (parse-i64 (bytes "-42")) (Some v) v None -999)) (print " ")
(print (match (parse-i64 (bytes "+7")) (Some v) v None -999)) (print " ")
(print (match (parse-i64 (bytes "9007199254740993")) (Some v) v None -999))
(println "")
;; Refused. Each of these is a 0 out of strtoll, which is the point.
(print (match (parse-i64 (bytes "")) (Some v) v None -999)) (print " ")
(print (match (parse-i64 (bytes "abc")) (Some v) v None -999)) (print " ")
(print (match (parse-i64 (bytes "12x")) (Some v) v None -999)) (print " ")
(print (match (parse-i64 (bytes "-")) (Some v) v None -999)) (print " ")
(print (match (parse-i64 (bytes " 1")) (Some v) v None -999))
(println "")
(print (sign-f32 3.5)) (print " ")
(print (sign-f32 -3.5)) (print " ")
(print (sign-f32 0.0))
(println "")
;; t = 1.0 must return b exactly, which a + t*(b - a) does not always do.
(print (lerp 0.0 10.0 0.0)) (print " ")
(print (lerp 0.0 10.0 0.25)) (print " ")
(print (lerp 0.0 10.0 1.0)) (print " ")
(print (lerp 2.0 -2.0 0.5))
(println "")
;; The RNG ranges, off a fixed seed, so the numbers are the sequence and not
;; just "something in range". An empty range answers lo and must not divide.
(rand-seed 7)
(dotimes [i 5]
(when (> i 0) (print " "))
(print (rand-i32-range 10 20)))
(println "")
(print (rand-i32-range 5 5)) (print " ")
(print (rand-i32-range 5 -5))
(println "")
(rand-seed 7)
(dotimes [i 3]
(when (> i 0) (print " "))
(print (rand-f32-range 0.0 1.0)))
(println "")
0)