Three things the review found, and the refusal it was right about. The returned-Vec refusal is gone. It called a leak a dangle: the storage a returned Vec owns outlives the expression, so the view reads what it says it reads, and what is lost is the owner. (len (mk)) and (at (mk) 0) lose the same owner and compile, spec-memory.md already says an overwritten global Vec leaks its first block, and under a region there is nothing to leak at all. "We're purposely doing manual memory management for the static side, so whatever." The array refusal stays exactly as it is — that one is a view into a frame that is gone and answers bytes the frame has since reused. Wrong answers are the compiler's business and leaks are the program's, and both docs now draw that line, because the two forms look alike. The -1 sentinel was reachable from user syntax: (slice v 0 -1) answered the whole Vec on both backends while (slice a 0 -1) was refused as a negative bound. The refusal now runs on the bounds the reader wrote, before the implicit hi is built — the only order that works, since the sentinel is itself a -1 and a check on the finished pair would refuse (slice v). The backwards-pair check moved into the branch where both ends are written. The bounds seam is closed toward index_expr, and the tiebreaker is not which half is older. indexed and vec_at both take their index through it, so (at a c) over a u32 compiled where (slice a c) did not: the fork was between slice and at as much as between two targets. A bound is a subscript. Also an x86 row for vec.flan, so the new arity is pinned on both backends in CI rather than by hand, and the comment columns the sweep shifted left in slurp, into, format and algorithms.
121 lines
5.5 KiB
Plaintext
121 lines
5.5 KiB
Plaintext
;;;; The slice family at its second and third element types.
|
|
;;;;
|
|
;;;; sort was the only sort in the language. These are the other two, and
|
|
;;;; they are copies rather than an abstraction: map, filter, reduce and a sort
|
|
;;;; taking a comparator all need a *function value*, which check.ml refuses
|
|
;;;; with "a function type is not implemented yet -- milestone 5". So the
|
|
;;;; honest form is the concrete one, and the claim this file makes is only
|
|
;;;; that the concrete ones are right.
|
|
|
|
(defn show-f32 [s [f32]] ()
|
|
(dotimes [i (len s)]
|
|
(print (at s i))
|
|
(print " "))
|
|
(println ""))
|
|
|
|
(defn show-fields [s [[u8]]] ()
|
|
(dotimes [i (len s)]
|
|
(print (string (at s i)))
|
|
(print " "))
|
|
(println ""))
|
|
|
|
(defn main [] i32
|
|
;; Every float literal is cast. A literal defaults to f64 and an array
|
|
;; literal has no context to say otherwise -- a let has no type annotation --
|
|
;; so [3.5 -1.0] is an [f64] and (sort) refuses it by type. The cast is
|
|
;; the only spelling available today.
|
|
;;
|
|
;; sort: duplicates, negatives, a zero and an odd length, which is the
|
|
;; input shape the i32 sort is tested on for the same reasons.
|
|
(let [xs [(f32 3.5) (f32 -1.0) (f32 0.0) (f32 3.5) (f32 -2.25) (f32 10.0) (f32 0.5)]]
|
|
(sort (slice xs 0 7))
|
|
(show-f32 (slice xs 0 7))) ; -2.25 -1 0 0.5 3.5 3.5 10
|
|
|
|
;; In place and ptr+len: sorting a subslice leaves its neighbours alone. That
|
|
;; is the whole content of the in-place claim, and a version that copied
|
|
;; would pass every test above and fail this one.
|
|
(let [xs [(f32 9.0) (f32 4.0) (f32 3.0) (f32 2.0) (f32 1.0) (f32 9.0)]]
|
|
(sort (slice xs 1 5))
|
|
(show-f32 (slice xs 0 6))) ; 9 1 2 3 4 9
|
|
|
|
;; Already sorted, reverse sorted, and a single element -- the three inputs
|
|
;; where an insertion loop with the comparison the wrong way round still
|
|
;; looks plausible.
|
|
(let [xs [(f32 1.0) (f32 2.0) (f32 3.0)]]
|
|
(sort (slice xs 0 3))
|
|
(show-f32 (slice xs 0 3))) ; 1 2 3
|
|
(let [xs [(f32 3.0) (f32 2.0) (f32 1.0)]]
|
|
(sort (slice xs 0 3))
|
|
(show-f32 (slice xs 0 3))) ; 1 2 3
|
|
(let [xs [(f32 7.0)]]
|
|
(sort (slice xs 0 1))
|
|
(show-f32 (slice xs 0 1))) ; 7
|
|
;; The empty slice must not read (at s -1).
|
|
(let [xs [(f32 7.0)]]
|
|
(sort (slice xs 0 0))
|
|
(show-f32 (slice xs 0 0))) ;
|
|
|
|
(let [xs [(f32 1.0) (f32 2.0) (f32 3.0) (f32 4.0)]]
|
|
(reverse (slice xs 0 4))
|
|
(show-f32 (slice xs 0 4))) ; 4 3 2 1
|
|
|
|
;; min, max and sum. The empty slice is None for the first two -- there is no
|
|
;; least f32 that is also an honest answer -- and the sum accumulates in f64,
|
|
;; which is why 16777216 + 1 does not absorb here the way it would in f32.
|
|
(let [xs [(f32 3.5) (f32 -1.0) (f32 10.0)]]
|
|
(match (min-of (slice xs 0 3)) (Some m) (print m) None (print "none"))
|
|
(print " ")
|
|
(match (max-of (slice xs 0 3)) (Some m) (print m) None (print "none"))
|
|
(print " ")
|
|
(print (sum-f32 (slice xs 0 3)))
|
|
(println "")) ; -1 10 12.5
|
|
(let [xs [(f32 1.0)]]
|
|
(match (min-of (slice xs 0 0)) (Some m) (print m) None (print "none"))
|
|
(print " ")
|
|
(print (sum-f32 (slice xs 0 0)))
|
|
(println "")) ; none 0
|
|
;; The accumulator's width, made visible. 2^24 is where an f32 stops having
|
|
;; a bit for 1, so an f32 running total absorbs both of these and the
|
|
;; difference below is 0. In f64 they both land, and it is 2.
|
|
(let [xs [(f32 16777216.0) (f32 1.0) (f32 1.0)]]
|
|
(print (- (sum-f32 (slice xs 0 3)) 16777216.0))
|
|
(println "")) ; 2
|
|
|
|
;; bytes<? is bytewise and unsigned, and explicitly not alphabetical: "Zebra"
|
|
;; comes before "apple" because 'Z' is 90. A prefix comes before what extends
|
|
;; it, which is the case a loop running only to (len a) reads off the end
|
|
;; for, and 0x80 above 0x00 is the case a signed byte gets backwards.
|
|
(print (bytes<? (bytes-view "a") (bytes-view "b"))) (print " ") ; true
|
|
(print (bytes<? (bytes-view "b") (bytes-view "a"))) (print " ") ; false
|
|
(print (bytes<? (bytes-view "a") (bytes-view "a"))) (print " ") ; false
|
|
(print (bytes<? (bytes-view "ab") (bytes-view "abc"))) (print " ") ; true
|
|
(print (bytes<? (bytes-view "abc") (bytes-view "ab"))) (print " ") ; false
|
|
(print (bytes<? (bytes-view "") (bytes-view "a"))) (print " ") ; true
|
|
(print (bytes<? (bytes-view "Zebra") (bytes-view "apple"))) ; true
|
|
(println "")
|
|
;; 0x00 below 0x80, which is the pair a comparison over a *signed* byte gets
|
|
;; backwards -- and there is no \x escape in the reader, so these are built
|
|
;; as u8 arrays rather than written as string literals.
|
|
(let [lo [(u8 0)]
|
|
hi [(u8 128)]]
|
|
(print (bytes<? (slice lo 0 1) (slice hi 0 1))) (print " ") ; true
|
|
(print (bytes<? (slice hi 0 1) (slice lo 0 1))) ; false
|
|
(println ""))
|
|
|
|
;; sort-bytes over the fields split out of one buffer. The slices move and
|
|
;; the bytes never do, so this sorts a borrowed view of a string literal --
|
|
;; which an in-place byte sort could not, since a literal lives in .rodata.
|
|
(let [f (split (bytes-view "pear,apple,Fig,apple,banana") \,)]
|
|
(sort-bytes (slice f))
|
|
(show-fields (slice f)) ; Fig apple apple banana pear
|
|
(free f))
|
|
|
|
;; And the round trip the whole second tier is for: split, sort, join.
|
|
(let [f (split (bytes-view "delta,alpha,charlie,bravo") \,)]
|
|
(sort-bytes (slice f))
|
|
(let [j (join (slice f) (bytes-view " < "))]
|
|
(println (string (slice j))) ; alpha < bravo < charlie < delta
|
|
(free j))
|
|
(free f))
|
|
0)
|