diff --git a/FIX.org b/FIX.org index 4649ca5..c4a1e18 100644 --- a/FIX.org +++ b/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. diff --git a/NEXT.md b/NEXT.md index 3dc8d62..131d97c 100644 --- a/NEXT.md +++ b/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. diff --git a/calc-me.flan b/calc-me.flan index d7a46b1..0af5019 100644 --- a/calc-me.flan +++ b/calc-me.flan @@ -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) diff --git a/docs/BUGS-2026-09-18.md b/docs/BUGS-2026-09-18.md index 54a9f5e..02617e1 100644 --- a/docs/BUGS-2026-09-18.md +++ b/docs/BUGS-2026-09-18.md @@ -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 diff --git a/docs/BUILT.md b/docs/BUILT.md index b4112fa..76055af 100644 --- a/docs/BUILT.md +++ b/docs/BUILT.md @@ -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 diff --git a/docs/PORTING.md b/docs/PORTING.md index 73ba2a6..f69ec1e 100644 --- a/docs/PORTING.md +++ b/docs/PORTING.md @@ -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. diff --git a/docs/SPIKE-DYNAMIC.md b/docs/SPIKE-DYNAMIC.md index 8b3a771..b3efb39 100644 --- a/docs/SPIKE-DYNAMIC.md +++ b/docs/SPIKE-DYNAMIC.md @@ -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` | diff --git a/docs/SPIKE-GENERICS.md b/docs/SPIKE-GENERICS.md index ff80bbc..c20cc4d 100644 --- a/docs/SPIKE-GENERICS.md +++ b/docs/SPIKE-GENERICS.md @@ -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) diff --git a/docs/handoffs/HANDOFF-raylib-ports.md b/docs/handoffs/HANDOFF-raylib-ports.md index 9f731a0..3b26ea9 100644 --- a/docs/handoffs/HANDOFF-raylib-ports.md +++ b/docs/handoffs/HANDOFF-raylib-ports.md @@ -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 diff --git a/emacs/MANUAL.md b/emacs/MANUAL.md index e7c686e..87d6617 100644 --- a/emacs/MANUAL.md +++ b/emacs/MANUAL.md @@ -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 diff --git a/emacs/flan-inspect.el b/emacs/flan-inspect.el index 2e76e0a..be5c8be 100644 --- a/emacs/flan-inspect.el +++ b/emacs/flan-inspect.el @@ -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. diff --git a/emacs/flan-mode.el b/emacs/flan-mode.el index 8aff887..400250d 100644 --- a/emacs/flan-mode.el +++ b/emacs/flan-mode.el @@ -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. diff --git a/examples/text-codepoints-loading.flan b/examples/text-codepoints-loading.flan index 26a509d..7c610e5 100644 --- a/examples/text-codepoints-loading.flan +++ b/examples/text-codepoints-loading.flan @@ -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. diff --git a/examples/text-rectangle-bounds.flan b/examples/text-rectangle-bounds.flan index 9051b40..0332b10 100644 --- a/examples/text-rectangle-bounds.flan +++ b/examples/text-rectangle-bounds.flan @@ -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) diff --git a/examples/text-writing-anim.flan b/examples/text-writing-anim.flan index 40fb396..b381574 100644 --- a/examples/text-writing-anim.flan +++ b/examples/text-writing-anim.flan @@ -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) diff --git a/lib/check.ml b/lib/check.ml index 34a1953..f523457 100644 --- a/lib/check.ml +++ b/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 diff --git a/lib/js.ml b/lib/js.ml index 6dfdbea..a5fdd74 100644 --- a/lib/js.ml +++ b/lib/js.ml @@ -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 diff --git a/lib/load.ml b/lib/load.ml index 0606298..c1d3af8 100644 --- a/lib/load.ml +++ b/lib/load.ml @@ -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 diff --git a/lib/parse.ml b/lib/parse.ml index e1d5451..52fb247 100644 --- a/lib/parse.ml +++ b/lib/parse.ml @@ -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 diff --git a/lib/prelude.ml b/lib/prelude.ml index 40c0d4b..0d89657 100644 --- a/lib/prelude.ml +++ b/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 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)))) diff --git a/lib/shim.ml b/lib/shim.ml index 6b75f0e..47d1fd4 100644 --- a/lib/shim.ml +++ b/lib/shim.ml @@ -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 diff --git a/lib/x86.ml b/lib/x86.ml index 2b0d10d..911ee6d 100644 --- a/lib/x86.ml +++ b/lib/x86.ml @@ -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 diff --git a/plan.org b/plan.org index 53872ce..73d2be6 100644 --- a/plan.org +++ b/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. diff --git a/runtime/flan_dyn.c b/runtime/flan_dyn.c index b10ea91..a3908c9 100644 --- a/runtime/flan_dyn.c +++ b/runtime/flan_dyn.c @@ -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 diff --git a/spec-memory.md b/spec-memory.md index 9a24af5..671bf53 100644 --- a/spec-memory.md +++ b/spec-memory.md @@ -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 diff --git a/spike/generics/measure.ml b/spike/generics/measure.ml index 9c1b212..457795a 100644 --- a/spike/generics/measure.ml +++ b/spike/generics/measure.ml @@ -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\ diff --git a/spike/generics/prelude-shapes.flan b/spike/generics/prelude-shapes.flan index d0a5ac6..519a737 100644 --- a/spike/generics/prelude-shapes.flan +++ b/spike/generics/prelude-shapes.flan @@ -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)))) diff --git a/spike/generics/sort.flan b/spike/generics/sort.flan index 444e852..632ed1b 100644 --- a/spike/generics/sort.flan +++ b/spike/generics/sort.flan @@ -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) diff --git a/spike/x86/p11-reversed-slice.flan b/spike/x86/p11-reversed-slice.flan index 4e2ad00..26d762d 100644 --- a/spike/x86/p11-reversed-slice.flan +++ b/spike/x86/p11-reversed-slice.flan @@ -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 "")) diff --git a/spike/x86/p13-dyn-collect.flan b/spike/x86/p13-dyn-collect.flan index 9e1a743..5bbc294 100644 --- a/spike/x86/p13-dyn-collect.flan +++ b/spike/x86/p13-dyn-collect.flan @@ -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"))) diff --git a/spike/x86/p4-convention.flan b/spike/x86/p4-convention.flan index e719e6c..47ad249 100644 --- a/spike/x86/p4-convention.flan +++ b/spike/x86/p4-convention.flan @@ -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}) diff --git a/spike/x86/p7-slice-from-ptr.flan b/spike/x86/p7-slice-from-ptr.flan index 0152641..95fdd3f 100644 --- a/spike/x86/p7-slice-from-ptr.flan +++ b/spike/x86/p7-slice-from-ptr.flan @@ -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] () diff --git a/syntax-sketch.flan b/syntax-sketch.flan index bd1eacf..f52bb6e 100644 --- a/syntax-sketch.flan +++ b/syntax-sketch.flan @@ -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 diff --git a/test/dyn_ops.c b/test/dyn_ops.c index be8711f..75fc794 100644 --- a/test/dyn_ops.c +++ b/test/dyn_ops.c @@ -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); diff --git a/test/programs/agent-queue.flan b/test/programs/agent-queue.flan index d205563..a2bf450 100644 --- a/test/programs/agent-queue.flan +++ b/test/programs/agent-queue.flan @@ -20,7 +20,7 @@ ticks) (defn main [args [string]] i32 - (if (< (len args) 2) + (if (< (length args) 2) (do (println "usage: agent-queue ") 2) (do (if (< (agent/start (at args 1)) 0) diff --git a/test/programs/agent.flan b/test/programs/agent.flan index 0ed99ce..2cea497 100644 --- a/test/programs/agent.flan +++ b/test/programs/agent.flan @@ -20,7 +20,7 @@ ticks) (defn main [args [string]] i32 - (if (< (len args) 2) + (if (< (length args) 2) (do (println "usage: agent ") 2) (do (if (< (agent/start (at args 1)) 0) diff --git a/test/programs/algorithms.flan b/test/programs/algorithms.flan index 3135821..33d339c 100644 --- a/test/programs/algorithms.flan +++ b/test/programs/algorithms.flan @@ -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 (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) diff --git a/test/programs/arena-value.flan b/test/programs/arena-value.flan index 2948e1d..3c1b846 100644 --- a/test/programs/arena-value.flan +++ b/test/programs/arena-value.flan @@ -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))) diff --git a/test/programs/bounds-condition.flan b/test/programs/bounds-condition.flan index 42cccff..646032f 100644 --- a/test/programs/bounds-condition.flan +++ b/test/programs/bounds-condition.flan @@ -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)) diff --git a/test/programs/bounds.flan b/test/programs/bounds.flan index d86cfa3..9442fd1 100644 --- a/test/programs/bounds.flan +++ b/test/programs/bounds.flan @@ -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)) diff --git a/test/programs/builtin-qualified.flan b/test/programs/builtin-qualified.flan index 054ff7b..27d6bf9 100644 --- a/test/programs/builtin-qualified.flan +++ b/test/programs/builtin-qualified.flan @@ -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))) diff --git a/test/programs/bytes-copy.flan b/test/programs/bytes-copy.flan index 44cbcbc..8084f2a 100644 --- a/test/programs/bytes-copy.flan +++ b/test/programs/bytes-copy.flan @@ -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): diff --git a/test/programs/defonce-dyn.flan b/test/programs/defonce-dyn.flan index 1ba855a..6a7f201 100644 --- a/test/programs/defonce-dyn.flan +++ b/test/programs/defonce-dyn.flan @@ -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 " ") diff --git a/test/programs/destructure.flan b/test/programs/destructure.flan index 66225da..2fbfadf 100644 --- a/test/programs/destructure.flan +++ b/test/programs/destructure.flan @@ -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 "")) diff --git a/test/programs/dyn-class.flan b/test/programs/dyn-class.flan index 5bc6ed4..5cb29f4 100644 --- a/test/programs/dyn-class.flan +++ b/test/programs/dyn-class.flan @@ -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) diff --git a/test/programs/dyn-map.flan b/test/programs/dyn-map.flan index 671e187..b400e50 100644 --- a/test/programs/dyn-map.flan +++ b/test/programs/dyn-map.flan @@ -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) diff --git a/test/programs/dyn-struct.flan b/test/programs/dyn-struct.flan index 026e64d..50bb945 100644 --- a/test/programs/dyn-struct.flan +++ b/test/programs/dyn-struct.flan @@ -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. diff --git a/test/programs/dyn-vec.flan b/test/programs/dyn-vec.flan index 6d74b5a..12b9896 100644 --- a/test/programs/dyn-vec.flan +++ b/test/programs/dyn-vec.flan @@ -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"))) diff --git a/test/programs/dyn-view.flan b/test/programs/dyn-view.flan index 5dbaf91..bf7699c 100644 --- a/test/programs/dyn-view.flan +++ b/test/programs/dyn-view.flan @@ -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")) diff --git a/test/programs/edn-provide.flan b/test/programs/edn-provide.flan index 8f730f3..2d36ccd 100644 --- a/test/programs/edn-provide.flan +++ b/test/programs/edn-provide.flan @@ -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 diff --git a/test/programs/edn-read.flan b/test/programs/edn-read.flan index 0e3f9ab..9cf5c82 100644 --- a/test/programs/edn-read.flan +++ b/test/programs/edn-read.flan @@ -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))))) diff --git a/test/programs/embed.flan b/test/programs/embed.flan index 76ecdba..e0736ca 100644 --- a/test/programs/embed.flan +++ b/test/programs/embed.flan @@ -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")) diff --git a/test/programs/exhausted.flan b/test/programs/exhausted.flan index dbb0794..528fafe 100644 --- a/test/programs/exhausted.flan +++ b/test/programs/exhausted.flan @@ -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. diff --git a/test/programs/fn-values.flan b/test/programs/fn-values.flan index bd5241f..e618b5a 100644 --- a/test/programs/fn-values.flan +++ b/test/programs/fn-values.flan @@ -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)) diff --git a/test/programs/generic-alloc.flan b/test/programs/generic-alloc.flan index 6026cf2..028a74f 100644 --- a/test/programs/generic-alloc.flan +++ b/test/programs/generic-alloc.flan @@ -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 diff --git a/test/programs/generics.flan b/test/programs/generics.flan index 853d4cf..6f9ae90 100644 --- a/test/programs/generics.flan +++ b/test/programs/generics.flan @@ -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) diff --git a/test/programs/global-init.flan b/test/programs/global-init.flan index 8b0a121..629dfe8 100644 --- a/test/programs/global-init.flan +++ b/test/programs/global-init.flan @@ -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. diff --git a/test/programs/handler-case.flan b/test/programs/handler-case.flan index 852efa4..32423fa 100644 --- a/test/programs/handler-case.flan +++ b/test/programs/handler-case.flan @@ -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) diff --git a/test/programs/higher-order.flan b/test/programs/higher-order.flan index 6d64cd2..cbb1cea 100644 --- a/test/programs/higher-order.flan +++ b/test/programs/higher-order.flan @@ -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 "")) diff --git a/test/programs/import-builtin-alias.flan b/test/programs/import-builtin-alias.flan index 97c2bce..9315a4b 100644 --- a/test/programs/import-builtin-alias.flan +++ b/test/programs/import-builtin-alias.flan @@ -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. diff --git a/test/programs/int-generic.flan b/test/programs/int-generic.flan index 374b289..d24e981 100644 --- a/test/programs/int-generic.flan +++ b/test/programs/int-generic.flan @@ -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 diff --git a/test/programs/into.flan b/test/programs/into.flan index 136fbde..f9768d4 100644 --- a/test/programs/into.flan +++ b/test/programs/into.flan @@ -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)) diff --git a/test/programs/json-provide.flan b/test/programs/json-provide.flan index 80191bc..e5b9f8a 100644 --- a/test/programs/json-provide.flan +++ b/test/programs/json-provide.flan @@ -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 diff --git a/test/programs/json.flan b/test/programs/json.flan index 925a750..b5010fc 100644 --- a/test/programs/json.flan +++ b/test/programs/json.flan @@ -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")) diff --git a/test/programs/len-bound.flan b/test/programs/len-bound.flan new file mode 100644 index 0000000..ae47128 --- /dev/null +++ b/test/programs/len-bound.flan @@ -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))) diff --git a/test/programs/len-gone.flan b/test/programs/len-gone.flan new file mode 100644 index 0000000..3ebd284 --- /dev/null +++ b/test/programs/len-gone.flan @@ -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)) diff --git a/test/programs/macro-params.flan b/test/programs/macro-params.flan index 3eb1fdc..e2f6dbd 100644 --- a/test/programs/macro-params.flan +++ b/test/programs/macro-params.flan @@ -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))) diff --git a/test/programs/macros.flan b/test/programs/macros.flan index 76c9328..09ffa1c 100644 --- a/test/programs/macros.flan +++ b/test/programs/macros.flan @@ -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))) diff --git a/test/programs/map-exhausted.flan b/test/programs/map-exhausted.flan index 8d2e226..5811c7d 100644 --- a/test/programs/map-exhausted.flan +++ b/test/programs/map-exhausted.flan @@ -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")) diff --git a/test/programs/map-iter.flan b/test/programs/map-iter.flan index 913b18e..29392b5 100644 --- a/test/programs/map-iter.flan +++ b/test/programs/map-iter.flan @@ -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 diff --git a/test/programs/map-remove.flan b/test/programs/map-remove.flan index 20f2e78..eaf1def 100644 --- a/test/programs/map-remove.flan +++ b/test/programs/map-remove.flan @@ -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)) diff --git a/test/programs/maps.flan b/test/programs/maps.flan index 0b917a8..5a23560 100644 --- a/test/programs/maps.flan +++ b/test/programs/maps.flan @@ -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) diff --git a/test/programs/pkgs/gen/gen.flan b/test/programs/pkgs/gen/gen.flan index 24146a0..7581c59 100644 --- a/test/programs/pkgs/gen/gen.flan +++ b/test/programs/pkgs/gen/gen.flan @@ -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)) diff --git a/test/programs/pkgs/shadowed/shadowed.flan b/test/programs/pkgs/shadowed/shadowed.flan index 2595e45..d643a9f 100644 --- a/test/programs/pkgs/shadowed/shadowed.flan +++ b/test/programs/pkgs/shadowed/shadowed.flan @@ -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) diff --git a/test/programs/pkgs/tree/tree.flan b/test/programs/pkgs/tree/tree.flan index 6261e6d..920cbf6 100644 --- a/test/programs/pkgs/tree/tree.flan +++ b/test/programs/pkgs/tree/tree.flan @@ -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))) diff --git a/test/programs/println.flan b/test/programs/println.flan index 8a27c7f..9425cb2 100644 --- a/test/programs/println.flan +++ b/test/programs/println.flan @@ -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) diff --git a/test/programs/recur.flan b/test/programs/recur.flan index 7bc54aa..5c6f32a 100644 --- a/test/programs/recur.flan +++ b/test/programs/recur.flan @@ -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)) diff --git a/test/programs/registry.flan b/test/programs/registry.flan index ae27e29..a71460c 100644 --- a/test/programs/registry.flan +++ b/test/programs/registry.flan @@ -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 diff --git a/test/programs/reload-generic.flan b/test/programs/reload-generic.flan index fe66719..042716b 100644 --- a/test/programs/reload-generic.flan +++ b/test/programs/reload-generic.flan @@ -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)) diff --git a/test/programs/restarts.flan b/test/programs/restarts.flan index 0270f96..18d88fe 100644 --- a/test/programs/restarts.flan +++ b/test/programs/restarts.flan @@ -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)) diff --git a/test/programs/shadow-builtin.flan b/test/programs/shadow-builtin.flan index 226f5db..826b4f8 100644 --- a/test/programs/shadow-builtin.flan +++ b/test/programs/shadow-builtin.flan @@ -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))) diff --git a/test/programs/slice-from-ptr.flan b/test/programs/slice-from-ptr.flan index 92225d4..1bfd239 100644 --- a/test/programs/slice-from-ptr.flan +++ b/test/programs/slice-from-ptr.flan @@ -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. diff --git a/test/programs/slices.flan b/test/programs/slices.flan index 7924767..c4250eb 100644 --- a/test/programs/slices.flan +++ b/test/programs/slices.flan @@ -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 diff --git a/test/programs/slurp.flan b/test/programs/slurp.flan index 7aa4022..153b422 100644 --- a/test/programs/slurp.flan +++ b/test/programs/slurp.flan @@ -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)) diff --git a/test/programs/string-of-bytes.flan b/test/programs/string-of-bytes.flan index c1bc2bc..91bec2e 100644 --- a/test/programs/string-of-bytes.flan +++ b/test/programs/string-of-bytes.flan @@ -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 diff --git a/test/programs/strings.flan b/test/programs/strings.flan index e15ea35..a781376 100644 --- a/test/programs/strings.flan +++ b/test/programs/strings.flan @@ -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 diff --git a/test/programs/struct-ergonomics.flan b/test/programs/struct-ergonomics.flan index 54e5e38..418b3bf 100644 --- a/test/programs/struct-ergonomics.flan +++ b/test/programs/struct-ergonomics.flan @@ -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)))) diff --git a/test/programs/time.flan b/test/programs/time.flan index cea0388..4823722 100644 --- a/test/programs/time.flan +++ b/test/programs/time.flan @@ -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) diff --git a/test/programs/utf8.flan b/test/programs/utf8.flan index 51e08cc..fb8d27d 100644 --- a/test/programs/utf8.flan +++ b/test/programs/utf8.flan @@ -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 "") diff --git a/test/programs/vec-global.flan b/test/programs/vec-global.flan index 2c6c1c4..8e74616 100644 --- a/test/programs/vec-global.flan +++ b/test/programs/vec-global.flan @@ -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) diff --git a/test/programs/vec-moved.flan b/test/programs/vec-moved.flan index 7d2ce89..02a8bfc 100644 --- a/test/programs/vec-moved.flan +++ b/test/programs/vec-moved.flan @@ -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)) diff --git a/test/programs/vec.flan b/test/programs/vec.flan index 732d8bd..2a70e88 100644 --- a/test/programs/vec.flan +++ b/test/programs/vec.flan @@ -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)) diff --git a/test/test_acceptance.ml b/test/test_acceptance.ml index 43143d0..42173eb 100644 --- a/test/test_acceptance.ml +++ b/test/test_acceptance.ml @@ -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 diff --git a/test/test_dev.ml b/test/test_dev.ml index f39ece6..a25cc2f 100644 --- a/test/test_dev.ml +++ b/test/test_dev.ml @@ -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 diff --git a/test/test_dyn.ml b/test/test_dyn.ml index 6d94cc3..8152b9c 100644 --- a/test/test_dyn.ml +++ b/test/test_dyn.ml @@ -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) -> diff --git a/test/test_flan.ml b/test/test_flan.ml index 11a0d8f..d3faadd 100644 --- a/test/test_flan.ml +++ b/test/test_flan.ml @@ -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 )], 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:"" "(defn g [] i32 (len \"a\" \"b\"))")) + (program "(defn length [a string b string] i32 999)" + @ Parse.program + (read ~file:"" "(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, diff --git a/test/test_session.ml b/test/test_session.ml index f8f395a..8bb2d27 100644 --- a/test/test_session.ml +++ b/test/test_session.ml @@ -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"; diff --git a/test/test_valgrind.ml b/test/test_valgrind.ml index 724b879..eb05ec1 100644 --- a/test/test_valgrind.ml +++ b/test/test_valgrind.ml @@ -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\ diff --git a/vendor/agent/agent.flan b/vendor/agent/agent.flan index 749eab4..cbae3ce 100644 --- a/vendor/agent/agent.flan +++ b/vendor/agent/agent.flan @@ -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))) diff --git a/vendor/edn/edn.flan b/vendor/edn/edn.flan index 8b13c4b..7c52424 100644 --- a/vendor/edn/edn.flan +++ b/vendor/edn/edn.flan @@ -260,7 +260,7 @@ ;; Nothing below is part of the API and none of it will keep its shape. (defn at-end? [c (Ptr Cursor)] bool - (>= (.pos c) (len (.src c)))) + (>= (.pos c) (length (.src c)))) ;; An empty slice of src, positioned at p. Used for the tokens that have no ;; text of their own — eof, error, and every delimiter. It is still a slice of @@ -301,7 +301,7 @@ ;; end of input. (defn scan-atom [c (Ptr Cursor) lo i32] i32 (let [i lo] - (while (and (< i (len (.src c))) (not (delim? (at (.src c) i)))) + (while (and (< i (length (.src c))) (not (delim? (at (.src c) i)))) (set i (+ i 1))) i)) @@ -327,12 +327,12 @@ ;; `-` alone is a symbol in EDN and stays one here. (defn number-start? [c (Ptr Cursor) i i32] bool (let [s (.src c)] - (when (>= i (len s)) + (when (>= i (length s)) (return false)) (when (digit? (at s i)) (return true)) (and (or (= (at s i) \-) (= (at s i) \+) (= (at s i) \.)) - (< (+ i 1) (len s)) + (< (+ i 1) (length s)) (digit? (at s (+ i 1)))))) (defn read-number [c (Ptr Cursor) lo i32] Token @@ -365,7 +365,7 @@ (defn read-string [c (Ptr Cursor) lo i32] Token (let [i (+ lo 1) s (.src c)] - (while (< i (len s)) + (while (< i (length s)) (let [b (at s i)] (when (= b \\) (set (.pos c) i) @@ -477,7 +477,7 @@ ;; The closer pushed is tok-map-close, because the byte that closes a ;; set is `}`. That is not a compromise: the balance stack holds the ;; *closing kind still owed*, and a set and a map owe the same one. - (and (< (+ lo 1) (len s)) (= (at s (+ lo 1)) \{)) + (and (< (+ lo 1) (length s)) (= (at s (+ lo 1)) \{)) (do (set (.pos c) (+ lo 2)) (if (push-open c tok-map-close lo) (token c tok-set-open lo lo lo) diff --git a/vendor/edn/provide.flan b/vendor/edn/provide.flan index f4d9170..d281446 100644 --- a/vendor/edn/provide.flan +++ b/vendor/edn/provide.flan @@ -117,7 +117,7 @@ (Derived {.ty ty .decls decls .reader reader .bad ""})) (defn bad? [d Derived] bool - (> (len (bytes-view (.bad d))) 0)) + (> (length (bytes-view (.bad d))) 0)) ;; ── The scalars a generated reader calls ──────────────────────────── ;; @@ -426,7 +426,7 @@ d (derive c (joined3 name "-" fname) src)] (when (bad? d) (return d)) - (dotimes [i (len (.decls d))] + (dotimes [i (length (.decls d))] (push decls (at (.decls d) i))) (push fields (Form.Sym {.s fname})) (push fields (.ty d)) @@ -501,7 +501,7 @@ (defn render-items [xs [Form]] string (let [out ""] - (dotimes [i (len xs)] + (dotimes [i (length xs)] (set out (if (= i 0) (render (at xs i)) (joined3 out " " (render (at xs i)))))) @@ -529,7 +529,7 @@ ;; is the point of generating readable code rather than the smallest code — a ;; provider whose output nobody can look at is a plugin. (defmacro defedn [& args] - (if (!= (len args) 2) + (if (!= (length args) 2) (refuse "defedn is (defedn Name \"path.edn\") — a name for the struct, and a path to the file its shape is read out of") (match (at args 1) (Form.Str path) diff --git a/vendor/json/json.flan b/vendor/json/json.flan index bdb80c9..707d66f 100644 --- a/vendor/json/json.flan +++ b/vendor/json/json.flan @@ -308,7 +308,7 @@ ;; Nothing below is part of the API and none of it will keep its shape. (defn at-end? [c (Ptr Cursor)] bool - (>= (.pos c) (len (.src c)))) + (>= (.pos c) (length (.src c)))) ;; An empty slice of src, positioned at p. Used for the tokens that have no ;; text of their own — eof, error, and every delimiter — so that `text` has one @@ -332,7 +332,7 @@ ;; end of input. (defn scan-atom [c (Ptr Cursor) lo i32] i32 (let [i lo] - (while (and (< i (len (.src c))) (not (delim? (at (.src c) i)))) + (while (and (< i (length (.src c))) (not (delim? (at (.src c) i)))) (set i (+ i 1))) i)) @@ -381,7 +381,7 @@ (set (.pos c) (+ lo 1)) (fail c err-leading-dot lo) (return (error-token c))) (when (= (at s i) \-) (set i (+ i 1)) - (when (or (>= i (len s)) (not (digit? (at s i)))) + (when (or (>= i (length s)) (not (digit? (at s i)))) ;; `-` with nothing usable after it. The scan-atom is only so that the ;; cursor lands past the whole bad token and not in the middle of it. (set (.pos c) (scan-atom c lo)) @@ -392,40 +392,40 @@ (if (= (at s i) \0) (do (set i (+ i 1)) - (when (and (< i (len s)) (or (= (at s i) \x) (= (at s i) \X))) + (when (and (< i (length s)) (or (= (at s i) \x) (= (at s i) \X))) (set (.pos c) (scan-atom c lo)) (fail c err-hex-number lo) (return (error-token c))) - (when (and (< i (len s)) (digit? (at s i))) + (when (and (< i (length s)) (digit? (at s i))) (set (.pos c) (scan-atom c lo)) (fail c err-leading-zero lo) (return (error-token c)))) - (while (and (< i (len s)) (digit? (at s i))) + (while (and (< i (length s)) (digit? (at s i))) (set i (+ i 1)))) ;; The fraction. A dot with no digit after it is `1.` or `1.e3`, both of ;; which JSON5 accepts and JSON does not. - (when (and (< i (len s)) (= (at s i) \.)) + (when (and (< i (length s)) (= (at s i) \.)) (set float? true) (set i (+ i 1)) - (when (or (>= i (len s)) (not (digit? (at s i)))) + (when (or (>= i (length s)) (not (digit? (at s i)))) (set (.pos c) (scan-atom c lo)) (fail c err-trailing-dot lo) (return (error-token c))) - (while (and (< i (len s)) (digit? (at s i))) + (while (and (< i (length s)) (digit? (at s i))) (set i (+ i 1)))) ;; The exponent. Its sign is allowed where the number's own leading + was ;; not, which is JSON's rule and reads like an inconsistency until you have ;; seen 1e+3 in a file written by a serialiser. - (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 float? true) (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))) - (when (or (>= i (len s)) (not (digit? (at s i)))) + (when (or (>= i (length s)) (not (digit? (at s i)))) (set (.pos c) (scan-atom c lo)) (fail c err-bad-number lo) (return (error-token c))) - (while (and (< i (len s)) (digit? (at s i))) + (while (and (< i (length s)) (digit? (at s i))) (set i (+ i 1)))) ;; Whatever is left has to end the token. Without this `12x` and `1.5.5` ;; would be accepted as their own prefixes and the junk would be read as ;; the next value. - (when (and (< i (len s)) (not (delim? (at s i)))) + (when (and (< i (length s)) (not (delim? (at s i)))) (set (.pos c) (scan-atom c lo)) (fail c err-bad-number lo) (return (error-token c))) (set (.pos c) i) @@ -449,7 +449,7 @@ ;; it is a function. (defn hex4 [c (Ptr Cursor) i i32] i32 (let [s (.src c)] - (when (> (+ i 4) (len s)) + (when (> (+ i 4) (length s)) (return -1)) (let [v 0] (dotimes [k 4] @@ -475,7 +475,7 @@ (defn read-string [c (Ptr Cursor) lo i32] Token (let [s (.src c) i (+ lo 1)] - (while (< i (len s)) + (while (< i (length s)) (let [b (at s i)] (cond (= b \") @@ -489,7 +489,7 @@ (= b \\) (do - (when (>= (+ i 1) (len s)) + (when (>= (+ i 1) (length s)) (set (.pos c) (+ i 1)) (fail c err-unterminated lo) (return (error-token c))) (let [e (at s (+ i 1))] (cond @@ -511,7 +511,7 @@ ;; rather than chosen, and it is the reason the pairing rule ;; is in the tokenizer at all. (when (high-surrogate? r) - (let [ok (and (< (+ i 1) (len s)) + (let [ok (and (< (+ i 1) (length s)) (= (at s i) \\) (= (at s (+ i 1)) \u) (low-surrogate? (hex4 c (+ i 2))))] @@ -600,7 +600,7 @@ ;; that is not there. (= b \/) (do (set (.pos c) (+ lo 1)) - (if (and (< (+ lo 1) (len s)) + (if (and (< (+ lo 1) (length s)) (or (= (at s (+ lo 1)) \/) (= (at s (+ lo 1)) \*))) (fail c err-comment lo) (fail c err-unexpected-byte lo)) @@ -677,7 +677,7 @@ (let [s (.text t) b (vec-new u8) i 0] - (while (< i (len s)) + (while (< i (length s)) (let [ch (at s i)] (if (!= ch \\) (do (push b ch) (set i (+ i 1))) diff --git a/vendor/json/provide.flan b/vendor/json/provide.flan index 2312b5e..f5a75c6 100644 --- a/vendor/json/provide.flan +++ b/vendor/json/provide.flan @@ -94,7 +94,7 @@ (Derived {.ty ty .decls decls .reader reader .bad ""})) (defn bad? [d Derived] bool - (> (len (bytes-view (.bad d))) 0)) + (> (length (bytes-view (.bad d))) 0)) (defn with-decl [decls [Form] d Form] [Form] (form-append decls (form-cons d (form-nil)))) @@ -288,7 +288,7 @@ d (derive c (joined3 name "-" fname) src)] (when (bad? d) (return d)) - (dotimes [i (len (.decls d))] + (dotimes [i (length (.decls d))] (push decls (at (.decls d) i))) (push fields (Form.Sym {.s fname})) (push fields (.ty d)) @@ -350,7 +350,7 @@ (bytes=? (.text t) (bytes-view s))) (defn has-escape? [s [u8]] bool - (dotimes [i (len s)] + (dotimes [i (length s)] (when (= (at s i) \\) (return true))) false) @@ -359,9 +359,9 @@ ;; would accept: this is the set a *person* would recognise as a name, and a ;; member called "a b" or "x.y" has no field it could become. (defn name-like? [s [u8]] bool - (when (= (len s) 0) + (when (= (length s) 0) (return false)) - (dotimes [i (len s)] + (dotimes [i (length s)] (let [b (at s i)] (when (not (or (alpha? b) (or (digit? b) @@ -394,7 +394,7 @@ (defn render-items [xs [Form]] string (let [out ""] - (dotimes [i (len xs)] + (dotimes [i (length xs)] (set out (if (= i 0) (render (at xs i)) (joined3 out " " (render (at xs i)))))) @@ -410,7 +410,7 @@ ;; first, then the struct named here, a reader per struct, and the two entry ;; points over the whole thing. (defmacro defjson [& args] - (if (!= (len args) 2) + (if (!= (length args) 2) (refuse "defjson is (defjson Name \"path.json\") — a name for the struct, and a path to the file its shape is read out of") (match (at args 1) (Form.Str path) diff --git a/vendor/raylib/modes.flan b/vendor/raylib/modes.flan index 327bc74..4346cf4 100644 --- a/vendor/raylib/modes.flan +++ b/vendor/raylib/modes.flan @@ -94,8 +94,8 @@ ;; The frame. Everything drawn lands on the back buffer; end-drawing swaps it ;; and waits out the frame time set by set-target-fps. (defmacro with-drawing [& args] - (if (or (< (len args) 1) - (and (= (len args) 1) (form-empty-list? (at args 0)))) + (if (or (< (length args) 1) + (and (= (length args) 1) (form-empty-list? (at args 0)))) `(with-drawing-takes-a-body) `(do (begin-drawing) ~@args @@ -105,8 +105,8 @@ ;; always was. Remember that a fresh (Camera2D {}) has zoom 0.0 and is not ;; usable as an identity — raylib.flan says so beside the struct. (defmacro with-mode-2d [& args] - (if (or (< (len args) 2) - (and (= (len args) 2) (form-empty-list? (at args 1)))) + (if (or (< (length args) 2) + (and (= (length args) 2) (form-empty-list? (at args 1)))) `(with-mode-2d-takes-a-camera-and-a-body) `(do (begin-mode-2d ~(at args 0)) ~@(form-rest args 1) @@ -116,8 +116,8 @@ ;; more here than anywhere: ending a 3D mode with end-mode-2d type-checks ;; fine and leaves the projection matrix wrong for everything after it. (defmacro with-mode-3d [& args] - (if (or (< (len args) 2) - (and (= (len args) 2) (form-empty-list? (at args 1)))) + (if (or (< (length args) 2) + (and (= (length args) 2) (form-empty-list? (at args 1)))) `(with-mode-3d-takes-a-camera-and-a-body) `(do (begin-mode-3d ~(at args 0)) ~@(form-rest args 1) @@ -128,8 +128,8 @@ ;; that correction is the caller's and is deliberately not hidden here, since ;; it belongs with the draw and not with the mode. (defmacro with-texture-mode [& args] - (if (or (< (len args) 2) - (and (= (len args) 2) (form-empty-list? (at args 1)))) + (if (or (< (length args) 2) + (and (= (length args) 2) (form-empty-list? (at args 1)))) `(with-texture-mode-takes-a-target-and-a-body) `(do (begin-texture-mode ~(at args 0)) ~@(form-rest args 1) @@ -139,8 +139,8 @@ ;; scalars rather than a Rectangle, because that is what BeginScissorMode ;; takes and this file is not the place to invent a second spelling. (defmacro with-scissor-mode [& args] - (if (or (< (len args) 5) - (and (= (len args) 5) (form-empty-list? (at args 4)))) + (if (or (< (length args) 5) + (and (= (length args) 5) (form-empty-list? (at args 4)))) `(with-scissor-mode-takes-x-y-width-height-and-a-body) `(do (begin-scissor-mode ~(at args 0) ~(at args 1) ~(at args 2) ~(at args 3)) ~@(form-rest args 4) diff --git a/vendor/raylib/raylib.flan b/vendor/raylib/raylib.flan index 5d70ea1..a6c985e 100644 --- a/vendor/raylib/raylib.flan +++ b/vendor/raylib/raylib.flan @@ -586,9 +586,9 @@ "CheckCollisionPointPoly") (defn collision-point-poly? [point Vector2 points [Vector2]] bool - (if (= (len points) 0) + (if (= (length points) 0) false - (collision-point-poly?-raw point (addr (at points 0)) (len points)))) + (collision-point-poly?-raw point (addr (at points 0)) (length points)))) ;; The one that answers with more than yes or no: where the two segments meet. ;; None is "they do not", so the point cannot be read when there isn't one — @@ -812,9 +812,9 @@ (defonce no-image Image) (defn load-image-from-memory [file-type string data [u8]] Image - (if (= (len data) 0) + (if (= (length data) 0) no-image - (load-image-from-memory-raw file-type (addr (at data 0)) (len data)))) + (load-image-from-memory-raw file-type (addr (at data 0)) (length data)))) ;; By value, as raylib has it. The caller's copy is dangling afterwards — ;; `data` pointed at the buffer this just freed — so an Image is used or @@ -983,7 +983,7 @@ ;; Eleven entry points take an array of vectors as a pointer plus an `int` ;; count. A Flan slice already carries both, so every call site that does not ;; go through a wrapper has to take the slice apart itself — `(addr (at pts -;; 0))` and `(len pts)`, twice, in the right order — and the compiler cannot +;; 0))` and `(length pts)`, twice, in the right order — and the compiler cannot ;; check that the two halves came from the same slice. The wrapper is where ;; that idiom lives, which is the rule collision-point-poly? set. ;; @@ -1016,56 +1016,57 @@ ;; `name` lines at the foot of `bindings`. (defn draw-line-strip [points [Vector2] color Color] () - (when (> (len points) 0) - (draw-line-strip-raw (addr (at points 0)) (len points) color))) + (when (> (length points) 0) + (draw-line-strip-raw (addr (at points 0)) (length points) color))) (defn draw-triangle-fan [points [Vector2] color Color] () - (when (> (len points) 0) - (draw-triangle-fan-raw (addr (at points 0)) (len points) color))) + (when (> (length points) 0) + (draw-triangle-fan-raw (addr (at points 0)) (length points) color))) (defn draw-triangle-strip [points [Vector2] color Color] () - (when (> (len points) 0) - (draw-triangle-strip-raw (addr (at points 0)) (len points) color))) + (when (> (length points) 0) + (draw-triangle-strip-raw (addr (at points 0)) (length points) color))) (defn draw-triangle-strip-3d [points [Vector3] color Color] () - (when (> (len points) 0) - (draw-triangle-strip-3d-raw (addr (at points 0)) (len points) color))) + (when (> (length points) 0) + (draw-triangle-strip-3d-raw (addr (at points 0)) (length points) color))) ;; The five spline drawers. raylib reads the same point array five different ;; ways; the only difference between these wrappers is which one it calls. (defn draw-spline-linear [points [Vector2] thick f32 color Color] () - (when (> (len points) 0) - (draw-spline-linear-raw (addr (at points 0)) (len points) thick color))) + (when (> (length points) 0) + (draw-spline-linear-raw (addr (at points 0)) (length points) thick color))) (defn draw-spline-basis [points [Vector2] thick f32 color Color] () - (when (> (len points) 0) - (draw-spline-basis-raw (addr (at points 0)) (len points) thick color))) + (when (> (length points) 0) + (draw-spline-basis-raw (addr (at points 0)) (length points) thick color))) (defn draw-spline-catmull-rom [points [Vector2] thick f32 color Color] () - (when (> (len points) 0) - (draw-spline-catmull-rom-raw (addr (at points 0)) (len points) thick color))) + (when (> (length points) 0) + (draw-spline-catmull-rom-raw (addr (at points 0)) (length points) thick color))) (defn draw-spline-bezier-quadratic [points [Vector2] thick f32 color Color] () - (when (> (len points) 0) + (when (> (length points) 0) (draw-spline-bezier-quadratic-raw - (addr (at points 0)) (len points) thick color))) + (addr (at points 0)) (length points) thick color))) (defn draw-spline-bezier-cubic [points [Vector2] thick f32 color Color] () - (when (> (len points) 0) + (when (> (length points) 0) (draw-spline-bezier-cubic-raw - (addr (at points 0)) (len points) thick color))) + (addr (at points 0)) (length points) thick color))) ;; The same two into an Image rather than the frame. `dst` stays a pointer: ;; it is the thing being written, not an array, and raylib's convention for ;; an in-place Image is the whole Image* family in this file. (defn image-draw-triangle-fan [dst (Ptr Image) points [Vector2] color Color] () - (when (> (len points) 0) - (image-draw-triangle-fan-raw dst (addr (at points 0)) (len points) color))) + (when (> (length points) 0) + (image-draw-triangle-fan-raw dst (addr (at points 0)) + (length points) color))) (defn image-draw-triangle-strip [dst (Ptr Image) points [Vector2] color Color] () - (when (> (len points) 0) - (image-draw-triangle-strip-raw dst (addr (at points 0)) (len points) color))) + (when (> (length points) 0) + (image-draw-triangle-strip-raw dst (addr (at points 0)) (length points) color))) ;; ── Text ──────────────────────────────────────────────────────────── ;; @@ -1508,9 +1509,9 @@ "LoadFontEx") (defn load-font-ex [path string font-size i32 codepoints [i32]] Font - (if (= (len codepoints) 0) + (if (= (length codepoints) 0) (load-font-ex-raw path font-size default-codepoints 0) - (load-font-ex-raw path font-size (addr (at codepoints 0)) (len codepoints)))) + (load-font-ex-raw path font-size (addr (at codepoints 0)) (length codepoints)))) ;; raylib 5.5 spells this IsFontValid. It reads the texture id and both ;; arrays, so a font that loaded but could not upload its atlas — which is diff --git a/web/examples/arrays.flan b/web/examples/arrays.flan index e31eb14..8dd5152 100644 --- a/web/examples/arrays.flan +++ b/web/examples/arrays.flan @@ -10,7 +10,7 @@ (defn main [] () (set (at grid 1 2) 7) (print (at grid 1 2)) (println "") ; 7 - (print (len palette)) (println "") ; 4 + (print (length palette)) (println "") ; 4 ;; A slice is ptr+len and non-owning: it views the array, it does not copy it. (let [row (slice (at grid 1) 0 cols)] diff --git a/web/examples/control.flan b/web/examples/control.flan index 2820eaf..5f151c4 100644 --- a/web/examples/control.flan +++ b/web/examples/control.flan @@ -15,7 +15,7 @@ (println ""))) (defn first-even [s [i32]] (Option i32) - (dotimes [i (len s)] + (dotimes [i (length s)] (when (= 0 (% (at s i) 2)) (return (Some (at s i))))) None) @@ -25,6 +25,6 @@ (countdown 4) (unless false (println "unless runs when the test is false")) - (match (first-even (slice nums 0 (len nums))) + (match (first-even (slice nums 0 (length nums))) (Some n) (do (print n) (println "")) None (println "none"))) diff --git a/web/examples/structs.flan b/web/examples/structs.flan index d48c9ca..e9c58d8 100644 --- a/web/examples/structs.flan +++ b/web/examples/structs.flan @@ -3,7 +3,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)) diff --git a/web/index.html b/web/index.html index d831a60..dd7a2fe 100644 --- a/web/index.html +++ b/web/index.html @@ -575,7 +575,7 @@ its fields, and omitted fields are zeroed.

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)) @@ -696,7 +696,7 @@ whose type matters is named at the top level rather than written inline.

