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.
79 lines
3.2 KiB
Plaintext
79 lines
3.2 KiB
Plaintext
;;;; atan2, pow, and clamp — the three gaps NEXT.md's second-tier list names
|
|
;;;; under "maths gaps".
|
|
;;;;
|
|
;;;; Two of them are declares over libm and the third is a macro, and that
|
|
;;;; split is the whole content of this file. atan2f and powf are not correctly
|
|
;;;; rounded under IEEE-754, exactly as sinf and cosf are not, so every case
|
|
;;;; below is a value whose answer is exact in binary — a quadrant boundary, a
|
|
;;;; power of two, a perfect square — rather than one that would pin a
|
|
;;;; particular libm's last bit and then fail on wasi-libc.
|
|
;;;;
|
|
;;;; clamp is a macro because a function could not be: min and max are builtins
|
|
;;;; at every numeric type and there are no generics, so a clamp function is
|
|
;;;; one copy per type. The proof that the macro is not one copy per type is
|
|
;;;; that the same three-word call below is made at i32, i64, u8 and f32.
|
|
|
|
(defn show [x f32] ()
|
|
(print x)
|
|
(print " "))
|
|
|
|
;;; clamp must evaluate each of its three arguments exactly once. A macro that
|
|
;;; repeated one — say (if (< x lo) lo (if (> x hi) hi x)), which names x twice
|
|
;;; — would read identically and call this twice.
|
|
(defvar calls i32 0)
|
|
|
|
(defn tick [x i32] i32
|
|
(set calls (+ calls 1))
|
|
x)
|
|
|
|
(defn main [] i32
|
|
;; atan2 across all four quadrants and on both axes, which is the whole
|
|
;; reason for it over a division: the quadrant is recovered from two signs,
|
|
;; and (/ y x) has thrown it away before atan sees it. The two on the x = 0
|
|
;; axis are where the division does not exist at all.
|
|
(show (atan2-f32 1.0 1.0)) ; pi/4
|
|
(show (atan2-f32 1.0 -1.0)) ; 3pi/4
|
|
(show (atan2-f32 -1.0 -1.0)) ; -3pi/4
|
|
(show (atan2-f32 -1.0 1.0)) ; -pi/4
|
|
(show (atan2-f32 0.0 1.0)) ; 0
|
|
(show (atan2-f32 1.0 0.0)) ; pi/2
|
|
(show (atan2-f32 -1.0 0.0)) ; -pi/2
|
|
(println "")
|
|
|
|
;; pow. A power of two, a half-power that is a perfect square, a negative
|
|
;; exponent, and the two edge exponents every implementation special-cases.
|
|
(show (pow-f32 2.0 10.0)) ; 1024
|
|
(show (pow-f32 9.0 0.5)) ; 3
|
|
(show (pow-f32 2.0 -2.0)) ; 0.25
|
|
(show (pow-f32 5.0 0.0)) ; 1
|
|
(show (pow-f32 5.0 1.0)) ; 5
|
|
(println "")
|
|
|
|
;; clamp: below the range, above it, and inside it untouched.
|
|
(print (clamp 0 1 3)) (print " ") ; 1
|
|
(print (clamp 5 1 3)) (print " ") ; 3
|
|
(print (clamp 2 1 3)) (print " ") ; 2
|
|
;; Both ends are inclusive, which is the off-by-one a hand-written clamp
|
|
;; gets wrong with a < where it wanted <=.
|
|
(print (clamp 1 1 3)) (print " ") ; 1
|
|
(print (clamp 3 1 3)) ; 3
|
|
(println "")
|
|
|
|
;; The same three words at three more types, none of which a function could
|
|
;; have served without a copy of its own.
|
|
(print (clamp (i64 900) (i64 0) (i64 255))) (print " ") ; 255
|
|
(print (clamp (u8 200) (u8 0) (u8 255))) (print " ") ; 200
|
|
(show (clamp 2.5 0.0 1.0)) ; 1
|
|
(println "")
|
|
|
|
;; lo above hi is not an error and answers hi: the outer min wins. Written
|
|
;; down here rather than left for someone to discover.
|
|
(print (clamp 7 5 1))
|
|
(println "")
|
|
|
|
;; Three arguments, three evaluations.
|
|
(print (clamp (tick 5) (tick 1) (tick 3))) (print " ")
|
|
(print calls)
|
|
(println "")
|
|
0)
|