Merge branch 'worktree-agent-a2b2b99f84ca798de' into dev-loop
This commit is contained in:
commit
970d5ac38f
87
FIX.org
87
FIX.org
@ -5542,3 +5542,90 @@ condition's payload" reached the struct-field refusal that fired first, never
|
||||
the condition arm it was named for. The struct refusal is gone since the
|
||||
descriptors landed, so the row is an [accepts] now and a new [rejects_check]
|
||||
signals a dyn directly to reach the arm that is still there.
|
||||
|
||||
* len is a variable name now, 2026-09-21
|
||||
The author, on why:
|
||||
|
||||
#+begin_quote
|
||||
I think I prefer length over len, because then I'll use len as the variable
|
||||
name
|
||||
#+end_quote
|
||||
|
||||
So the count is ~length~, and ~len~ is left to programs. One arm in
|
||||
~lib/check.ml~ and one row in the table beside it; everything that wrote
|
||||
~(len x)~ across lib, test, examples, vendor, spike, docs, web, emacs,
|
||||
plan.org and NEXT.md writes ~(length x)~ now.
|
||||
|
||||
** What this adds to shadowing, which landed beside it
|
||||
Shadowing and ~builtin/~ had already taken most of the sting out. A
|
||||
~(defn len ...)~ was legal, it won at every call site in its file, and
|
||||
~builtin/len~ reached past it. What was still true is that ~len~ was a
|
||||
builtin: the defn earned a warning, and anything that wanted to wrap it had
|
||||
to say ~builtin/~. Now there is no builtin under the name at all — nothing
|
||||
warns, the qualifier is not needed to reach past anything, and the name is
|
||||
free in every position, which was never the question for a binding and was
|
||||
the question for a call.
|
||||
|
||||
So the two features do different work and the rename does not repeal any of
|
||||
the other. ~length~ simply becomes shadowing's worked example in place of
|
||||
~len~: ~shadow-builtin.flan~, ~builtin-qualified.flan~, the package under
|
||||
~pkgs/shadowed~ and the ~builtin/~ rows in ~test_flan.ml~ all moved to it, and
|
||||
they go on testing shadowing rather than quietly becoming tests of a free
|
||||
name.
|
||||
|
||||
** The refusal
|
||||
A call to a ~len~ that nothing in the program defines is answered where an
|
||||
unknown function is answered — after every table and after the shadowing
|
||||
guard, so a program with its own ~len~ never sees it:
|
||||
|
||||
#+begin_example
|
||||
test/programs/len-gone.flan:15:3: there is no len. The number of elements in an
|
||||
array, a slice, a string, a Vec or a Map is length. Write (length a)
|
||||
15 | (len a))
|
||||
| ^^^^^^^
|
||||
#+end_example
|
||||
|
||||
Said rather than guessed at: ~len~ and ~length~ are three edits apart and the
|
||||
did-you-mean's net is one. The reader's own argument is written back out
|
||||
through ~spell_arg~, which is ~as-slice~'s spelling lifted out of it and now
|
||||
shared — a name is its name, an integer its digits, anything with structure
|
||||
inside it becomes a stand-in.
|
||||
|
||||
*Only at one argument.* ~length~ takes exactly one, so ~(len xs 1)~ written
|
||||
back out as ~(length xs 1)~ would be refused a second time the moment it was
|
||||
pasted, and a suggestion that does not compile is the whole thing this
|
||||
spelling exists to prevent. At any other arity the shape ~(length v)~ is
|
||||
suggested instead. ~as-slice~ can write every argument out because ~slice~
|
||||
takes one, two or three; the difference is the arity and not the style. Two
|
||||
drafts of this lane got it wrong in turn — the first quoted the source line
|
||||
and could print an unbalanced form, the second spelled every argument — and
|
||||
the arity cases are pinned now, which neither draft was.
|
||||
|
||||
~(builtin/len xs)~ is the one rough edge left. It reaches ~not_a_builtin~ and
|
||||
reads "len is not a builtin, so builtin/len reaches nothing" with no
|
||||
did-you-mean, for the same three-edit reason. Left as it is rather than
|
||||
special-cased.
|
||||
|
||||
** Certified against b8856be
|
||||
Rebased onto the diagnostics rewrite, which is where the refusal above has to
|
||||
read against its neighbours — it takes ~as-slice~'s shape, because that is the
|
||||
refusal beside it and the two answer the same kind of question. dev-loop moves
|
||||
hourly; this is the base the green result below was measured on.
|
||||
|
||||
** sand.flan, for the author
|
||||
Two calls are left in the tree, both in the file this lane did not touch
|
||||
because it is the author's:
|
||||
|
||||
: 31 | length (len coll)]
|
||||
: 55 | (set current-color (% (+ current-color 1) (len colors))))
|
||||
|
||||
Line 31 is the one to read twice: the binding is ~length~ and the call is
|
||||
~len~, and a sequential ~let~ makes ~length (length coll)~ legal — the
|
||||
initialiser is checked before the name it binds exists.
|
||||
|
||||
Until both say ~length~, ~test_acceptance~ and ~test_session~ abort on the
|
||||
first of them — a fatal exception, not a failing row, which is the
|
||||
hidden-failure shape a previous lane found. sand.flan also feeds ~@x86~,
|
||||
~@js~, ~@sanitize~ and ~@valgrind~ through their workspace-file deps, so those
|
||||
go red too. Everything underneath was verified green against a copy of the
|
||||
file with those two lines changed.
|
||||
|
||||
18
NEXT.md
18
NEXT.md
@ -1386,7 +1386,7 @@ cells already solve. A generic function is a cell whose body is a dispatch table
|
||||
expensive half of classes is therefore already built and tested.
|
||||
- **The pool is not one storage option among three.** `migrate-instances` has to *enumerate* live instances. A pool
|
||||
behind generational `(Handle T)` gives that by construction; a world arena and an owned region do not obviously.
|
||||
plan.org presents the three as a free choice and they are not. **The pool is built**, and `(len p)` with
|
||||
plan.org presents the three as a free choice and they are not. **The pool is built**, and `(length p)` with
|
||||
`(pool-handle p i)` is that enumeration.
|
||||
- **`Enemy@1` has to stay resolvable** for `migrate` to dispatch on it, so the session retains every layout version's
|
||||
metadata for as long as any instance holds it. Same rule as "nothing is ever `dlclose`d", and worth stating as one.
|
||||
@ -1599,7 +1599,7 @@ See [`docs/BUILT.md`](docs/BUILT.md), "`(Handle T)` and the pool, which is what
|
||||
It was not an incidental precondition. `migrate-instances` has to *enumerate* live instances, and a pool behind a
|
||||
generational `(Handle T)` gives that by construction while a world arena and an owned region do not. plan.org presents
|
||||
the three storage strategies as a free choice and they are not: handles are the one that makes migration possible.
|
||||
`(len p)` plus `(pool-handle p i)` is that enumeration, and it is two entry points rather than an iteration protocol.
|
||||
`(length p)` plus `(pool-handle p i)` is that enumeration, and it is two entry points rather than an iteration protocol.
|
||||
|
||||
Already banked, and it means classes are less work than plan.org implies: **a generic function is an indirection cell**
|
||||
whose body is a dispatch table, which a reload extends. That is the expensive half of method dispatch, and it is built
|
||||
@ -1764,7 +1764,7 @@ to avoid rather than inherit. Four, with what to do instead.
|
||||
**1. One argument-order rule, held everywhere.** Clojure's sequence functions take the collection *last*
|
||||
(`(map f coll)`) and its collection functions take it *first* (`(assoc m k v)`). The split is deliberate there, and it
|
||||
is why Clojure needs **two** threading macros instead of one. **Our rule: the thing being operated on comes first.**
|
||||
That is already what the language does — `(at a i)`, `(len xs)`, `(push v x)`, `(slice v)` — and `into` follows it
|
||||
That is already what the language does — `(at a i)`, `(length xs)`, `(push v x)`, `(slice v)` — and `into` follows it
|
||||
with the source first. Hold it; do not ship two of anything to paper over a split.
|
||||
|
||||
**2. A membership test says which thing it tests.** Clojure's `contains?` checks *keys*, so `(contains? [1 2 3] 1)` is
|
||||
@ -1965,8 +1965,8 @@ run one lane at a time; item 4 is disjoint and runs alongside any of them.
|
||||
|
||||
What the spec did not settle and this lane did, beyond those: `resolve` answers `(Option (Ptr T))` and not
|
||||
`(Option T)` — the spec's own worked example is annotated that way, for the reason written a line above it, that a
|
||||
pattern binding binds a value and a copy cannot be written back. `(len p)` is the *slot high-water* and `(live p)`
|
||||
is the live count, in that direction, so a loop bounded by `len` cannot silently skip a live entry. A slot is
|
||||
pattern binding binds a value and a copy cannot be written back. `(length p)` is the *slot high-water* and `(live p)`
|
||||
is the live count, in that direction, so a loop bounded by `length` cannot silently skip a live entry. A slot is
|
||||
recycled by `(release p h)` on the owner and never by `free`, because a handle owns nothing and consuming one copy
|
||||
would say nothing about the others — so `spec-memory.md`'s two release points are untouched.
|
||||
|
||||
@ -2465,8 +2465,8 @@ expander last, on 6's unions.
|
||||
rather than a literal calling-convention parameter; both are stated as amendments in `docs/BUILT.md`. A user-written
|
||||
allocator is refused by name with milestone 5 as the reason.
|
||||
|
||||
~~2. **`(Vec T)`**~~ **Done**, over the type-erased runtime, with `push`, `reserve`, `at`, `len`, `slice`, `free` and
|
||||
`clone`, and with move-only enforced by a dead set that unions at an `if` or a `match` join. `at` and `len` were
|
||||
~~2. **`(Vec T)`**~~ **Done**, over the type-erased runtime, with `push`, `reserve`, `at`, `length`, `slice`, `free` and
|
||||
`clone`, and with move-only enforced by a dead set that unions at an `if` or a `match` join. `at` and `length` were
|
||||
extended rather than duplicated. The header is six words in *every* build, not four in release — a layout that
|
||||
changes with a build flag can disagree silently across the reload boundary — and that is the third amendment.
|
||||
Ownership is not transitive yet, so a struct field of `Vec` type, a global `Vec` and a `(Vec (Vec T))` are each
|
||||
@ -2481,7 +2481,7 @@ expander last, on 6's unions.
|
||||
restart; `exhausted-unhandled.flan` is the same failure with nothing handling it.
|
||||
|
||||
~~4. **`(Map K V)`**~~ **Done**, following Odin: open-addressed Robin Hood hashing at a 75% load factor, cache-line
|
||||
cell packing, and pointer-width integers through the probe loop. `map-new`, `put`, `get`, `has-key?`, and `len`,
|
||||
cell packing, and pointer-width integers through the probe loop. `map-new`, `put`, `get`, `has-key?`, and `length`,
|
||||
`reserve`, `clone` and `free` extended rather than duplicated. Two departures from Odin, both deliberate: no
|
||||
tombstones, because the spec defers removal, which deletes the backward-shift loop entirely; and no capacity tagged
|
||||
into the data pointer, because this header has room and tagging would make correctness depend on an alignment that
|
||||
@ -2848,7 +2848,7 @@ What follows is only the part that is still missing.
|
||||
`macros.flan` and `macro-unless.flan` were not added to it by landing them. `dune build --root . @sanitize` is
|
||||
clean as it stands; adding the two is a one-line edit in a file this lane did not own.
|
||||
|
||||
- **No `&rest` sugar.** A macro takes one parameter, the slice of forms at its call site, and `(len args)` is the
|
||||
- **No `&rest` sugar.** A macro takes one parameter, the slice of forms at its call site, and `(length args)` is the
|
||||
arity. That is deliberate — it is where variadics come from — but a `when` written against it reads worse than
|
||||
`parse.ml`'s version did.
|
||||
|
||||
|
||||
@ -4,8 +4,9 @@
|
||||
;;;; 3.5
|
||||
;;;;
|
||||
;;;; Chosen to be the smallest program that is still a real one. What it needs:
|
||||
;;;; functions, recursion, structs, (Ptr T) and `addr`, byte slices, `at`/`len`,
|
||||
;;;; `while`, `set` on locals and on fields, `cond`, `match`, Option, i32/u8/f64,
|
||||
;;;; functions, recursion, structs, (Ptr T) and `addr`, byte slices,
|
||||
;;;; `at`/`length`, `while`, `set` on locals and on fields, `cond`, `match`,
|
||||
;;;; Option, i32/u8/f64,
|
||||
;;;; and argv. What it deliberately does NOT need: an allocator, Vec, Map, any
|
||||
;;;; generic function, any macro the user wrote, FFI beyond argv and stdout,
|
||||
;;;; a window, or a frame loop. It runs headless, so it is the same test on
|
||||
@ -23,7 +24,7 @@
|
||||
pos i32]) ; no initialiser means zeroed
|
||||
|
||||
(defn peek [c (Ptr Cursor)] u8
|
||||
(if (< (.pos c) (len (.src c)))
|
||||
(if (< (.pos c) (length (.src c)))
|
||||
(at (.src c) (.pos c))
|
||||
0)) ; 0 doubles as end-of-input
|
||||
|
||||
@ -118,7 +119,7 @@
|
||||
;; argument's concrete type happens at compile time, so there is nothing to
|
||||
;; dispatch on at run time and no type to name at the call site.
|
||||
(defn main [args [string]] i32
|
||||
(if (< (len args) 2)
|
||||
(if (< (length args) 2)
|
||||
(do (println "usage: calc-me \"1 + 2 * 3\"") 1)
|
||||
(match (evaluate (bytes-view (at args 1)))
|
||||
(Some v) (do (print v) (println "") 0)
|
||||
|
||||
@ -15,7 +15,7 @@ of any `Field`. A move nested there is never recorded.
|
||||
|
||||
Confirmed by execution, two programs:
|
||||
- `(println (at rows (eat w)))` then `(free w)` — double free, exit 134.
|
||||
- Same shape with a move-only global `g`: accepted, `g` freed, `(len g)` reads a freed
|
||||
- Same shape with a move-only global `g`: accepted, `g` freed, `(length g)` reads a freed
|
||||
header, exit 0 silent. Defeats the rule test/test_flan.ml:1067-1068 pins.
|
||||
|
||||
Fix direction: narrow the flag to the target's own read — restore `ctx.borrow` for the
|
||||
|
||||
@ -47,7 +47,8 @@ without touching the generator.)
|
||||
| `(rand-int-range lo hi)` | an `i64` in `[lo, hi)`; an empty or reversed range answers `lo` |
|
||||
| `(rand-float-range lo hi)` | an `f64` in `[lo, hi)` |
|
||||
|
||||
An index is an `i32` (`len` answers one), so indexing with a draw reads `(at xs (i32 (rand-int-range 0 (i64 (len xs)))))`.
|
||||
An index is an `i32` (`length` answers one), so indexing with a draw reads
|
||||
`(at xs (i32 (rand-int-range 0 (i64 (length xs)))))`.
|
||||
|
||||
**It is predictable, and that is deliberate.** Getting 64 output bits out of 64 state bits costs reversibility: the
|
||||
permutation is a bijection, so anyone holding one result can work back to the state and know every number after it.
|
||||
@ -129,7 +130,7 @@ malloc failure, where the alternative is handing C a null pointer.
|
||||
and `collision-lines` answers with an `Option`; neither is raylib's own signature. A slice parameter in a `declare-c` is
|
||||
refused by name, because a slice's length crosses as i64 and the type of the C count parameter beside the pointer is not
|
||||
recoverable from `[T]` — so that one declares `(Ptr Vector2)` with an explicit `count i32` and the Flan wrapper passes
|
||||
`(addr (at points 0))` and `(len points)`. Every other refusal — an Option, a union, a fixed array, a map, a returned
|
||||
`(addr (at points 0))` and `(length points)`. Every other refusal — an Option, a union, a fixed array, a map, a returned
|
||||
string, a callback, an unknown type, an unrepresentable struct field, two Flan names for one C symbol — is by name with
|
||||
the reason, and the acceptance table asserts on the reasons.
|
||||
|
||||
@ -848,7 +849,7 @@ fact without cutting anything in half. See "The browser is the third target" bel
|
||||
- `(defconst gravity 0.05)` → `(defconst gravity f32 0.05)`. An untyped float constant is `f64`, `velocity` is `[f32]`,
|
||||
and `f64` into `f32` is a narrowing — still written, and still written after implicit widening landed (FIX.org
|
||||
2026-09-20), because widening is only the conversions that cannot change the number and this one can.
|
||||
- `(defonce current-color u32)` → `i32`. It is an index into `colors`, and `(len colors)` is an `i32`.
|
||||
- `(defonce current-color u32)` → `i32`. It is an index into `colors`, and `(length colors)` is an `i32`.
|
||||
- `(defn main [])` is unchanged — the short form, as plan.org says.
|
||||
|
||||
Painting is on **hold left mouse button** rather than on space, since the mouse bindings exist now. Space is still what
|
||||
@ -2445,7 +2446,7 @@ fires.
|
||||
| `(with-allocator a body...)` | rebinds for a dynamic extent and releases nothing |
|
||||
| `(vec-new T)` / `(vec-new T a)` / `(vec-new)` | a Vec, against the context or a named allocator |
|
||||
| `(push v x)` / `(reserve v n)` | `()`, both. **Either may reallocate, and any `[T]` view of this `Vec` is stale afterwards** — see below |
|
||||
| `(at v i)` / `(len v)` | the array names, extended — not a parallel pair |
|
||||
| `(at v i)` / `(length v)` | the array names, extended — not a parallel pair |
|
||||
| `(slice v)` / `(slice v lo)` / `(slice v lo hi)` | a non-owning `[T]` view — the array names again, extended |
|
||||
| `(clone v)` / `(clone v a)` | the only copy; assignment moves |
|
||||
| `(free v)` | consumes its argument |
|
||||
@ -2464,7 +2465,7 @@ one rule: take the view again after the `push`.
|
||||
One thing it is *not*: a dangle. `(slice (make-vec))` is legal, and the view it answers reads what it says it reads —
|
||||
the storage a returned `Vec` owns outlives the expression. What does not survive is the header, which was a
|
||||
temporary, and it was the only handle `free` could have taken, so that block is leaked unless the allocator reclaims
|
||||
it wholesale at `free-all` or destroy. That is the same leak `(len (mk))` and `(at (mk) 0)` already write, and the
|
||||
it wholesale at `free-all` or destroy. That is the same leak `(length (mk))` and `(at (mk) 0)` already write, and the
|
||||
same one `spec-memory.md` names when a global `Vec` is overwritten: manual memory management, and the program's
|
||||
business. `(slice (mk))` over a fixed **array** is refused, and the difference is worth being exact about — that one
|
||||
is a view into a frame that is gone, so it answers whatever the frame was reused for. A wrong answer is not a leak.
|
||||
@ -2531,10 +2532,24 @@ known — which without generics is simply the concrete call site — and passed
|
||||
- `AddrOf` of any expression, place or not, because the element a `push` copies may be computed. The backend already
|
||||
spilled a non-place to a temporary for exactly this.
|
||||
|
||||
`at` and `len` were already the names for a fixed array and a slice, so a Vec extends them rather than adding a
|
||||
`at` and `length` were already the names for a fixed array and a slice, so a Vec extends them rather than adding a
|
||||
parallel pair — the asymmetry `nth` was removed for. The value form `(at v i)` and the place form `(set (at v i) x)` go
|
||||
through one helper, so they cannot drift apart the way `nth` did.
|
||||
|
||||
**The word is `length` and not `len`, so that `len` is a name a program can have.** Shadowing makes a `(defn len ...)`
|
||||
legal and `builtin/len` reaches past one, so the call position was already recoverable — but at the price of a warning
|
||||
at the definition and a qualifier at every inner call. With no builtin under the short name there is neither: `len` is
|
||||
an ordinary identifier in every position, which is what `(let [len (length xs)] ...)` wants. `length` takes over as
|
||||
the worked example wherever shadowing is demonstrated. A call to a `len` nothing defines is answered where an unknown
|
||||
function is answered — after every table and after the shadowing guard, so a program with its own `len` never reaches
|
||||
it — and the sentence is said rather than guessed at, because `len` and `length` are three edits apart and the
|
||||
did-you-mean's net is one. It writes the reader's own argument back out through `spell_arg`, the same spelling
|
||||
`as-slice`'s refusal uses: a name is its name, an integer its digits, anything with structure inside it becomes a
|
||||
stand-in. Only at one argument, though — `length` takes exactly one, so writing three of them back out would produce
|
||||
a suggestion refused a second time the moment it was pasted; at any other arity the shape `(length v)` is suggested
|
||||
instead. `as-slice` can spell every argument because `slice` takes one to three. A printed suggestion has to compile,
|
||||
and the arity is half of what that costs.
|
||||
|
||||
A Vec's length and index are `i32`, like every other length here. Widening indices is one change across every
|
||||
container and not a Vec question.
|
||||
|
||||
@ -2556,8 +2571,8 @@ what was built and why. Move-only as a *type* property (what may be copied, what
|
||||
|
||||
Reading a move-only local is a move unless the site said it was a borrow. That is the conservative direction: passing
|
||||
one to a function, binding it, returning it and `free`ing it are all moves and all reach one place, and the handful of
|
||||
operations that only look at a container (`at`, `len`, `slice`, `push`, `reserve`, `clone`) say so. Only a
|
||||
*syntactically simple* target counts as a borrow — in `(len (f v))` the call still moves `v`.
|
||||
operations that only look at a container (`at`, `length`, `slice`, `push`, `reserve`, `clone`) say so. Only a
|
||||
*syntactically simple* target counts as a borrow — in `(length (f v))` the call still moves `v`.
|
||||
|
||||
At an `if` and at a `match`, every arm is checked from the state before the form and the **union** of what they moved
|
||||
survives the join. A flat set is wrong in both directions: it refuses `(if c (free v) (free v))`, which is legal, and it
|
||||
@ -2584,7 +2599,7 @@ three shapes are refused where they are declared, each naming `drop`:
|
||||
duplicate inner headers instead of copying what they own and `free` would drop their buffers.
|
||||
|
||||
And a `Vec` does not cross to C: handing a header that owns storage to C hands out an owner. `(slice v)` as
|
||||
`(Ptr T)` plus `(len v)` is the shape that does cross, and the refusal says so.
|
||||
`(Ptr T)` plus `(length v)` is the shape that does cross, and the refusal says so.
|
||||
|
||||
### `StorageExhausted` went in *with* `Vec`, not after it
|
||||
|
||||
@ -2726,15 +2741,15 @@ calls per field, which has no channel to hand on.
|
||||
| `(get m k)` | `(Option V)` — absence is `None` |
|
||||
| `(has-key? m k)` | `bool`, copying no value — **an addition; the spec does not name it** |
|
||||
| `(map-remove m k)` | `(Option V)` — the value that was there, or `None` |
|
||||
| `(len m)` `(reserve m n)` `(clone m)` `(clone m a)` `(free m)` | extended, not duplicated |
|
||||
| `(length m)` `(reserve m n)` `(clone m)` `(clone m a)` `(free m)` | extended, not duplicated |
|
||||
|
||||
`has-key?` is **not in `spec-memory.md`** and is an addition, flagged because everything else here is the spec's.
|
||||
`(get m k)` answers the same question, but through an `Option` the caller then has to match, and the common use is a
|
||||
condition. It copies no value, which is also why it is not just `get` with the result thrown away. The `?` suffix
|
||||
follows `can-free?`.
|
||||
|
||||
`len`, `reserve`, `clone` and `free` were **extended rather than given map-shaped names of their own**, which is what
|
||||
`at` and `len` already did for `Vec`: one question, one word. `reserve`'s `n` is entries, not slots — the runtime sizes
|
||||
`length`, `reserve`, `clone` and `free` were **extended rather than given map-shaped names of their own**, which is what
|
||||
`at` and `length` already did for `Vec`: one question, one word. `reserve`'s `n` is entries, not slots — the runtime sizes
|
||||
the block so `n` still sits under the load factor, which is the only reading of "room for n" that does not reallocate
|
||||
on the nth put.
|
||||
|
||||
@ -3052,15 +3067,15 @@ reintroduces the silent-wrong-answer mode the handle just removed for anyone who
|
||||
insert. Chunked never-moving storage is the fix and it costs code; taking the contract is the smaller correct thing,
|
||||
given `slice` already established it.
|
||||
|
||||
### `len` is the slot high-water and `live` is the count, in that direction
|
||||
### `length` is the slot high-water and `live` is the count, in that direction
|
||||
|
||||
`(len p)` is how many slots have ever been handed out. `(live p)` is how many of them are live now. It had to be that
|
||||
way round: `0..(len p)` are the indices `(pool-handle p i)` accepts, so a loop bounded by `len` visits every live
|
||||
`(length p)` is how many slots have ever been handed out. `(live p)` is how many of them are live now. It had to be that
|
||||
way round: `0..(length p)` are the indices `(pool-handle p i)` accepts, so a loop bounded by `length` visits every live
|
||||
entry. Bounded by the live count instead, it would silently skip entries the moment anything had been released —
|
||||
which is exactly the quiet wrong answer the whole type exists to remove.
|
||||
|
||||
`(pool-handle p i)` answers `(Option (Handle T))`: the handle of slot `i`, or `None` if that slot is dead. That plus
|
||||
`len` is the whole of iteration. An index outside `0..(len p)` **traps**, exactly as `(at v i)` traps: an index is an
|
||||
`length` is the whole of iteration. An index outside `0..(length p)` **traps**, exactly as `(at v i)` traps: an index is an
|
||||
index here, and answering `None` for one would hide a bug rather than a death.
|
||||
|
||||
### A slot is released through the pool, and that is not a third release point
|
||||
@ -3129,7 +3144,7 @@ the tree.
|
||||
### What classes still need
|
||||
|
||||
The enumeration primitive is the piece migration was blocked on, and it exists now. What is left is `defclass` itself
|
||||
and its runtime shape metadata; `migrate-instances`, which is a walk over `(len p)` and `(pool-handle p i)`; generic
|
||||
and its runtime shape metadata; `migrate-instances`, which is a walk over `(length p)` and `(pool-handle p i)`; generic
|
||||
functions and method dispatch, whose expensive half is already built and tested (a generic function is an indirection
|
||||
cell whose body is a dispatch table, and a reload extends the table); and the rule that `Enemy@1` stays resolvable for
|
||||
as long as any instance holds it — the same rule as "nothing is ever `dlclose`d".
|
||||
@ -3455,10 +3470,10 @@ is. So `into` collects, and a reducing macro of the same shape is a separate for
|
||||
which says *push takes a (Vec T)* and names the real problem. There is no second lowering and no reason to invent
|
||||
one before something asks.
|
||||
- **A source that is already a name is used as it is; anything else is bound to a gensym.** Both halves are needed and
|
||||
neither is cosmetic. Binding is what a source that is a *call* needs — `(len s)` and `(at s i)` have to be the same
|
||||
neither is cosmetic. Binding is what a source that is a *call* needs — `(length s)` and `(at s i)` have to be the same
|
||||
`s`, or the call is made once per element. Not binding a name is what everything else needs: a `(Vec T)` is
|
||||
move-only, so `(let [s v] ...)` would hand the caller's `v` to a binding it cannot see and `v` would be dead after
|
||||
an `into` that only read it; and a fixed array would be *copied* into the binding, once per `into`. `len` and `at`
|
||||
an `into` that only read it; and a fixed array would be *copied* into the binding, once per `into`. `length` and `at`
|
||||
borrow, so used directly the source is only read.
|
||||
- **An owning temporary as the source leaks**, and this is the wart. `(into (make-a-vec) ...)` binds the result to a
|
||||
name the caller cannot reach and therefore cannot free. The macro cannot know whether the type owns anything. A call
|
||||
@ -3586,8 +3601,8 @@ array *literal* of two elements, whose second element is a type name nothing dec
|
||||
declaration written the other way and either spelling can be the parameter — `array-ctor.flan` asserts exactly that.
|
||||
|
||||
It is a parser form and not a builtin call, because the second argument is a *type* and there are no types in the
|
||||
parser's callers. `Parse` assembles the whole `Tarray (len COUNT, TYPE)` itself, which is why the count takes a
|
||||
constant's name for free — `len` is the same function `[n T]` goes through — and why a non-type second argument is
|
||||
parser's callers. `Parse` assembles the whole `Tarray (length COUNT, TYPE)` itself, which is why the count takes a
|
||||
constant's name for free — `length` is the same function `[n T]` goes through — and why a non-type second argument is
|
||||
refused by the type reader's own message rather than as an unknown name. The checker resolves it and hands back
|
||||
`Tast.Zero`, the same node a declaration with no initialiser gets. There is no new backend node and no new type.
|
||||
|
||||
@ -4017,7 +4032,7 @@ an ordinary prelude function over a `[EmbedFile]`. A directory embed is tens of
|
||||
`.rodata`; a compile-time perfect hash would be a build-time map with its own failure modes that nothing has asked for,
|
||||
and sort-and-bisect is the next step if a program ever embeds thousands of files — it would not change the type. It
|
||||
takes a **slice** rather than the array, because an array's length is part of its type and there are no generics, so
|
||||
the call reads `(embed-find (slice assets 0 (len assets)) "brush.png")`. Entries are sorted by name because `readdir`
|
||||
the call reads `(embed-find (slice assets 0 (length assets)) "brush.png")`. Entries are sorted by name because `readdir`
|
||||
order is filesystem-dependent and an unsorted embed would make two builds of identical sources emit different `.ll`.
|
||||
Non-recursive, files only — Odin again.
|
||||
|
||||
@ -5939,7 +5954,7 @@ they are not. It stays behind `f.md.checks`.
|
||||
|
||||
`lo <= hi` is **not a bounds check**, and the old comment beside it said as much while the
|
||||
code did the opposite. A slice is `{ptr, i64}` and the `i64` is a count: the emitted
|
||||
`sub` computes it as `hi - lo`, and the `len` primitive, a re-slice, `flan_write_stdout`
|
||||
`sub` computes it as `hi - lo`, and the `length` primitive, a re-slice, `flan_write_stdout`
|
||||
and any C the value is handed to all read it as a non-negative number of elements.
|
||||
`(slice s 2 1)` does not build an out-of-range slice, it builds a value that is not a
|
||||
slice — the length word holds -1, which as a count is 18446744073709551615. No build
|
||||
@ -6176,7 +6191,7 @@ for before — and `@sanitize` is clean over both programs.
|
||||
|
||||
`(slice a)` is the whole of `a`, `(slice a n)` is the tail from `n`, and `(slice a n m)` is the half-open range it
|
||||
has always been. The two short forms exist because a fixed array does not decay to a slice at a call: passing
|
||||
`[6 2 4 9 1 9 4 5]` to the prelude's `sort`, which takes `[$t]`, used to require `(slice a 0 (len a))` written out
|
||||
`[6 2 4 9 1 9 4 5]` to the prelude's `sort`, which takes `[$t]`, used to require `(slice a 0 (length a))` written out
|
||||
at every call site.
|
||||
|
||||
```flan
|
||||
@ -6186,7 +6201,7 @@ at every call site.
|
||||
|
||||
**They are one node, not three.** `check.ml` fills the missing arguments in and hands the backends the
|
||||
three-argument form. The implicit `lo` is the literal 0. The implicit `hi` is the literal length when the target is
|
||||
a fixed array — the same constant `(len a)` folds to — and the `Len` primitive otherwise, which reads the length
|
||||
a fixed array — the same constant `(length a)` folds to — and the `Len` primitive otherwise, which reads the length
|
||||
word a slice or a string is already carrying. So the short spelling costs exactly what the long one costs, the
|
||||
static refusals apply to it unchanged (a literal `lo` past the end of a fixed array is an error at compile time,
|
||||
not a trap), and the runtime bounds check is the same check on the same numbers. Neither backend grew an arity
|
||||
|
||||
@ -712,7 +712,7 @@ It does not crash, which is the bad part. Run headless:
|
||||
(let [text "いろはに"
|
||||
b (bytes text)
|
||||
sz 0]
|
||||
(println (rl/get-codepoint-previous (string (slice b 3 (len b))) (addr sz)))
|
||||
(println (rl/get-codepoint-previous (string (slice b 3 (length b))) (addr sz)))
|
||||
(println sz))
|
||||
```
|
||||
|
||||
@ -839,7 +839,7 @@ character is — a `string` is bytes and `(bytes s)` / `(string b)` say so in bo
|
||||
at no cost — and for this job that is exactly the right amount of opinion. There is a
|
||||
`valid-utf8?` in the prelude and this example never needs it.
|
||||
|
||||
**An interior pointer into a string has an idiom already.** `(string (slice b off (len b)))`
|
||||
**An interior pointer into a string has an idiom already.** `(string (slice b off (length b)))`
|
||||
is the C's `char *ptr` and compiles to nothing: a `string` and a `[u8]` are the same two
|
||||
words. Every forward-reading `const char *` entry point is reachable that way.
|
||||
|
||||
|
||||
@ -299,9 +299,9 @@ actually arises — a cycle compared against itself. Two *distinct* cyclic vecs
|
||||
false, which is a wrong answer to a question nobody has asked yet; the honest fix is a visited set and it
|
||||
can be added the day somebody needs it.
|
||||
|
||||
### `len`, `at`, `set-at`, `push`
|
||||
### `length`, `at`, `set-at`, `push`
|
||||
|
||||
`len` is bytes of a text or elements of a vec. `at` is an element of a vec or a byte of a text as an int
|
||||
`length` is bytes of a text or elements of a vec. `at` is an element of a vec or a byte of a text as an int
|
||||
— which is what `(at s i)` on a `(Slice u8)` does in the typed language; codepoints are `utf8`'s job and
|
||||
stay there. `set-at` and `push` are vec-only, and a `set-at` on a text says *"a text is immutable — build
|
||||
another one"* rather than quietly copying.
|
||||
@ -386,7 +386,7 @@ rather than discovered later. Two ways out, both the compiler's and neither this
|
||||
`flan_dyn_need_f64` — which sees only a tag and could not tell a literal-derived int from a computed
|
||||
one.
|
||||
|
||||
Softening the boundary itself is the option not to take: it would accept `(g (len xs))` as readily as
|
||||
Softening the boundary itself is the option not to take: it would accept `(g (length xs))` as readily as
|
||||
`(g 1)`, and those are not the same mistake.
|
||||
|
||||
---
|
||||
@ -470,7 +470,7 @@ No microbenchmarks; these are counts of what the code does.
|
||||
| `div`/`rem` | the above plus a zero test and an overflow test |
|
||||
| `lt`/`le`/`gt`/`ge` | two tag tests and a compare; text is `memcmp` |
|
||||
| `eq` | word compare first; then tags, then bytes or elements — O(size) at worst |
|
||||
| `len` | tag test and a load |
|
||||
| `length` | tag test and a load |
|
||||
| `at` | two tag tests, a bounds compare, a load |
|
||||
| `set_at` | the same, plus a store; **no write barrier**, because there is no generation to have one for |
|
||||
| `push` | tag test, a capacity test, amortised O(1); the doubling is a `realloc` |
|
||||
|
||||
@ -81,7 +81,7 @@ is the `is_polymorphic_type_assignable` walk rather than a name match.
|
||||
|
||||
(defn sort-by! [s [$t] before? (Fn [$t $t] bool)] ()
|
||||
(let [i 1]
|
||||
(while (< i (len s))
|
||||
(while (< i (length s))
|
||||
(let [j i]
|
||||
(while (and (> j 0) (before? (at s j) (at s (- j 1))))
|
||||
(swap! s (- j 1) j)
|
||||
|
||||
@ -77,7 +77,7 @@ Four commits, each one a working state:
|
||||
`flan generate-c`. A hand-written `declare-c` that disagrees with `raylib-5.5.h` therefore
|
||||
breaks every build in the tree, not just regeneration. That is what makes open question 2
|
||||
a blocker rather than an inconvenience.
|
||||
- `(string (slice b off (len b)))` is the idiom for a C `char *` cursor into the middle of a
|
||||
- `(string (slice b off (length b)))` is the idiom for a C `char *` cursor into the middle of a
|
||||
string, and it costs nothing. It is correct for every entry point that reads forwards and
|
||||
wrong for every one that reads backwards.
|
||||
- `slice-from-ptr` is how a raylib pointer-plus-count becomes something with a length. Both
|
||||
|
||||
@ -173,7 +173,7 @@ The expression before point is compiled, run **inside the running program**, and
|
||||
its value shown as `=> 2` at the end of the line the form is on. Not a copy of
|
||||
the program, not a simulation — the actual process, with its actual state.
|
||||
|
||||
So in a game you can type `(len enemies)` and get the real number.
|
||||
So in a game you can type `(length enemies)` and get the real number.
|
||||
|
||||
**Where it appears.** At the end of the *line*, which is where the watch buffer's
|
||||
ghost text goes too — the two are the same thing seen twice and they are drawn
|
||||
@ -405,7 +405,7 @@ everything it had is still there.
|
||||
get its fields, one per line. It takes the innermost thing point is on rather
|
||||
than only the one behind point, so the middle of a name works as well as the end
|
||||
of one — point anywhere in `enemies` takes `enemies`, and on the open paren of
|
||||
`(len enemies)` takes the whole call.
|
||||
`(length enemies)` takes the whole call.
|
||||
|
||||
**`C-u C-c C-i`** opens the minibuffer instead, pre-filled with whatever was at
|
||||
point — for inspecting something that is not written in the buffer, or is
|
||||
|
||||
@ -689,7 +689,7 @@ form is taken back from there; on an opening delimiter the form that opens
|
||||
there is taken instead, which is the rule `flan-macroexpand\=' follows for the
|
||||
same reason.
|
||||
|
||||
Innermost, so point on `enemies\=' inside `(len enemies)\=' takes `enemies\=' and
|
||||
Innermost, so point on `enemies\=' inside `(length enemies)\=' takes `enemies\=' and
|
||||
not the call — which is the right answer for a command that shows you one
|
||||
value, and the enclosing call is a paren away in either direction.
|
||||
|
||||
|
||||
@ -131,7 +131,7 @@
|
||||
"loop" "recur" "break" "continue" "match" "set" "return" "fn" "array"
|
||||
"defer" "some" "none" "try" "signal" "error"
|
||||
"handler-bind" "handler-case" "restart-case" "invoke-restart"
|
||||
"zeroed" "uninit" "slice" "at" "len" "addr"
|
||||
"zeroed" "uninit" "slice" "at" "length" "addr"
|
||||
"bytes" "cast" "true" "false" "nil" "print" "println")
|
||||
"Forms with meaning to the checker.
|
||||
|
||||
|
||||
@ -111,7 +111,7 @@
|
||||
;; of a raylib example a headless case can assert.
|
||||
(defn collect-unique [codepoints [i32]] ()
|
||||
(set unique-count 0)
|
||||
(dotimes [i (len codepoints)]
|
||||
(dotimes [i (length codepoints)]
|
||||
(let [cp (at codepoints i)]
|
||||
(when (and (not (seen? cp unique-count))
|
||||
(< unique-count max-codepoints))
|
||||
@ -126,16 +126,16 @@
|
||||
|
||||
;; The codepoint starting at `off`, and its size in bytes through `size-out`.
|
||||
;;
|
||||
;; `(string (slice b off (len b)))` is the whole of what the C's `ptr` is: the
|
||||
;; tail of the text from here on. It costs nothing to say — a `string` and a
|
||||
;; `(string (slice b off (length b)))` is the whole of what the C's `ptr`
|
||||
;; is: the tail of the text from here on. It costs nothing to say — a `string` and a
|
||||
;; `[u8]` are the same two words — and the shim NUL-terminates a copy of it
|
||||
;; for the duration of the call, which is all GetCodepointNext wants, because
|
||||
;; it only ever reads forwards.
|
||||
(defn codepoint-at [off i32 size-out (Ptr i32)] i32
|
||||
(let [b (bytes-view text)]
|
||||
(if (>= off (len b))
|
||||
(if (>= off (length b))
|
||||
0
|
||||
(rl/get-codepoint-next (string (slice b off (len b))) size-out))))
|
||||
(rl/get-codepoint-next (string (slice b off (length b))) size-out))))
|
||||
|
||||
;; One codepoint forward, clamped at the end.
|
||||
;;
|
||||
@ -146,10 +146,10 @@
|
||||
(defn step-forward [off i32] i32
|
||||
(let [size 0
|
||||
b (bytes-view text)]
|
||||
(if (>= off (len b))
|
||||
(if (>= off (length b))
|
||||
off
|
||||
(do (codepoint-at off (addr size))
|
||||
(if (>= (+ off size) (len b)) off (+ off size))))))
|
||||
(if (>= (+ off size) (length b)) off (+ off size))))))
|
||||
|
||||
;; One codepoint back, which is GetCodepointPrevious and no longer a stand-in
|
||||
;; for it.
|
||||
|
||||
@ -68,7 +68,7 @@
|
||||
tint rl/Color] ()
|
||||
(let [glyphs (rl/font-glyphs font)
|
||||
recs (rl/font-recs font)
|
||||
length (i32 (len text))
|
||||
length (i32 (length text))
|
||||
scale (/ font-size (f32 (.base-size font)))
|
||||
line-h (* (f32 (+ (.base-size font) (/ (.base-size font) 2))) scale)
|
||||
off-x (f32 0.0)
|
||||
|
||||
@ -67,7 +67,7 @@
|
||||
;; One character every ten frames. The clamp is the whole difference from
|
||||
;; the C — see the header comment.
|
||||
(let [b (bytes-view message)
|
||||
n (min (i32 (len b)) (/ frames-counter 10))]
|
||||
n (min (i32 (length b)) (/ frames-counter 10))]
|
||||
(rl/draw-text (string (slice b 0 n)) 210 160 20 rl/maroon))
|
||||
|
||||
(rl/draw-text "PRESS [ENTER] to RESTART!" 240 260 20 rl/lightgray)
|
||||
|
||||
113
lib/check.ml
113
lib/check.ml
@ -329,15 +329,15 @@ let builtin_names : string list ref = ref []
|
||||
let builtin_set : (string, unit) Hashtbl.t = Hashtbl.create 128
|
||||
|
||||
(* ── builtin/, the reserved qualifier ──────────────────────────────────
|
||||
[builtin/len] is the builtin [len], whatever else the program has decided
|
||||
[len] means. It is the way out of the dead end shadowing used to leave: a
|
||||
[(defn len ...)] takes the bare name over for its whole file, and before
|
||||
[builtin/length] is the builtin [length], whatever else the program has
|
||||
decided [length] means. It is the way out of the dead end shadowing used to leave: a
|
||||
[(defn length ...)] takes the bare name over for its whole file, and before
|
||||
this there was no remaining spelling for the thing it was wrapping, so the
|
||||
wrapper was unbounded recursion instead.
|
||||
|
||||
The spelling is the package qualifier's, deliberately. A reader who knows
|
||||
that [rl/draw-fps] is [draw-fps] from the package imported as [rl] already
|
||||
knows what [builtin/len] is, and needs no second syntax to learn. What
|
||||
knows what [builtin/length] is, and needs no second syntax to learn. What
|
||||
makes it work is that [builtin] is reserved rather than resolved: [Load]'s
|
||||
qualifier comes from the alias in an [import] form and from nowhere else,
|
||||
so refusing that one alias ([Load.reserved_alias]) is the whole of keeping
|
||||
@ -364,7 +364,7 @@ let not_a_builtin loc bare =
|
||||
if bare = "" then
|
||||
Loc.failk "check/unknown-builtin" loc
|
||||
"%s needs a name after it — the qualifier reaches a builtin, as \
|
||||
(%slen v)" builtin_prefix builtin_prefix
|
||||
(%slength v)" builtin_prefix builtin_prefix
|
||||
else
|
||||
match nearest !builtin_names bare with
|
||||
| Some m ->
|
||||
@ -378,6 +378,24 @@ let not_a_builtin loc bare =
|
||||
function is called by the name it was defined under"
|
||||
bare builtin_prefix bare builtin_prefix
|
||||
|
||||
(* One argument of a call, written back out as source. A name is its name and
|
||||
an integer is its digits; anything with structure inside it — a call, a
|
||||
field, an index — becomes the stand-in the caller supplies, because a
|
||||
suggestion with a hole in it is worse than one that names its blank. Shared
|
||||
by the refusals that answer a name nothing defines by writing the call the
|
||||
reader should have written.
|
||||
|
||||
This spells one argument and says nothing about how many there are. Whether
|
||||
the result compiles is the caller's to arrange: a call site that writes out
|
||||
every argument it was given is only honest where the name it is suggesting
|
||||
takes that many, so the caller either knows the arity matches or falls back
|
||||
to a shape of its own. *)
|
||||
let spell_arg stand_for (a : Ast.expr) =
|
||||
match a.Ast.e with
|
||||
| Ast.Var v -> v
|
||||
| Ast.Int n -> Int64.to_string n
|
||||
| _ -> stand_for
|
||||
|
||||
(* What a [break] or a [continue] may be talking about, innermost first.
|
||||
|
||||
[Lloop] is a loop it is lexically inside, carrying its label if it was given
|
||||
@ -3364,7 +3382,7 @@ and var ctx ?(qualified = false) loc ~want name =
|
||||
|
||||
A qualified name that gets past these arms must be refused and not
|
||||
handed on to the tables below, which is the whole difference between
|
||||
this and the call path. Falling through would look up [len] in
|
||||
this and the call path. Falling through would look up [length] in
|
||||
[env.fns] and answer with the address of the very definition the reader
|
||||
wrote [builtin/] to get away from — the feature inverted, silently. So
|
||||
the catch-all arm below asks [qualified] before it looks anything up. *)
|
||||
@ -3411,7 +3429,7 @@ and var ctx ?(qualified = false) loc ~want name =
|
||||
arms above this one, so it is a builtin that exists only as a call.
|
||||
There is no value to hand back: a builtin is an arm in the compiler,
|
||||
not a function in the program, so it has no address for a [Tast.FnAddr]
|
||||
to carry. Bare [len] in this position says "unknown name"; this says
|
||||
to carry. Bare [length] in this position says "unknown name"; this says
|
||||
the true thing instead, which is that the name is real and the position
|
||||
is wrong. *)
|
||||
fail loc
|
||||
@ -6165,7 +6183,7 @@ and vec_at ctx loc (target : Tast.expr) (idx : Ast.expr list) =
|
||||
it says it reads. What a returned Vec loses is the owner, and losing the
|
||||
owner is a leak, which this language has already decided is defined
|
||||
behaviour (spec-memory.md on overwriting a global Vec: "overwrites the
|
||||
first block and leaks it; there is no drop"). [(len (mk))] and
|
||||
first block and leaks it; there is no drop"). [(length (mk))] and
|
||||
[(at (mk) 0)] lose exactly the same owner and are accepted; refusing the
|
||||
third of those three would be a rule about one spelling rather than about
|
||||
a hazard, and under a region allocator there is nothing to leak at all. *)
|
||||
@ -6222,17 +6240,17 @@ and vec_slice ctx ~want loc (target : Tast.expr) elem (bounds : Ast.expr list) =
|
||||
and named_call ?(qualified = false) ctx ~want loc name args =
|
||||
let prim p ty args = expect ctx loc ~want (mk loc ty (Tast.Prim (p, args))) in
|
||||
match name with
|
||||
(* [builtin/len], before anything else including the shadowing guard below.
|
||||
(* [builtin/length], before anything else including the shadowing guard below.
|
||||
The prefix is stripped and the same dispatch runs again with [qualified],
|
||||
which is the one thing the guard consults: a qualified call has said
|
||||
which of the two it means, so there is nothing left for shadowing to
|
||||
decide. Everything after this point sees the bare name, so an arity or a
|
||||
type refusal on [(builtin/len 1 2)] reads exactly as it does on
|
||||
[(len 1 2)] — which is the point of the spelling, not a loss of detail.
|
||||
type refusal on [(builtin/length 1 2)] reads exactly as it does on
|
||||
[(length 1 2)] — which is the point of the spelling, not a loss of detail.
|
||||
|
||||
Recursion rather than a flag threaded through the arms because there is
|
||||
only one thing to skip. It cannot loop: the stripped name has no second
|
||||
[builtin/] on it unless somebody wrote [builtin/builtin/len], which is
|
||||
[builtin/] on it unless somebody wrote [builtin/builtin/length], which is
|
||||
stripped once and then refused by name. *)
|
||||
| _ when not qualified && qualified_builtin name <> None ->
|
||||
let bare = Option.get (qualified_builtin name) in
|
||||
@ -6643,7 +6661,7 @@ and named_call ?(qualified = false) ctx ~want loc name args =
|
||||
fail loc
|
||||
"a pattern cannot destructure %s — a slice's length is not known \
|
||||
until the program runs, so nothing here can check it has %Ld \
|
||||
element%s. Use (at s i) and test (len s) yourself"
|
||||
element%s. Use (at s i) and test (length s) yourself"
|
||||
(Types.to_string target.Tast.ty) n (plural n)
|
||||
| other ->
|
||||
fail loc
|
||||
@ -6809,7 +6827,7 @@ and named_call ?(qualified = false) ctx ~want loc name args =
|
||||
(* [(vec-new dyn)] is not a [(Vec dyn)]. At milestone 1 the heterogeneous
|
||||
container is the dyn runtime's own object, and its type is [dyn] like
|
||||
everything else the runtime hands back — which is what lets [push], [at]
|
||||
and [len] on it go through the dyn operations rather than through a
|
||||
and [length] on it go through the dyn operations rather than through a
|
||||
type-erased Vec over eight-byte elements.
|
||||
|
||||
The two could be made to coincide later, and the reason not to now is
|
||||
@ -7596,12 +7614,12 @@ and named_call ?(qualified = false) ctx ~want loc name args =
|
||||
| _ -> assert false)
|
||||
|
||||
(* ── containers ────────────────────────────────────────────────── *)
|
||||
(* [at] and [len] were already the names for a fixed array and a slice, so a
|
||||
Vec extends them rather than adding a parallel pair — which is the
|
||||
(* [at] and [length] were already the names for a fixed array and a slice,
|
||||
so a Vec extends them rather than adding a parallel pair — which is the
|
||||
asymmetry [nth] was removed for. A Vec's length is i32 like every other
|
||||
length here (index_ty): widening indices is one change across all of them
|
||||
and not a Vec question. *)
|
||||
| "len" ->
|
||||
| "length" ->
|
||||
arity ctx loc name 1 args;
|
||||
let target = List.hd args in
|
||||
let a = check ctx target in
|
||||
@ -7612,21 +7630,21 @@ and named_call ?(qualified = false) ctx ~want loc name args =
|
||||
let n = rt loc (Types.Int Types.I64) "flan_vec_len" [ a; here loc ] in
|
||||
expect ctx loc ~want (mk loc index_ty (Tast.Prim (Tast.Cast index_ty, [ n ])))
|
||||
(* Extended rather than given a name of its own, for the reason [at] and
|
||||
[len] were extended over Vec: one question, one word. *)
|
||||
[length] were extended over Vec: one question, one word. *)
|
||||
| Types.Map _ ->
|
||||
let n = rt loc (Types.Int Types.I64) "flan_map_len" [ a; here loc ] in
|
||||
expect ctx loc ~want (mk loc index_ty (Tast.Prim (Tast.Cast index_ty, [ n ])))
|
||||
(* A dyn length is an i32 like every other length here, not a dyn holding
|
||||
one. [len] is what an index loop compares against, and handing back a
|
||||
boxed number would make [(< i (len xs))] a dyn comparison and a pair of
|
||||
allocations per iteration. The runtime answers a dyn; it is unboxed at
|
||||
one. [length] is what an index loop compares against, and handing back a
|
||||
boxed number would make [(< i (length xs))] a dyn comparison and a
|
||||
pair of allocations per iteration. The runtime answers a dyn; it is unboxed at
|
||||
once and narrowed the way the Vec's i64 above is. *)
|
||||
| Types.Dyn ->
|
||||
let n = unbox loc (Types.Int Types.I64) (rt loc Types.Dyn "flan_dyn_len" [ a ]) in
|
||||
expect ctx loc ~want (mk loc index_ty (Tast.Prim (Tast.Cast index_ty, [ n ])))
|
||||
| other ->
|
||||
fail loc
|
||||
"len takes an array, a slice, a string, a Vec or a Map, found %s"
|
||||
"length takes an array, a slice, a string, a Vec or a Map, found %s"
|
||||
(Types.to_string other))
|
||||
| "at" ->
|
||||
(match args with
|
||||
@ -7657,7 +7675,7 @@ and named_call ?(qualified = false) ctx ~want loc name args =
|
||||
this line: same node, same checks, same code. Nothing is added at run
|
||||
time, because neither missing argument needs anything computed —
|
||||
[lo] is 0, and [hi] is the length, which on a fixed array is the
|
||||
constant [len] already folds to and on a slice or a string is the
|
||||
constant [length] already folds to and on a slice or a string is the
|
||||
length word the value is carrying anyway.
|
||||
|
||||
The target is read twice when [hi] is the implicit length, so a target
|
||||
@ -8345,16 +8363,10 @@ and ordinary_call ctx ~want loc name args =
|
||||
says which. Asked here, after every table, so that a program that
|
||||
defines an [as-slice] of its own still reaches its own. *)
|
||||
(* The call is written back out rather than described, and every
|
||||
argument the reader wrote that can be spelled is spelled. One that
|
||||
cannot — a call, a field, anything with structure — becomes the
|
||||
name it stands for, so the suggestion is always a form that
|
||||
compiles rather than a form with a hole in it. *)
|
||||
let spell stand_for (a : Ast.expr) =
|
||||
match a.Ast.e with
|
||||
| Ast.Var v -> v
|
||||
| Ast.Int n -> Int64.to_string n
|
||||
| _ -> stand_for
|
||||
in
|
||||
argument the reader wrote that can be spelled is spelled
|
||||
([spell_arg]), so the suggestion is always a form that compiles
|
||||
rather than a form with a hole in it. *)
|
||||
let spell = spell_arg in
|
||||
let call =
|
||||
match args with
|
||||
| [] -> "(slice v)"
|
||||
@ -8379,6 +8391,31 @@ and ordinary_call ctx ~want loc name args =
|
||||
what to write, and the line in [no_such_rand] does. *)
|
||||
Loc.failk "check/unknown-function" loc "%s"
|
||||
(Option.get (no_such_rand name))
|
||||
else if name = "len" then
|
||||
(* [len] is an ordinary name and the count is [length], so this is the
|
||||
one sentence a program that reached for the short word needs. It is
|
||||
said rather than guessed at because the did-you-mean below cannot
|
||||
reach it: [len] and [length] are three edits apart, and the net is
|
||||
one. Asked here, after every table and after the shadowing guard at
|
||||
the head of the dispatch, so a program that defines a [len] of its
|
||||
own reaches its own — this is only ever the answer for a name that
|
||||
nothing in the program has taken.
|
||||
|
||||
The reader's own argument is spelled back only when there is one of
|
||||
it. [length] takes exactly one, so writing three of them out would
|
||||
produce a suggestion that is refused for a second reason the moment
|
||||
it is pasted — and a suggestion that does not compile is the bug
|
||||
this spelling exists to avoid. [as-slice] above can write every
|
||||
argument out because [slice] takes one, two or three; this cannot,
|
||||
and the difference is the arity and not the style. *)
|
||||
let call =
|
||||
match args with
|
||||
| [ a ] -> "(length " ^ spell_arg "v" a ^ ")"
|
||||
| _ -> "(length v)"
|
||||
in
|
||||
Loc.failk "check/unknown-function" loc
|
||||
"there is no len. The number of elements in an array, a slice, a \
|
||||
string, a Vec or a Map is length. Write %s" call
|
||||
else
|
||||
(* The did-you-mean comes first, and for a capitalised head it is asked
|
||||
of the *type* tables as well: [(Piont 1 2)] with [Point] declared is
|
||||
@ -8443,7 +8480,8 @@ and ordinary_call ctx ~want loc name args =
|
||||
carries a slash answers correctly everywhere a call sits inside a
|
||||
function — and wrongly in the one place a call does not: a package's
|
||||
global initialiser, which is checked with no owner at all. An importer
|
||||
defining [len] reached inside an imported [(defonce sz i32 (len "abcd"))]
|
||||
defining [length] reached inside an imported
|
||||
[(defonce sz i32 (length "abcd"))]
|
||||
and changed what it computed. The files were never wrong about it.
|
||||
|
||||
What it costs is the REPL: an expression evaluated with no file behind it
|
||||
@ -9292,7 +9330,7 @@ let builtins : (string * string * string) list =
|
||||
the direction a handler can act on.");
|
||||
|
||||
(* containers *)
|
||||
("len", "len [[n T]|[T]|string|(Vec T)|(Map K V)] i32",
|
||||
("length", "length [[n T]|[T]|string|(Vec T)|(Map K V)] i32",
|
||||
"How many elements. One question and one word across an array, a slice, \
|
||||
a string, a Vec and a Map.");
|
||||
("at", "at [collection i32 ...] T",
|
||||
@ -9406,7 +9444,8 @@ let () =
|
||||
builtin or it did not, and the line is one line and rare.
|
||||
|
||||
The second half of the sentence names the way out. Until [builtin/] there
|
||||
was none: a file that defined [len] had given the builtin [len] up for the
|
||||
was none: a file that defined [length] had given the builtin [length] up
|
||||
for the
|
||||
whole file, and a defn that meant to *wrap* it was unbounded recursion
|
||||
instead. Saying so here is the cheapest place it can be said — the reader
|
||||
is being told the name was taken over, and the next thing they want to
|
||||
@ -9498,7 +9537,7 @@ let collect env (decls : Ast.decl list) =
|
||||
would otherwise be found by LLVM, as [redefinition of function
|
||||
'@flan.item'], or not at all. A [defn item] and a [defonce item] are two
|
||||
declarations of one name and are rejected here. *)
|
||||
(* The qualifier is reserved on this side too. [(defn builtin/len ...)]
|
||||
(* The qualifier is reserved on this side too. [(defn builtin/length ...)]
|
||||
reads — the reader treats [/] as an ordinary symbol character — and would
|
||||
otherwise land in [env.fns] under a name nothing can ever call, because
|
||||
[named_call] strips the prefix before any table is consulted. A
|
||||
|
||||
@ -51,7 +51,7 @@
|
||||
shortcut.} [Tast.Bytes] and [Tast.StrOfBytes] are documented there as
|
||||
non-instructions — in both existing backends a [string] {e is} a (ptr, len)
|
||||
pair over bytes, and the two prims reinterpret rather than convert. Making
|
||||
[string] a JS string would make [(len s)] count UTF-16 code units where
|
||||
[string] a JS string would make [(length s)] count UTF-16 code units where
|
||||
every other backend counts bytes, and the prelude is byte-oriented
|
||||
throughout ([rune-count], [valid-utf8?], [decode-rune] all take [[u8]]).
|
||||
So the JS string appears in exactly one place: the argument of
|
||||
@ -878,7 +878,7 @@ and prim f (e : Tast.expr) (p : Tast.prim) (args : Tast.expr list) =
|
||||
match x.Tast.ty with
|
||||
| Types.Array (n, _) -> Printf.sprintf "%Ld" n
|
||||
| Types.String | Types.Slice _ -> Printf.sprintf "%s.n" (spill f x)
|
||||
| t -> at loc "(len %s) is not in the JS dialect" (Types.to_string t))
|
||||
| t -> at loc "(length %s) is not in the JS dialect" (Types.to_string t))
|
||||
| Tast.At, target :: idx -> index f loc target idx
|
||||
| Tast.Slice, [ target; lo; hi ] -> (
|
||||
match evals f [ target; lo; hi ] with
|
||||
|
||||
@ -144,8 +144,8 @@ let entries dir suffix =
|
||||
|
||||
let qualify alias n = alias ^ "/" ^ n
|
||||
|
||||
(* The one alias this module will not hand out. [builtin/len] is the builtin
|
||||
[len] no matter what a file has defined ([Check]'s [qualified_builtin]),
|
||||
(* The one alias this module will not hand out. [builtin/length] is the builtin
|
||||
[length] no matter what a file has defined ([Check]'s [qualified_builtin]),
|
||||
and that only stays true while nothing else can produce the qualifier
|
||||
[builtin]. Every qualifier in a finished program comes from [qualify], and
|
||||
every [qualify] takes its alias from an [import] form, so refusing it here
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
syntax with the location of the offending form.
|
||||
|
||||
Everything not recognised here is a call, which is how a Lisp should work:
|
||||
[at], [len], [push], [abort] and the rest are ordinary functions resolved
|
||||
[at], [length], [push], [abort] and the rest are ordinary functions resolved
|
||||
by the checker, not syntax. *)
|
||||
|
||||
open Form
|
||||
@ -997,8 +997,8 @@ and dvec (p : Form.t) (t : Ast.expr) (items : Form.t list) : Ast.binding list =
|
||||
match rest with
|
||||
| None -> []
|
||||
| Some r ->
|
||||
(* An ordinary (slice t n (len t)): the tail of the temporary, which is a
|
||||
local and outlives the body that reads it. Nothing new. *)
|
||||
(* An ordinary (slice t n (length t)): the tail of the temporary, which
|
||||
is a local and outlives the body that reads it. Nothing new. *)
|
||||
let name =
|
||||
match r.v with
|
||||
| Sym s -> s
|
||||
@ -1013,7 +1013,8 @@ and dvec (p : Form.t) (t : Ast.expr) (items : Form.t list) : Ast.binding list =
|
||||
(Ast.Call (var r.loc "slice",
|
||||
[ t;
|
||||
ex r.loc (Ast.Int (Int64.of_int n));
|
||||
ex r.loc (Ast.Call (var r.loc "len", [ t ])) ])) } ]
|
||||
ex r.loc
|
||||
(Ast.Call (var r.loc "length", [ t ])) ])) } ]
|
||||
in
|
||||
each 0 elems @ rest_binding
|
||||
|
||||
|
||||
169
lib/prelude.ml
169
lib/prelude.ml
@ -332,7 +332,7 @@ let source = {flan|
|
||||
|
||||
(defn reverse [s [$t]] ()
|
||||
(let [i 0
|
||||
j (- (len s) 1)]
|
||||
j (- (length s) 1)]
|
||||
(while (< i j)
|
||||
(swap s i j)
|
||||
(set i (+ i 1))
|
||||
@ -353,7 +353,7 @@ let source = {flan|
|
||||
(defn sort [s [$t]] ()
|
||||
{:where (ordered? $t)}
|
||||
(let [i 1]
|
||||
(while (< i (len s))
|
||||
(while (< i (length s))
|
||||
(let [j i]
|
||||
;; `and` short-circuits, which is load-bearing: at j = 0 the left test
|
||||
;; fails and (at s -1) is never evaluated, so this does not trap.
|
||||
@ -379,7 +379,7 @@ let source = {flan|
|
||||
;; thing to want and not only a workaround.
|
||||
(defn sort-by [s [$t] before? (Fn [$t $t] bool)] ()
|
||||
(let [i 1]
|
||||
(while (< i (len s))
|
||||
(while (< i (length s))
|
||||
(let [j i]
|
||||
(while (and (> j 0) (before? (at s j) (at s (- j 1))))
|
||||
(swap s (- j 1) j)
|
||||
@ -390,7 +390,7 @@ let source = {flan|
|
||||
;; language has and a sentinel index is the bug this avoids.
|
||||
(defn index-of [s [$t] x $t] (Option i32)
|
||||
{:where (equal? $t)}
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(when (= (at s i) x)
|
||||
(return (Some i))))
|
||||
None)
|
||||
@ -407,19 +407,19 @@ let source = {flan|
|
||||
;; different arity, so the different name is honest rather than a workaround.
|
||||
(defn min-of [s [$t]] (Option $t)
|
||||
{:where (ordered? $t)}
|
||||
(if (= (len s) 0)
|
||||
(if (= (length s) 0)
|
||||
None
|
||||
(let [m (at s 0)]
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(set m (min m (at s i))))
|
||||
(Some m))))
|
||||
|
||||
(defn max-of [s [$t]] (Option $t)
|
||||
{:where (ordered? $t)}
|
||||
(if (= (len s) 0)
|
||||
(if (= (length s) 0)
|
||||
None
|
||||
(let [m (at s 0)]
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(set m (max m (at s i))))
|
||||
(Some m))))
|
||||
|
||||
@ -493,7 +493,7 @@ let source = {flan|
|
||||
;; here: it is two type variables and a second signature, and nothing has
|
||||
;; wanted it.
|
||||
(defn map-in-place [s [$t] f (Fn [$t] $t)] ()
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(set (at s i) (f (at s i)))))
|
||||
|
||||
;; The general fold, of which sum-i32 is the special case with the + written
|
||||
@ -501,7 +501,7 @@ let source = {flan|
|
||||
;; as (f acc x) and the order Odin's slice.reduce uses.
|
||||
(defn reduce [s [$t] init $t f (Fn [$t $t] $t)] $t
|
||||
(let [acc init]
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(set acc (f acc (at s i))))
|
||||
acc))
|
||||
|
||||
@ -514,7 +514,7 @@ let source = {flan|
|
||||
;; the instantiation site, where the element type is concrete.
|
||||
(defn filter [s [$t] keep? (Fn [$t] bool)] (Vec $t)
|
||||
(let [v (vec-new t)]
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(when (keep? (at s i))
|
||||
(push v (at s i))))
|
||||
v))
|
||||
@ -578,7 +578,7 @@ let source = {flan|
|
||||
;; it.
|
||||
(defn sum-i32 [s [i32]] i64
|
||||
(let [t (i64 0)]
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(set t (+ t (i64 (at s i)))))
|
||||
t))
|
||||
|
||||
@ -591,7 +591,7 @@ let source = {flan|
|
||||
;; game holds.
|
||||
(defn sum-f32 [s [f32]] f64
|
||||
(let [t 0.0]
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(set t (+ t (f64 (at s i)))))
|
||||
t))
|
||||
|
||||
@ -603,10 +603,10 @@ let source = {flan|
|
||||
;; result is a bool, an index, or a number.
|
||||
|
||||
(defn bytes=? [a [u8] b [u8]] bool
|
||||
(if (!= (len a) (len b))
|
||||
(if (!= (length a) (length b))
|
||||
false
|
||||
(do
|
||||
(dotimes [i (len a)]
|
||||
(dotimes [i (length a)]
|
||||
(when (!= (at a i) (at b i))
|
||||
(return false)))
|
||||
true)))
|
||||
@ -615,12 +615,12 @@ let source = {flan|
|
||||
;; built once it is known to be in bounds — otherwise a prefix longer than the
|
||||
;; string would trap rather than answer false.
|
||||
(defn starts-with? [s [u8] p [u8]] bool
|
||||
(and (<= (len p) (len s))
|
||||
(bytes=? (slice s 0 (len p)) p)))
|
||||
(and (<= (length p) (length s))
|
||||
(bytes=? (slice s 0 (length p)) p)))
|
||||
|
||||
(defn ends-with? [s [u8] p [u8]] bool
|
||||
(and (<= (len p) (len s))
|
||||
(bytes=? (slice s (- (len s) (len p)) (len s)) p)))
|
||||
(and (<= (length p) (length s))
|
||||
(bytes=? (slice s (- (length s) (length p)) (length s)) p)))
|
||||
|
||||
;; The whole slice is an integer, or it is None. bytes->i64 is strtoll, which
|
||||
;; answers 0 for "" and for "abc" and stops at the first junk byte in "12x" —
|
||||
@ -633,14 +633,14 @@ let source = {flan|
|
||||
(let [i 0
|
||||
n (i64 0)
|
||||
neg false]
|
||||
(when (= (len s) 0)
|
||||
(when (= (length s) 0)
|
||||
(return None))
|
||||
(when (or (= (at s 0) \-) (= (at s 0) \+))
|
||||
(set neg (= (at s 0) \-))
|
||||
(set i 1))
|
||||
(when (= i (len s))
|
||||
(when (= i (length s))
|
||||
(return None)) ; a lone sign is not a number
|
||||
(while (< i (len s))
|
||||
(while (< i (length s))
|
||||
(let [b (at s i)]
|
||||
(when (or (< b \0) (> b \9))
|
||||
(return None))
|
||||
@ -758,7 +758,7 @@ let source = {flan|
|
||||
;; this file), so a diagnostic would have to be a run-time one, in the one
|
||||
;; construct whose whole point is that it costs nothing at run time.
|
||||
(defmacro clamp [& args]
|
||||
(if (!= (len args) 3)
|
||||
(if (!= (length args) 3)
|
||||
`(clamp-takes-a-value-a-low-and-a-high)
|
||||
`(min ~(at args 2) (max ~(at args 1) ~(at args 0)))))
|
||||
|
||||
@ -798,8 +798,8 @@ let source = {flan|
|
||||
;; sampling would remove it and would consume an unpredictable number of draws,
|
||||
;; which is the one thing this generator exists not to do.
|
||||
;;
|
||||
;; An index is an i32 here (len answers one), so indexing with this reads
|
||||
;; (at xs (i32 (rand-int-range 0 (i64 (len xs))))).
|
||||
;; An index is an i32 here (length answers one), so indexing with this reads
|
||||
;; (at xs (i32 (rand-int-range 0 (i64 (length xs))))).
|
||||
(defn rand-int-range [lo i64 hi i64] i64
|
||||
(if (<= hi lo)
|
||||
lo
|
||||
@ -1220,12 +1220,12 @@ let source = {flan|
|
||||
;; Naive, O(n·m), and that is the deliberate choice: Boyer–Moore wants a skip
|
||||
;; table, which is an array sized by the needle, which is an allocation.
|
||||
(defn index-of-bytes [s [u8] p [u8]] (Option i32)
|
||||
(when (> (len p) (len s))
|
||||
(when (> (length p) (length s))
|
||||
(return None))
|
||||
(let [last (- (len s) (len p))
|
||||
(let [last (- (length s) (length p))
|
||||
i 0]
|
||||
(while (<= i last)
|
||||
(when (bytes=? (slice s i (+ i (len p))) p)
|
||||
(when (bytes=? (slice s i (+ i (length p))) p)
|
||||
(return (Some i)))
|
||||
(set i (+ i 1))))
|
||||
None)
|
||||
@ -1240,7 +1240,7 @@ let source = {flan|
|
||||
;; lo would pass hi and (slice s lo hi) would be a reversed range, which traps.
|
||||
(defn trim [s [u8]] [u8]
|
||||
(let [lo 0
|
||||
hi (len s)]
|
||||
hi (length s)]
|
||||
(while (and (< lo hi) (space? (at s lo)))
|
||||
(set lo (+ lo 1)))
|
||||
(while (and (< lo hi) (space? (at s (- hi 1))))
|
||||
@ -1269,33 +1269,33 @@ let source = {flan|
|
||||
(defn parse-f64 [s [u8]] (Option f64)
|
||||
(let [i 0
|
||||
digits 0]
|
||||
(when (or (= (len s) 0) (> (len s) 511))
|
||||
(when (or (= (length s) 0) (> (length s) 511))
|
||||
(return None))
|
||||
(when (or (= (at s 0) \-) (= (at s 0) \+))
|
||||
(set i 1))
|
||||
(while (and (< i (len s)) (digit? (at s i)))
|
||||
(while (and (< i (length s)) (digit? (at s i)))
|
||||
(set i (+ i 1))
|
||||
(set digits (+ digits 1)))
|
||||
(when (and (< i (len s)) (= (at s i) \.))
|
||||
(when (and (< i (length s)) (= (at s i) \.))
|
||||
(set i (+ i 1))
|
||||
(while (and (< i (len s)) (digit? (at s i)))
|
||||
(while (and (< i (length s)) (digit? (at s i)))
|
||||
(set i (+ i 1))
|
||||
(set digits (+ digits 1))))
|
||||
(when (= digits 0)
|
||||
(return None)) ; "." and "+" and "e5" are not numbers
|
||||
(when (and (< i (len s)) (or (= (at s i) \e) (= (at s i) \E)))
|
||||
(when (and (< i (length s)) (or (= (at s i) \e) (= (at s i) \E)))
|
||||
(set i (+ i 1))
|
||||
(when (and (< i (len s)) (or (= (at s i) \-) (= (at s i) \+)))
|
||||
(when (and (< i (length s)) (or (= (at s i) \-) (= (at s i) \+)))
|
||||
(set i (+ i 1)))
|
||||
(let [e 0]
|
||||
(while (and (< i (len s)) (digit? (at s i)))
|
||||
(while (and (< i (length s)) (digit? (at s i)))
|
||||
(set i (+ i 1))
|
||||
(set e (+ e 1)))
|
||||
(when (= e 0)
|
||||
(return None)))) ; a lone exponent marker
|
||||
;; Trailing junk is the case strtod is silent about, so the position has
|
||||
;; to land exactly on the end.
|
||||
(if (= i (len s)) (Some (bytes->f64 s)) None)))
|
||||
(if (= i (length s)) (Some (bytes->f64 s)) None)))
|
||||
|
||||
;; ── UTF-8 ─────────────────────────────────────────────────────────────
|
||||
;;
|
||||
@ -1349,7 +1349,7 @@ let source = {flan|
|
||||
(!= (bit-and b 0xc0) 0x80))
|
||||
|
||||
(defn decode-rune [s [u8]] Rune
|
||||
(when (= (len s) 0)
|
||||
(when (= (length s) 0)
|
||||
(return (Rune {.code 0 .width 0 .ok false})))
|
||||
(let [b0 (at s 0)]
|
||||
(when (< b0 0x80)
|
||||
@ -1375,7 +1375,7 @@ let source = {flan|
|
||||
(return (Rune {.code 0 .width 1 .ok false})))
|
||||
;; A sequence cut off by the end of the slice. Width 1, so a caller
|
||||
;; scanning a buffer boundary makes progress instead of stalling.
|
||||
(when (> size (len s))
|
||||
(when (> size (length s))
|
||||
(return (Rune {.code 0 .width 1 .ok false})))
|
||||
(let [b1 (at s 1)]
|
||||
(when (or (< b1 lo) (> b1 hi))
|
||||
@ -1405,9 +1405,9 @@ let source = {flan|
|
||||
;; the bytes there are malformed, which is stricter than Odin's rune_at — that
|
||||
;; one hands back RUNE_ERROR and the caller carries on with a wrong character.
|
||||
(defn rune-at [s [u8] i i32] (Option i32)
|
||||
(if (or (< i 0) (>= i (len s)))
|
||||
(if (or (< i 0) (>= i (length s)))
|
||||
None
|
||||
(let [r (decode-rune (slice s i (len s)))]
|
||||
(let [r (decode-rune (slice s i (length s)))]
|
||||
(if (.ok r) (Some (.code r)) None))))
|
||||
|
||||
;; Counted through decode-rune rather than through a second walk of its own.
|
||||
@ -1420,16 +1420,16 @@ let source = {flan|
|
||||
(defn rune-count [s [u8]] i32
|
||||
(let [i 0
|
||||
n 0]
|
||||
(while (< i (len s))
|
||||
(let [r (decode-rune (slice s i (len s)))]
|
||||
(while (< i (length s))
|
||||
(let [r (decode-rune (slice s i (length s)))]
|
||||
(set i (+ i (.width r)))
|
||||
(set n (+ n 1))))
|
||||
n))
|
||||
|
||||
(defn valid-utf8? [s [u8]] bool
|
||||
(let [i 0]
|
||||
(while (< i (len s))
|
||||
(let [r (decode-rune (slice s i (len s)))]
|
||||
(while (< i (length s))
|
||||
(let [r (decode-rune (slice s i (length s)))]
|
||||
(when (not (.ok r))
|
||||
(return false))
|
||||
(set i (+ i (.width r)))))
|
||||
@ -1467,7 +1467,7 @@ let source = {flan|
|
||||
(match (rune-size code)
|
||||
None None
|
||||
(Some w)
|
||||
(if (> w (len dst))
|
||||
(if (> w (length dst))
|
||||
None
|
||||
(do
|
||||
(cond
|
||||
@ -1516,12 +1516,13 @@ let source = {flan|
|
||||
(match (index-of (.rest it) (.sep it))
|
||||
(Some i)
|
||||
(let [field (slice (.rest it) 0 i)]
|
||||
(set (.rest it) (slice (.rest it) (+ i 1) (len (.rest it))))
|
||||
(set (.rest it) (slice (.rest it) (+ i 1) (length (.rest it))))
|
||||
(Some field))
|
||||
None
|
||||
(let [field (.rest it)]
|
||||
(set (.more it) false)
|
||||
(set (.rest it) (slice (.rest it) (len (.rest it)) (len (.rest it))))
|
||||
(set (.rest it)
|
||||
(slice (.rest it) (length (.rest it)) (length (.rest it))))
|
||||
(Some field))))
|
||||
|
||||
;; ── ASCII case ────────────────────────────────────────────────────────
|
||||
@ -1565,10 +1566,10 @@ let source = {flan|
|
||||
;; half of to_lower and needs no storage at all: comparing two lowered copies
|
||||
;; is what a caller wanted, and this is that answer without either copy.
|
||||
(defn bytes-ci=? [a [u8] b [u8]] bool
|
||||
(if (!= (len a) (len b))
|
||||
(if (!= (length a) (length b))
|
||||
false
|
||||
(do
|
||||
(dotimes [i (len a)]
|
||||
(dotimes [i (length a)]
|
||||
(when (!= (lower-ascii (at a i)) (lower-ascii (at b i)))
|
||||
(return false)))
|
||||
true)))
|
||||
@ -1592,13 +1593,13 @@ let source = {flan|
|
||||
;;
|
||||
;; A prefix sorts before what extends it — "ab" before "abc" — which falls out
|
||||
;; of running to the shorter length and then comparing lengths, and is the case
|
||||
;; a loop written to (len a) alone reads off the end for.
|
||||
;; a loop written to (length a) alone reads off the end for.
|
||||
(defn bytes<? [a [u8] b [u8]] bool
|
||||
(let [n (min (len a) (len b))]
|
||||
(let [n (min (length a) (length b))]
|
||||
(dotimes [i n]
|
||||
(when (!= (at a i) (at b i))
|
||||
(return (< (at a i) (at b i)))))
|
||||
(< (len a) (len b))))
|
||||
(< (length a) (length b))))
|
||||
|
||||
;; sort-by with the comparison written in, over the same in-place contract:
|
||||
;; the *slices* move, never the bytes they point at, so this sorts a [[u8]] of
|
||||
@ -1652,7 +1653,7 @@ let source = {flan|
|
||||
;; style: a Vec parameter *moves*, so (append b s) taking one by value would
|
||||
;; consume the caller's builder on the first call and refuse the second.
|
||||
(defn append [b (Ptr (Vec u8)) s [u8]] ()
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(push (deref b) (at s i))))
|
||||
|
||||
;; The two number appends, and they are the reason this shape is worth having
|
||||
@ -1677,7 +1678,7 @@ let source = {flan|
|
||||
;; that reads like a mistake at the call site.
|
||||
(defn concat [parts [[u8]]] (Vec u8)
|
||||
(let [b (vec-new u8)]
|
||||
(dotimes [i (len parts)]
|
||||
(dotimes [i (length parts)]
|
||||
(append (addr b) (at parts i)))
|
||||
b))
|
||||
|
||||
@ -1687,7 +1688,7 @@ let source = {flan|
|
||||
;; exactly that input, because there is no tail to chop.
|
||||
(defn join [parts [[u8]] sep [u8]] (Vec u8)
|
||||
(let [b (vec-new u8)]
|
||||
(dotimes [i (len parts)]
|
||||
(dotimes [i (length parts)]
|
||||
(when (> i 0)
|
||||
(append (addr b) sep))
|
||||
(append (addr b) (at parts i)))
|
||||
@ -1706,13 +1707,13 @@ let source = {flan|
|
||||
;; its own.
|
||||
(defn to-lower [s [u8]] (Vec u8)
|
||||
(let [b (vec-new u8)]
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(push b (lower-ascii (at s i))))
|
||||
b))
|
||||
|
||||
(defn to-upper [s [u8]] (Vec u8)
|
||||
(let [b (vec-new u8)]
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(push b (upper-ascii (at s i))))
|
||||
b))
|
||||
|
||||
@ -1732,19 +1733,19 @@ let source = {flan|
|
||||
(defn replace-bytes [s [u8] from [u8] to [u8]] (Vec u8)
|
||||
(let [b (vec-new u8)
|
||||
i 0]
|
||||
(if (= (len from) 0)
|
||||
(if (= (length from) 0)
|
||||
(append (addr b) s)
|
||||
(while (< i (len s))
|
||||
(match (index-of-bytes (slice s i (len s)) from)
|
||||
(while (< i (length s))
|
||||
(match (index-of-bytes (slice s i (length s)) from)
|
||||
(Some k)
|
||||
(do
|
||||
(append (addr b) (slice s i (+ i k)))
|
||||
(append (addr b) to)
|
||||
(set i (+ i k (len from))))
|
||||
(set i (+ i k (length from))))
|
||||
None
|
||||
(do
|
||||
(append (addr b) (slice s i (len s)))
|
||||
(set i (len s))))))
|
||||
(append (addr b) (slice s i (length s)))
|
||||
(set i (length s))))))
|
||||
b))
|
||||
|
||||
;; A (Vec [u8]) cannot be written at a let, and this one-line function is where
|
||||
@ -1865,7 +1866,7 @@ let source = {flan|
|
||||
;; scale by the carry above, so it never needs more, and
|
||||
;; without the padding 1.005 at three places prints "1.5".
|
||||
(let [d (i64->bytes fr)]
|
||||
(dotimes [i (- p (len d))]
|
||||
(dotimes [i (- p (length d))]
|
||||
(push b \0))
|
||||
(append (addr b) d))))))))
|
||||
b))
|
||||
@ -1938,9 +1939,9 @@ let source = {flan|
|
||||
;;
|
||||
;; It takes a slice rather than the array (embed-dir) answers, because an array
|
||||
;; length is part of its type and there are no generics: write
|
||||
;; (embed-find (slice assets 0 (len assets)) "brush.png").
|
||||
;; (embed-find (slice assets 0 (length assets)) "brush.png").
|
||||
(defn embed-find [files [EmbedFile] name string] (Option [u8])
|
||||
(dotimes [i (len files)]
|
||||
(dotimes [i (length files)]
|
||||
(when (bytes=? (bytes-view (.name (at files i))) (bytes-view name))
|
||||
(return (Some (.data (at files i))))))
|
||||
None)
|
||||
@ -2091,15 +2092,15 @@ let source = {flan|
|
||||
(defn form-cons [x Form rest [Form]] [Form]
|
||||
(let [v (vec-new Form)]
|
||||
(push v x)
|
||||
(dotimes [i (len rest)]
|
||||
(dotimes [i (length rest)]
|
||||
(push v (at rest i)))
|
||||
(slice v)))
|
||||
|
||||
(defn form-append [a [Form] b [Form]] [Form]
|
||||
(let [v (vec-new Form)]
|
||||
(dotimes [i (len a)]
|
||||
(dotimes [i (length a)]
|
||||
(push v (at a i)))
|
||||
(dotimes [i (len b)]
|
||||
(dotimes [i (length b)]
|
||||
(push v (at b i)))
|
||||
(slice v)))
|
||||
|
||||
@ -2108,7 +2109,7 @@ let source = {flan|
|
||||
(defn form-rest [xs [Form] from i32] [Form]
|
||||
(let [v (vec-new Form)
|
||||
i from]
|
||||
(while (< i (len xs))
|
||||
(while (< i (length xs))
|
||||
(push v (at xs i))
|
||||
(set i (+ i 1)))
|
||||
(slice v)))
|
||||
@ -2142,7 +2143,7 @@ let source = {flan|
|
||||
(push v 126) ; ~
|
||||
(push v 103) ; g
|
||||
(let [d (i64->bytes gensym-n)]
|
||||
(dotimes [i (len d)]
|
||||
(dotimes [i (length d)]
|
||||
(push v (at d i))))
|
||||
(Form.Sym {.s (string (slice v))})))
|
||||
|
||||
@ -2173,7 +2174,7 @@ let source = {flan|
|
||||
;; keeping. A test is still required, because there is nothing to negate
|
||||
;; without one.
|
||||
(defmacro unless [& args]
|
||||
(if (< (len args) 1)
|
||||
(if (< (length args) 1)
|
||||
`(unless-takes-a-test)
|
||||
`(if (not ~(at args 0)) (do ~@(form-rest args 1)))))
|
||||
|
||||
@ -2230,22 +2231,22 @@ let source = {flan|
|
||||
;; rl/with-drawing and rl/with-mode-2d already take the same trade on their
|
||||
;; arguments. Write the index out first if it does anything.
|
||||
(defmacro inc [& args]
|
||||
(if (!= (len args) 1)
|
||||
(if (!= (length args) 1)
|
||||
`(inc-takes-one-number)
|
||||
`(+ ~(at args 0) 1)))
|
||||
|
||||
(defmacro dec [& args]
|
||||
(if (!= (len args) 1)
|
||||
(if (!= (length args) 1)
|
||||
`(dec-takes-one-number)
|
||||
`(- ~(at args 0) 1)))
|
||||
|
||||
(defmacro ++ [& args]
|
||||
(if (!= (len args) 1)
|
||||
(if (!= (length args) 1)
|
||||
`(++-takes-one-place)
|
||||
`(set ~(at args 0) (+ ~(at args 0) 1))))
|
||||
|
||||
(defmacro -- [& args]
|
||||
(if (!= (len args) 1)
|
||||
(if (!= (length args) 1)
|
||||
`(---takes-one-place)
|
||||
`(set ~(at args 0) (- ~(at args 0) 1))))
|
||||
|
||||
@ -2306,12 +2307,12 @@ let source = {flan|
|
||||
;; place and the wrong sentence. The bad transform is passed along so that at
|
||||
;; least it is named.
|
||||
(defn into-wrap [ts [Form] dst Form x Form] Form
|
||||
(loop [k (len ts) body `(push ~dst ~x)]
|
||||
(loop [k (length ts) body `(push ~dst ~x)]
|
||||
(if (= k 0)
|
||||
body
|
||||
(let [t (at ts (- k 1))
|
||||
items (form-items t)]
|
||||
(if (!= (len items) 2)
|
||||
(if (!= (length items) 2)
|
||||
`(into-transform-is-map-or-filter-of-one-function ~t)
|
||||
(let [head (at items 0)
|
||||
f (at items 1)]
|
||||
@ -2340,7 +2341,7 @@ let source = {flan|
|
||||
;; body that was not written rather than a body of one form.
|
||||
(defn form-empty-list? [f Form] bool
|
||||
(match f
|
||||
(Form.List xs) (= (len xs) 0)
|
||||
(Form.List xs) (= (length xs) 0)
|
||||
_ false))
|
||||
|
||||
(defn form-is-sym? [f Form] bool
|
||||
@ -2354,7 +2355,7 @@ let source = {flan|
|
||||
;; A source that is already a name is used as it is, and a source that is
|
||||
;; anything else is bound to one. Both halves matter.
|
||||
;;
|
||||
;; Binding it is what a source that is a *call* needs: (len s) and (at s i)
|
||||
;; Binding it is what a source that is a *call* needs: (length s) and (at s i)
|
||||
;; have to be the same s, and without the binding the call would be made twice
|
||||
;; per element.
|
||||
;;
|
||||
@ -2362,11 +2363,11 @@ let source = {flan|
|
||||
;; (let [s v] ...) would hand v's ownership to the macro's own binding and the
|
||||
;; caller would find v dead after an (into v ...) that only read it — and a
|
||||
;; fixed array would be *copied* into the binding, once per into. Neither is
|
||||
;; what was written. len and at borrow, so used directly the source is only
|
||||
;; what was written. length and at borrow, so used directly the source is only
|
||||
;; read. A source that is a call and produces a Vec is still consumed, which is
|
||||
;; right: nobody else is holding it.
|
||||
(defmacro into [& args]
|
||||
(if (< (len args) 2)
|
||||
(if (< (length args) 2)
|
||||
`(into-takes-a-source-a-destination-and-transforms)
|
||||
(let [from (at args 0)
|
||||
named? (form-is-sym? from)
|
||||
@ -2376,7 +2377,7 @@ let source = {flan|
|
||||
x (gensym)
|
||||
i (gensym)]
|
||||
`(let [~dst ~(at args 1) ~@bind]
|
||||
(dotimes [~i (len ~src)]
|
||||
(dotimes [~i (length ~src)]
|
||||
(let [~x (at ~src ~i)]
|
||||
~(into-wrap (form-rest args 2) dst x)))
|
||||
~dst))))
|
||||
|
||||
@ -226,7 +226,7 @@ let rec cty env ~needed ~loc ~what (t : Ast.texpr) : string =
|
||||
fail loc
|
||||
"%s is a slice, and nothing here says what type the C count parameter \
|
||||
is — declare (Ptr T) with an explicit count, and pass \
|
||||
(addr (at s 0)) and (len s) from Flan"
|
||||
(addr (at s 0)) and (length s) from Flan"
|
||||
what
|
||||
| Ast.Tarray _ ->
|
||||
fail loc
|
||||
@ -244,7 +244,7 @@ let rec cty env ~needed ~loc ~what (t : Ast.texpr) : string =
|
||||
| Ast.Tapp ("Vec", _) ->
|
||||
fail loc
|
||||
"%s is a Vec, which owns its storage. Pass (slice v) as (Ptr T) and \
|
||||
(len v)"
|
||||
(length v)"
|
||||
what
|
||||
| Ast.Tfn _ ->
|
||||
fail loc "%s is a function type, and a C callback is not implemented" what
|
||||
|
||||
@ -3013,7 +3013,7 @@ and prim f (e : Tast.expr) (p : Tast.prim) (args : Tast.expr list) dst =
|
||||
let l = lvalue f a in
|
||||
load_int f.b ~dst:rax ~mm:(lmem f (shift l 8) ~scratch:r11) ~size:8
|
||||
~signed:true
|
||||
| ty -> unsupported "len of %s" (Types.to_string ty));
|
||||
| ty -> unsupported "length of %s" (Types.to_string ty));
|
||||
store_loc f ~reg:rax dst t
|
||||
| Tast.At, a :: is when is <> [] ->
|
||||
let l = elements f (lvalue f a) a.Tast.ty is in
|
||||
|
||||
6
plan.org
6
plan.org
@ -104,7 +104,7 @@ world.
|
||||
names its types: ~(let [enemies (map-new string Enemy)] ...)~. ~get~
|
||||
returns ~(Option V)~; ~put~ is the ~()~-returning upsert. See
|
||||
spec-memory.md for the deferred move-aware operations.
|
||||
- Operations: ~get~, ~put~, ~remove~, ~push~, ~pop~, ~at~, ~len~, ~update~.
|
||||
- Operations: ~get~, ~put~, ~remove~, ~push~, ~pop~, ~at~, ~length~, ~update~.
|
||||
Copying is explicit: ~(clone m)~, and owning containers move rather than copy on
|
||||
assignment. No ~!~ convention — nothing is immutable, so it
|
||||
would carry no information. No ~assoc~; it only existed as the copy-returning form.
|
||||
@ -436,7 +436,7 @@ wasm32 target cheap, because a primitive is the only thing implemented twice.
|
||||
| ~argv~ | ~[string]~, borrowed, never freed |
|
||||
| ~write-stdout~ | takes ~[u8]~; the ONE output primitive |
|
||||
| ~exit~ | ~i32~ status |
|
||||
| ~len~ ~at~ ~slice~ | on fixed arrays and slices |
|
||||
| ~length~ ~at~ ~slice~ | on fixed arrays and slices |
|
||||
| ~bytes~ | ~string~ → ~[u8]~, a view, no copy |
|
||||
| ~bytes->f64~ ~bytes->i64~ | and the inverses, for printing |
|
||||
| ~addr~ | address of a place |
|
||||
@ -805,7 +805,7 @@ building the whole live environment at once.
|
||||
throughput number; with no interpreter that criterion is gone and the narrow
|
||||
host ABI took its place on the critical path (see Compilation). Packages,
|
||||
structs, ~(Ptr T)~ + ~addr~, byte slices,
|
||||
~at~/~len~, ~while~, ~set~ on the fixed place list, ~cond~, ~match~, ~Option~
|
||||
~at~/~length~, ~while~, ~set~ on the fixed place list, ~cond~, ~match~, ~Option~
|
||||
+ ~some~, ~i32~/~u8~/~f64~, recursion, argv, stdout. No allocator, no ~Vec~,
|
||||
no generics, no user macros, no FFI, no window. Headless, so the acceptance
|
||||
test is a table of expression/result pairs.
|
||||
|
||||
@ -1489,7 +1489,7 @@ int64_t flan_dyn_need_i64(flan_dyn v) {
|
||||
* down in the doc's boundary section so that closing it is a decision somebody
|
||||
* makes rather than a surprise somebody meets. Softening the check here is the
|
||||
* option not to take: this function sees a tag and nothing else, so it could
|
||||
* not tell (g 1) from (g (len xs)). */
|
||||
* not tell (g 1) from (g (length xs)). */
|
||||
double flan_dyn_need_f64(flan_dyn v) {
|
||||
if (flan_dyn_tag(v) != FLAN_DYN_TAG_FLOAT)
|
||||
trap1(NULL, 0, TYPE_TRAP, "f64", "a float was wanted", v);
|
||||
@ -2074,10 +2074,10 @@ flan_dyn flan_dyn_len(flan_dyn v) {
|
||||
}
|
||||
if (is_vec(v)) {
|
||||
flan_obj *o = dyn_obj(v);
|
||||
if (o->kind == OBJ_VIEW) return flan_dyn_from_i64(view_len("len", o));
|
||||
if (o->kind == OBJ_VIEW) return flan_dyn_from_i64(view_len("length", o));
|
||||
return flan_dyn_from_i64(o->len);
|
||||
}
|
||||
trap1(NULL, 0, TYPE_TRAP, "len", "only a text, a vec or a map has one", v);
|
||||
trap1(NULL, 0, TYPE_TRAP, "length", "only a text, a vec or a map has one", v);
|
||||
}
|
||||
|
||||
/* The index has to be an int, and that is a separate sentence from the
|
||||
|
||||
@ -110,7 +110,7 @@ region" for the rule that stands in its place and for what it costs.
|
||||
- A view of a `Vec` a call returned is legal and does not dangle — the storage
|
||||
outlives the expression — but the header was the only handle `free` could
|
||||
have taken, so that block is leaked unless its allocator reclaims it
|
||||
wholesale. Same leak as `(len (mk))`, same rule as overwriting a global
|
||||
wholesale. Same leak as `(length (mk))`, same rule as overwriting a global
|
||||
`Vec`: manual memory management, and the program's business.
|
||||
- **The first implementation follows Zig/Odin's explicit model, not Rust's
|
||||
borrow checker.** Dev builds carry a generation word on `Vec` and trap on use
|
||||
|
||||
@ -38,7 +38,7 @@ let generic_src n =
|
||||
\ (set (at xs j) tmp)))\n\n\
|
||||
(defn gsort [s [$t] before? (Fn [$t $t] bool)] ()\n\
|
||||
\ (let [i 1]\n\
|
||||
\ (while (< i (len s))\n\
|
||||
\ (while (< i (length s))\n\
|
||||
\ (let [j i]\n\
|
||||
\ (while (and (> j 0) (before? (at s j) (at s (- j 1))))\n\
|
||||
\ (gswap s (- j 1) j)\n\
|
||||
@ -69,7 +69,7 @@ let mono_src n =
|
||||
\ (set (at xs j) tmp)))\n\n\
|
||||
(defn msort-%s [s [%s] before? (Fn [%s %s] bool)] ()\n\
|
||||
\ (let [i 1]\n\
|
||||
\ (while (< i (len s))\n\
|
||||
\ (while (< i (length s))\n\
|
||||
\ (let [j i]\n\
|
||||
\ (while (and (> j 0) (before? (at s j) (at s (- j 1))))\n\
|
||||
\ (mswap-%s s (- j 1) j)\n\
|
||||
|
||||
@ -4,24 +4,24 @@
|
||||
|
||||
(defn keep [s [$t] keep? (Fn [$t] bool)] (Vec $t)
|
||||
(let [v (vec-new t)]
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(when (keep? (at s i))
|
||||
(push v (at s i))))
|
||||
v))
|
||||
|
||||
(defn apply! [s [$t] f (Fn [$t] $t)] ()
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(set (at s i) (f (at s i)))))
|
||||
|
||||
(defn fold [s [$t] init $t f (Fn [$t $t] $t)] t
|
||||
(let [acc init]
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(set acc (f acc (at s i))))
|
||||
acc))
|
||||
|
||||
(defn flip! [s [$t]] ()
|
||||
(let [i 0
|
||||
j (- (len s) 1)]
|
||||
j (- (length s) 1)]
|
||||
(while (< i j)
|
||||
(let [tmp (at s i)]
|
||||
(set (at s i) (at s j))
|
||||
@ -45,7 +45,7 @@
|
||||
(println (fold xs 0 (fn [a b] (+ a b))))
|
||||
(println (fold ys (f32 0) (fn [a b] (+ a b))))
|
||||
(let [evens (keep xs (fn [x] (= (% x 20) 0)))]
|
||||
(println (len evens))
|
||||
(println (length evens))
|
||||
(free evens))
|
||||
(println (at xs 0))
|
||||
(println (at ys 0))))
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
|
||||
(defn sort-by! [s [$t] before? (Fn [$t $t] bool)] ()
|
||||
(let [i 1]
|
||||
(while (< i (len s))
|
||||
(while (< i (length s))
|
||||
(let [j i]
|
||||
(while (and (> j 0) (before? (at s j) (at s (- j 1))))
|
||||
(swap! s (- j 1) j)
|
||||
|
||||
@ -14,12 +14,12 @@
|
||||
;;;; A backend that quietly builds the slice exits 0 while the other exits 134
|
||||
;;;; and the survey says DIFFER.
|
||||
;;;;
|
||||
;;;; The two ends come from (len args), which is 1 for a program run with no
|
||||
;;;; The two ends come from (length args), which is 1 for a program run with no
|
||||
;;;; arguments and is not a number either optimiser can see, so the branch
|
||||
;;;; cannot be folded away and the checker has no literal to object to.
|
||||
(defn main [args [string]] i32
|
||||
(let [s (bytes-view "hello")
|
||||
hi (i32 (len args))
|
||||
hi (i32 (length args))
|
||||
lo (+ hi 1)]
|
||||
(print (slice s lo hi))
|
||||
(println ""))
|
||||
|
||||
@ -45,11 +45,11 @@
|
||||
;; main's own root outlived every collection build triggered.
|
||||
(let [keep "kept"
|
||||
xs (build 40000)]
|
||||
(print (len xs))
|
||||
(print (length xs))
|
||||
(print "\n")
|
||||
(print (at xs 0))
|
||||
(print "\n")
|
||||
(print (at xs (- (len xs) 1)))
|
||||
(print (at xs (- (length xs) 1)))
|
||||
(print "\n")
|
||||
(print keep)
|
||||
(print "\n")))
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
(+ (+ (+ a b) (+ c d)) (+ (+ e f) (+ g h))))
|
||||
|
||||
(defn taglen [s [u8]] i64
|
||||
(i64 (len s)))
|
||||
(i64 (length s)))
|
||||
|
||||
(defn main [] i32
|
||||
(let [v (V3 {.x 1.0 .y 2.0 .z 3.0})
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
;; n is a parameter, so the checker has no literal to look at.
|
||||
(restart-case
|
||||
(let [s (slice-from-ptr (addr (at a 0)) n)]
|
||||
(len s))
|
||||
(length s))
|
||||
(give-up [] -1)))
|
||||
|
||||
(defn show [name string n i64] ()
|
||||
|
||||
@ -70,9 +70,9 @@
|
||||
;; inferred from the argument types; there is no explicit instantiation.
|
||||
(defn largest [xs [$t] gt (Fn [$t $t] bool)] (Option $t)
|
||||
{:where (copyable? $t)}
|
||||
(if (> (len xs) 0)
|
||||
(if (> (length xs) 0)
|
||||
(let [best (at xs 0)]
|
||||
(dotimes [i (len xs)]
|
||||
(dotimes [i (length xs)]
|
||||
(when (gt (at xs i) best) (set best (at xs i))))
|
||||
(Some best))
|
||||
None))
|
||||
@ -82,9 +82,9 @@
|
||||
;; and each instantiation is checked against the ones the signature declares.
|
||||
(defn smallest [xs [$t]] (Option $t)
|
||||
{:where (ordered? $t)}
|
||||
(if (> (len xs) 0)
|
||||
(if (> (length xs) 0)
|
||||
(let [m (at xs 0)]
|
||||
(dotimes [i (len xs)] (set m (min m (at xs i))))
|
||||
(dotimes [i (length xs)] (set m (min m (at xs i))))
|
||||
(Some m))
|
||||
None))
|
||||
|
||||
@ -106,7 +106,7 @@
|
||||
;; exists, this is a `dotimes` accumulating into a local.
|
||||
(defn centroid [es [Enemy]] Vec2
|
||||
(/ (reduce (fn [acc e] (+ acc (.pos e))) [0 0] es)
|
||||
(f32 (len es))))
|
||||
(f32 (length es))))
|
||||
|
||||
;; ── Handles, not pointers, for anything cross-referenced ──────────────
|
||||
;; Pattern bindings bind VALUES, so matching a struct out of a pool would give
|
||||
@ -201,7 +201,7 @@
|
||||
(push errors c) ; value struct: copies out of
|
||||
(invoke-restart 'skip-form))] ; the signalling frame
|
||||
(let [ast (parse-all (parser src))]
|
||||
(if (zero? (len errors))
|
||||
(if (zero? (length errors))
|
||||
(Ok ast)
|
||||
(Err (Errors errors))))))) ; errors moves into the Err
|
||||
|
||||
|
||||
@ -539,6 +539,19 @@ static void refuse_view(const char *what) {
|
||||
} else if (strcmp(what, "flatpush") == 0) {
|
||||
v = flan_dyn_view_flat(buf, 2, FLAN_VIEW_I64);
|
||||
flan_dyn_push(v, flan_dyn_from_i64(9));
|
||||
} else if (strcmp(what, "stalelen") == 0) {
|
||||
/* A Vec view whose allocator has moved on, asked for its length. The
|
||||
* operator name is what this mode is for: the sentence is built from the
|
||||
* string the entry point hands down, and [flan_dyn_len] has two of them
|
||||
* — its own trap and this one — which is how one of them came to be a
|
||||
* word the language no longer has. The prefix of flan_allocator is
|
||||
* restated here for the same reason [hand_vec] restates flan_vec's. */
|
||||
static struct { void *proc; void *data; uint32_t caps; uint64_t epoch; }
|
||||
alloc = { NULL, NULL, 0, 7 };
|
||||
static hand_vec hv;
|
||||
hv.ptr = buf; hv.len = 2; hv.cap = 2; hv.alloc = &alloc; hv.epoch = 3;
|
||||
v = flan_dyn_view_vec(&hv, FLAN_VIEW_I64);
|
||||
(void)flan_dyn_len(v);
|
||||
} else {
|
||||
printf("no such refusal: %s\n", what);
|
||||
exit(2);
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
ticks)
|
||||
|
||||
(defn main [args [string]] i32
|
||||
(if (< (len args) 2)
|
||||
(if (< (length args) 2)
|
||||
(do (println "usage: agent-queue <socket>") 2)
|
||||
(do
|
||||
(if (< (agent/start (at args 1)) 0)
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
ticks)
|
||||
|
||||
(defn main [args [string]] i32
|
||||
(if (< (len args) 2)
|
||||
(if (< (length args) 2)
|
||||
(do (println "usage: agent <socket>") 2)
|
||||
(do
|
||||
(if (< (agent/start (at args 1)) 0)
|
||||
|
||||
@ -8,13 +8,13 @@
|
||||
;;;; that the concrete ones are right.
|
||||
|
||||
(defn show-f32 [s [f32]] ()
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(print (at s i))
|
||||
(print " "))
|
||||
(println ""))
|
||||
|
||||
(defn show-fields [s [[u8]]] ()
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(print (string (at s i)))
|
||||
(print " "))
|
||||
(println ""))
|
||||
@ -83,7 +83,7 @@
|
||||
|
||||
;; 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
|
||||
;; it, which is the case a loop running only to (length 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
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
|
||||
(defn main [args [string]] i32
|
||||
(set frame (arena-new 4096))
|
||||
(let [which (if (> (len args) 1) (i32 (bytes->i64 (bytes-view (at args 1)))) 0)]
|
||||
(let [which (if (> (length args) 1) (i32 (bytes->i64 (bytes-view (at args 1)))) 0)]
|
||||
(cond
|
||||
(= which 1)
|
||||
;; The refusal. The context here is the heap, which can free one
|
||||
@ -33,7 +33,7 @@
|
||||
;; the slots and strand every inner Vec — so the construction dies
|
||||
;; rather than the free three hundred lines later.
|
||||
(let [bad (vec-new Value)]
|
||||
(println (len bad)))
|
||||
(println (length bad)))
|
||||
|
||||
(= which 2)
|
||||
;; Use after free-all, which is a different mechanism and worth
|
||||
@ -50,9 +50,9 @@
|
||||
(push outer (Value.List {.items inner})))
|
||||
(match (at outer 0)
|
||||
(List items)
|
||||
(do (println (len items))
|
||||
(do (println (length items))
|
||||
(free-all frame)
|
||||
(println (len items)))
|
||||
(println (length items)))
|
||||
_ (println 0)))
|
||||
|
||||
(= which 3)
|
||||
@ -66,7 +66,7 @@
|
||||
(match v
|
||||
(List items)
|
||||
(do (push items (Value.Int {.n (i64 1)}))
|
||||
(println (len items)))
|
||||
(println (length items)))
|
||||
_ (println 0)))
|
||||
|
||||
:else
|
||||
@ -83,8 +83,8 @@
|
||||
(push row 1)
|
||||
(push row 2)
|
||||
(push rows row))
|
||||
(println (len rows))
|
||||
(println (len (at rows 0)))))
|
||||
(println (length rows))
|
||||
(println (length (at rows 0)))))
|
||||
(free-all frame)
|
||||
;; And the same container against the heap dies — asserted from the
|
||||
;; other side in run 1 above; here the point is only that the region
|
||||
@ -92,7 +92,7 @@
|
||||
(with-allocator frame
|
||||
(let [vs (vec-new Value)]
|
||||
(push vs (Value.Int {.n (i64 41)}))
|
||||
(println (len vs))))
|
||||
(println (length vs))))
|
||||
(free-all frame)
|
||||
;; And the zeroed field of run 3, this time in the region: the growth
|
||||
;; guard has to pass here as surely as it has to fail there, or every
|
||||
@ -102,7 +102,7 @@
|
||||
(match v
|
||||
(List items)
|
||||
(do (push items (Value.Int {.n (i64 1)}))
|
||||
(println (len items)))
|
||||
(println (length items)))
|
||||
_ (println 0))))
|
||||
(free-all frame))))
|
||||
(arena-destroy frame)
|
||||
|
||||
@ -52,7 +52,7 @@
|
||||
(Int n) n
|
||||
(List items)
|
||||
(let [t (i64 0)]
|
||||
(dotimes [i (len items)]
|
||||
(dotimes [i (length items)]
|
||||
(set t (+ t (total (at items i)))))
|
||||
t)
|
||||
(Table entries)
|
||||
@ -66,9 +66,9 @@
|
||||
(push outer (number-list (+ i 2))))
|
||||
(push outer (a-table))
|
||||
(push outer Value.Nil)
|
||||
(println (len outer))
|
||||
(println (length outer))
|
||||
(let [t (i64 0)]
|
||||
(dotimes [i (len outer)]
|
||||
(dotimes [i (length outer)]
|
||||
(set t (+ t (total (at outer i)))))
|
||||
t)))
|
||||
|
||||
|
||||
@ -87,7 +87,7 @@
|
||||
|
||||
(defn slice-frame [s [u8] lo i32 hi i32] ()
|
||||
(restart-case
|
||||
(do (show "slice" (i64 (len (slice s lo hi))))
|
||||
(do (show "slice" (i64 (length (slice s lo hi))))
|
||||
(set frames (+ frames 1)))
|
||||
(continue [] (set skipped (+ skipped 1)))))
|
||||
|
||||
@ -166,11 +166,11 @@
|
||||
(show "low" low)
|
||||
(show "length" length)
|
||||
(restart-case
|
||||
(do (show "vec-slice" (i64 (len (slice v 0 2))))
|
||||
(do (show "vec-slice" (i64 (length (slice v 0 2))))
|
||||
(set frames (+ frames 1)))
|
||||
(continue [] (set skipped (+ skipped 1))))
|
||||
(restart-case
|
||||
(do (show "vec-slice" (i64 (len (slice v 0 9))))
|
||||
(do (show "vec-slice" (i64 (length (slice v 0 9))))
|
||||
(set frames (+ frames 1)))
|
||||
(continue [] (set skipped (+ skipped 1))))
|
||||
(show "high" high))
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
;; number is not absurd. Signed, deliberately: the comparisons the other
|
||||
;; two checks use are unsigned, and a negative i32 sign-extended to i64
|
||||
;; is a huge unsigned value that sails straight through them.
|
||||
(= n -2) (print (len (slice-from-ptr (addr (at arr 0)) n)))
|
||||
(= n -2) (print (length (slice-from-ptr (addr (at arr 0)) n)))
|
||||
|
||||
:else (println "?"))
|
||||
0))
|
||||
|
||||
@ -2,14 +2,16 @@
|
||||
;;;;
|
||||
;;;; Shadowing a builtin is legal and program-wide within the file that does
|
||||
;;;; it (shadow-builtin.flan is that rule on its own). What it used to cost
|
||||
;;;; was the builtin itself: a (defn len ...) had given up the builtin len for
|
||||
;;;; was the builtin itself: a (defn length ...) had given up the builtin
|
||||
;;;; length for
|
||||
;;;; the whole file, so a definition that meant to *wrap* the builtin was
|
||||
;;;; unbounded recursion instead, and stack-overflowed at run time with
|
||||
;;;; nothing for the compiler to object to.
|
||||
;;;;
|
||||
;;;; builtin/len is the way out, and it is the package qualifier's own
|
||||
;;;; builtin/length is the way out, and it is the package qualifier's own
|
||||
;;;; spelling: rl/draw-fps is draw-fps from the package imported as rl, and
|
||||
;;;; builtin/len is len from the compiler. builtin is a reserved qualifier —
|
||||
;;;; builtin/length is length from the compiler. builtin is a reserved
|
||||
;;;; qualifier —
|
||||
;;;; an import may not take it as an alias — so the two can never be confused
|
||||
;;;; about which one a name means.
|
||||
;;;;
|
||||
@ -19,10 +21,10 @@
|
||||
;;;; qualifier is legal whether or not anything is shadowed; a spelling that
|
||||
;;;; only compiled while some other declaration existed would be a spelling
|
||||
;;;; nobody could write down in advance.
|
||||
;;;; - 5: this file's own len, which is a real wrapper — it is "1 + the
|
||||
;;;; - 5: this file's own length, which is a real wrapper — it is "1 + the
|
||||
;;;; builtin length", and the inner call reaches the builtin rather than
|
||||
;;;; itself. This is the program the earlier lane could not write.
|
||||
;;;; - 4: builtin/len in the same file, beside the bare name that means the
|
||||
;;;; - 4: builtin/length in the same file, beside the bare name that means the
|
||||
;;;; definition. Both spellings, one program, different answers.
|
||||
;;;; - 99: the shadowed operator, unchanged. An operator is a builtin like any
|
||||
;;;; other and shadows like any other.
|
||||
@ -31,16 +33,17 @@
|
||||
;;;; begin with a digit or a sign, so an operator needs no exception here.
|
||||
|
||||
;;; The wrapper, written the way the dead end said could not be written: the
|
||||
;;; inner builtin/len is the compiler's len, and the outer name is this one.
|
||||
;;; inner builtin/length is the compiler's length, and the outer name is this
|
||||
;;; one.
|
||||
;;; Both additions are qualified too, since + is shadowed below and this
|
||||
;;; function wants the arithmetic and not the 99.
|
||||
(defn len [s string] i32 (builtin/+ 1 (builtin/len s)))
|
||||
(defn length [s string] i32 (builtin/+ 1 (builtin/length s)))
|
||||
|
||||
(defn + [a i32 b i32] i32 99)
|
||||
|
||||
(defn main [] ()
|
||||
(println (builtin/max 3 9))
|
||||
(println (len "abcd"))
|
||||
(println (builtin/len "abcd"))
|
||||
(println (length "abcd"))
|
||||
(println (builtin/length "abcd"))
|
||||
(println (+ 1 2))
|
||||
(println (builtin/+ 1 2)))
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
|
||||
;; 3. The view still costs nothing and reads the string's own storage.
|
||||
(let [v (bytes-view "abc")]
|
||||
(println (len v)) ; 3
|
||||
(println (length v)) ; 3
|
||||
(println (at v 2))) ; 99
|
||||
|
||||
;; 4. (bytes s a) names the allocator, like (vec-new T a) and (clone v a):
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
;; The statics, untouched: zero, zero, zero, and an empty Vec that is a real
|
||||
;; empty Vec rather than a placeholder.
|
||||
(print current-color) (print " ") (print (at grid 1 2)) (print " ")
|
||||
(print (.x origin)) (print " ") (print (len bytes)) (println "")
|
||||
(print (.x origin)) (print " ") (print (length bytes)) (println "")
|
||||
|
||||
;; The dyn globals, as their initialisers left them.
|
||||
(print score) (print " ") (print label) (print " ")
|
||||
|
||||
@ -72,13 +72,14 @@
|
||||
(print b) (print " ")
|
||||
(print c) (println ""))
|
||||
|
||||
;; & rest is the tail as a slice, which is an ordinary (slice xs n (len xs))
|
||||
;; & rest is the tail as a slice, which is an ordinary
|
||||
;; (slice xs n (length xs))
|
||||
;; over a local — nothing new, and nothing that outlives the array.
|
||||
(let [xs [1 2 3 4 5]
|
||||
[head & tail] xs]
|
||||
(print "rest ")
|
||||
(print head) (print " ")
|
||||
(print (len tail)) (print " ")
|
||||
(print (length tail)) (print " ")
|
||||
(print (at tail 0)) (print " ")
|
||||
(print (at tail 3)) (println ""))
|
||||
|
||||
@ -88,7 +89,7 @@
|
||||
[p q & rest] xs]
|
||||
(print "empty-tail ")
|
||||
(print (+ p q)) (print " ")
|
||||
(print (len rest)) (println ""))
|
||||
(print (length rest)) (println ""))
|
||||
|
||||
;; Patterns nest through each other: a struct inside an array.
|
||||
(let [ps [(Point {.x 1 .y 2}) (Point {.x 3 .y 4})]
|
||||
@ -97,13 +98,13 @@
|
||||
|
||||
;; A tail of something wider than a machine word. The corpus slices arrays of
|
||||
;; i32, u8 and f32 and nothing else, so this is the one place the desugared
|
||||
;; (slice xs n (len xs)) has to get a struct's stride right rather than a
|
||||
;; (slice xs n (length xs)) has to get a struct's stride right rather than a
|
||||
;; scalar's.
|
||||
(let [ps [(Point {.x 1 .y 2}) (Point {.x 3 .y 4}) (Point {.x 5 .y 6})]
|
||||
[first & others] ps]
|
||||
(print "struct-tail ")
|
||||
(print (.x first)) (print " ")
|
||||
(print (len others)) (print " ")
|
||||
(print (length others)) (print " ")
|
||||
(print (.y (at others 0))) (print " ")
|
||||
(print (.x (at others 1))) (println ""))
|
||||
|
||||
|
||||
@ -4,7 +4,8 @@
|
||||
;;;; class's own name, positional over the slots; the slots are ordinary map
|
||||
;;;; keys, so get and put are how one is read and written and nothing new was
|
||||
;;;; needed for either. What the class adds is the tag, which lives in the
|
||||
;;;; object's header and not in the entries: (len p) is the slot count, no key
|
||||
;;;; object's header and not in the entries: (length p) is the slot count, no
|
||||
;;;; key
|
||||
;;;; a program can write collides with it, and it shows up in exactly three
|
||||
;;;; places -- class-of, equality, and the printed form #point{ :x 1 :y 2}.
|
||||
;;;;
|
||||
@ -57,7 +58,7 @@
|
||||
c (circle 2)]
|
||||
;; The instance is a map, and prints as one with its tag in front.
|
||||
(println p)
|
||||
(println (len p))
|
||||
(println (length p))
|
||||
(println (get p :x))
|
||||
(put p :x 10)
|
||||
(println (get p :x))
|
||||
@ -101,7 +102,7 @@
|
||||
;; An instance is an ordinary dyn value: it goes in a vec, keys a map,
|
||||
;; and is collected like anything else.
|
||||
(let [v [p c]]
|
||||
(println (len v))
|
||||
(println (length v))
|
||||
(println (class-of (at v 1))))
|
||||
|
||||
;; The renamed parameters, in the generic's own order: the first element
|
||||
@ -131,7 +132,7 @@
|
||||
(let [total (point 0 0)]
|
||||
(dotimes [i 50000]
|
||||
(let [q (point i "forty-seven bytes of text to fatten each row")]
|
||||
(put total :x (+ (get total :x) (len q)))))
|
||||
(put total :x (+ (get total :x) (length q)))))
|
||||
(println (get total :x))
|
||||
(println (class-of total)))
|
||||
0)
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
;; The literal, and what it prints as.
|
||||
(let [m {:a 1 :b "two" :xs [1 2 3] :inner {:c 2.5}}]
|
||||
(println m)
|
||||
(println (len m))
|
||||
(println (length m))
|
||||
(println (get m :a))
|
||||
(println (get m :b))
|
||||
(println (get m :xs))
|
||||
@ -35,7 +35,7 @@
|
||||
;; put replaces an equal key's value in place; the length holds still.
|
||||
(put m :a 99)
|
||||
(println (get m :a))
|
||||
(println (len m))
|
||||
(println (length m))
|
||||
|
||||
;; Keywords: identity equality, printing, and the runtime constructor —
|
||||
;; (keyword "a") has to be the same word as the literal :a.
|
||||
@ -48,7 +48,7 @@
|
||||
;; Keys are whole values compared structurally: a text and a keyword are
|
||||
;; two keys, and a vec of numbers can key a map.
|
||||
(put m "a" "text key")
|
||||
(println (len m))
|
||||
(println (length m))
|
||||
(println (get m "a"))
|
||||
(put m [1 2] "vec key")
|
||||
(println (get m [1 2]))
|
||||
@ -65,6 +65,6 @@
|
||||
(set config {:total 0})
|
||||
(dotimes [i 200000]
|
||||
(let [row {:i 1 :s "forty-seven bytes of text to fatten each row" :v [1 2 3]}]
|
||||
(put config :total (+ (get config :total) (len row)))))
|
||||
(put config :total (+ (get config :total) (length row)))))
|
||||
(println (get config :total))
|
||||
0)
|
||||
|
||||
@ -77,7 +77,7 @@
|
||||
;;; An aggregate parameter, which arrives in its slot before the roots are
|
||||
;;; pushed. Zeroing its dyn words over the top of the argument would be a
|
||||
;;; silent miscompile, so the count this returns is the check for it.
|
||||
(defn row-len [r Row] i64 (i64 (len (.rows r))))
|
||||
(defn row-len [r Row] i64 (i64 (length (.rows r))))
|
||||
|
||||
;;; The payload crosses as a pointer to a value in this frame, and the handler
|
||||
;;; below allocates before it reads it.
|
||||
@ -127,7 +127,7 @@
|
||||
(print stalls) (print "\n")
|
||||
(print echoed) (print "\n")
|
||||
(print (.name (.tag registry))) (print "\n")
|
||||
(print (len (.rows registry))) (print "\n")
|
||||
(print (length (.rows registry))) (print "\n")
|
||||
(print (at (.rows registry) 0)) (print "\n")
|
||||
;; The two that mangle alike, read after forty thousand rows of churn
|
||||
;; collected over them many times.
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
;;;;
|
||||
;;;; (vec-new dyn) is not a (Vec dyn) — it is the dyn runtime's own vector, and
|
||||
;;;; its type is dyn like everything else the runtime hands back. That is what
|
||||
;;;; lets push, at and len on it be the dyn operations rather than a
|
||||
;;;; lets push, at and length on it be the dyn operations rather than a
|
||||
;;;; type-erased Vec over eight-byte elements, and it is why no allocator is
|
||||
;;;; named: the storage is the collector's to walk.
|
||||
|
||||
@ -12,13 +12,13 @@
|
||||
(push xs 2.5)
|
||||
(push xs "three")
|
||||
(push xs true)
|
||||
(print (len xs))
|
||||
(print (length xs))
|
||||
(print "\n")
|
||||
(print xs)
|
||||
(print "\n")
|
||||
;; Read back out one at a time, to show that at answers a dyn and that the
|
||||
;; four of them are still four different things.
|
||||
(dotimes [i (len xs)]
|
||||
(dotimes [i (length xs)]
|
||||
(print (at xs i))
|
||||
(print " "))
|
||||
(print "\n")))
|
||||
|
||||
@ -79,7 +79,7 @@
|
||||
;; same header the view points at — so the typed side, asked
|
||||
;; afterwards, already agrees with the push it never made itself.
|
||||
(push dv 40)
|
||||
(print (len v))
|
||||
(print (length v))
|
||||
(print "\n")
|
||||
(print (at v 3))
|
||||
(print "\n"))
|
||||
@ -130,7 +130,7 @@
|
||||
(print dr)
|
||||
(print "\n")
|
||||
(push dr 333)
|
||||
(print (len (at rows 0)))
|
||||
(print (length (at rows 0)))
|
||||
(print "\n")
|
||||
(print (at (at rows 0) 2))
|
||||
(print "\n"))
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
;; 54 pairs, and the same three memberships and one miss. A derivation
|
||||
;; that flattened the pairs into 108 integers would have a different count
|
||||
;; and would answer no to every one of these.
|
||||
(println (len (.selected-cells t)))
|
||||
(println (length (.selected-cells t)))
|
||||
(println (has-key? (.selected-cells t) [3 4]))
|
||||
(println (has-key? (.selected-cells t) [0 0]))
|
||||
(println (has-key? (.selected-cells t) [4 11]))
|
||||
@ -47,11 +47,11 @@
|
||||
(println (.hp t))
|
||||
(println (.speed t))
|
||||
(println (if (.boss? t) "yes" "no"))
|
||||
(println (len (.drops t)))
|
||||
(println (length (.drops t)))
|
||||
;; 3 + 1 + 4 + 1 + 5. A vector read that stopped at the first element would
|
||||
;; still have a plausible length from a zeroed Vec, so the sum is the claim.
|
||||
(let [total (i64 0)]
|
||||
(dotimes [i (len (.drops t))]
|
||||
(dotimes [i (length (.drops t))]
|
||||
(set total (+ total (at (.drops t) i))))
|
||||
(println total))
|
||||
;; The nested structs, by the names the paths give them: Tuning-hitbox and
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
(let [doc (edn/read tileset)
|
||||
cells (get doc :selected-cells)]
|
||||
(println (get doc :texture-path))
|
||||
(println (len cells))
|
||||
(println (length cells))
|
||||
;; Two cells that are in the file and one that is not. A reader that
|
||||
;; flattened the pairs into 108 integers would still have the right count
|
||||
;; of *something*, and would miss all of these; [3 4] answering yes where
|
||||
@ -55,7 +55,7 @@
|
||||
;; The size of a set after the dedup, which is the whole of what the dedup can
|
||||
;; be asked for.
|
||||
(defn set-size [src string] ()
|
||||
(println (len (edn/read (bytes-view src)))))
|
||||
(println (length (edn/read (bytes-view src)))))
|
||||
|
||||
;; Malformed input, told apart from the document `nil` by the cursor — the
|
||||
;; return value alone cannot say it, and this is the spelling that can.
|
||||
@ -73,7 +73,7 @@
|
||||
(append (addr buf) (bytes-view "{:name \"level-1\" :xs [1 2]}"))
|
||||
(let [src (slice buf)
|
||||
v (edn/read src)]
|
||||
(dotimes [i (len src)]
|
||||
(dotimes [i (length src)]
|
||||
(set (at src i) \x))
|
||||
(println (get v :name)))))
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
(defn main [] i32
|
||||
;; The default answer is a [u8]: bytes, because that is what an asset is.
|
||||
(let [a (embed "assets/a.txt")]
|
||||
(println (len a)) ; 13
|
||||
(println (length a)) ; 13
|
||||
(print (string a))) ; hello from a
|
||||
|
||||
;; `string` is the second spelling, not a different meaning for the same
|
||||
@ -32,7 +32,7 @@
|
||||
;; escape hex-escapes everything outside printable ASCII, so a PNG makes the
|
||||
;; round trip through the .ll unchanged.
|
||||
(let [raw (embed "assets/raw.bin")]
|
||||
(println (len raw)) ; 4
|
||||
(println (length raw)) ; 4
|
||||
(println (at raw 0)) ; 0
|
||||
(println (at raw 2)) ; 255
|
||||
(println (at raw 3))) ; 254
|
||||
@ -40,7 +40,7 @@
|
||||
;; A directory embed is a fixed array of EmbedFile, sorted by name — sorted
|
||||
;; because readdir order is filesystem-dependent and an unsorted embed would
|
||||
;; make two builds of identical sources emit different .ll.
|
||||
(println (len assets)) ; 3
|
||||
(println (length assets)) ; 3
|
||||
(println (.name (at assets 0))) ; a.txt
|
||||
(println (.name (at assets 1))) ; b.bin
|
||||
(println (.name (at assets 2))) ; raw.bin
|
||||
@ -48,7 +48,7 @@
|
||||
;; The name-to-bytes lookup is a linear scan in the prelude. It takes a slice
|
||||
;; rather than the array, because an array's length is part of its type and
|
||||
;; there are no generics.
|
||||
(let [all (slice assets 0 (len assets))]
|
||||
(let [all (slice assets 0 (length assets))]
|
||||
(match (embed-find all "b.bin")
|
||||
(Some b) (println (string b)) ; BBB
|
||||
None (println "missing"))
|
||||
|
||||
@ -53,7 +53,7 @@
|
||||
;; push that failed is re-attempted. No push is lost: a failed push
|
||||
;; appends nothing and the retry appends exactly once.
|
||||
(dotimes [i 64] (push v (* i 2)))
|
||||
(println (len v)) ; 64
|
||||
(println (length v)) ; 64
|
||||
(println (at v 0)) ; 0
|
||||
(println (at v 63)) ; 126
|
||||
(free v)))
|
||||
@ -76,11 +76,11 @@
|
||||
(invoke-restart 'retry))]
|
||||
(let [v (vec-new i32 tight)]
|
||||
(reserve v 256)
|
||||
(println (len v)) ; 0
|
||||
(println (length v)) ; 0
|
||||
(dotimes [i 8] (push v i))
|
||||
(set-alloc-budget tight 4128)
|
||||
(let [w (clone v)]
|
||||
(println (len w)) ; 8
|
||||
(println (length w)) ; 8
|
||||
(println (at w 7)) ; 7
|
||||
(free w))
|
||||
(free v)))
|
||||
@ -106,7 +106,7 @@
|
||||
(set-alloc-budget tight 4096)
|
||||
(invoke-restart 'retry))]
|
||||
(let [b (bytes "INSERTIONSORT" tight)]
|
||||
(println (len b)) ; 13 — the whole string, not a prefix
|
||||
(println (length b)) ; 13 — the whole string, not a prefix
|
||||
(println (at b 0)) ; 73 — \I
|
||||
(println (at b 12)) ; 84 — \T, the last byte
|
||||
;; Writable, which is the point of the copy, and the literal is untouched.
|
||||
|
||||
@ -11,12 +11,12 @@
|
||||
;; The shape map/filter/reduce want: the function arrives as a parameter, is
|
||||
;; called, and is never stored.
|
||||
(defn each [xs [i32] f (Fn [i32] i32)] ()
|
||||
(dotimes [i (len xs)]
|
||||
(dotimes [i (length xs)]
|
||||
(set (at xs i) (f (at xs i)))))
|
||||
|
||||
(defn fold [xs [i32] f (Fn [i32] i32)] i32
|
||||
(let [t 0]
|
||||
(dotimes [i (len xs)]
|
||||
(dotimes [i (length xs)]
|
||||
(set t (+ t (f (at xs i)))))
|
||||
t))
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
;; told the order rather than having it written in. Insertion sort, because the
|
||||
;; point here is the parameter and not the algorithm.
|
||||
(defn insertion-by [xs [i32] before? (Fn [i32 i32] bool)] ()
|
||||
(dotimes [i (len xs)]
|
||||
(dotimes [i (length xs)]
|
||||
(let [j i]
|
||||
(while (and (> j 0) (before? (at xs j) (at xs (- j 1))))
|
||||
(swap xs j (- j 1))
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
(defn sorted [xs [$t]] (Vec $t)
|
||||
{:where (ordered? $t)}
|
||||
(let [v (vec-new $t)]
|
||||
(dotimes [i (len xs)] (push v (at xs i)))
|
||||
(dotimes [i (length xs)] (push v (at xs i)))
|
||||
(sort (slice v))
|
||||
v))
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
(defn sorted-in [xs [$t] al Allocator] (Vec $t)
|
||||
{:where (ordered? $t)}
|
||||
(let [v (vec-new $t al)]
|
||||
(dotimes [i (len xs)] (push v (at xs i)))
|
||||
(dotimes [i (length xs)] (push v (at xs i)))
|
||||
(sort (slice v))
|
||||
v))
|
||||
|
||||
@ -38,9 +38,9 @@
|
||||
(defn distinct-count [ks [$k]] i32
|
||||
{:where (hashable? $k)}
|
||||
(let [m (map-new $k i32)]
|
||||
(dotimes [i (len ks)]
|
||||
(dotimes [i (length ks)]
|
||||
(put m (at ks i) 1))
|
||||
(let [n (len m)] (free m) n)))
|
||||
(let [n (length m)] (free m) n)))
|
||||
|
||||
;; The zeroed fixed array. Its length is still a compile-time constant; only
|
||||
;; the element type is the variable, and that is answered per copy.
|
||||
@ -74,7 +74,7 @@
|
||||
(println (at (slice a) 0)) ; 1
|
||||
(println (at (slice a) 3)) ; 9
|
||||
(println (at (slice b) 0)) ; 0.5
|
||||
(println (len (slice b))) ; 3
|
||||
(println (length (slice b))) ; 3
|
||||
(free a)
|
||||
(free b))
|
||||
|
||||
@ -105,7 +105,7 @@
|
||||
(invoke-restart 'retry))]
|
||||
(let [ns [9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4]
|
||||
d (sorted-in (slice ns 0 16) tight)]
|
||||
(println (len (slice d))) ; 16
|
||||
(println (length (slice d))) ; 16
|
||||
(println (at (slice d) 0)) ; 0
|
||||
(free d)))
|
||||
(println (> failures 0)) ; true
|
||||
|
||||
@ -23,12 +23,12 @@
|
||||
;; The variable is bound *inside* a type constructor, which is a structural
|
||||
;; walk rather than a name match.
|
||||
(defn first-or [s [$t] d $t] $t
|
||||
(if (= (len s) 0) d (at s 0)))
|
||||
(if (= (length s) 0) d (at s 0)))
|
||||
|
||||
;; A generic calling a generic at its own variable: the copy of [swap] is
|
||||
;; generated when [rotate] is instantiated and not before.
|
||||
(defn rotate [s [$t]] ()
|
||||
(dotimes [i (- (len s) 1)]
|
||||
(dotimes [i (- (length s) 1)]
|
||||
(swap s i (+ i 1))))
|
||||
|
||||
;; numeric? admits + - * / %.
|
||||
@ -41,7 +41,7 @@
|
||||
(defn count-of [s [$t] x $t] i32
|
||||
{:where (equal? $t)}
|
||||
(let [n 0]
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(when (= (at s i) x)
|
||||
(set n (+ n 1))))
|
||||
n))
|
||||
@ -230,8 +230,8 @@
|
||||
empty (vec-new u8)]
|
||||
(push full 65)
|
||||
(push full 66)
|
||||
(println (len (or-else (Some full) empty))) ; 2, full's header
|
||||
(println (len (or-else (none-vec) empty))) ; 0, empty's
|
||||
(println (length (or-else (Some full) empty))) ; 2, full's header
|
||||
(println (length (or-else (none-vec) empty))) ; 0, empty's
|
||||
(free full)
|
||||
(free empty))
|
||||
|
||||
@ -246,7 +246,7 @@
|
||||
(let [a (arena-new 4096)
|
||||
keep (filter (slice ns 0 4) (fn [x] (> x 5)))
|
||||
one (one-of 4.5)]
|
||||
(println (len (slice keep)))
|
||||
(println (length (slice keep)))
|
||||
(println (at (slice one) 0))
|
||||
(free one)
|
||||
(free keep)
|
||||
|
||||
@ -73,7 +73,7 @@
|
||||
;; program's when main runs.
|
||||
(push names 11)
|
||||
(push names 22)
|
||||
(println (len (slice names)))
|
||||
(println (length (slice names)))
|
||||
(println (at names 1))
|
||||
(free names)
|
||||
;; And the arena, used the way sand.flan means to use it.
|
||||
|
||||
@ -169,7 +169,7 @@
|
||||
(let [v (vec-new i32)]
|
||||
(push v 1)
|
||||
(error (Missing {.id n}))
|
||||
(i32 (len v))))
|
||||
(i32 (length v))))
|
||||
[(Missing [c] (+ 20 (.id c)))]))
|
||||
|
||||
(defn main [] i32
|
||||
@ -226,6 +226,6 @@
|
||||
(arena-destroy frame)
|
||||
(let [h (vec-new i32)]
|
||||
(push h 9)
|
||||
(print (len h)) (println "")
|
||||
(print (length h)) (println "")
|
||||
(free h))
|
||||
0)
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
;; filter allocates and the caller frees.
|
||||
(let [v (filter s odd?)]
|
||||
(print (len v)) (print " ") (print (at v 0)) (println "")
|
||||
(print (length v)) (print " ") (print (at v 0)) (println "")
|
||||
(free v))
|
||||
|
||||
;; A comparator sort, both directions off the same slice.
|
||||
@ -43,7 +43,7 @@
|
||||
(print (at t 0)) (print " ") (print (at t 2)) (println "")
|
||||
(print (reduce t 0.0 (fn [a b] (+ a b)))) (println "")
|
||||
(let [w (filter t big?)]
|
||||
(print (len w)) (println "")
|
||||
(print (length w)) (println "")
|
||||
(free w))
|
||||
(sort-by t (fn [a b] (> a b)))
|
||||
(print (at t 0)) (print " ") (print (at t 3)) (println ""))
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
;;;; The reserved qualifier, from the import's side. Refused, never built.
|
||||
;;;;
|
||||
;;;; builtin/len means the compiler's len and must go on meaning it, which is
|
||||
;;;; builtin/length means the compiler's length and must go on meaning it,
|
||||
;;;; which is
|
||||
;;;; only true while nothing else can produce the qualifier "builtin". Every
|
||||
;;;; qualifier in a finished program comes from an import's alias, so refusing
|
||||
;;;; that one alias is the whole of the reservation — there is no other door.
|
||||
|
||||
@ -64,7 +64,7 @@
|
||||
(defn total [xs [$t]] i32
|
||||
{:where (integer? $t)}
|
||||
(let [acc 0]
|
||||
(dotimes [i (len xs)] (set acc (+ acc (i32 (at xs i)))))
|
||||
(dotimes [i (length xs)] (set acc (+ acc (i32 (at xs i)))))
|
||||
acc))
|
||||
|
||||
;; The same rule widening. i64 → f64 rounds above 2^53 and i32 → f64 does
|
||||
@ -74,8 +74,8 @@
|
||||
(defn mean [xs [$t]] f64
|
||||
{:where (integer? $t)}
|
||||
(let [sum 0.0]
|
||||
(dotimes [i (len xs)] (set sum (+ sum (f64 (at xs i)))))
|
||||
(/ sum (f64 (len xs)))))
|
||||
(dotimes [i (length xs)] (set sum (+ sum (f64 (at xs i)))))
|
||||
(/ sum (f64 (length xs)))))
|
||||
|
||||
;; numeric? is the weaker bound and narrowing is legal under it too, because
|
||||
;; it is legal at every type it admits: (i32 x) on a written f64 truncates
|
||||
|
||||
@ -33,13 +33,13 @@
|
||||
;; see, so an owning temporary here would be a leak nobody can reach.
|
||||
(defn source [xs [i32]] [i32]
|
||||
(set builds (+ builds 1))
|
||||
(slice xs 0 (len xs)))
|
||||
(slice xs 0 (length xs)))
|
||||
|
||||
(defn wide [x i32] f32 (f32 x))
|
||||
(defn bigf? [x f32] bool (> x 2.5))
|
||||
|
||||
(defn show [v [i32]] ()
|
||||
(dotimes [i (len v)] (print (at v i)) (print " "))
|
||||
(dotimes [i (length v)] (print (at v i)) (print " "))
|
||||
(println ""))
|
||||
|
||||
(defn main [] i32
|
||||
@ -80,7 +80,7 @@
|
||||
;; each stage reads the stage before it.
|
||||
(let [xs [1 2 3 4]
|
||||
v (into xs (vec-new f32) (map wide) (filter bigf?))]
|
||||
(dotimes [i (len v)] (print (at v i)) (print " "))
|
||||
(dotimes [i (length v)] (print (at v i)) (print " "))
|
||||
(println "") ; 3 4
|
||||
(free v))
|
||||
|
||||
|
||||
@ -23,11 +23,11 @@
|
||||
(println (.port cfg))
|
||||
(println (.scale cfg))
|
||||
(println (if (.debug cfg) "yes" "no"))
|
||||
(println (len (.layers cfg)))
|
||||
(println (length (.layers cfg)))
|
||||
;; 3 + 1 + 4 + 1 + 5. A length alone would pass on a Vec that was allocated
|
||||
;; and never filled, so the sum is the claim.
|
||||
(let [total (i64 0)]
|
||||
(dotimes [i (len (.layers cfg))]
|
||||
(dotimes [i (length (.layers cfg))]
|
||||
(set total (+ total (at (.layers cfg) i))))
|
||||
(println total))
|
||||
;; The nested structs, by the names the paths give them: Config-window and
|
||||
|
||||
@ -205,7 +205,7 @@
|
||||
(match v
|
||||
(Array items)
|
||||
(let [n 0]
|
||||
(dotimes [i (len items)]
|
||||
(dotimes [i (length items)]
|
||||
(set n (+ n (count-leaves (at items i)))))
|
||||
n)
|
||||
;; map-next fills an out-parameter with a copy of the value's bytes, which
|
||||
@ -227,7 +227,7 @@
|
||||
(Int n) n
|
||||
(Array items)
|
||||
(let [t (i64 0)]
|
||||
(dotimes [i (len items)]
|
||||
(dotimes [i (length items)]
|
||||
(set t (+ t (sum-ints (at items i)))))
|
||||
t)
|
||||
(Object entries)
|
||||
@ -419,11 +419,11 @@
|
||||
;; of twelve; and the eight one-character escapes are eight bytes.
|
||||
(println (text-at v "name"))
|
||||
(println (text-at v "note"))
|
||||
(println (len (text-at v "note")))
|
||||
(println (len (text-at v "esc")))
|
||||
(println (length (text-at v "note")))
|
||||
(println (length (text-at v "esc")))
|
||||
;; And now the source buffer is destroyed under the live document. An
|
||||
;; implementation that aliased it prints question marks from here on.
|
||||
(dotimes [i (len buf)]
|
||||
(dotimes [i (length buf)]
|
||||
(set (at buf i) \?))
|
||||
(println (text-at v "name"))
|
||||
(println (text-at v "nothing"))
|
||||
|
||||
37
test/programs/len-bound.flan
Normal file
37
test/programs/len-bound.flan
Normal file
@ -0,0 +1,37 @@
|
||||
;;;; `len` as an ordinary name, which is the whole reason the count is spelled
|
||||
;;;; `length`.
|
||||
;;;;
|
||||
;;;; Shadowing already made a `(defn len ...)` legal, and `builtin/len` already
|
||||
;;;; made the builtin reachable past one. What is left, and what this file is,
|
||||
;;;; is that `len` is not a builtin at all: nothing warns, nothing needs the
|
||||
;;;; qualifier, and the name is free in every position a name can be in. A
|
||||
;;;; global called `len` is the one case that cannot also be here — this is a
|
||||
;;;; Lisp-1 and a defn and a defonce may not share a name — so it is pinned in
|
||||
;;;; test_flan instead. `len-gone.flan` is the other half: the call to the name
|
||||
;;;; nothing defines.
|
||||
;;;;
|
||||
;;;; Four lines. In order: the local (3), the parameter doubled (6), the defn
|
||||
;;;; reached by its bare name (2), and the local again over a Vec (3).
|
||||
|
||||
(defn twice-len [len i32] i32
|
||||
(* len 2))
|
||||
|
||||
;;; A defn takes the name too, and a call to it is this program's. Nothing is
|
||||
;;; shadowed here — if `len` were still a builtin this definition would carry
|
||||
;;; a warning, and shadow-builtin.flan is where that sentence is pinned.
|
||||
(defn len [s [i32]] i32
|
||||
(length s))
|
||||
|
||||
(defn main [] ()
|
||||
(let [xs [10 20 30]]
|
||||
;; The shape the long word exists for: the count in a binding called len.
|
||||
(let [len (length xs)]
|
||||
(println len)
|
||||
(println (twice-len len)))
|
||||
;; And the bare name as a call, where no local has taken it.
|
||||
(println (len (slice xs 0 2))))
|
||||
(let [v (vec-new i32)]
|
||||
(push v 1) (push v 2) (push v 3)
|
||||
(let [len (length v)]
|
||||
(println len))
|
||||
(free v)))
|
||||
15
test/programs/len-gone.flan
Normal file
15
test/programs/len-gone.flan
Normal file
@ -0,0 +1,15 @@
|
||||
;;;; The count is `length`, and `len` is left to programs — it is the obvious
|
||||
;;;; name for a local holding one, and a builtin sitting on it would take that
|
||||
;;;; away. So a call to a `len` this program has not defined is a call to a
|
||||
;;;; name nothing defines, and the refusal has to say the word that does work
|
||||
;;;; and write the call out with it. `len-bound.flan` is the other half: the
|
||||
;;;; name binds like any other.
|
||||
;;;;
|
||||
;;;; The sentence is said rather than guessed at. `len` and `length` are three
|
||||
;;;; edits apart and the did-you-mean's net is one, so nothing else in the
|
||||
;;;; compiler would reach it.
|
||||
|
||||
(defonce a [4 i32])
|
||||
|
||||
(defn main [] i32
|
||||
(len a))
|
||||
@ -48,7 +48,7 @@
|
||||
;; [& args]. Every macro in the tree was migrated to this line, so it is the
|
||||
;; one that has to keep working unchanged.
|
||||
(defmacro all-of [& args]
|
||||
(if (= (len args) 0)
|
||||
(if (= (length args) 0)
|
||||
`true
|
||||
`(if ~(at args 0) (all-of ~@(form-rest args 1)) false)))
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
;;;;
|
||||
;;;; Every macro below is written [& args] and picks its arguments apart by
|
||||
;;;; hand, which is what a macro looked like before parameter lists: & binds
|
||||
;;;; the whole call as a slice of forms, and (len args) is how many were
|
||||
;;;; the whole call as a slice of forms, and (length args) is how many were
|
||||
;;;; written. macro-params.flan is the other spelling of the same grammar —
|
||||
;;;; named parameters, [ ] patterns, & only for the tail — and the two files
|
||||
;;;; are kept apart on purpose, so that each is a whole program in one style.
|
||||
@ -58,7 +58,7 @@
|
||||
;; conditional macro in every Lisp is. It gets smaller each time and stops at
|
||||
;; the empty case, so the expander's fuel never comes into it.
|
||||
(defmacro all-of [& args]
|
||||
(if (= (len args) 0)
|
||||
(if (= (length args) 0)
|
||||
`true
|
||||
`(if ~(at args 0) (all-of ~@(form-rest args 1)) false)))
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
;; puts nothing, and the retry puts exactly once — so no entry is lost
|
||||
;; and none is doubled.
|
||||
(dotimes [i 300] (put m i (* (i64 i) 7)))
|
||||
(println (len m)) ; 300
|
||||
(println (length m)) ; 300
|
||||
(let [bad 0]
|
||||
(dotimes [i 300]
|
||||
(match (get m i)
|
||||
@ -66,11 +66,11 @@
|
||||
(invoke-restart 'retry))]
|
||||
(let [m (map-new string i32 tight)]
|
||||
(reserve m 200)
|
||||
(println (len m)) ; 0
|
||||
(println (length m)) ; 0
|
||||
(put m "a" 1)
|
||||
(put m "b" 2)
|
||||
(let [w (clone m)]
|
||||
(println (len w)) ; 2
|
||||
(println (length w)) ; 2
|
||||
(match (get w "b")
|
||||
(Some v) (println v) ; 2
|
||||
None (println "missing"))
|
||||
|
||||
@ -76,7 +76,7 @@
|
||||
xs 0
|
||||
ys 0]
|
||||
(while (map-next m (addr cur) (addr k) (addr v))
|
||||
(set chars (+ chars (len k)))
|
||||
(set chars (+ chars (length k)))
|
||||
(set xs (+ xs (.x v)))
|
||||
(set ys (+ ys (.y v))))
|
||||
(print chars) (print " ") (print xs) (print " ") (print ys) (println ""))
|
||||
@ -97,7 +97,7 @@
|
||||
(while (map-next m (addr cur) (addr k) (addr v))
|
||||
(set n (+ n 1))
|
||||
(when (= v (* k 2)) (set doubled (+ doubled 1))))
|
||||
(print n) (print " ") (print doubled) (print " ") (print (len m)) (println ""))
|
||||
(print n) (print " ") (print doubled) (print " ") (print (length m)) (println ""))
|
||||
(free m)))
|
||||
|
||||
(defn main [] i32
|
||||
|
||||
@ -22,10 +22,10 @@
|
||||
(match (map-remove m 1)
|
||||
(Some v) (do (print v) (println "")) ; 100
|
||||
None (println "missing"))
|
||||
(print (len m)) (println "") ; 1
|
||||
(print (length m)) (println "") ; 1
|
||||
(print (has-key? m 1)) (println "") ; false
|
||||
(match (map-remove m 1) (Some v) (do (print v) (println "")) None (println "gone"))
|
||||
(println (len m)) ; gone, then 1
|
||||
(println (length m)) ; gone, then 1
|
||||
(free m))
|
||||
|
||||
;; (2) Removing every entry empties the map, and it is usable afterwards:
|
||||
@ -33,10 +33,10 @@
|
||||
(let [m (map-new i32 i32)]
|
||||
(dotimes [i 64] (put m i i))
|
||||
(dotimes [i 64] (map-remove m i))
|
||||
(print (len m)) (println "") ; 0
|
||||
(print (length m)) (println "") ; 0
|
||||
(put m 7 77)
|
||||
(match (get m 7) (Some v) (do (print v) (println "")) None (println "?")) ; 77
|
||||
(print (len m)) (println "") ; 1
|
||||
(print (length m)) (println "") ; 1
|
||||
(free m))
|
||||
|
||||
;; (3) The survivors, which is the row that matters. 2000 entries is eight
|
||||
@ -53,7 +53,7 @@
|
||||
(Some v) (if (= v (* (i64 i) 3)) (set taken (+ taken 1)))
|
||||
None (set taken taken))))
|
||||
(print taken) (println "")) ; 1000
|
||||
(print (len m)) (println "") ; 1000
|
||||
(print (length m)) (println "") ; 1000
|
||||
(let [lost 0 ghosts 0]
|
||||
(dotimes [i 2000]
|
||||
(match (get m i)
|
||||
@ -65,7 +65,7 @@
|
||||
;; Re-inserting what was taken out puts the length back, through no grow:
|
||||
;; the block still has the room the removals freed up.
|
||||
(dotimes [i 2000] (if (= 0 (% i 2)) (put m i (* (i64 i) 3))))
|
||||
(print (len m)) (println "") ; 2000
|
||||
(print (length m)) (println "") ; 2000
|
||||
(free m))
|
||||
|
||||
;; (4) A struct key and a string key, so the emitted hash/equality pair is
|
||||
@ -77,7 +77,7 @@
|
||||
None (println "?"))
|
||||
(print (has-key? g (Cell {.x 7 .y 9}))) (println "") ; false
|
||||
(print (has-key? g (Cell {.x 7 .y 10}))) (println "") ; true
|
||||
(print (len g)) (println "") ; 399
|
||||
(print (length g)) (println "") ; 399
|
||||
(free g))
|
||||
|
||||
(let [s (map-new string i32)]
|
||||
@ -87,7 +87,7 @@
|
||||
(Some v) (do (print v) (println "")) ; 1
|
||||
None (println "?"))
|
||||
(print (has-key? s "beta")) (println "") ; true
|
||||
(print (len s)) (println "") ; 1
|
||||
(print (length s)) (println "") ; 1
|
||||
(free s))
|
||||
|
||||
;; (5) Iteration after removals answers exactly the survivors. The cursor is
|
||||
@ -102,7 +102,7 @@
|
||||
(if (not (= k v)) (set sum (+ sum 1))))
|
||||
(print seen) (println "") ; 66
|
||||
(print sum) (println "")) ; 0
|
||||
(print (len m)) (println "") ; 66
|
||||
(print (length m)) (println "") ; 66
|
||||
(free m))
|
||||
|
||||
;; (6) An arena, which refuses can-free. Removal asks the allocator for
|
||||
@ -114,7 +114,7 @@
|
||||
(let [t (map-new i32 i32)]
|
||||
(dotimes [i 300] (put t i (* i 2)))
|
||||
(dotimes [i 300] (if (= 0 (% i 2)) (map-remove t i)))
|
||||
(print (len t)) (println "") ; 150
|
||||
(print (length t)) (println "") ; 150
|
||||
(match (get t 299) (Some v) (do (print v) (println "")) None (println "?")) ; 598
|
||||
(print (has-key? t 298)) (println ""))) ; false
|
||||
(free-all ar))
|
||||
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
;; Takes the map, which is a move: this owns it now, and frees it.
|
||||
(defn consume-grid [m (Map Cell2 i32)] i32
|
||||
(let [n (len m)]
|
||||
(let [n (length m)]
|
||||
(free m)
|
||||
n))
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
(let [m (map-new i32 i64)]
|
||||
(dotimes [i 2000]
|
||||
(put m i (* (i64 i) 3)))
|
||||
(print (len m)) (println "") ; 2000
|
||||
(print (length m)) (println "") ; 2000
|
||||
(let [bad 0]
|
||||
(dotimes [i 2000]
|
||||
(match (get m i)
|
||||
@ -53,7 +53,7 @@
|
||||
(dotimes [i 40]
|
||||
(dotimes [j 40]
|
||||
(put g (Cell {.x i .y j}) (+ (* i 100) j))))
|
||||
(print (len g)) (println "") ; 1600
|
||||
(print (length g)) (println "") ; 1600
|
||||
(match (get g (Cell {.x 7 .y 9}))
|
||||
(Some v) (do (print v) (println "")) ; 709
|
||||
None (println "missing"))
|
||||
@ -68,7 +68,7 @@
|
||||
(put n (Named {.tag "alpha" .n 1}) 10)
|
||||
(put n (Named {.tag "alpha" .n 2}) 20)
|
||||
(put n (Named {.tag "beta" .n 1}) 30)
|
||||
(print (len n)) (println "") ; 3
|
||||
(print (length n)) (println "") ; 3
|
||||
(match (get n (Named {.tag "alpha" .n 2}))
|
||||
(Some v) (do (print v) (println "")) ; 20
|
||||
None (println "missing"))
|
||||
@ -79,7 +79,7 @@
|
||||
(let [s (map-new Suit i32)]
|
||||
(put s :hearts 1)
|
||||
(put s :clubs 3)
|
||||
(print (len s)) (println "") ; 2
|
||||
(print (length s)) (println "") ; 2
|
||||
(match (get s :clubs)
|
||||
(Some v) (do (print v) (println "")) ; 3
|
||||
None (println "missing"))
|
||||
@ -97,7 +97,7 @@
|
||||
(put b 1 999)
|
||||
(match (get a 1) (Some v) (do (print v) (println "")) None (println "?")) ; 100
|
||||
(match (get b 1) (Some v) (do (print v) (println "")) None (println "?")) ; 999
|
||||
(print (len b)) (println "") ; 2
|
||||
(print (length b)) (println "") ; 2
|
||||
(free b))
|
||||
(free a))
|
||||
|
||||
@ -108,7 +108,7 @@
|
||||
(put u "k" 1)
|
||||
(put u "k" 2)
|
||||
(put u "k" 3)
|
||||
(print (len u)) (println "") ; 1
|
||||
(print (length u)) (println "") ; 1
|
||||
(match (get u "k") (Some v) (do (print v) (println "")) None (println "?")) ; 3
|
||||
(free u))
|
||||
|
||||
@ -119,11 +119,11 @@
|
||||
(with-allocator ar
|
||||
(let [t (map-new i32 i32)]
|
||||
(dotimes [i 500] (put t i (* i 2)))
|
||||
(print (len t)) (println "") ; 500
|
||||
(print (length t)) (println "") ; 500
|
||||
(match (get t 499) (Some v) (do (print v) (println "")) None (println "?")))) ; 998
|
||||
(free-all ar))
|
||||
|
||||
(let [g (make-grid)]
|
||||
(print (len g)) (println "") ; 2
|
||||
(print (length g)) (println "") ; 2
|
||||
(print (consume-grid g)) (println "")) ; 2
|
||||
0)
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
;;;; call site two files away.
|
||||
|
||||
(defn last-of [s [$t]] $t
|
||||
(at s (- (len s) 1)))
|
||||
(at s (- (length s) 1)))
|
||||
|
||||
;;; Calls last-of at its own variable: the copy of last-of is generated when
|
||||
;;; this is instantiated, and this is instantiated from the program.
|
||||
@ -28,6 +28,6 @@
|
||||
(defn largest [s [$t]] $t
|
||||
{:where (ordered? $t)}
|
||||
(let [m (at s 0)]
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(set m (max m (at s i))))
|
||||
m))
|
||||
|
||||
@ -11,8 +11,8 @@
|
||||
|
||||
;;; The same question asked where there is no enclosing function to be
|
||||
;;; qualified: a global's initialiser, which runs at startup and is checked
|
||||
;;; with no owner at all. The importer below defines a len of its own; this
|
||||
;;; with no owner at all. The importer below defines a length of its own; this
|
||||
;;; one is the builtin's and this global is 4.
|
||||
(defonce size i32 (len "abcd"))
|
||||
(defonce size i32 (length "abcd"))
|
||||
|
||||
(defn stored-size [] i32 size)
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
(Leaf n) n
|
||||
(Branch kids)
|
||||
(let [s (i64 0)]
|
||||
(dotimes [i (len kids)]
|
||||
(dotimes [i (length kids)]
|
||||
(set s (+ s (total (at kids i)))))
|
||||
s)
|
||||
Empty (i64 0)))
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
(defn nothing [] () )
|
||||
|
||||
(defn find-it [s [i32] k i32] (Option i32)
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(when (= (at s i) k) (return (Some i))))
|
||||
None)
|
||||
|
||||
|
||||
@ -89,7 +89,7 @@
|
||||
(if (= i 3)
|
||||
acc
|
||||
(do (push acc i) (recur acc (+ i 1)))))]
|
||||
(dotimes [i (len v)] (print (at v i)))
|
||||
(dotimes [i (length v)] (print (at v i)))
|
||||
(println "") ; 012
|
||||
(free v))
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
;; else is live by here — sections 1 and 2 both released — so the live
|
||||
;; count *is* the copy's block, and free-all takes it back to zero.
|
||||
(let [b (bytes "copy" frame)]
|
||||
(println (len b)) ; 4 — the string's length
|
||||
(println (length b)) ; 4 — the string's length
|
||||
(println (reg-count 1)) ; dev: 1 — the copy's block
|
||||
(free-all frame)
|
||||
(println (reg-count 1))) ; 0 either way
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
(defn pick [xs [$t]] $t
|
||||
{:where (ordered? $t)}
|
||||
(let [m (at xs 0)]
|
||||
(dotimes [i (len xs)]
|
||||
(dotimes [i (length xs)]
|
||||
(set m (min m (at xs i))))
|
||||
m))
|
||||
|
||||
|
||||
@ -103,7 +103,7 @@
|
||||
|
||||
(defn main [args [string]] i32
|
||||
;; One argument selects a trap; none runs the table's case.
|
||||
(if (> (len args) 1)
|
||||
(if (> (length args) 1)
|
||||
(let [k (i32 (bytes->i64 (bytes-view (at args 1))))]
|
||||
(cond
|
||||
(= k 1) (print (mismatched 90))
|
||||
|
||||
@ -13,9 +13,9 @@
|
||||
;;;; calls the builtin get on a dyn map. The shadow does not follow it
|
||||
;;;; there: a package's calls mean what they meant when it was written.
|
||||
;;;; - 99: an operator is a builtin like any other and shadows like one.
|
||||
;;;; - 999: this file's own len, which is what len means in this file.
|
||||
;;;; - 999: this file's own length, which is what length means in this file.
|
||||
;;;; - 4 again, and it is the one that needed the work: the package's global
|
||||
;;;; initialiser (defonce size i32 (len "abcd")) is checked with no
|
||||
;;;; initialiser (defonce size i32 (length "abcd")) is checked with no
|
||||
;;;; enclosing function at all, so there is no qualified name on it to say
|
||||
;;;; it belongs to a package. The file it was written in says so instead.
|
||||
|
||||
@ -33,13 +33,13 @@
|
||||
|
||||
;;; And a builtin the imported package uses in a *global initialiser*, which
|
||||
;;; is the one place there is no enclosing function to carry a package's
|
||||
;;; qualified name. The package's (defonce size i32 (len "abcd")) is 4; this
|
||||
;;; qualified name. The package's (defonce size i32 (length "abcd")) is 4; this
|
||||
;;; definition answers 999 and is reached only here.
|
||||
(defn len [s string] i32 999)
|
||||
(defn length [s string] i32 999)
|
||||
|
||||
(defn main [] ()
|
||||
(println (get (P {.x 7})))
|
||||
(println (shadowed/field {:a 1 :b 4}))
|
||||
(println (+ 1 2))
|
||||
(println (len "abcd"))
|
||||
(println (length "abcd"))
|
||||
(println (shadowed/stored-size)))
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
;;;;
|
||||
;;;; What is asserted, line by line:
|
||||
;;;;
|
||||
;;;; - the length is the one the caller stated, and (len s) answers it;
|
||||
;;;; - the length is the one the caller stated, and (length s) answers it;
|
||||
;;;; - the elements read through are the same storage, not a copy — the last
|
||||
;;;; two lines write through the slice and read the array back, which is
|
||||
;;;; the whole ptr+len claim;
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
(defn total [s [i32]] i32
|
||||
(let [acc 0]
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(set acc (+ acc (at s i))))
|
||||
acc))
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
;; The whole array, as the caller promises it: five elements behind the
|
||||
;; address of the first.
|
||||
(let [s (slice-from-ptr (addr (at a 0)) 5)]
|
||||
(println (len s)) ; 5
|
||||
(println (length s)) ; 5
|
||||
(println (at s 0)) ; 10
|
||||
(println (at s 4)) ; 50
|
||||
(println (total s)) ; 150
|
||||
@ -46,12 +46,12 @@
|
||||
;; complain with — and the length the caller gave is the length indexing and
|
||||
;; the bounds check both use.
|
||||
(let [s (slice-from-ptr (addr (at a 1)) 2)]
|
||||
(println (len s)) ; 2
|
||||
(println (length s)) ; 2
|
||||
(println (total s))) ; 50
|
||||
|
||||
;; Zero is a length like any other. An empty slice is not a null pointer and
|
||||
;; is not an error.
|
||||
(println (len (slice-from-ptr (addr (at a 0)) 0))) ; 0
|
||||
(println (length (slice-from-ptr (addr (at a 0)) 0))) ; 0
|
||||
|
||||
;; It is a view, not a copy: a write through the slice is a write to the
|
||||
;; array, and this is what would fail if the form ever grew a memcpy.
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
(defonce zs [8 i32])
|
||||
|
||||
(defn show [s [i32]] ()
|
||||
(dotimes [i (len s)]
|
||||
(dotimes [i (length s)]
|
||||
(when (> i 0) (print " "))
|
||||
(print (at s i)))
|
||||
(println ""))
|
||||
@ -32,56 +32,57 @@
|
||||
|
||||
(defn main [] i32
|
||||
(load-xs)
|
||||
(show (slice xs 0 (len xs))) ; 5 -3 5 0 12 -3 7
|
||||
(show (slice xs 0 (length 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-of (slice xs 0 (len xs))) (Some v) v None 99))
|
||||
(print (sum-i32 (slice xs 0 (length xs)))) (println "") ; 23
|
||||
(print (match (min-of (slice xs 0 (length xs))) (Some v) v None 99))
|
||||
(println "") ; -3
|
||||
(print (match (max-of (slice xs 0 (len xs))) (Some v) v None 99))
|
||||
(print (match (max-of (slice xs 0 (length xs))) (Some v) v None 99))
|
||||
(println "") ; 12
|
||||
;; First index, not the last: 5 appears at 0 and at 2.
|
||||
(print (match (index-of (slice xs 0 (len xs)) 5) (Some v) v None -1))
|
||||
(print (match (index-of (slice xs 0 (length xs)) 5) (Some v) v None -1))
|
||||
(println "") ; 0
|
||||
(print (match (index-of (slice xs 0 (len xs)) 4) (Some v) v None -1))
|
||||
(print (match (index-of (slice xs 0 (length xs)) 4) (Some v) v None -1))
|
||||
(println "") ; -1
|
||||
;; An empty slice has no least element, and None is the answer.
|
||||
(print (match (min-of (slice xs 3 3)) (Some v) v None 99))
|
||||
(println "") ; 99
|
||||
|
||||
;; Reverse of an odd-length slice: the middle element stays put.
|
||||
(reverse (slice xs 0 (len xs)))
|
||||
(show (slice xs 0 (len xs))) ; 7 -3 12 0 5 -3 5
|
||||
(reverse (slice xs 0 (length xs)))
|
||||
(show (slice xs 0 (length xs))) ; 7 -3 12 0 5 -3 5
|
||||
;; And of a two-element one, the smallest case that can actually move.
|
||||
(reverse (slice xs 0 2))
|
||||
(show (slice xs 0 (len xs))) ; -3 7 12 0 5 -3 5
|
||||
(show (slice xs 0 (length xs))) ; -3 7 12 0 5 -3 5
|
||||
|
||||
(load-xs)
|
||||
(sort (slice xs 0 (len xs)))
|
||||
(show (slice xs 0 (len xs))) ; -3 -3 0 5 5 7 12
|
||||
(sort (slice xs 0 (length xs)))
|
||||
(show (slice xs 0 (length 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 (slice ys 0 (len ys)))
|
||||
(show (slice ys 0 (len ys))) ; 1 2 3 4 5
|
||||
(sort (slice ys 0 (length ys)))
|
||||
(show (slice ys 0 (length 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 (slice zs 1 6))
|
||||
(show (slice zs 0 (len zs))) ; 100 -1 0 4 9 9 200 300
|
||||
(show (slice zs 0 (length zs))) ; 100 -1 0 4 9 9 200 300
|
||||
|
||||
;; Degenerate lengths must do nothing rather than run off an end.
|
||||
(sort (slice zs 0 0))
|
||||
(reverse (slice zs 0 0))
|
||||
(sort (slice zs 2 3))
|
||||
(reverse (slice zs 2 3))
|
||||
(show (slice zs 0 (len zs))) ; 100 -1 0 4 9 9 200 300
|
||||
(show (slice zs 0 (length zs))) ; 100 -1 0 4 9 9 200 300
|
||||
|
||||
;; ── The short arities ──────────────────────────────────────────────
|
||||
;; (slice a) and (slice a n) are (slice a 0 (len a)) and (slice a n (len a))
|
||||
;; (slice a) and (slice a n) are (slice a 0 (length a)) and
|
||||
;; (slice a n (length a))
|
||||
;; written out in the checker, so each line here is read against the
|
||||
;; spelling above it that it stands for. A fixed array does not decay to a
|
||||
;; slice at a call, so passing one to a function over [$t] is what these
|
||||
|
||||
@ -28,13 +28,13 @@
|
||||
(defn main [] i32
|
||||
;; ── The happy path ────────────────────────────────────────────────
|
||||
(let [v (slurp "programs/assets/a.txt")]
|
||||
(println (len v)) ; 13
|
||||
(println (length v)) ; 13
|
||||
(print (string (slice v))) ; hello from a
|
||||
(free v))
|
||||
|
||||
;; Byte-exact, the same as an embed: nothing here decodes anything.
|
||||
(let [v (slurp "programs/assets/raw.bin")]
|
||||
(println (len v)) ; 4
|
||||
(println (length v)) ; 4
|
||||
(println (at v 0)) ; 0
|
||||
(println (at v 2)) ; 255
|
||||
(free v))
|
||||
@ -51,7 +51,7 @@
|
||||
(set last-path (.path c))
|
||||
(invoke-restart 'use-value "programs/assets/b.bin"))]
|
||||
(let [v (slurp "programs/assets/does-not-exist")]
|
||||
(println (len v)) ; 3
|
||||
(println (length v)) ; 3
|
||||
(println (string (slice v))) ; BBB
|
||||
(free v)))
|
||||
(println seen) ; 1
|
||||
@ -64,7 +64,7 @@
|
||||
;; ── barf, and reading back what it wrote ──────────────────────────
|
||||
(barf "slurp-out.txt" (bytes-view "round trip\n"))
|
||||
(let [v (slurp "slurp-out.txt")]
|
||||
(println (len v)) ; 11
|
||||
(println (length v)) ; 11
|
||||
(print (string (slice v))) ; round trip
|
||||
(free v))
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
(print "[")
|
||||
(print s)
|
||||
(print "] ")
|
||||
(print (len (bytes-view s)))
|
||||
(print (length (bytes-view s)))
|
||||
(println ""))
|
||||
|
||||
(defn main [] i32
|
||||
@ -46,7 +46,7 @@
|
||||
|
||||
;; Round trip: (bytes-view (string b)) is b, and both directions are the identity.
|
||||
(let [b (i64->bytes 1234567)]
|
||||
(print (len (bytes-view (string b))))
|
||||
(print (length (bytes-view (string b))))
|
||||
(println ""))
|
||||
|
||||
;; Across the declare-c boundary. The first is a sub-view — five bytes out of
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
(free c)))
|
||||
(let [parts [(bytes-view "unused")]]
|
||||
(let [c (concat (slice parts 0 0))]
|
||||
(println (len c)) ; 0
|
||||
(println (length c)) ; 0
|
||||
(free c)))
|
||||
|
||||
;; join: n parts, n-1 separators. The one-part case is the one that must not
|
||||
@ -51,7 +51,7 @@
|
||||
(show (addr j)) ; a
|
||||
(free j))
|
||||
(let [j (join (slice parts 0 0) (bytes-view ", "))]
|
||||
(println (len j)) ; 0
|
||||
(println (length j)) ; 0
|
||||
(free j))
|
||||
;; An empty separator is concat.
|
||||
(let [j (join (slice parts 0 3) (bytes-view ""))]
|
||||
@ -63,7 +63,7 @@
|
||||
(show (addr r)) ; ababab
|
||||
(free r))
|
||||
(let [r (repeat-bytes (bytes-view "ab") 0)]
|
||||
(println (len r)) ; 0
|
||||
(println (length r)) ; 0
|
||||
(free r))
|
||||
|
||||
;; The allocating case pair. The input is a string literal, which lives in
|
||||
@ -103,26 +103,26 @@
|
||||
;; present, which is where Odin's own iterator and its allocating split
|
||||
;; disagree with each other.
|
||||
(let [f (split (bytes-view "a,b,c") \,)]
|
||||
(println (len f)) ; 3
|
||||
(println (length f)) ; 3
|
||||
(println (string (at f 0))) ; a
|
||||
(println (string (at f 2))) ; c
|
||||
(free f))
|
||||
(let [f (split (bytes-view "a,b,") \,)]
|
||||
(println (len f)) ; 3
|
||||
(println (len (at f 2))) ; 0
|
||||
(println (length f)) ; 3
|
||||
(println (length (at f 2))) ; 0
|
||||
(free f))
|
||||
(let [f (split (bytes-view ",a") \,)]
|
||||
(println (len f)) ; 2
|
||||
(println (len (at f 0))) ; 0
|
||||
(println (length f)) ; 2
|
||||
(println (length (at f 0))) ; 0
|
||||
(free f))
|
||||
;; No separator at all is one field, and the empty input is one empty field.
|
||||
(let [f (split (bytes-view "abc") \,)]
|
||||
(println (len f)) ; 1
|
||||
(println (length f)) ; 1
|
||||
(println (string (at f 0))) ; abc
|
||||
(free f))
|
||||
(let [f (split (bytes-view "") \,)]
|
||||
(println (len f)) ; 1
|
||||
(println (len (at f 0))) ; 0
|
||||
(println (length f)) ; 1
|
||||
(println (length (at f 0))) ; 0
|
||||
(free f))
|
||||
|
||||
;; The fields are slices of the input and nothing was copied: this one
|
||||
|
||||
@ -66,5 +66,5 @@
|
||||
;; The dyn map literal is untouched by any of this: keyword keys, and a
|
||||
;; .field-keyed brace was never part of that spelling.
|
||||
(let [m {:a 1 :b 2}]
|
||||
(println (len m))
|
||||
(println (length m))
|
||||
(println (get m :b))))
|
||||
|
||||
@ -71,6 +71,6 @@
|
||||
;; PATH is set for every process that gets as far as running this, and the
|
||||
;; only portable thing about its contents is that there are some.
|
||||
(match (getenv "PATH")
|
||||
(Some v) (println (> (len v) 0))
|
||||
(Some v) (println (> (length v) 0))
|
||||
None (println "no PATH"))
|
||||
0)
|
||||
|
||||
@ -130,7 +130,7 @@
|
||||
(print (rune-count (bytes-view ""))) (print " ")
|
||||
(print (rune-count (bytes-view "abc"))) (print " ")
|
||||
(print (rune-count (bytes-view "héllo 日本"))) (print " ")
|
||||
(print (len (bytes-view "héllo 日本"))) (print " ")
|
||||
(print (length (bytes-view "héllo 日本"))) (print " ")
|
||||
(print (rune-count (slice bad-tail 0 3)))
|
||||
(println "")
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
;; Reading it here is a borrow. So is reading it in [total] below, which is the
|
||||
;; case the old rule could not express: two functions holding the same global
|
||||
;; at once is fine exactly because neither of them can free it.
|
||||
(defn loaded? [] bool (> (len the-data) 0))
|
||||
(defn loaded? [] bool (> (length the-data) 0))
|
||||
|
||||
(defn load [] ()
|
||||
(when (not (loaded?))
|
||||
@ -24,7 +24,7 @@
|
||||
|
||||
(defn total [] i64
|
||||
(let [s (i64 0)]
|
||||
(dotimes [i (len the-data)] (set s (+ s (i64 (at the-data i)))))
|
||||
(dotimes [i (length the-data)] (set s (+ s (i64 (at the-data i)))))
|
||||
s))
|
||||
|
||||
;; Mutating in place, through the global rather than through a copy of it. The
|
||||
@ -39,7 +39,7 @@
|
||||
(load)
|
||||
(bump)
|
||||
(put counts 1 (total))
|
||||
(println (len the-data))
|
||||
(println (length the-data))
|
||||
(println (total))
|
||||
(match (get counts 1) (Some v) (println v) None (println "?")))
|
||||
|
||||
@ -48,11 +48,11 @@
|
||||
;; The second run. Nothing re-initialises the global between them, so the
|
||||
;; length keeps climbing and the loaded data is the same block it was.
|
||||
(entry)
|
||||
(println (len (slice the-data)))
|
||||
(println (length (slice the-data)))
|
||||
;; A copy is the one thing something else may own, and freeing that copy
|
||||
;; leaves the global untouched.
|
||||
(let [c (clone the-data)]
|
||||
(println (len c))
|
||||
(println (length c))
|
||||
(free c))
|
||||
(println (len the-data))
|
||||
(println (length the-data))
|
||||
0)
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
;;;; source binding is dead afterwards. That rule is what makes a double free
|
||||
;;;; unrepresentable, which is why `free` needs no analysis of its own.
|
||||
(defn take [v (Vec i32)] i32
|
||||
(let [n (len v)]
|
||||
(let [n (length v)]
|
||||
(free v)
|
||||
n))
|
||||
|
||||
@ -11,5 +11,5 @@
|
||||
(push v 1)
|
||||
(println (take v))
|
||||
;; v went with the call. Being refused here is the whole test.
|
||||
(println (len v))
|
||||
(println (length v))
|
||||
0))
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
;;; Ownership transfers on the call. The caller's binding is dead after this,
|
||||
;;; which is what the refusal cases in test_acceptance assert.
|
||||
(defn consume [v (Vec i32)] i32
|
||||
(let [n (len v)]
|
||||
(let [n (length v)]
|
||||
(free v)
|
||||
n))
|
||||
|
||||
@ -25,16 +25,16 @@
|
||||
|
||||
(defn sum [xs [i32]] i32
|
||||
(let [total 0]
|
||||
(dotimes [i (len xs)] (set total (+ total (at xs i))))
|
||||
(dotimes [i (length xs)] (set total (+ total (at xs i))))
|
||||
total))
|
||||
|
||||
(defn main [] i32
|
||||
(let [v (vec-new i32)]
|
||||
(println (len v)) ; 0
|
||||
(println (length v)) ; 0
|
||||
(push v 10)
|
||||
(push v 20)
|
||||
(push v 30)
|
||||
(println (len v)) ; 3
|
||||
(println (length v)) ; 3
|
||||
(println (at v 0)) ; 10
|
||||
(println (at v 2)) ; 30
|
||||
;; A Vec element is a place, and the same bounds and epoch check stands
|
||||
@ -44,15 +44,15 @@
|
||||
|
||||
;; slice over a Vec is a non-owning view: it copies ptr+len and never the
|
||||
;; elements, and it carries no allocator, so nothing can be freed through
|
||||
;; one. [at] and [len] over it are the array operations, unchanged, and
|
||||
;; one. [at] and [length] over it are the array operations, unchanged, and
|
||||
;; the three arities are the ones every other target has -- the tail form
|
||||
;; included, which the Vec had no spelling for while it had a name of its
|
||||
;; own. The view is of storage v owns: a push here would move it, and
|
||||
;; nothing would say so.
|
||||
(println (sum (slice v))) ; 139
|
||||
(println (len (slice v 1 3))) ; 2
|
||||
(println (length (slice v 1 3))) ; 2
|
||||
(println (at (slice v 1 3) 0)) ; 99
|
||||
(println (len (slice v 1))) ; 2
|
||||
(println (length (slice v 1))) ; 2
|
||||
(println (at (slice v 1) 1)) ; 30
|
||||
|
||||
;; clone is the only copy: assignment moves. The copy is independent, and
|
||||
@ -66,9 +66,9 @@
|
||||
;; reserve does not change the length, only the capacity, so a reserve
|
||||
;; that succeeds is invisible except that the pushes after it do not grow.
|
||||
(reserve v 64)
|
||||
(println (len v)) ; 3
|
||||
(println (length v)) ; 3
|
||||
(push v 40)
|
||||
(println (len v)) ; 4
|
||||
(println (length v)) ; 4
|
||||
|
||||
;; The structural printer reaches both new types. Neither is followed: a
|
||||
;; Vec's elements are printed through (slice v), which says at the call
|
||||
@ -84,7 +84,7 @@
|
||||
(let [ps (vec-new Point)]
|
||||
(push ps (Point {.x 1 .y 2}))
|
||||
(push ps (Point {.x 3 .y 4}))
|
||||
(println (len ps)) ; 2
|
||||
(println (length ps)) ; 2
|
||||
(println (.y (at ps 1))) ; 4
|
||||
(free ps))
|
||||
|
||||
|
||||
@ -411,6 +411,15 @@ let () =
|
||||
chain_out;
|
||||
outputs ~x86:true "chained comparisons, --x86" "programs/chain.flan"
|
||||
chain_out;
|
||||
(* The count is [length] so that [len] is left to programs, and this is
|
||||
the claim that it really is one: a local holding a count, a
|
||||
parameter, and a defn the program calls by its bare name, all of
|
||||
them called len, all of them running. Shadowing and [builtin/] had
|
||||
already made a defn named after a builtin legal; what this adds is
|
||||
that there is no builtin here to be shadowed, so nothing warns and
|
||||
the qualifier is not needed to reach past anything. *)
|
||||
outputs "len is an ordinary name" "programs/len-bound.flan"
|
||||
"3\n6\n2\n3\n";
|
||||
(* A global of move-only type, which this compiler used to refuse outright.
|
||||
What the numbers assert is the half of the rule no checker test can: the
|
||||
global is loaded once and *stays* loaded across a second entry, which is
|
||||
@ -1032,26 +1041,26 @@ let () =
|
||||
refuses anything that is not Addressing_Constant). This is the refusal
|
||||
that keeps the result genuinely free at run time. *)
|
||||
refuses_src "an embedded path that is computed"
|
||||
"(defn main [] i32 (let [p \"x\"] (len (embed p))))"
|
||||
"(defn main [] i32 (let [p \"x\"] (length (embed p))))"
|
||||
"must be a literal string";
|
||||
(* A file that is not there is a compile error naming it, not an empty
|
||||
embed: an asset silently missing is the class of quiet wrongness this
|
||||
whole feature exists to remove. *)
|
||||
refuses_src "an embedded file that does not exist"
|
||||
"(defn main [] i32 (len (embed \"no-such-asset.bin\")))"
|
||||
"(defn main [] i32 (length (embed \"no-such-asset.bin\")))"
|
||||
"cannot embed";
|
||||
(* A directory where a file was meant. On Linux open_in_bin on a directory
|
||||
succeeds and the *read* is where EISDIR arrives, so this was an uncaught
|
||||
exception out of the checker until the whole read was guarded - the one
|
||||
way a user could make the compiler crash rather than refuse. *)
|
||||
refuses_src "embed given a directory"
|
||||
"(defn main [] i32 (len (embed \"programs/assets\")))"
|
||||
"(defn main [] i32 (length (embed \"programs/assets\")))"
|
||||
"it is a directory";
|
||||
|
||||
(* One extra argument, and `string` is the only thing it can be. Two
|
||||
spellings, not one form that changes type with its context. *)
|
||||
refuses_src "embed asked for a type it cannot read a file as"
|
||||
"(defn main [] i32 (len (embed \"no-such-asset.bin\" i32)))"
|
||||
"(defn main [] i32 (length (embed \"no-such-asset.bin\" i32)))"
|
||||
"embed's second argument is string";
|
||||
|
||||
(* Allocators, spec-memory.md. The tier on its own, with no container
|
||||
@ -2741,12 +2750,12 @@ let () =
|
||||
the other reading: 7 is the program's own one-argument (get p), which
|
||||
the builtin get has no arity for at all; 4 is the builtin get called
|
||||
inside the imported package on a dyn map; 99 is an operator shadowed
|
||||
like any other name; 999 is this program's len.
|
||||
like any other name; 999 is this program's length.
|
||||
|
||||
The last 4 is the one that was a bug. It is the package's global
|
||||
initialiser, (defonce size i32 (len "abcd")), which is the one place a
|
||||
initialiser, (defonce size i32 (length "abcd")), which is the one place a
|
||||
call sits inside no function and so carries no package-qualified name
|
||||
— the importer's len reached into it and made it 999. The shadow is
|
||||
— the importer's length reached into it and made it 999. The shadow is
|
||||
decided by the file the definition was written in, and a file is
|
||||
something a global initialiser has.
|
||||
|
||||
@ -2759,8 +2768,9 @@ let () =
|
||||
(* And the way back out of it. builtin/name is the builtin whatever the
|
||||
file has decided the bare name means, so the two spellings sit side by
|
||||
side in one program and answer differently: 9 is builtin/max with
|
||||
nothing named max in the program at all, 5 is a len that is a real
|
||||
wrapper — 1 + the builtin length — 4 is builtin/len beside it, 99 is
|
||||
nothing named max in the program at all, 5 is a length that is a
|
||||
real wrapper — 1 + the builtin length — 4 is builtin/length beside
|
||||
it, 99 is
|
||||
the shadowed operator and 3 is builtin/+.
|
||||
|
||||
The 5 is the line that could not be written before this. The same defn
|
||||
@ -3055,6 +3065,15 @@ let () =
|
||||
and this row is what says so. *)
|
||||
refuses "nth is not a name" "programs/nth-gone.flan"
|
||||
"unknown function nth";
|
||||
(* [len] is not a removal the way [nth] is — it is a name a program may
|
||||
have, and the count is [length]. A program that reached for the
|
||||
short word and defined nothing under it gets the word that works,
|
||||
with its own call written back out: the arguments are spelled from
|
||||
the forms the reader wrote, so what is printed compiles. *)
|
||||
refuses "len is not a builtin" "programs/len-gone.flan"
|
||||
"there is no len";
|
||||
refuses "and the refusal writes the call out" "programs/len-gone.flan"
|
||||
"Write (length a)";
|
||||
(* Still the one thing in the allocator tier that does not work, and the
|
||||
reason changed when function values landed: it *has* a defn's name in
|
||||
value position now. What it does not have is a way to be called — the
|
||||
@ -3750,7 +3769,7 @@ level "1"
|
||||
Unit as a value is refused rather than dividing a cache line by zero,
|
||||
and it is named because it is the natural spelling of a set. *)
|
||||
(* Removal is a map's operation and says so, rather than reaching for a
|
||||
[len] that a Vec would also answer. *)
|
||||
[length] that a Vec would also answer. *)
|
||||
refuses_src "map-remove wants a map"
|
||||
"(defn main [] i32 (let [v (vec-new i32)] (map-remove v 1) (free v)) 0)"
|
||||
"map-remove takes a (Map K V)";
|
||||
@ -4698,7 +4717,7 @@ level "1"
|
||||
end)
|
||||
ds
|
||||
in
|
||||
(* The vec file reports nine: a vec-new, four boxed pushes, a len, an at
|
||||
(* The vec file reports nine: a vec-new, four boxed pushes, a length, an at
|
||||
and the dotimes bound. The floor is under that rather than equal to it
|
||||
so an added line does not fail the test, and well over one so that a
|
||||
pass which named the first site and stopped would. *)
|
||||
@ -5350,7 +5369,7 @@ level "1"
|
||||
first macro hands back a Form the compiler misreads. *)
|
||||
let form_src =
|
||||
"(defn shape [f Form] i32\n\
|
||||
\ (match f (Int _n) 1 (Str _s) 2 (List xs) (i32 (len xs)) _ 0))\n\
|
||||
\ (match f (Int _n) 1 (Str _s) 2 (List xs) (i32 (length xs)) _ 0))\n\
|
||||
(defn main [] i32 (shape (Form.Int {.i 1})))\n"
|
||||
in
|
||||
layout_case "DWARF offsets agree with LLVM: Form" form_src
|
||||
|
||||
@ -6626,7 +6626,7 @@ let () =
|
||||
holds "a kept slot keeps its value"
|
||||
"(if (= (get (at instances 0) :x) 3) 1 0)";
|
||||
holds "the migrated instance has the new slot count"
|
||||
"(if (= (len (at instances 0)) 3) 1 0)";
|
||||
"(if (= (length (at instances 0)) 3) 1 0)";
|
||||
(* Dispatch after migration. The generic reaches its method by the
|
||||
instance's shape tag, and a migration rebuilds the instance's
|
||||
entries — so this is the line that says the tag came through it.
|
||||
@ -6638,7 +6638,7 @@ let () =
|
||||
special: it migrates when it is asked, not when the class
|
||||
changed. *)
|
||||
holds "an untouched instance migrates on its own first touch"
|
||||
"(if (= (len (at instances 1)) 3) 1 0)"
|
||||
"(if (= (length (at instances 1)) 3) 1 0)"
|
||||
end;
|
||||
|
||||
(* ── A slot lost, and a second generation ──
|
||||
@ -6651,7 +6651,7 @@ let () =
|
||||
holds "a lost slot reads as absent"
|
||||
"(if (= (get (at instances 0) :y) nil) 1 0)";
|
||||
holds "a lost slot is gone from the count"
|
||||
"(if (= (len (at instances 0)) 2) 1 0)";
|
||||
"(if (= (length (at instances 0)) 2) 1 0)";
|
||||
holds "the slots either side of it are untouched"
|
||||
"(if (= (get (at instances 0) :x) 3) 1 0)"
|
||||
end;
|
||||
@ -6665,7 +6665,7 @@ let () =
|
||||
if status r <> "ok" then fail "a third redefinition: %s" (said r)
|
||||
else begin
|
||||
holds "the third definition's slot count"
|
||||
"(if (= (len (at instances 1)) 3) 1 0)";
|
||||
"(if (= (length (at instances 1)) 3) 1 0)";
|
||||
holds "the third definition's new slot is nil"
|
||||
"(if (= (get (at instances 1) :w) nil) 1 0)";
|
||||
holds "and the value from before the first edit is still there"
|
||||
@ -6771,7 +6771,7 @@ let () =
|
||||
holds "a kept slot keeps its value through the LLVM backend"
|
||||
"(if (= (get (at instances 0) :x) 3) 1 0)";
|
||||
holds "the slot count through the LLVM backend"
|
||||
"(if (= (len (at instances 0)) 3) 1 0)";
|
||||
"(if (= (length (at instances 0)) 3) 1 0)";
|
||||
holds "a generic still dispatches through the LLVM backend"
|
||||
"(if (= (area (at instances 0)) 12) 1 0)"
|
||||
end
|
||||
|
||||
@ -215,7 +215,7 @@ let () =
|
||||
("le", "dyn <=: text and nil");
|
||||
("gt", "dyn >: vec and vec");
|
||||
("ge", "dyn >=: bool and bool");
|
||||
("len", "dyn len: int");
|
||||
("len", "dyn length: int");
|
||||
("at", "dyn at: int and int");
|
||||
("atindex", "an index must be an int");
|
||||
("atrange", "index 9 is out of bounds for text of length 2");
|
||||
@ -249,7 +249,14 @@ let () =
|
||||
("wrongwrite", "this view's elements are int");
|
||||
("wrongbool", "this view's elements are bool");
|
||||
("wrongfloat", "this view's elements are float");
|
||||
("flatpush", "this view is a slice or an array and cannot grow") ]
|
||||
("flatpush", "this view is a slice or an array and cannot grow");
|
||||
(* And the operator's own name in that sentence. [flan_dyn_len] hands
|
||||
a string down twice — once to its type trap and once to the view
|
||||
check — and the two are easy to move apart, which is exactly what
|
||||
happened when the builtin was renamed. Both say [length] because
|
||||
both are naming the same Flan word. *)
|
||||
("stalelen", "dyn length: this view's container's allocator was \
|
||||
released") ]
|
||||
in
|
||||
List.iter
|
||||
(fun (mode, phrase) ->
|
||||
|
||||
@ -879,7 +879,7 @@ let probe_env = lazy (snd (Check.program_with_env []))
|
||||
Checked as an expression, the way a session checks one sent from the editor.
|
||||
It used to be the type of [(defconst probe <src>)], which stopped working on
|
||||
2026-09-20: a defconst's initialiser has to be a compile-time constant now
|
||||
and most of the probes below are calls — (cast ...), (len ...), a
|
||||
and most of the probes below are calls — (cast ...), (length ...), a
|
||||
comparison. A defonce would not do either, since only the defconst form
|
||||
takes no type. This asks [check] the question the wrapper was only ever a
|
||||
way of asking. *)
|
||||
@ -1044,7 +1044,7 @@ let () =
|
||||
infers "array-fill of nothing" "(array-fill [0] 1)" "[0 i32]";
|
||||
infers "bytes of a string" "(bytes \"hi\")" "[u8]";
|
||||
infers "bytes-view of a string" "(bytes-view \"hi\")" "[u8]";
|
||||
infers "len is i32" "(len (bytes \"hi\"))" "i32";
|
||||
infers "length is i32" "(length (bytes \"hi\"))" "i32";
|
||||
infers "slice of a slice" "(slice (bytes \"hi\") 0 1)" "[u8]";
|
||||
infers "slice of the whole" "(slice (bytes \"hi\"))" "[u8]";
|
||||
infers "slice from n" "(slice (bytes \"hi\") 1)" "[u8]";
|
||||
@ -1738,15 +1738,15 @@ let () =
|
||||
symbol are a dyn map, in binding position and as a call argument alike —
|
||||
the argument spelling used to be swallowed by the struct-literal rule. *)
|
||||
accepts "a map literal in a binding"
|
||||
"(defn main [] i32 (let [m {:a 1 :b \"two\"}] (len m)))";
|
||||
"(defn main [] i32 (let [m {:a 1 :b \"two\"}] (length m)))";
|
||||
accepts "a map literal as an argument"
|
||||
"(defn take [d dyn] i32 1)\n(defn main [] i32 (take {:a 1}))";
|
||||
accepts "the empty braces are an empty map"
|
||||
"(defn main [] i32 (let [m {}] (len m)))";
|
||||
"(defn main [] i32 (let [m {}] (length m)))";
|
||||
accepts "map literals nest, and brackets inside are dyn vecs"
|
||||
"(defn main [] i32 (let [m {:xs [1 2] :inner {:c 2.5}}] (len m)))";
|
||||
"(defn main [] i32 (let [m {:xs [1 2] :inner {:c 2.5}}] (length m)))";
|
||||
rejects_check "a map literal with an odd number of forms"
|
||||
"(defn main [] i32 (let [m {:a 1 :b}] (len m)))"
|
||||
"(defn main [] i32 (let [m {:a 1 :b}] (length m)))"
|
||||
~needle:"odd number of forms";
|
||||
(* The struct spelling is untouched on both of its sides: bare braces
|
||||
opening on a .field are still a struct field list and not a map, and
|
||||
@ -1954,13 +1954,13 @@ let () =
|
||||
~needle:"numeric?";
|
||||
|
||||
(* The map operations ride the words the typed map already owns: get, put,
|
||||
len, has-key? — one question, one word, on both sides. has-key? on a
|
||||
length, has-key? — one question, one word, on both sides. has-key? on a
|
||||
typed map still checks against its K. *)
|
||||
accepts "get, put, len and has-key? over a dyn map"
|
||||
accepts "get, put, length and has-key? over a dyn map"
|
||||
"(defn main [] i32\n\
|
||||
\ (let [m {:a 1}]\n\
|
||||
\ (put m :b 2)\n\
|
||||
\ (if (has-key? m :b) (len m) 0)))";
|
||||
\ (if (has-key? m :b) (length m) 0)))";
|
||||
accepts "has-key? still serves the typed map"
|
||||
"(defn main [] i32\n\
|
||||
\ (let [m (map-new string i32 (heap-allocator))]\n\
|
||||
@ -2048,7 +2048,8 @@ let () =
|
||||
|
||||
(* The short arities are the three-argument form written out, so they are
|
||||
held to the same standard: the implicit hi on a fixed array is the same
|
||||
literal (len a) folds to, and a lo past it is refused here and not later.
|
||||
literal (length a) folds to, and a lo past it is refused here and not
|
||||
later.
|
||||
A 1-argument slice cannot fail either check — 0 and the length are both
|
||||
in range by construction — so what is pinned about it is that it is
|
||||
accepted on each of the three things slice takes. *)
|
||||
@ -2096,7 +2097,7 @@ let () =
|
||||
(* A Vec a call returned is *accepted*, where an array a call returned is
|
||||
not. The array dangles; this does not — the storage outlives the
|
||||
expression — and what it loses is the owner, which is a leak, which is
|
||||
defined behaviour here. (len (mk)) and (at (mk) 0) lose the same owner
|
||||
defined behaviour here. (length (mk)) and (at (mk) 0) lose the same owner
|
||||
and compile, so refusing only the third would be a rule about a spelling
|
||||
rather than about a hazard. *)
|
||||
accepts "slice of a returned Vec"
|
||||
@ -2179,7 +2180,7 @@ let () =
|
||||
(* And a string is still not a [u8]: slicing one does not smuggle a byte
|
||||
slice out of it. *)
|
||||
rejects_check "a string slice is not a byte slice"
|
||||
"(defn g [b [u8]] i32 (len b)) (defn f [s string] i32 (g (slice s)))"
|
||||
"(defn g [b [u8]] i32 (length b)) (defn f [s string] i32 (g (slice s)))"
|
||||
~needle:"expected [u8], found string";
|
||||
|
||||
(* (slice-from-ptr p n). The one form in the language whose central claim the
|
||||
@ -2189,12 +2190,12 @@ let () =
|
||||
accepts "a pointer plus a length is a slice"
|
||||
"(defn f [p (Ptr i32) n i32] i32 (at (slice-from-ptr p n) 0))";
|
||||
accepts "zero is a length"
|
||||
"(defn f [p (Ptr i32)] i32 (len (slice-from-ptr p 0)))";
|
||||
"(defn f [p (Ptr i32)] i32 (length (slice-from-ptr p 0)))";
|
||||
rejects_check "slice-from-ptr of something that is not a pointer"
|
||||
"(defn f [s [i32]] i32 (len (slice-from-ptr s 3)))"
|
||||
"(defn f [s [i32]] i32 (length (slice-from-ptr s 3)))"
|
||||
~needle:"takes a (Ptr T)";
|
||||
rejects_check "slice-from-ptr with a negative literal length"
|
||||
"(defn f [p (Ptr i32)] i32 (len (slice-from-ptr p -1)))"
|
||||
"(defn f [p (Ptr i32)] i32 (length (slice-from-ptr p -1)))"
|
||||
~needle:"is negative";
|
||||
(* The storage stays C's. A slice carries no allocator, so free refuses one
|
||||
by the rule it already had — this pins that the new form did not become
|
||||
@ -2285,7 +2286,7 @@ let () =
|
||||
accepts "a keyword-keyed literal is still a dyn map at a dyn want"
|
||||
"(defn f [] dyn {:a 1 :b [2 3]})";
|
||||
accepts "the empty braces are still an empty dyn map"
|
||||
"(defn main [] i32 (let [m {}] (len m)))";
|
||||
"(defn main [] i32 (let [m {}] (length m)))";
|
||||
|
||||
(* ── (Cell 1 2), positional ────────────────────────────────────── *)
|
||||
accepts "positional struct construction"
|
||||
@ -2386,6 +2387,48 @@ let () =
|
||||
rejects_check "unknown function" "(defn f [] i32 (nope 1))"
|
||||
~needle:"unknown function";
|
||||
|
||||
(* ── len is a name, and length is the builtin ───────────────────────
|
||||
[len] is short enough to want as a variable, so the builtin takes the
|
||||
long word and the short one is left to programs. Shadowing had already
|
||||
made a defn named after a builtin legal; what is new is that there is no
|
||||
builtin here to shadow, so nothing warns and [builtin/] is not needed to
|
||||
reach past anything. Four claims: the call is refused, the refusal says
|
||||
what to write instead, the name binds in every position, and a defn under
|
||||
it earns no shadowing warning. *)
|
||||
rejects_check "len is not a builtin"
|
||||
"(defn f [s [i32]] i32 (len s))" ~needle:"there is no len";
|
||||
rejects_check "and the refusal writes the call out"
|
||||
"(defn f [s [i32]] i32 (len s))" ~needle:"Write (length s)";
|
||||
(* What it suggests has to compile, and [length] takes exactly one argument.
|
||||
So the reader's own argument is written back only when there is one of
|
||||
it: at any other arity the shape is suggested instead, because a call
|
||||
with three arguments in it would be refused a second time the moment it
|
||||
was pasted. Three arities, because the failure was silent at all of
|
||||
them. *)
|
||||
rejects_check "a len at the wrong arity is not written back out"
|
||||
"(defn f [s [i32]] i32 (len s 1))" ~needle:"Write (length v)";
|
||||
rejects_check "and neither is a len with no arguments"
|
||||
"(defn f [] i32 (len))" ~needle:"Write (length v)";
|
||||
rejects_check "and neither is one with a great many"
|
||||
"(defn f [s [i32]] i32 (len s s s s s))" ~needle:"Write (length v)";
|
||||
(* An argument with structure inside it is the stand-in too, at the one
|
||||
arity that does spell: a form written back half-quoted would not
|
||||
compile. *)
|
||||
rejects_check "an argument that is a call is stood in for"
|
||||
"(defn f [s [i32]] i32 (len (slice s 0 1)))" ~needle:"Write (length v)";
|
||||
accepts "len is an ordinary binding"
|
||||
"(defn f [s [i32]] i32 (let [len (length s)] (+ len 1)))";
|
||||
accepts "and an ordinary parameter"
|
||||
"(defn f [len i32] i32 (+ len 1))";
|
||||
accepts "and an ordinary global"
|
||||
"(defonce len i32 0)\n(defn f [] i32 (do (set len 3) len))";
|
||||
accepts "and a function a program defines and calls"
|
||||
"(defn len [s [i32]] i32 (length s))\n\
|
||||
(defn f [s [i32]] i32 (len s))";
|
||||
check "and a defn called len shadows nothing, so it is not warned about"
|
||||
(Check.shadowed_builtins
|
||||
(program "(defn len [s [i32]] i32 (length s))") = []);
|
||||
|
||||
(* ── Did-you-mean, and the dot habit ───────────────────────────────
|
||||
[near_miss] was written, tested and wired to the type tables alone, so a
|
||||
mistyped *value* got the bare refusal. The candidate list at a value
|
||||
@ -2895,7 +2938,7 @@ let () =
|
||||
which is also why a malformed one stays a type error rather than turning
|
||||
into a call to something named Vec. *)
|
||||
defvar_reading "a parenthesised type stays a zeroed static"
|
||||
"(defonce v (Vec i32)) (defn f [] i32 (len v))"
|
||||
"(defonce v (Vec i32)) (defn f [] i32 (length v))"
|
||||
"v" ~ty:"(Vec i32)" ~zeroed:true;
|
||||
rejects_check "a malformed parenthesised type stays a type error"
|
||||
"(defonce v (Vec i32 i32)) (defn f [] ())"
|
||||
@ -3183,7 +3226,7 @@ let () =
|
||||
accepts "a global Vec is borrowed, mutated and cloned"
|
||||
"(defonce g (Vec u8)) \
|
||||
(defn f [] () (set g (vec-new u8)) (push g 1) (set (at g 0) 2) \
|
||||
(println (len (slice g))) (let [c (clone g)] (free c)))";
|
||||
(println (length (slice g))) (let [c (clone g)] (free c)))";
|
||||
rejects_check "try is milestone 6" "(defn f [] i32 (try 1))"
|
||||
~needle:"try (Result) is not implemented";
|
||||
(* dotimes and defer are implemented, and a defer in a [let] is now one of
|
||||
@ -3307,7 +3350,7 @@ let () =
|
||||
~needle:"would leave a (loop ...)";
|
||||
accepts "a while condition is an ordinary expression"
|
||||
"(defn f [] () (let [v (vec-new i32) n 0] \
|
||||
(while (and (< n 10) (> (len v) 0)) (set n (+ n 1))) (free v)))";
|
||||
(while (and (< n 10) (> (length v) 0)) (set n (+ n 1))) (free v)))";
|
||||
rejects_check "loop takes no label"
|
||||
"(defn f [] () (loop :o [i 0] (recur i)))" ~needle:"loop takes no label";
|
||||
rejects_check "a loop binding is a plain name"
|
||||
@ -3918,9 +3961,9 @@ let () =
|
||||
accepts "an array pattern naming every element"
|
||||
"(defn f [] i32 (let [xs [1 2 3] [a b c] xs] (+ a (+ b c))))";
|
||||
accepts "an array pattern with & rest"
|
||||
"(defn f [] i32 (let [xs [1 2 3] [a & r] xs] (+ a (len r))))";
|
||||
"(defn f [] i32 (let [xs [1 2 3] [a & r] xs] (+ a (length r))))";
|
||||
accepts "& rest taking an empty tail"
|
||||
"(defn f [] i32 (let [xs [1 2] [a b & r] xs] (+ a (+ b (len r)))))";
|
||||
"(defn f [] i32 (let [xs [1 2] [a b & r] xs] (+ a (+ b (length r)))))";
|
||||
accepts "a struct pattern nested in an array pattern"
|
||||
(pt ^ "(defn f [ps [2 Point]] i32 \
|
||||
(let [[{:keys [x]} {y .y}] ps] (+ x y)))");
|
||||
@ -3943,7 +3986,7 @@ let () =
|
||||
"(defn f [s [i32]] i32 (let [[a b] s] (+ a b)))"
|
||||
~needle:"a slice's length is not known until the program runs";
|
||||
rejects_check "an array pattern over a slice, even with & rest"
|
||||
"(defn f [s [i32]] i32 (let [[a & r] s] (+ a (len r))))"
|
||||
"(defn f [s [i32]] i32 (let [[a & r] s] (+ a (length r))))"
|
||||
~needle:"a slice's length is not known until the program runs";
|
||||
rejects_check "an array pattern over something with no elements at all"
|
||||
"(defn f [n i32] i32 (let [[a b] n] (+ a b)))"
|
||||
@ -3959,7 +4002,7 @@ let () =
|
||||
"(defn f [] i32 (let [xs [1 2] [a & r s] xs] a))"
|
||||
~needle:"& takes one name";
|
||||
rejects_check "a pattern that is only & rest"
|
||||
"(defn f [] i32 (let [xs [1 2] [& r] xs] (len r)))"
|
||||
"(defn f [] i32 (let [xs [1 2] [& r] xs] (length r)))"
|
||||
~needle:"binds the whole value";
|
||||
rejects_check "one array pattern binding a name twice"
|
||||
"(defn f [] i32 (let [xs [1 2] [a a] xs] a))"
|
||||
@ -5236,48 +5279,54 @@ let () =
|
||||
resolution, which is a check that cannot fail — found in review. *)
|
||||
(match
|
||||
Check.program
|
||||
(program "(defn len [a string b string] i32 999)"
|
||||
@ Parse.program (read ~file:"<elsewhere>" "(defn g [] i32 (len \"a\" \"b\"))"))
|
||||
(program "(defn length [a string b string] i32 999)"
|
||||
@ Parse.program
|
||||
(read ~file:"<elsewhere>" "(defn g [] i32 (length \"a\" \"b\"))"))
|
||||
with
|
||||
| _ -> check "a call in another file does not reach the shadow" false
|
||||
| exception Loc.Error d ->
|
||||
check "a call in another file reaches the builtin, at the builtin's arity"
|
||||
(contains d.Loc.dmsg "len takes 1 argument, given 2"));
|
||||
(contains d.Loc.dmsg "length takes 1 argument, given 2"));
|
||||
|
||||
(* ── builtin/, the reserved qualifier ──────────────────────────────
|
||||
The escape from the dead end above: [builtin/len] is the builtin [len]
|
||||
whatever the file has decided [len] means. Pinned from both ends —
|
||||
The escape from the dead end above: [builtin/length] is the builtin
|
||||
[length]
|
||||
whatever the file has decided [length] means. Pinned from both ends —
|
||||
with a shadow in the way and with nothing in the way at all — because a
|
||||
spelling that only worked while some other declaration existed would be
|
||||
one nobody could write down in advance. *)
|
||||
accepts "builtin/ is legal with nothing shadowed"
|
||||
"(defn f [] i32 (builtin/len \"abcd\"))";
|
||||
"(defn f [] i32 (builtin/length \"abcd\"))";
|
||||
(* The pair that says the two spellings part company. One declaration list,
|
||||
two calls: the shadow takes two arguments and the builtin takes one, so
|
||||
each call is refusable only under one of the two readings. The bare name
|
||||
at two arguments checks, and the qualified one at two arguments is
|
||||
measured against the builtin's arity. *)
|
||||
accepts "the bare name reaches the shadowing defn"
|
||||
"(defn len [a string b string] i32 999)\n\
|
||||
(defn f [] i32 (len \"a\" \"b\"))";
|
||||
"(defn length [a string b string] i32 999)\n\
|
||||
(defn f [] i32 (length \"a\" \"b\"))";
|
||||
rejects_check "and builtin/ beside it reaches the builtin"
|
||||
"(defn len [a string b string] i32 999)\n\
|
||||
(defn f [] i32 (builtin/len \"a\" \"b\"))"
|
||||
~needle:"len takes 1 argument, given 2";
|
||||
"(defn length [a string b string] i32 999)\n\
|
||||
(defn f [] i32 (builtin/length \"a\" \"b\"))"
|
||||
~needle:"length takes 1 argument, given 2";
|
||||
(* The program the earlier lane could not write: a shadowing defn that
|
||||
*wraps* what it shadows. Without the qualifier the inner call reached
|
||||
the definition being written and the program stack-overflowed at run
|
||||
time; what is pinned here is that the body holds no call to [len] at
|
||||
time; what is pinned here is that the body holds no call to [length] at
|
||||
all, which is the difference between a wrapper and a loop. *)
|
||||
(match checked "(defn len [s string] i32 (builtin/+ 1 (builtin/len s)))" with
|
||||
(match
|
||||
checked "(defn length [s string] i32 (builtin/+ 1 (builtin/length s)))"
|
||||
with
|
||||
| p ->
|
||||
let recurs = ref false in
|
||||
(match List.find_opt (fun (f : Tast.fn) -> f.Tast.name = "len") p.Tast.fns with
|
||||
(match
|
||||
List.find_opt (fun (f : Tast.fn) -> f.Tast.name = "length") p.Tast.fns
|
||||
with
|
||||
| Some f ->
|
||||
List.iter
|
||||
(Tast.walk (fun (e : Tast.expr) ->
|
||||
match e.Tast.e with
|
||||
| Tast.Call ("len", _) -> recurs := true
|
||||
| Tast.Call ("length", _) -> recurs := true
|
||||
| _ -> ()))
|
||||
f.Tast.body;
|
||||
check "a shadowing defn wraps the builtin instead of recurring"
|
||||
@ -5309,8 +5358,9 @@ let () =
|
||||
than falling through to the function table, which holds the very
|
||||
definition the qualifier was written to get away from. *)
|
||||
rejects_check "a call-only builtin is refused as a value, not resolved"
|
||||
"(defn len [s string] i32 1)\n(defn f [] () (println builtin/len))"
|
||||
~needle:"builtin/len is the builtin len, which is a call and not a value";
|
||||
"(defn length [s string] i32 1)\n(defn f [] () (println builtin/length))"
|
||||
~needle:
|
||||
"builtin/length is the builtin length, which is a call and not a value";
|
||||
(* The qualifier reaching nothing. Named as not a builtin, and the
|
||||
did-you-mean is over the builtins alone. *)
|
||||
(match diag_of "(defn f [] i32 (builtin/nosuch))" with
|
||||
@ -5324,26 +5374,26 @@ let () =
|
||||
else; an ordinary function is called by the name it was defined \
|
||||
under")
|
||||
| None -> check "builtin/nosuch is refused" false);
|
||||
(match diag_of "(defn f [] i32 (builtin/lne \"ab\"))" with
|
||||
(match diag_of "(defn f [] i32 (builtin/lenght \"ab\"))" with
|
||||
| Some d ->
|
||||
check "and a near miss is offered in the qualified spelling"
|
||||
(d.Loc.dmsg
|
||||
= "lne is not a builtin, so builtin/lne reaches nothing — did you \
|
||||
mean builtin/len?")
|
||||
| None -> check "builtin/lne is refused" false);
|
||||
= "lenght is not a builtin, so builtin/lenght reaches nothing — did you \
|
||||
mean builtin/length?")
|
||||
| None -> check "builtin/lenght is refused" false);
|
||||
(* And the did-you-mean everywhere else is untouched: a bare typo is still
|
||||
answered with a bare name, not with a qualifier nobody reached for. *)
|
||||
rejects_check "an unqualified typo is not answered with builtin/"
|
||||
"(defn f [] i32 (lne \"ab\"))" ~needle:"did you mean len?";
|
||||
(* The reservation from the other side. [(defn builtin/len ...)] reads —
|
||||
"(defn f [] i32 (lenght \"ab\"))" ~needle:"did you mean length?";
|
||||
(* The reservation from the other side. [(defn builtin/length ...)] reads —
|
||||
'/' is an ordinary symbol character — and would land in the function
|
||||
table under a name nothing can ever call, because the prefix is stripped
|
||||
before any table is consulted. *)
|
||||
(match diag_of "(defn builtin/len [s string] i32 1)" with
|
||||
(match diag_of "(defn builtin/length [s string] i32 1)" with
|
||||
| Some d ->
|
||||
check "a declaration cannot take the reserved qualifier"
|
||||
(contains d.Loc.dmsg
|
||||
"builtin/len cannot be declared: builtin/ is a reserved qualifier")
|
||||
"builtin/length cannot be declared: builtin/ is a reserved qualifier")
|
||||
| None -> check "a declaration under builtin/ is refused" false);
|
||||
|
||||
(* and's last operand is the then arm and the sentinel carrying the previous
|
||||
@ -5913,9 +5963,9 @@ let () =
|
||||
itself is refused where it is written, at the definition. *)
|
||||
rejects_check "a map keyed by a type variable that is not hashable?"
|
||||
~needle:"is not a map key"
|
||||
"(defn f [m (Map $t i32)] i32 {:where (numeric? $t)} (len m))";
|
||||
"(defn f [m (Map $t i32)] i32 {:where (numeric? $t)} (length m))";
|
||||
accepts "and hashable? is what says it is"
|
||||
"(defn f [m (Map $t i32)] i32 {:where (hashable? $t)} (len m))";
|
||||
"(defn f [m (Map $t i32)] i32 {:where (hashable? $t)} (length m))";
|
||||
accepts "and under it the operations are deferred, not refused"
|
||||
"(defn f [m (Map $t i32) k $t] () {:where (hashable? $t)} (put m k 1))";
|
||||
accepts "get over a type-variable key answers an (Option V)"
|
||||
@ -5951,7 +6001,7 @@ let () =
|
||||
(let [v (vec-new $t a)] (push v x) v))";
|
||||
accepts "map-new over type variables written with the sigil"
|
||||
"(defn f [k $t] i32 {:where (hashable? $t)} \
|
||||
(let [m (map-new $t i32)] (put m k 1) (let [n (len m)] (free m) n)))";
|
||||
(let [m (map-new $t i32)] (put m k 1) (let [n (length m)] (free m) n)))";
|
||||
accepts "a zeroed fixed array of a type variable"
|
||||
"(defn f [x $t] $t (let [a (array 3 $t)] (set (at a 1) x) (at a 1)))";
|
||||
accepts "a cast to a type variable written with the sigil"
|
||||
@ -6153,7 +6203,7 @@ let () =
|
||||
\ (put tm \"k\" 1)\n\
|
||||
\ (reserve tv 4)\n\
|
||||
\ (arith 1 2)\n\
|
||||
\ (print (len txt))))"
|
||||
\ (print (length txt))))"
|
||||
[ (5, 11, native,
|
||||
"allocates: an arena takes its whole region from the host here");
|
||||
(8, 13, native,
|
||||
|
||||
@ -1092,7 +1092,7 @@ let () =
|
||||
let c =
|
||||
Session.eval t
|
||||
"(defn pick [xs [$t]] $t {:where (ordered? $t)} (let [m (at xs 0)] \
|
||||
(dotimes [i (len xs)] (set m (max m (at xs i)))) m))"
|
||||
(dotimes [i (length xs)] (set m (max m (at xs i)))) m))"
|
||||
in
|
||||
if not (List.mem "pick-i32" c.Session.fns) then
|
||||
fail "redefining a generic did not reinstall pick-i32";
|
||||
|
||||
@ -359,7 +359,7 @@ let padded_key =
|
||||
(defn main [] i32\n\
|
||||
\ (let [m (map-new Padded i32)]\n\
|
||||
\ (dotimes [i 40] (put m (Padded {.a (i8 i) .b (i64 i) .c 7}) i))\n\
|
||||
\ (print (len m)) (println \"\")\n\
|
||||
\ (print (length m)) (println \"\")\n\
|
||||
\ (match (get m (Padded {.a (i8 9) .b (i64 9) .c 7}))\n\
|
||||
\ (Some v) (do (print v) (println \"\"))\n\
|
||||
\ None (println \"missing\"))\n\
|
||||
|
||||
2
vendor/agent/agent.flan
vendored
2
vendor/agent/agent.flan
vendored
@ -39,7 +39,7 @@
|
||||
;;; dropped — they are spliced into [start-at], which then refuses them by its
|
||||
;;; own arity, at the call site, in the usual words.
|
||||
(defmacro start [& args]
|
||||
(if (= (len args) 0)
|
||||
(if (= (length args) 0)
|
||||
`(start-auto)
|
||||
`(start-at ~@args)))
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user