(println ""))) (defn first-even [s [i32]] (Option i32) - (dotimes [i (len s)] + (dotimes [i (length s)] (when (= 0 (% (at s i) 2)) (return (Some (at s i))))) None) @@ -706,7 +706,7 @@ whose type matters is named at the top level rather than written inline.

(countdown 4) (unless false (println "unless runs when the test is false")) - (match (first-even (slice nums 0 (len nums))) + (match (first-even (slice nums 0 (length nums))) (Some n) (do (print n) (println "")) None (println "none"))) @@ -834,7 +834,7 @@ extent, because nothing is released at scope exit)

at indexes a fixed array or a slice, and takes any number of indices, so (at grid r c) indexes a two-dimensional fixed array directly. It is a place: (set (at grid r c) v) and (addr (at grid r c)) both work. -len works on a fixed array, a slice or a string. +length works on a fixed array, a slice or a string. (slice s lo hi) takes a half-open range and never copies.

(defconst rows 3)
@@ -849,7 +849,7 @@ It is a place: (set (at grid r c) v) and (addr (at grid r c))
 (defn main [] ()
   (set (at grid 1 2) 7)
   (print (at grid 1 2)) (println "")           ; 7
-  (print (len palette)) (println "")           ; 4
+  (print (length palette)) (println "")        ; 4
 
   ;; A slice is ptr+len and non-owning: it views the array, it does not copy it.
   (let [row (slice (at grid 1) 0 cols)]
@@ -896,7 +896,7 @@ type as an argument — (vec-new t), (map-new t i32),
 
 (defn first-or [s [$t]  d $t] $t         ; the variable inside a slice type
   {:where (equal? $t)}
-  (if (= (len s) 0) d (at s 0)))
+  (if (= (length s) 0) d (at s 0)))
 
 (defn one-of [x $t] (Vec $t)             ; bare t is the type-name argument
   (let [v (vec-new t)]
@@ -1132,7 +1132,7 @@ here; a whole file at a time is the surface.

The primitives underneath are few — a primitive is the only thing implemented twice per backend: argv, -write-stdout, exit, len, at, +write-stdout, exit, length, at, slice, bytes, bytes->f64, bytes->i64, f64->bytes, i64->bytes, addr, and arithmetic, comparison and casts.