Merge branch 'master' into worktree-agent-ae8e466a60e7cf35a

This commit is contained in:
Joseph Ferano 2026-09-26 05:44:39 +07:00
commit 895828992e
160 changed files with 762 additions and 693 deletions

View File

@ -182,7 +182,7 @@ problems:
$ flan import-c test/headers/sample.h $ flan import-c test/headers/sample.h
(declare-c set-seed [seed u32] "set_seed") (declare-c set-seed [seed u32] "set_seed")
(declare-c add-ints [a i32 b i32] i32 "add_ints") (declare-c add-ints [a i32 b i32] i32 "add_ints")
(declare-c name-length [text string] i32 "name_length") (declare-c name-length [text str] i32 "name_length")
... ...
;; 9 imported, 12 refused, of 22 functions in test/headers/sample.h ;; 9 imported, 12 refused, of 22 functions in test/headers/sample.h
;; refused owned-text: owned_text returns char *, which the caller owns and ;; refused owned-text: owned_text returns char *, which the caller owns and

View File

@ -12,8 +12,8 @@ rejected without a record is an idea that gets re-proposed.
* Language surface * Language surface
** NEXT str and String ** NEXT str and String
Decided 2026-09-25: the typed read-only text is =str= (renamed from =string=); =String= Decided 2026-09-25: the typed read-only text is =str= (the rename from =string= is
is new, owned, growable, always valid UTF-8, through an allocator. The prelude's text done; =string= is refused with the fix); =String= is new, owned, growable, always valid UTF-8, through an allocator. The prelude's text
builders return =String=; =(Vec u8)= stays for raw bytes. Text is UTF-8 everywhere and builders return =String=; =(Vec u8)= stays for raw bytes. Text is UTF-8 everywhere and
a character is a code point; dyn gets a character tag, so \\I prints as \\I. a character is a code point; dyn gets a character tag, so \\I prints as \\I.
Dyn text stays immutable, with chars and text converting to and from a dyn Dyn text stays immutable, with chars and text converting to and from a dyn
@ -2076,7 +2076,7 @@ an unannotated two-name parameter vector compiles as two dyn parameters, and a
=defn= named after a builtin stopped being unreachable. =defn= named after a builtin stopped being unreachable.
** WAIT Every diagnostic carries a stable kind at the end of its first line ** WAIT Every diagnostic carries a stable kind at the end of its first line
Decided 2026-09-25: wanted, in the shape =... found string [type-mismatch]=. Waits Decided 2026-09-25: wanted, in the shape =... found str [type-mismatch]=. Waits
for the documentation rewrite, which is what a kind would link to. The clause for the documentation rewrite, which is what a kind would link to. The clause
order — understood, then the conflict, then the fix — is a rule in =CLAUDE.md=, order — understood, then the conflict, then the fix — is a rule in =CLAUDE.md=,
and reporting every error in a form is its own entry under the Editor heading. and reporting every error in a form is its own entry under the Editor heading.

View File

@ -118,7 +118,7 @@
;; print and println are compiler-provided and structural: the walk over the ;; print and println are compiler-provided and structural: the walk over the
;; argument's concrete type happens at compile time, so there is nothing to ;; 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. ;; dispatch on at run time and no type to name at the call site.
(defn main [args [string]] i32 (defn main [args [str]] i32
(if (< (length args) 2) (if (< (length args) 2)
(do (println "usage: calc-me \"1 + 2 * 3\"") 1) (do (println "usage: calc-me \"1 + 2 * 3\"") 1)
(match (evaluate (bytes-view (at args 1))) (match (evaluate (bytes-view (at args 1)))

View File

@ -676,8 +676,8 @@ exercised by running `sand.flan` and looking, which is the whole of what can be
### A string returned from C is copied into the context allocator ### A string returned from C is copied into the context allocator
A `declare-c` may return `string`. The C wrapper answers the pointer and its length, and the generated Flan wrapper is A `declare-c` may return `str`. The C wrapper answers the pointer and its length, and the generated Flan wrapper is
`(string (bytes (string (slice-from-ptr p n))))`: a view of C's bytes, copied by `bytes` into the context allocator. The `(str (bytes (str (slice-from-ptr p n))))`: a view of C's bytes, copied by `bytes` into the context allocator. The
copy is Flan's rather than C's so that it goes through the same allocation guard (`StorageExhausted` with `retry`) and copy is Flan's rather than C's so that it goes through the same allocation guard (`StorageExhausted` with `retry`) and
the same registry note every other allocation does, and `with-allocator` picks where it lands. The string lives until the same registry note every other allocation does, and `with-allocator` picks where it lands. The string lives until
that allocator's `free-all` or destroy, which is the lifetime `(bytes s)` already has; a caller calling one every frame that allocator's `free-all` or destroy, which is the lifetime `(bytes s)` already has; a caller calling one every frame
@ -689,7 +689,7 @@ in that case the wrapper first moves the text into one scratch buffer the shim o
there. Without a string argument the pointer is the library's own and nothing is moved twice. A null is the empty there. Without a string argument the pointer is the library's own and nothing is moved twice. A null is the empty
string. string.
The importer follows `const`. `const char *` returned is text the caller only reads, so it becomes `string`; `char *` The importer follows `const`. `const char *` returned is text the caller only reads, so it becomes `str`; `char *`
without `const` is text the caller owns and releases through the library (`LoadFileText` and `UnloadFileText`), and a without `const` is text the caller owns and releases through the library (`LoadFileText` and `UnloadFileText`), and a
copy would leave the original with nothing to release it through, so that one stays refused and a hand-written copy would leave the original with nothing to release it through, so that one stays refused and a hand-written
`declare-c` saying `(Ptr u8)` binds it. `TextFormat` stays unbound because it is variadic. Eighteen raylib functions came `declare-c` saying `(Ptr u8)` binds it. `TextFormat` stays unbound because it is variadic. Eighteen raylib functions came
@ -711,7 +711,7 @@ over a mesh built in Flan and a model translated by its `transform`.
`FilePathList` is a `char **` raylib owns until the matching `Unload`. It crosses by the returned-string rule: `FilePathList` is a `char **` raylib owns until the matching `Unload`. It crosses by the returned-string rule:
`dropped-files`, `directory-files` and `directory-files-ex` copy each path into the context allocator, release raylib's `dropped-files`, `directory-files` and `directory-files-ex` copy each path into the context allocator, release raylib's
list before returning, and answer a `(Vec string)`. There is nothing of raylib's left for the caller to unload, which is list before returning, and answer a `(Vec str)`. There is nothing of raylib's left for the caller to unload, which is
why they are not named `load-`. why they are not named `load-`.
### rlgl is its own package ### rlgl is its own package
@ -1167,7 +1167,7 @@ i8..i64 / u8..u64 i8..i64 signedness lives in the ops
f32 f64 float double f32 f64 float double
bool i1 bool i1
an enum i32 an enum i32
[T] and string { ptr, i64 } ptr+len, non-owning [T] and str { ptr, i64 } ptr+len, non-owning
[n T] [n x T] inline, a value [n T] [n x T] inline, a value
(Ptr T) ptr opaque pointers (Ptr T) ptr opaque pointers
(Option T) { i8, T } tag 0 None, 1 Some (Option T) { i8, T } tag 0 None, 1 Some
@ -2962,7 +2962,7 @@ as the shape of the whole feature: the answer is a Flan expression, and there is
program. program.
**Lookup stayed by name, and the signature is checked against what it found.** `flan_find_restart` matches the name **Lookup stayed by name, and the signature is checked against what it found.** `flan_find_restart` matches the name
hash and nothing else, so an inner `(use-value [s string] ...)` shadows an outer `(use-value [v i32] ...)` and an i32 hash and nothing else, so an inner `(use-value [s str] ...)` shadows an outer `(use-value [v i32] ...)` and an i32
is refused there — the outer frame that would have taken it is never consulted. That is §4 read straight ("the first is refused there — the outer frame that would have taken it is never consulted. That is §4 read straight ("the first
frame offering the name") and it is the thing a reader will assume works the other way, so `restarts.flan` has a case frame offering the name") and it is the thing a reader will assume works the other way, so `restarts.flan` has a case
for it. Searching outward for a frame whose signature fits would make which restart runs depend on the arguments, for it. Searching outward for a frame whose signature fits would make which restart runs depend on the arguments,
@ -3002,7 +3002,7 @@ function values and the work doubles.
None of the four is anywhere near this. All four are about *surface syntax*, and a value the compiler builds that no None of the four is anywhere near this. All four are about *surface syntax*, and a value the compiler builds that no
surface form names trips none of them: surface form names trips none of them:
- `Allocator` is a `Types.t` case with no user-writable constructor, the way `string` is a builtin ptr+len. No Flan - `Allocator` is a `Types.t` case with no user-writable constructor, the way `str` is a builtin ptr+len. No Flan
type names its procedure. type names its procedure.
- The procedure is a C symbol the emitter names. - The procedure is a C symbol the emitter names.
- `vec-new`, `push`, `free`, `free-all` and the rest are ordinary named calls, which `check_call` already routes - `vec-new`, `push`, `free`, `free-all` and the rest are ordinary named calls, which `check_call` already routes
@ -3564,7 +3564,7 @@ the payload at offset 8, and 40/8 for a struct holding one.
[Empty [Empty
(Dot [x f64 y f64]) (Dot [x f64 y f64])
(Rect [w i32 h i32]) (Rect [w i32 h i32])
(Tag [name string n u8])]) (Tag [name str n u8])])
(Shape.Rect {.w 3 .h 3}) ; a value: the type and the case, then the fields (Shape.Rect {.w 3 .h 3}) ; a value: the type and the case, then the fields
(Shape.Rect {.w 3}) ; ZII, exactly as in a struct literal — .h is 0 (Shape.Rect {.w 3}) ; ZII, exactly as in a struct literal — .h is 0
@ -3614,7 +3614,7 @@ is a layout change, the same way reordering a struct's fields is.
- **`(.x u)`.** A union's fields belong to a case, and which case is being held is what the tag says. `match` is how - **`(.x u)`.** A union's fields belong to a case, and which case is being held is what the tag says. `match` is how
one is opened, and its arms bind the fields of the case they matched. one is opened, and its arms bind the fields of the case they matched.
- **A global initialised with a case.** `(defonce g U (U.B {.x 1}))` would mean serialising the fields into the payload - **A global initialised with a case.** `(defonce g U (U.B {.x 1}))` would mean serialising the fields into the payload
blob at link time, which is a byte-level encoder this compiler does not have and which could not express a `string` blob at link time, which is a byte-level encoder this compiler does not have and which could not express a `str`
field at all — that is a pointer the linker has to relocate and a byte array has nowhere to put a relocation. A field at all — that is a pointer the linker has to relocate and a byte array has nowhere to put a relocation. A
*zeroed* global is fine and needs none of it: it is the first declared case. *zeroed* global is fine and needs none of it: it is the first declared case.
@ -3771,7 +3771,7 @@ macro returns. That is the structural answer to "keep the source location of the
produces", and it is what the queued structured-error work reads. produces", and it is what the queued structured-error work reads.
Case order is tag order, so the list in the prelude is a layout contract and says so. The widest cases are Case order is tag order, so the list in the prelude is a layout contract and says so. The widest cases are
`(Str [s string])` and `(List [xs [Form]])`; a string and a slice are both `%slice` = `{ptr, i64}`, 16 bytes at `(Str [s str])` and `(List [xs [Form]])`; a string and a slice are both `%slice` = `{ptr, i64}`, 16 bytes at
align 8. So the image is `{ i32 tag, [2 x i64] payload }`: **24 bytes, align 8, payload at offset 8**, and every case align 8. So the image is `{ i32 tag, [2 x i64] payload }`: **24 bytes, align 8, payload at offset 8**, and every case
holds its one member at the payload's start, so there is no third offset anywhere in the marshaller. holds its one member at the payload's start, so there is no third offset anywhere in the marshaller.
@ -4310,8 +4310,8 @@ be wrong in a way worth being able to see: the prelude *was* reaching the expand
The list at the foot of `prelude.ml` used to be one sentence — every entry needed to produce bytes that did not exist The list at the foot of `prelude.ml` used to be one sentence — every entry needed to produce bytes that did not exist
in its input, and there was no allocator. `join`, `concat`, `split`, `to-lower`, `to-upper`, `repeat` and `replace` in its input, and there was no allocator. `join`, `concat`, `split`, `to-lower`, `to-upper`, `repeat` and `replace`
have moved up into the code; `string-from-bytes` turned out to be the `string` builtin all along, and have moved up into the code; `string-from-bytes` turned out to be the `str` builtin all along, and
`(string (slice v))` is the round trip, free precisely because the layouts are identical. `(str (slice v))` is the round trip, free precisely because the layouts are identical.
What remains is refused for four different reasons, and is now written that way: `pad`/`center` for *nothing at all* What remains is refused for four different reasons, and is now written that way: `pad`/`center` for *nothing at all*
except that no caller has asked; `format`/`sprintf` for variadics of mixed type; `map`/`filter`/`reduce`/`sort-by` except that no caller has asked; `format`/`sprintf` for variadics of mixed type; `map`/`filter`/`reduce`/`sort-by`
@ -4879,7 +4879,7 @@ and the rule is easier to state and to trust with one construct in it.
## Assets are baked in, and the reason it is a compiler feature ## Assets are baked in, and the reason it is a compiler feature
TODO.org, "Assets are embedded at compile time". `(embed "brush.png")` is a `[const u8]`, `(embed "brush.png" string)` is a `string`, and TODO.org, "Assets are embedded at compile time". `(embed "brush.png")` is a `[const u8]`, `(embed "brush.png" str)` is a `str`, and
`(embed-dir "assets")` is a `[n EmbedFile]` sorted by name. Odin's `#load` and `#load_directory` are the model `(embed-dir "assets")` is a `[n EmbedFile]` sorted by name. Odin's `#load` and `#load_directory` are the model
(`src/parser.cpp`, and `check_load_directive` / `check_load_directory_directive` in `src/check_builtin.cpp`); Odin's (`src/parser.cpp`, and `check_load_directive` / `check_load_directory_directive` in `src/check_builtin.cpp`); Odin's
`#` is not imported, because an s-expression language already has a head position for a name and these resolve as `#` is not imported, because an s-expression language already has a head position for a name and these resolve as
@ -4897,16 +4897,16 @@ into the same `private unnamed_addr constant` every string literal already becom
across the whole 0–255 range, so a PNG survives the round trip through the `.ll`. Bound with `defconst` at top level an across the whole 0–255 range, so a PNG survives the round trip through the `.ll`. Bound with `defconst` at top level an
`embed-dir` is an LLVM constant outright, through emit.ml's `const`. `embed-dir` is an LLVM constant outright, through emit.ml's `const`.
**A `Str` node typed `[const u8]`, not a `Bytes` prim over a `string`.** This is the one non-obvious choice. `Bytes` is **A `Str` node typed `[const u8]`, not a `Bytes` prim over a `str`.** This is the one non-obvious choice. `Bytes` is
identity — emit.ml lowers `Types.String` and `Types.Slice _` to the same `%slice` — but wrapping the literal in a prim identity — emit.ml lowers `Types.String` and `Types.Slice _` to the same `%slice` — but wrapping the literal in a prim
makes the node non-constant, and `const` then refuses an `embed-dir` in a `defconst` with *a global's value must be a makes the node non-constant, and `const` then refuses an `embed-dir` in a `defconst` with *a global's value must be a
compile-time constant*. Both of emit.ml's string emitters take the bytes and ignore the node's type, so it is the same compile-time constant*. Both of emit.ml's string emitters take the bytes and ignore the node's type, so it is the same
constant either way and this one is a constant a global can hold. constant either way and this one is a constant a global can hold.
**Two spellings, not one form that changes type with its context.** Odin threads a `type_hint` everywhere and can **Two spellings, not one form that changes type with its context.** Odin threads a `type_hint` everywhere and can
afford `#load("p")` to mean a `string` here and a `[]u8` there. With structural equality and no conversion between one afford `#load("p")` to mean a `str` here and a `[]u8` there. With structural equality and no conversion between one
container and another — implicit widening is numbers only — the same text meaning two types would be a wart, so container and another — implicit widening is numbers only — the same text meaning two types would be a wart, so
`string` is written down when it is wanted. The `str` is written down when it is wanted. The
site's expectation is a fallback only and nothing depends on it. site's expectation is a fallback only and nothing depends on it.
**The path is a literal and resolves relative to the file the form is written in.** Both are Odin's rules and for **The path is a literal and resolves relative to the file the form is written in.** Both are Odin's rules and for
@ -5702,7 +5702,7 @@ Measured on a 200-million-iteration loop at `-O2`: 0.53s with the call, 0.00s wi
Four entry points a program can reach through `declare-c` write a scalar: Four entry points a program can reach through `declare-c` write a scalar:
```flan ```flan
(declare-c watch-i64 [name string x i64] i32 "flan_dev_watch_i64") (declare-c watch-i64 [name str x i64] i32 "flan_dev_watch_i64")
(watch-i64 "ticks" ticks) (watch-i64 "ticks" ticks)
``` ```
@ -7079,7 +7079,7 @@ expression the checker walks can live.
### The rules it derives, and the one the game file decided ### The rules it derives, and the one the game file decided
A map with keyword keys is a struct, one field per key. An integer is `i64`, a float A map with keyword keys is a struct, one field per key. An integer is `i64`, a float
`f64`, a boolean `bool`, a string `string` — *copied*, which is `read.flan`'s contract and `f64`, a boolean `bool`, a string `str` — *copied*, which is `read.flan`'s contract and
not the tokenizer's: a `Token`'s text points into the buffer and a struct that outlives not the tokenizer's: a `Token`'s text points into the buffer and a struct that outlives
the buffer cannot hold one. A vector of one repeated shape is `(Vec T)`. A nested map is a the buffer cannot hold one. A vector of one repeated shape is `(Vec T)`. A nested map is a
struct named for the path that reaches it, `Tileset-selected-cells`, with a hyphen because struct named for the path that reaches it, `Tileset-selected-cells`, with a hyphen because
@ -7403,7 +7403,7 @@ it: an assignment that *completed* before the signal stands, and rolling those b
restart is not a transaction"). What no longer happens is a single assignment left halfway. restart is not a transaction"). What no longer happens is a single assignment left halfway.
**A `def` whose type changed** is refused before anything is stored, by `Session.compatible`, which already said the **A `def` whose type changed** is refused before anything is stored, by `Session.compatible`, which already said the
right sentence for this path: *`speed` changes type, from `i64` to `string`; the running program already laid that right sentence for this path: *`speed` changes type, from `i64` to `str`; the running program already laid that
storage out. Restart to change it.* storage out. Restart to change it.*
**`defonce` is untouched**, and that is its contract: `kind = Once` never reaches the store list, so re-evaluating one **`defonce` is untouched**, and that is its contract: `kind = Once` never reaches the store list, so re-evaluating one

View File

@ -255,7 +255,7 @@ host does:
1. Hold the state in a `let` in `main` and pass `(Ptr Game)` to update and draw. Costs 1. Hold the state in a `let` in `main` and pass `(Ptr Game)` to update and draw. Costs
one parameter on three functions. one parameter on three functions.
2. For the texture cache specifically, a `[8 CacheEntry]` fixed array with a count beats a 2. For the texture cache specifically, a `[8 CacheEntry]` fixed array with a count beats a
`(Map string Texture2D)` outright — the game loads five textures, and a linear scan of `(Map str Texture2D)` outright — the game loads five textures, and a linear scan of
five path comparisons at load time is not worth a hash table. This is a case where the five path comparisons at load time is not worth a hash table. This is a case where the
restriction pushes toward the right answer. restriction pushes toward the right answer.
@ -692,7 +692,7 @@ each finding in §A names the command that produced the failure.
> differences it accepted. `vendor/raylib/raylib.flan` carries > differences it accepted. `vendor/raylib/raylib.flan` carries
> `get-codepoint-previous-raw` — the exact declaration this section says it wanted — and > `get-codepoint-previous-raw` — the exact declaration this section says it wanted — and
> a `get-codepoint-previous` wrapper over it that takes the bytes and an offset; > a `get-codepoint-previous` wrapper over it that takes the bytes and an offset;
> `generated.flan` no longer carries the `string`-faced version at all, because a binding > `generated.flan` no longer carries the `str`-faced version at all, because a binding
> that is wrong for the only direction it reads in is worse than no binding. The example's > that is wrong for the only direction it reads in is worse than no binding. The example's
> `step-back` is one call to it, the continuation-byte walk is gone, and the expression > `step-back` is one call to it, the continuation-byte walk is gone, and the expression
> below now prints 12356 and 3. The section is kept as written; the reasoning is the part > below now prints 12356 and 3. The section is kept as written; the reasoning is the part
@ -700,7 +700,7 @@ each finding in §A names the command that produced the failure.
This is the real find of the round, and it is general: **a C function that reads This is the real find of the round, and it is general: **a C function that reads
*backwards* from the pointer it is handed cannot be given a Flan `string`.** *backwards* from the pointer it is handed cannot be given a Flan `str`.**
`lib/shim.ml` is explicit about why — `Pstr` is "ptr+len in, a NUL-terminated copy out", `lib/shim.ml` is explicit about why — `Pstr` is "ptr+len in, a NUL-terminated copy out",
and the wrapper builds that copy for the duration of the call. Forwards, that is invisible and the wrapper builds that copy for the duration of the call. Forwards, that is invisible
@ -714,7 +714,7 @@ It does not crash, which is the bad part. Run headless:
(let [text "いろはに" (let [text "いろはに"
b (bytes text) b (bytes text)
sz 0] sz 0]
(println (rl/get-codepoint-previous (string (slice b 3 (length b))) (addr sz))) (println (rl/get-codepoint-previous (str (slice b 3 (length b))) (addr sz)))
(println sz)) (println sz))
``` ```
@ -735,15 +735,15 @@ and means it, as `load-image-from-memory-raw` and `load-font-ex-raw` do:
``` ```
with `(addr (at b i))` for the interior pointer. It does not work, and the reason is not with `(addr (at b i))` for the interior pointer. It does not work, and the reason is not
the FFI. `lib/cimport.ml` maps `const char *` to `string`, and `agrees` — the function that the FFI. `lib/cimport.ml` maps `const char *` to `str`, and `agrees` — the function that
decides whether a hand-written declaration and the header say the same thing — accepts decides whether a hand-written declaration and the header say the same thing — accepts
exactly one difference, an enum against a 32-bit integer. `(Ptr u8)` against `string` is not exactly one difference, an enum against a 32-bit integer. `(Ptr u8)` against `str` is not
it. Adding the line above and building *anything*, not only regenerating, gives: it. Adding the line above and building *anything*, not only regenerating, gives:
``` ```
vendor/raylib/generated.flan:233:12: the declare-c of get-codepoint-previous-raw vendor/raylib/generated.flan:233:12: the declare-c of get-codepoint-previous-raw
disagrees with vendor/raylib/raylib-5.5.h: parameter text is (Ptr u8) and the disagrees with vendor/raylib/raylib-5.5.h: parameter text is (Ptr u8) and the
header says string (const char *) header says str (const char *)
``` ```
`headers` is read on every build, so the binding cannot be added without relaxing the `headers` is read on every build, so the binding cannot be added without relaxing the
@ -823,12 +823,12 @@ something with a length, and it was reached for three times across the two files
open question and the answer is clean. The reader takes the bytes, the object file carries open question and the answer is clean. The reader takes the bytes, the object file carries
them, the shim hands them over, and `LoadCodepoints` decodes the 54 codepoints of the Iroha them, the shim hands them over, and `LoadCodepoints` decodes the 54 codepoints of the Iroha
into the 49 distinct ones the atlas is built from. Nothing in Flan claims to know what a into the 49 distinct ones the atlas is built from. Nothing in Flan claims to know what a
character is — a `string` is bytes and `(bytes s)` / `(string b)` say so in both directions character is — a `str` is bytes and `(bytes s)` / `(str b)` say so in both directions
at no cost — and for this job that is exactly the right amount of opinion. There is a 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. `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 (length b)))` **An interior pointer into a string has an idiom already.** `(str (slice b off (length b)))`
is the C's `char *ptr` and compiles to nothing: a `string` and a `[u8]` are the same two is the C's `char *ptr` and compiles to nothing: a `str` and a `[u8]` are the same two
words. Every forward-reading `const char *` entry point is reachable that way. words. Every forward-reading `const char *` entry point is reachable that way.
**`load-font-ex` needed nothing.** It takes a `[i32]` of codepoints, takes the **`load-font-ex` needed nothing.** It takes a `[i32]` of codepoints, takes the
@ -852,7 +852,7 @@ example.
> test rather than a raylib example, and there is not one. > test rather than a raylib example, and there is not one.
**Fixed arrays were the right shape for both.** A codepoint table with a count, a **Fixed arrays were the right shape for both.** A codepoint table with a count, a
`[9 Rectangle]` of toggle buttons, a `[9 string]` of labels. The §3 finding from the `[9 Rectangle]` of toggle buttons, a `[9 str]` of labels. The §3 finding from the
siam-farmer report — a global cannot hold a `Vec`, and fixed arrays with counts are usually siam-farmer report — a global cannot hold a `Vec`, and fixed arrays with counts are usually
the better answer anyway — held again, in two more programs, with no friction at all. the better answer anyway — held again, in two more programs, with no friction at all.

View File

@ -429,7 +429,7 @@ frames that have no file.
know which restart you want and do not need the buffer. know which restart you want and do not need the buffer.
**The condition's fields are named and typed, and have no values.** Under the **The condition's fields are named and typed, and have no values.** Under the
condition you get the struct it is — `:path string`, `:tried i32` — because the condition you get the struct it is — `:path str`, `:tried i32` — because the
daemon compiled the program and knows what that type looks like without asking daemon compiled the program and knows what that type looks like without asking
the program anything. What is beside each field is a note saying the value is the program anything. What is beside each field is a note saying the value is
not available, not a blank: a value lives in the stopped frame, and nothing yet not available, not a blank: a value lives in the stopped frame, and nothing yet
@ -551,7 +551,7 @@ answers a different question — it answers what the **next** run does.
now, and what you type is a Flan **expression**, evaluated in the program and now, and what you type is a Flan **expression**, evaluated in the program and
checked against the type of the place it is going into. `(+ 1 2)`, a string checked against the type of the place it is going into. `(+ 1 2)`, a string
literal, a whole struct literal all work; one that does not fit comes back with literal, a whole struct literal all work; one that does not fit comes back with
the checker's own sentence — *expected f32, found string* — and nothing is the checker's own sentence — *expected f32, found str* — and nothing is
stored. A literal arrives at the place's width, so `3` into an `f32` field is stored. A literal arrives at the place's width, so `3` into an `f32` field is
three point oh and not a refusal you would have had to write `3.0` to avoid. three point oh and not a refusal you would have had to write `3.0` to avoid.
Point on no field line at all sets the whole value, which is how a data type's Point on no field line at all sets the whole value, which is how a data type's
@ -772,8 +772,8 @@ last. These two are the other half. A slot keeps **count, min, max, last and
mean**, and the buffer renders them as one line: mean**, and the buffer renders them as one line:
```flan ```flan
(declare-c watch-num-i64 [name string x i64] i32 "flan_dev_watch_num_i64") (declare-c watch-num-i64 [name str x i64] i32 "flan_dev_watch_num_i64")
(declare-c watch-num-f64 [name string x f64] i32 "flan_dev_watch_num_f64") (declare-c watch-num-f64 [name str x f64] i32 "flan_dev_watch_num_f64")
(watch-num-i64 "cell" (at grid i)) (watch-num-i64 "cell" (at grid i))
``` ```
@ -795,9 +795,9 @@ table is also reachable as plain C functions, one per scalar type, which a
program declares like any other: program declares like any other:
```flan ```flan
(declare-c watch-i64 [name string x i64] i32 "flan_dev_watch_i64") (declare-c watch-i64 [name str x i64] i32 "flan_dev_watch_i64")
(declare-c watch-f64 [name string x f64] i32 "flan_dev_watch_f64") (declare-c watch-f64 [name str x f64] i32 "flan_dev_watch_f64")
(declare-c watch-str [name string s string] i32 "flan_dev_watch_str") (declare-c watch-str [name str s str] i32 "flan_dev_watch_str")
``` ```
Each returns 1 if the value was written and 0 if nobody is watching or the Each returns 1 if the value was written and 0 if nobody is watching or the

View File

@ -717,7 +717,7 @@ visits the source of the one it lands on."
(defun flan-cnr-param-types (params) (defun flan-cnr-param-types (params)
"The types in PARAMS, a restart's parameters as the program spells them. "The types in PARAMS, a restart's parameters as the program spells them.
PARAMS is a parenthesised list such as \"(i64 (Option string))\"; the result PARAMS is a parenthesised list such as \"(i64 (Option str))\"; the result
is one string per type, or nil when it takes none or cannot be read." is one string per type, or nil when it takes none or cannot be read."
(let ((form (and (stringp params) (let ((form (and (stringp params)
(condition-case nil (car (read-from-string params)) (condition-case nil (car (read-from-string params))

View File

@ -1647,7 +1647,7 @@ lambda or a `Fn(...)' type, and not after a match arm's."
(flan-fln--return-type-matcher 1 font-lock-type-face) (flan-fln--return-type-matcher 1 font-lock-type-face)
;; The package half of a qualified name, as `flan-mode' draws it. ;; The package half of a qualified name, as `flan-mode' draws it.
("\\_<\\([a-zA-Z][a-zA-Z0-9!?*+=<>._-]*/\\)" 1 font-lock-type-face) ("\\_<\\([a-zA-Z][a-zA-Z0-9!?*+=<>._-]*/\\)" 1 font-lock-type-face)
("\\_<\\(?:[iu]\\(?:8\\|16\\|32\\|64\\)\\|f\\(?:32\\|64\\)\\|bool\\|string\\|dyn\\|Never\\|Allocator\\|Ptr\\|Option\\|Vec\\|Map\\|C?Fn\\)\\_>" ("\\_<\\(?:[iu]\\(?:8\\|16\\|32\\|64\\)\\|f\\(?:32\\|64\\)\\|bool\\|str\\|dyn\\|Never\\|Allocator\\|Ptr\\|Option\\|Vec\\|Map\\|C?Fn\\)\\_>"
. font-lock-type-face) . font-lock-type-face)
("\\_<\\$[^][ \t\n(){},;\":]*" . font-lock-type-face) ("\\_<\\$[^][ \t\n(){},;\":]*" . font-lock-type-face)
;; A character literal, `\c' or `\space'. ;; A character literal, `\c' or `\space'.

View File

@ -175,15 +175,15 @@ face says.")
;; containers and memory ;; containers and memory
"length" "at" "slice" "slice-from" "addr" "deref" "length" "at" "slice" "slice-from" "addr" "deref"
;; options, bytes, the host ;; options, bytes, the host
"Some" "bytes" "bytes-view" "string" "Some" "bytes" "bytes-view" "str"
"bytes->f64" "bytes->i64" "f64->bytes" "i64->bytes" "bytes->f64" "bytes->i64" "f64->bytes" "i64->bytes"
"write-stdout" "print" "println" "exit" "argv") "write-stdout" "print" "println" "exit" "argv")
"The functions the compiler provides, from `lib/check.ml''s `builtins' table. "The functions the compiler provides, from `lib/check.ml''s `builtins' table.
Ordinary calls — nothing here is special to the parser — so they are drawn as Ordinary calls — nothing here is special to the parser — so they are drawn as
builtins and not as keywords. `string' is in this list and in the type rule builtins and not as keywords. `str' is in this list and in the type rule
below and means a different thing in each: `(string b)' converts and a bare below and means a different thing in each: `(str b)' converts and a bare
`string' names a type, which the rules tell apart by the paren. `str' names a type, which the rules tell apart by the paren.
`destructure~nth' is in the table and not here: the compiler writes it into a `destructure~nth' is in the table and not here: the compiler writes it into a
destructuring `let' and nobody types it. destructuring `let' and nobody types it.
@ -253,7 +253,7 @@ reason and is the odd one — it is legal only as the last item of a `def' or a
;; word outright — unit is spelled `()'. Drawing it as a valid type would ;; word outright — unit is spelled `()'. Drawing it as a valid type would
;; advertise a spelling the parser rejects, which is the same reason ;; advertise a spelling the parser rejects, which is the same reason
;; `find-restart' and `await' are left out of `flan--special'. ;; `find-restart' and `await' are left out of `flan--special'.
("\\_<\\(?:[iu]\\(?:8\\|16\\|32\\|64\\)\\|f\\(?:32\\|64\\)\\|bool\\|string\\|dyn\\|const\\|int\\|float\\|Never\\|Allocator\\|Ptr\\|Option\\|Vec\\|Map\\|C?Fn\\)\\_>" ("\\_<\\(?:[iu]\\(?:8\\|16\\|32\\|64\\)\\|f\\(?:32\\|64\\)\\|bool\\|str\\|dyn\\|const\\|int\\|float\\|Never\\|Allocator\\|Ptr\\|Option\\|Vec\\|Map\\|C?Fn\\)\\_>"
. font-lock-type-face) . font-lock-type-face)
;; A type variable, `$t', which is what a generic `defn' names its ;; A type variable, `$t', which is what a generic `defn' names its
;; parameter types with and what `{:where (ordered? $t)}' constrains. ;; parameter types with and what `{:where (ordered? $t)}' constrains.

View File

@ -1093,7 +1093,7 @@ would be overwritten. Look again and re-do the edit")
:at "/src/files.flan:12:3" :arity 0 :params "()") :at "/src/files.flan:12:3" :arity 0 :params "()")
(:report "Try again with another path" (:report "Try again with another path"
:at "/src/files.flan:12:3" :arity 1 :at "/src/files.flan:12:3" :arity 1
:params "(string)") :params "(str)")
(:report "" :at nil :arity 0 :params "()"))))) (:report "" :at nil :arity 0 :params "()")))))
(text (with-current-buffer buf (buffer-string)))) (text (with-current-buffer buf (buffer-string))))
(test-flan--check "a restart's report is beside its name" (test-flan--check "a restart's report is beside its name"
@ -1101,7 +1101,7 @@ would be overwritten. Look again and re-do the edit")
(test-flan--check "and where its clause is written" (test-flan--check "and where its clause is written"
(string-match-p "again (/src/files.flan:12:3)" text)) (string-match-p "again (/src/files.flan:12:3)" text))
(test-flan--check "a restart taking values shows their types" (test-flan--check "a restart taking values shows their types"
(string-match-p "\\[use-value\\] +(string) Try again with another path" (string-match-p "\\[use-value\\] +(str) Try again with another path"
text)) text))
(test-flan--check "one with no report and no source shows its name alone" (test-flan--check "one with no report and no source shows its name alone"
(string-match-p " 2: \\[plain\\] *\n" text)) (string-match-p " 2: \\[plain\\] *\n" text))
@ -1117,8 +1117,8 @@ would be overwritten. Look again and re-do the edit")
;; A restart that takes values asks for one per parameter and sends them. ;; A restart that takes values asks for one per parameter and sends them.
(test-flan--check "a restart's parameter types are read from their spelling" (test-flan--check "a restart's parameter types are read from their spelling"
(equal (flan-cnr-param-types "(i64 (Option string))") (equal (flan-cnr-param-types "(i64 (Option str))")
'("i64" "(Option string)"))) '("i64" "(Option str)")))
(let ((sent nil) (asked nil)) (let ((sent nil) (asked nil))
(let ((flan-cnr-request-function (let ((flan-cnr-request-function
(lambda (form) (setq sent form) (list :status "ok" :note "accepted")))) (lambda (form) (setq sent form) (list :status "ok" :note "accepted"))))
@ -1613,12 +1613,12 @@ would be overwritten. Look again and re-do the edit")
;; blame is even a field of this condition. ;; blame is even a field of this condition.
(let ((text (with-current-buffer (let ((text (with-current-buffer
(test-flan--cnr (list :condition "Missing" :restarts '("retry") (test-flan--cnr (list :condition "Missing" :restarts '("retry")
:fields '(("path" "string" nil) :fields '(("path" "str" nil)
("tried" "i32" "3")) ("tried" "i32" "3"))
:fields-why "the stop's value was not readable")) :fields-why "the stop's value was not readable"))
(buffer-string)))) (buffer-string))))
(test-flan--check "a field is named and typed even with no value" (test-flan--check "a field is named and typed even with no value"
(string-match-p ":path *string *not read" text)) (string-match-p ":path *str *not read" text))
(test-flan--check "and the reason is one sentence, said once above the rows" (test-flan--check "and the reason is one sentence, said once above the rows"
(and (string-match-p "not read — the stop's value" text) (and (string-match-p "not read — the stop's value" text)
(= 1 (seq-count (= 1 (seq-count
@ -1696,7 +1696,7 @@ would be overwritten. Look again and re-do the edit")
(let ((text (with-current-buffer (let ((text (with-current-buffer
(test-flan--cnr (test-flan--cnr
(list :condition "Missing" :restarts '("retry" "a" "retry") (list :condition "Missing" :restarts '("retry" "a" "retry")
:fields '(("path" "string" nil)) :fields '(("path" "str" nil))
:stack (list (list :fn "f" :fetched t :locals nil)))) :stack (list (list :fn "f" :fetched t :locals nil))))
(buffer-string)))) (buffer-string))))
(test-flan--check "no bracketed implementation note leaks into the buffer" (test-flan--check "no bracketed implementation note leaks into the buffer"
@ -1731,7 +1731,7 @@ would be overwritten. Look again and re-do the edit")
("break" '(:status "ok" :stopped t :condition "sim/Missing" ("break" '(:status "ok" :stopped t :condition "sim/Missing"
:restarts ("retry"))) :restarts ("retry")))
("layout" '(:status "ok" :type "sim/Missing" ("layout" '(:status "ok" :type "sim/Missing"
:fields (("path" "string") ("tried" "i32")))))))) :fields (("path" "str") ("tried" "i32"))))))))
(let ((text (with-current-buffer (save-window-excursion (flan-cnr-show)) (let ((text (with-current-buffer (save-window-excursion (flan-cnr-show))
(buffer-string)))) (buffer-string))))
(test-flan--check "the condition's name is what `layout' is asked for" (test-flan--check "the condition's name is what `layout' is asked for"
@ -1747,7 +1747,7 @@ would be overwritten. Look again and re-do the edit")
:type) :type)
"sim/Missing")) "sim/Missing"))
(test-flan--check "the fields are drawn, named and typed" (test-flan--check "the fields are drawn, named and typed"
(string-match-p ":path *string" text)) (string-match-p ":path *str\\_>" text))
;; Shape and contents are two different questions, and this stub's ;; Shape and contents are two different questions, and this stub's
;; daemon answers only the first — so the values fall back to the ;; daemon answers only the first — so the values fall back to the
;; layout, each row saying in one sentence why it is empty. ;; layout, each row saying in one sentence why it is empty.
@ -1783,7 +1783,7 @@ would be overwritten. Look again and re-do the edit")
;; that touches each one. ;; that touches each one.
:globals '(("grid" "[4 i32]" "[7 5 0 0]" (0 1)) :globals '(("grid" "[4 i32]" "[7 5 0 0]" (0 1))
("pressure" "i64" "12" (0)) ("pressure" "i64" "12" (0))
("label" "string" "\"running\"" (1))))) ("label" "str" "\"running\"" (1)))))
(buf (test-flan--cnr state)) (buf (test-flan--cnr state))
(text (with-current-buffer buf (buffer-string)))) (text (with-current-buffer buf (buffer-string))))
(test-flan--check "a global is named, typed and valued" (test-flan--check "a global is named, typed and valued"
@ -1825,7 +1825,7 @@ would be overwritten. Look again and re-do the edit")
(test-flan--cnr (test-flan--cnr
(list :condition "Boom" :restarts '("retry") (list :condition "Boom" :restarts '("retry")
:stack (list (list :fn "f")) :stack (list (list :fn "f"))
:globals '(("label" "string" "\"x\"" (1))) :globals '(("label" "str" "\"x\"" (1)))
:globals-refused '(("weird" "no printer for (Map i64 i64)")) :globals-refused '(("weird" "no printer for (Map i64 i64)"))
:globals-skipped :globals-skipped
'(("0: sim/inner" "this frame's body was redefined since it was entered")))) '(("0: sim/inner" "this frame's body was redefined since it was entered"))))

View File

@ -111,13 +111,13 @@
;; there. ;; there.
(test-flan-mode--check (test-flan-mode--check
"a defn with a return type indents its body by two" "a defn with a return type indents its body by two"
"(defn look [n i64 label string] i64 "(defn look [n i64 label str] i64
(print label) (print label)
n)") n)")
(test-flan-mode--check (test-flan-mode--check
"and a defn without one indents it the same" "and a defn without one indents it the same"
"(defn show-trim [s string] () "(defn show-trim [s str] ()
(print s) (print s)
(println \"\"))") (println \"\"))")

View File

@ -67,7 +67,7 @@
(defconst gesture-log-size 20) (defconst gesture-log-size 20)
(defconst max-touch-count 32) (defconst max-touch-count 32)
(defonce gesture-log [gesture-log-size string]) (defonce gesture-log [gesture-log-size str])
;; The C's inverted circular queue: the index counts DOWN and wraps at the top, ;; The C's inverted circular queue: the index counts DOWN and wraps at the top,
;; so the newest entry is always at gesture-log-index and the draw loop walks ;; so the newest entry is always at gesture-log-index and the draw loop walks
;; forward from there. Starting at the size rather than at size-1 is the C's ;; forward from there. Starting at the size rather than at size-1 is the C's
@ -103,7 +103,7 @@
;; belongs under a :gesture-none guard. The C's switch over single members ;; belongs under a :gesture-none guard. The C's switch over single members
;; hides both; a range test cannot. ;; hides both; a range test cannot.
(defn gesture-name [g rl/Gesture] string (defn gesture-name [g rl/Gesture] str
(cond (cond
(= g :gesture-none) "None" (= g :gesture-none) "None"
(= g :gesture-tap) "Tap" (= g :gesture-tap) "Tap"

View File

@ -30,7 +30,7 @@
(defconst max-gesture-strings 20) (defconst max-gesture-strings 20)
(defonce gesture-log [max-gesture-strings string]) (defonce gesture-log [max-gesture-strings str])
(defonce gestures-count i32) (defonce gestures-count i32)
(defonce current-gesture rl/Gesture) (defonce current-gesture rl/Gesture)
(defonce last-gesture rl/Gesture) (defonce last-gesture rl/Gesture)
@ -38,7 +38,7 @@
;; The C's switch, as a function. `:else` is its `default:` — an unnamed ;; The C's switch, as a function. `:else` is its `default:` — an unnamed
;; gesture logs the empty string, which draws nothing, which is what falling ;; gesture logs the empty string, which draws nothing, which is what falling
;; through the C's switch without a TextCopy leaves in the slot. ;; through the C's switch without a TextCopy leaves in the slot.
(defn gesture-name [g rl/Gesture] string (defn gesture-name [g rl/Gesture] str
(cond (cond
(= g :gesture-tap) "GESTURE TAP" (= g :gesture-tap) "GESTURE TAP"
(= g :gesture-double-tap) "GESTURE DOUBLETAP" (= g :gesture-double-tap) "GESTURE DOUBLETAP"

View File

@ -60,7 +60,7 @@
;; and a table of one-character strings; (string b) makes the ;; and a table of one-character strings; (string b) makes the
;; number a string with no instructions, so it is one draw-text and ;; number a string with no instructions, so it is one draw-text and
;; this file imports nothing but raylib. ;; this file imports nothing but raylib.
(rl/draw-text (string (i64->bytes (i64 i))) (rl/draw-text (str (i64->bytes (i64 i)))
(- (i32 (.x p)) 10) (- (i32 (.y p)) 70) 40 (- (i32 (.x p)) 10) (- (i32 (.y p)) 70) 40
rl/black)))) rl/black))))

View File

@ -188,7 +188,7 @@
;; Not in the C: which button the search picked, as a number, so the ;; Not in the C: which button the search picked, as a number, so the
;; headless case and the window agree about the same thing. ;; headless case and the window agree about the same thing.
(rl/draw-text "button: " 10 34 20 rl/lightgray) (rl/draw-text "button: " 10 34 20 rl/lightgray)
(rl/draw-text (string (i64->bytes (i64 pressed))) (rl/draw-text (str (i64->bytes (i64 pressed)))
(+ 10 (rl/measure-text "button: " 20)) 34 20 (+ 10 (rl/measure-text "button: " 20)) 34 20
rl/lightgray)) rl/lightgray))

View File

@ -47,7 +47,7 @@
;; One line of the state list: the label, then "on" or "off" in lime or ;; One line of the state list: the label, then "on" or "off" in lime or
;; maroon, at the x the label ended. The C writes two whole DrawText calls per ;; maroon, at the x the label ended. The C writes two whole DrawText calls per
;; flag with the text repeated; this says it once. ;; flag with the text repeated; this says it once.
(defn draw-flag [label string flag u32 y i32] () (defn draw-flag [label str flag u32 y i32] ()
(rl/draw-text label 10 y 10 rl/gray) (rl/draw-text label 10 y 10 rl/gray)
(let [x (+ 10 (rl/measure-text label 10))] (let [x (+ 10 (rl/measure-text label 10))]
(if (rl/window-state? flag) (if (rl/window-state? flag)

View File

@ -45,7 +45,7 @@
;; ;;
;; Nothing may be formatted between the draw and the measure: `s` may be a view ;; Nothing may be formatted between the draw and the measure: `s` may be a view
;; of the shared buffer, and both calls have to see the same bytes. ;; of the shared buffer, and both calls have to see the same bytes.
(defn draw-piece [s string x i32 y i32 size i32 color rl/Color] i32 (defn draw-piece [s str x i32 y i32 size i32 color rl/Color] i32
(rl/draw-text s x y size color) (rl/draw-text s x y size color)
(rl/measure-text s size)) (rl/measure-text s size))
@ -74,7 +74,7 @@
;; the old note about i32's most negative value — it is widened to i64 before ;; the old note about i32's most negative value — it is widened to i64 before
;; formatting, so there is no negation to wrap. ;; formatting, so there is no negation to wrap.
(defn draw-int [n i32 x i32 y i32 size i32 color rl/Color] i32 (defn draw-int [n i32 x i32 y i32 size i32 color rl/Color] i32
(draw-piece (string (i64->bytes (i64 n))) x y size color)) (draw-piece (str (i64->bytes (i64 n))) x y size color))
;; The same with a fixed minimum number of digits — the C's "%03i". A number ;; The same with a fixed minimum number of digits — the C's "%03i". A number
;; wider than `width` is drawn in full rather than truncated, which is what ;; wider than `width` is drawn in full rather than truncated, which is what

View File

@ -135,7 +135,7 @@
(let [b (bytes-view text)] (let [b (bytes-view text)]
(if (>= off (length b)) (if (>= off (length b))
0 0
(rl/get-codepoint-next (string (slice b off (length b))) size-out)))) (rl/get-codepoint-next (str (slice b off (length b))) size-out))))
;; One codepoint forward, clamped at the end. ;; One codepoint forward, clamped at the end.
;; ;;

View File

@ -122,7 +122,7 @@
(i32 (.width text-box)) (i32 (.height text-box)) (i32 (.width text-box)) (i32 (.height text-box))
(if mouse-on-text rl/red rl/darkgray)) (if mouse-on-text rl/red rl/darkgray))
(let [typed (string (slice name 0 letter-count))] (let [typed (str (slice name 0 letter-count))]
(rl/draw-text typed (+ (i32 (.x text-box)) 5) (+ (i32 (.y text-box)) 8) (rl/draw-text typed (+ (i32 (.x text-box)) 5) (+ (i32 (.y text-box)) 8)
40 rl/maroon) 40 rl/maroon)

View File

@ -82,7 +82,7 @@
i 0] i 0]
(while (< i length) (while (< i length)
(let [size 0 (let [size 0
cp (rl/get-codepoint (string (slice text i length)) (addr size))] cp (rl/get-codepoint (str (slice text i length)) (addr size))]
;; A byte that decodes to nothing answers '?' and raylib would normally ;; A byte that decodes to nothing answers '?' and raylib would normally
;; stop; this walks on one byte at a time so the bad bytes get drawn. ;; stop; this walks on one byte at a time so the bad bytes get drawn.
(when (= cp (i32 \?)) (set size 1)) (when (= cp (i32 \?)) (set size 1))

View File

@ -68,7 +68,7 @@
;; the C — see the header comment. ;; the C — see the header comment.
(let [b (bytes-view message) (let [b (bytes-view message)
n (min (i32 (length 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 (str (slice b 0 n)) 210 160 20 rl/maroon))
(rl/draw-text "PRESS [ENTER] to RESTART!" 240 260 20 rl/lightgray) (rl/draw-text "PRESS [ENTER] to RESTART!" 240 260 20 rl/lightgray)
(rl/draw-text "HOLD [SPACE] to SPEED UP!" 239 300 20 rl/lightgray)))) (rl/draw-text "HOLD [SPACE] to SPEED UP!" 239 300 20 rl/lightgray))))

View File

@ -80,7 +80,7 @@
(defconst proc-flip-vertical 7) (defconst proc-flip-vertical 7)
(defconst proc-flip-horizontal 8) (defconst proc-flip-horizontal 8)
(defonce process-names [num-processes string]) (defonce process-names [num-processes str])
;; The nine toggle buttons down the left-hand side, laid out once at startup. ;; The nine toggle buttons down the left-hand side, laid out once at startup.
(defonce toggle-recs [num-processes rl/Rectangle]) (defonce toggle-recs [num-processes rl/Rectangle])

View File

@ -293,7 +293,7 @@ and decl_kind =
(* The same, but written in the C library's own terms — structs by value, (* The same, but written in the C library's own terms — structs by value,
strings as strings. [Shim] generates the C that flattens it and rewrites strings as strings. [Shim] generates the C that flattens it and rewrites
this into a [Declare] plus an ordinary [Defn], so nothing downstream sees this into a [Declare] plus an ordinary [Defn], so nothing downstream sees
one. Two forms and not one because [(declare f [p string] ...)] already one. Two forms and not one because [(declare f [p str] ...)] already
means "the symbol takes ptr+len", which is the opposite of what this means "the symbol takes ptr+len", which is the opposite of what this
means. *) means. *)
| DeclareC of fn * string | DeclareC of fn * string

View File

@ -570,7 +570,7 @@ let foreign_spelling = function
| "byte" -> Some "u8" | "byte" -> Some "u8"
| "double" -> Some "f64" | "double" -> Some "f64"
| "boolean" -> Some "bool" | "boolean" -> Some "bool"
| "str" -> Some "string" | "string" -> Some "str"
| _ -> None | _ -> None
(* The builtin names, for the did-you-mean at a call — [prinltn] is a typo for (* The builtin names, for the did-you-mean at a call — [prinltn] is a typo for
@ -1953,7 +1953,7 @@ and resolve_name env ~seen loc n =
| None -> | None ->
match n with match n with
| "bool" -> Types.Bool | "bool" -> Types.Bool
| "string" -> Types.String | "str" -> Types.String
(* Lowercase and concrete, which the rule three screens down says is a (* Lowercase and concrete, which the rule three screens down says is a
type variable. It is spelled this way because it is a primitive and type variable. It is spelled this way because it is a primitive and
every other primitive is lowercase — [dyn] beside [i64] and [bool] every other primitive is lowercase — [dyn] beside [i64] and [bool]
@ -2077,16 +2077,14 @@ and array_len env loc = function
another name that is not a type, or by nothing, takes one and is [dyn]. That another name that is not a type, or by nothing, takes one and is [dyn]. That
is the whole rule, and it reads the way the vector reads. is the whole rule, and it reads the way the vector reads.
A name that *is* a type name is refused rather than paired. [(defn f [i64 x] A name that *is* a type name pairs only when a type follows it: [[str str]]
...)] has no good reading: taken as written it is a parameter called [i64], has one reading. [(defn f [i64 x] ...)] has two — a dyn parameter called
which shadows nothing but confuses everything, and the likelier intent is a [i64], or a pair written backwards — so it is refused rather than handed
pair written backwards. Refusing here costs a rename in the one program that back as a signature nobody wrote. *)
meant it and closes the one place where this rule could still hand somebody
a signature they did not write. *)
let is_type_name env n = let is_type_name env n =
Types.ikind_of_name n <> None Types.ikind_of_name n <> None
|| Types.fkind_of_name n <> None || Types.fkind_of_name n <> None
|| List.mem n [ "bool"; "string"; "dyn"; "Unit"; "Never"; "Allocator" ] || List.mem n [ "bool"; "str"; "dyn"; "Unit"; "Never"; "Allocator" ]
|| Hashtbl.mem env.aliases n || Hashtbl.mem env.aliases n
|| Hashtbl.mem env.structs n || Hashtbl.mem env.structs n
|| Hashtbl.mem env.gstructs n || Hashtbl.mem env.gstructs n
@ -2188,6 +2186,16 @@ let dyn_param_or_typo env n loc =
(* In a .fln file the type sits after [name:], so it cannot be read as a (* In a .fln file the type sits after [name:], so it cannot be read as a
second parameter and needs no word about parameter vectors. *) second parameter and needs no word about parameter vectors. *)
let fln = Source.indented_at loc in let fln = Source.indented_at loc in
(* [string] is what most languages call the text type, so [[s string]] is
far likelier one parameter of a misspelled type than two dyn ones. The
other foreign spellings ([long], [byte], [double]) are ordinary parameter
names and keep the dyn reading. *)
if n = "string" then
Loc.failk "check/unknown-type" loc
"unknown type string — Flan spells it str%s"
(if fln then ""
else ". Otherwise string reads as a second parameter, because a \
parameter with no type is dyn, and wants another name");
match suggestion with match suggestion with
| Some m when fln -> | Some m when fln ->
Loc.failk "check/unknown-type" loc "unknown type %s — did you mean %s?" n m Loc.failk "check/unknown-type" loc "unknown type %s — did you mean %s?" n m
@ -2245,11 +2253,20 @@ let pair_params ?(also = fun _ -> false) ?(declared = fun _ -> None)
Loc.failk "check/parameter-name-expected" t.Ast.tloc Loc.failk "check/parameter-name-expected" t.Ast.tloc
"a parameter's name was expected here, and this is a type. Parameters \ "a parameter's name was expected here, and this is a type. Parameters \
are [name Type ...]" are [name Type ...]"
| Ast.Pname (n, loc) :: rest when is_type_name env n -> (* A parameter may share a type's name — [str] is a common name for text —
ignore rest; when a type follows it: [[str str]] and [str: str] can only be a name
then a type. Without a type after it, [[i64 x]] is a pair written
backwards as likely as a dyn parameter called [i64], so that one is
refused with both fixes. *)
| Ast.Pname (n, loc) :: Ast.Ptype t :: rest when is_type_name env n ->
{ Ast.fname = n; fty = t; floc = loc } :: go rest
| Ast.Pname (n, loc) :: Ast.Pname (t, tloc) :: rest
when is_type_name env n && is_type_name env t ->
{ Ast.fname = n; fty = { Ast.t = Ast.Tname t; tloc }; floc = loc } :: go rest
| Ast.Pname (n, loc) :: _ when is_type_name env n ->
Loc.failk "check/parameter-named-type" loc Loc.failk "check/parameter-named-type" loc
"%s names a type, so it cannot also be this parameter's name. Write \ "%s names a type, and no type follows this parameter called %s. Give \
[name %s], or rename the parameter" n n it one, as [%s %s], or if the pair is backwards write [name %s]" n n n n n
(* [_] reads a type off a body, and a parameter has none to read. *) (* [_] reads a type off a body, and a parameter has none to read. *)
| Ast.Pname (n, _) :: Ast.Pname ("_", tloc) :: _ -> | Ast.Pname (n, _) :: Ast.Pname ("_", tloc) :: _ ->
Loc.failk "check/infer-misplaced" tloc Loc.failk "check/infer-misplaced" tloc
@ -10397,12 +10414,27 @@ and type_named ctx n =
mistake, instead of this form reporting a missing element type. *) mistake, instead of this form reporting a missing element type. *)
|| n <> tyvar_bare n || n <> tyvar_bare n
|| List.mem n Types.primitive_names || List.mem n Types.primitive_names
(* Answered as a type so [resolve_name] says it is spelled [str], rather
than this form saying no element type was written. *)
|| n = "string"
|| Hashtbl.mem ctx.env.structs n || Hashtbl.mem ctx.env.structs n
|| Hashtbl.mem ctx.env.datas n || Hashtbl.mem ctx.env.datas n
|| Hashtbl.mem ctx.env.unions n || Hashtbl.mem ctx.env.unions n
|| Hashtbl.mem ctx.env.enums n || Hashtbl.mem ctx.env.enums n
|| Hashtbl.mem ctx.env.aliases n || Hashtbl.mem ctx.env.aliases n
(* A local or global that shares a type's name — [str] is a common name for
text — is the binding in (vec-new str), and then nothing names the type.
Said here so the refusal names the binding rather than a missing type. *)
and shadowed_type_arg ctx loc what args =
match args with
| { Ast.e = Ast.Var n; _ } :: _
when type_named ctx n && (lookup ctx n <> None || global_value ctx n) ->
fail loc
"%s here is the value named %s and not the type, so nothing says what \
(%s) makes — rename that binding to write the type" n n what
| _ -> ()
(* The element type for [vec-new]: a leading bare symbol naming a type, a (* The element type for [vec-new]: a leading bare symbol naming a type, a
leading type expression — [(vec-new [u8])], [(vec-new (Ptr Cell))], which leading type expression — [(vec-new [u8])], [(vec-new (Ptr Cell))], which
Parse has already read as one — or the expectation at the site. A bare Parse has already read as one — or the expectation at the site. A bare
@ -10427,6 +10459,7 @@ and vec_new_elem ctx ~want loc args =
(match want with (match want with
| Some (Types.Vec t) -> t, args | Some (Types.Vec t) -> t, args
| _ -> | _ ->
shadowed_type_arg ctx loc "vec-new" args;
fail loc fail loc
"nothing here says what (vec-new) is a Vec of — write the element \ "nothing here says what (vec-new) is a Vec of — write the element \
type, as (vec-new i32)") type, as (vec-new i32)")
@ -10488,7 +10521,7 @@ and map_kv loc what (t : Types.t) =
(* The key and value for [map-new]: two leading bare symbols naming types, or (* The key and value for [map-new]: two leading bare symbols naming types, or
the expectation at the site. The same rule [vec-new] uses, with the same the expectation at the site. The same rule [vec-new] uses, with the same
escape for a symbol that is really a binding — an allocator, in practice — escape for a symbol that is really a binding — an allocator, in practice —
and the pair is written together or not at all, because (map-new string) and the pair is written together or not at all, because (map-new str)
says half of a type and half is not a type. *) says half of a type and half is not a type. *)
and map_new_types ctx ~want loc args = and map_new_types ctx ~want loc args =
let is_type n = let is_type n =
@ -10507,7 +10540,7 @@ and map_new_types ctx ~want loc args =
Option.get (as_type k), Option.get (as_type v), rest Option.get (as_type k), Option.get (as_type v), rest
| a :: _ when type_of_expr ~generic:(Hashtbl.mem ctx.env.gstructs) a <> None -> | a :: _ when type_of_expr ~generic:(Hashtbl.mem ctx.env.gstructs) a <> None ->
fail loc fail loc
"(map-new) names a key and no value — write both, as (map-new string \ "(map-new) names a key and no value — write both, as (map-new str \
i32)" i32)"
| { Ast.e = Ast.Var k; _ } :: rest when is_type k && rest = [] -> | { Ast.e = Ast.Var k; _ } :: rest when is_type k && rest = [] ->
fail loc fail loc
@ -10517,9 +10550,10 @@ and map_new_types ctx ~want loc args =
(match want with (match want with
| Some (Types.Map (k, v)) -> k, v, args | Some (Types.Map (k, v)) -> k, v, args
| _ -> | _ ->
shadowed_type_arg ctx loc "map-new" args;
fail loc fail loc
"nothing here says what (map-new) maps — write the key and value \ "nothing here says what (map-new) maps — write the key and value \
types, as (map-new string i32)") types, as (map-new str i32)")
(* The element type, or the reason this is not a Vec. *) (* The element type, or the reason this is not a Vec. *)
and vec_elem loc what (t : Types.t) = and vec_elem loc what (t : Types.t) =
@ -12066,10 +12100,10 @@ and named_call ?(qualified = false) ctx ~want loc name args =
being told the file is missing and left to discover the other half being told the file is missing and left to discover the other half
after fixing it. *) after fixing it. *)
(match args with (match args with
| [ _; { Ast.e = Ast.Var "string"; _ } ] | [ _ ] -> () | [ _; { Ast.e = Ast.Var "str"; _ } ] | [ _ ] -> ()
| [ _; t ] -> | [ _; t ] ->
fail t.Ast.loc fail t.Ast.loc
"embed's second argument is string, or nothing for a [const u8]" "embed's second argument is str, or nothing for a [const u8]"
| _ -> ()); | _ -> ());
let data = read_embed_file (embed_path loc p) p.Ast.loc in let data = read_embed_file (embed_path loc p) p.Ast.loc in
let as_string () = mk loc Types.String (Tast.Str data) in let as_string () = mk loc Types.String (Tast.Str data) in
@ -12088,7 +12122,7 @@ and named_call ?(qualified = false) ctx ~want loc name args =
two types would be a wart. [want] is a fallback only, and nothing two types would be a wart. [want] is a fallback only, and nothing
depends on it. *) depends on it. *)
(match args with (match args with
| [ _; { Ast.e = Ast.Var "string"; _ } ] -> | [ _; { Ast.e = Ast.Var "str"; _ } ] ->
expect ctx loc ~want (as_string ()) expect ctx loc ~want (as_string ())
| _ -> | _ ->
(match want with (match want with
@ -12096,7 +12130,7 @@ and named_call ?(qualified = false) ctx ~want loc name args =
| _ -> expect ctx loc ~want (as_bytes ()))) | _ -> expect ctx loc ~want (as_bytes ())))
| _ -> | _ ->
fail loc fail loc
"embed is (embed \"path\") for a [const u8], or (embed \"path\" string)") "embed is (embed \"path\") for a [const u8], or (embed \"path\" str)")
(* ── What a macro says when it has to refuse ─────────────────── (* ── What a macro says when it has to refuse ───────────────────
The one thing a macro could not do, written down in the prelude where The one thing a macro could not do, written down in the prelude where
[unless] settles for it: "a macro has no error facility: it runs inside [unless] settles for it: "a macro has no error facility: it runs inside
@ -12666,7 +12700,7 @@ and named_call ?(qualified = false) ctx ~want loc name args =
expect ctx loc ~want (dup_elems ctx loc (Types.Int Types.U8) s a) expect ctx loc ~want (dup_elems ctx loc (Types.Int Types.U8) s a)
| _ -> fail loc "bytes is (bytes s) or (bytes s allocator)") | _ -> fail loc "bytes is (bytes s) or (bytes s allocator)")
(* (string b): a [u8] seen as a string. The mirror of (bytes-view s), (* (str b): a [u8] seen as a str. The mirror of (bytes-view s),
spelled the same way — a type name in head position, like (bytes s) and spelled the same way — a type name in head position, like (bytes s) and
unlike the numeric casts, which go through [is_cast] and really do unlike the numeric casts, which go through [is_cast] and really do
convert. convert.
@ -12680,7 +12714,7 @@ and named_call ?(qualified = false) ctx ~want loc name args =
Two decisions are baked in here. Two decisions are baked in here.
1. It does NOT check UTF-8, because `string` does not claim UTF-8. The 1. It does NOT check UTF-8, because `str` does not claim UTF-8. The
prelude settles this: valid-utf8? is an ordinary function you call when prelude settles this: valid-utf8? is an ordinary function you call when
you care, decode-rune/rune-at/rune-count all take [u8] rather than you care, decode-rune/rune-at/rune-count all take [u8] rather than
string, and decode-rune answers {.ok false .width 1} on a malformed string, and decode-rune answers {.ok false .width 1} on a malformed
@ -12694,13 +12728,13 @@ and named_call ?(qualified = false) ctx ~want loc name args =
2. It takes a [const u8], so a [u8] and a (bytes-view s) are both 2. It takes a [const u8], so a [u8] and a (bytes-view s) are both
accepted. This direction only loses the ability to write — a string accepted. This direction only loses the ability to write — a string
is read-only everywhere — so the result of (string b) can reach is read-only everywhere — so the result of (str b) can reach
strictly fewer stores than b could. strictly fewer stores than b could.
The text i64->bytes and f64->bytes answer lives in the temp allocator The text i64->bytes and f64->bytes answer lives in the temp allocator
until the next (free-temp); calling it a string does not copy it, so text until the next (free-temp); calling it a string does not copy it, so text
kept past the frame is cloned first. *) kept past the frame is cloned first. *)
| "string" -> | "str" ->
arity ctx loc name 1 args; arity ctx loc name 1 args;
prim Tast.StrOfBytes Types.String [ byte_slice ctx (List.hd args) ] prim Tast.StrOfBytes Types.String [ byte_slice ctx (List.hd args) ]
| "bytes->f64" -> | "bytes->f64" ->
@ -13362,6 +13396,13 @@ and ordinary_call ctx ~want loc name args =
not (List.mem m [ "true"; "false"; "nil"; "None"; not (List.mem m [ "true"; "false"; "nil"; "None";
"context/allocator"; "context/temp" ]) "context/allocator"; "context/temp" ])
in in
(* [(string b)], [(byte x)]: another language's name for a type, used
as the conversion, which Flan spells with its own type name. *)
(match foreign_spelling name with
| Some m ->
Loc.failk "check/unknown-function" loc
"unknown function %s — Flan spells it %s" name m
| None -> ());
match guess with match guess with
| Some m when not (callable m) -> | Some m when not (callable m) ->
if args = [] then if args = [] then
@ -14507,15 +14548,15 @@ let builtins : (string * string * string) list =
:keyword :map — or, for a value built by a defclass constructor, the \ :keyword :map — or, for a value built by a defclass constructor, the \
class's name as class-of answers it. A typed value answers the kind it \ class's name as class-of answers it. A typed value answers the kind it \
has as a dyn value: an i32 is :int."); has as a dyn value: an i32 is :int.");
("keyword", "keyword [string|[const u8]] dyn", ("keyword", "keyword [str|[const u8]] dyn",
"The interned dyn keyword named by the bytes, for a name that only \ "The interned dyn keyword named by the bytes, for a name that only \
exists at run time — a reader building :texture-path out of a token's \ exists at run time — a reader building :texture-path out of a token's \
text. A literal :foo is already one."); text. A literal :foo is already one.");
(* assets, embedded at compile time *) (* assets, embedded at compile time *)
("embed", "embed [\"path\" string?] [const u8]", ("embed", "embed [\"path\" str?] [const u8]",
"The file's bytes, read at compile time and baked in as a constant; \ "The file's bytes, read at compile time and baked in as a constant; \
(embed \"p\" string) reads it as a string instead. The path is \ (embed \"p\" str) reads it as a str instead. The path is \
relative to the file the form is written in, and the slice points into \ relative to the file the form is written in, and the slice points into \
read-only data."); read-only data.");
("embed-dir", "embed-dir [\"path\"] [n EmbedFile]", ("embed-dir", "embed-dir [\"path\"] [n EmbedFile]",
@ -14527,27 +14568,27 @@ let builtins : (string * string * string) list =
carries a name, and this carries a sentence."); carries a name, and this carries a sentence.");
(* files *) (* files *)
("slurp", "slurp [string Allocator?] (Vec u8)", ("slurp", "slurp [str Allocator?] (Vec u8)",
"Reads a whole file. No Result and no out-parameter: a failure to read \ "Reads a whole file. No Result and no out-parameter: a failure to read \
signals FileError under retry and use-value, and a failure to allocate \ signals FileError under retry and use-value, and a failure to allocate \
signals StorageExhausted."); signals StorageExhausted.");
("barf", "barf [string [const u8]] ()", ("barf", "barf [str [const u8]] ()",
"Writes a whole file. On the web target it signals FileError every \ "Writes a whole file. On the web target it signals FileError every \
time, with the path — there is no conditional compilation, so the \ time, with the path — there is no conditional compilation, so the \
program decides rather than the build."); program decides rather than the build.");
("delete-file", "delete-file [string] ()", ("delete-file", "delete-file [str] ()",
"Removes the file, or signals FileError with retry and use-value. It \ "Removes the file, or signals FileError with retry and use-value. It \
answers () and not a bool, because the failure is the condition."); answers () and not a bool, because the failure is the condition.");
("make-directory", "make-directory [string] ()", ("make-directory", "make-directory [str] ()",
"Creates the directory, or signals FileError. () for the reason \ "Creates the directory, or signals FileError. () for the reason \
delete-file answers one."); delete-file answers one.");
("rename-file", "rename-file [string string] ()", ("rename-file", "rename-file [str str] ()",
"Renames the first path to the second, or signals FileError. A \ "Renames the first path to the second, or signals FileError. A \
use-value names a different source for the same destination, which is \ use-value names a different source for the same destination, which is \
the direction a handler can act on."); the direction a handler can act on.");
(* containers *) (* containers *)
("length", "length [[n T]|[T]|string|(Vec T)|(Map K V)] i32", ("length", "length [[n T]|[T]|str|(Vec T)|(Map K V)] i32",
"How many elements. One question and one word across an array, a slice, \ "How many elements. One question and one word across an array, a slice, \
a string, a Vec and a Map."); a string, a Vec and a Map.");
("at", "at [collection i32 ...] T", ("at", "at [collection i32 ...] T",
@ -14555,7 +14596,7 @@ let builtins : (string * string * string) list =
allocator's epoch checked first. On a string it is the byte, a u8. It \ allocator's epoch checked first. On a string it is the byte, a u8. It \
is also a place, so (set (at v i) x) goes through the same check; a \ is also a place, so (set (at v i) x) goes through the same check; a \
string is the exception, being a view it does not own."); string is the exception, being a view it does not own.");
("slice", "slice [[n T]|[T]|string|(Vec T) i32? i32?] [T]|string", ("slice", "slice [[n T]|[T]|str|(Vec T) i32? i32?] [T]|str",
"The half-open range [lo hi) as a non-owning view. lo defaults to 0 and \ "The half-open range [lo hi) as a non-owning view. lo defaults to 0 and \
hi to the length, so (slice a) is the whole of it and (slice a n) is \ hi to the length, so (slice a) is the whole of it and (slice a n) is \
the tail from n. A bound may sit one past the end; a literal pair that \ the tail from n. A bound may sit one past the end; a literal pair that \
@ -14580,20 +14621,20 @@ let builtins : (string * string * string) list =
written as a name rather than as a call."); written as a name rather than as a call.");
(* the host primitives *) (* the host primitives *)
("bytes", "bytes [string Allocator?] [u8]", ("bytes", "bytes [str Allocator?] [u8]",
"A writable copy of the string's bytes, from the current allocator or \ "A writable copy of the string's bytes, from the current allocator or \
one named. It allocates like vec-new does — a failure signals \ one named. It allocates like vec-new does — a failure signals \
StorageExhausted with retry — and (free b) releases it, through the \ StorageExhausted with retry — and (free b) releases it, through the \
current allocator or (free b a) through the one it came from. For reading without a copy, \ current allocator or (free b a) through the one it came from. For reading without a copy, \
bytes-view."); bytes-view.");
("bytes-view", "bytes-view [string] [const u8]", ("bytes-view", "bytes-view [str] [const u8]",
"The string's own storage seen as a read-only byte slice. It costs \ "The string's own storage seen as a read-only byte slice. It costs \
nothing — both are a ptr and a length at run time — and it decodes \ nothing — both are a ptr and a length at run time — and it decodes \
nothing. A store through it is a compile error; bytes is the writable \ nothing. A store through it is a compile error; bytes is the writable \
copy."); copy.");
("string", "string [[const u8]] string", ("str", "str [[const u8]] str",
"A byte slice seen as a string, and free at run time. It does not check \ "A byte slice seen as a str, and free at run time. It does not check \
UTF-8, because `string` does not claim UTF-8 — valid-utf8? is an \ UTF-8, because `str` does not claim UTF-8 — valid-utf8? is an \
ordinary function you call when you care."); ordinary function you call when you care.");
("bytes->f64", "bytes->f64 [[const u8]] f64", "Parses a float out of the bytes."); ("bytes->f64", "bytes->f64 [[const u8]] f64", "Parses a float out of the bytes.");
("bytes->i64", "bytes->i64 [[const u8]] i64", ("bytes->i64", "bytes->i64 [[const u8]] i64",
@ -14615,7 +14656,7 @@ let builtins : (string * string * string) list =
is a read, so it does not consume the value."); is a read, so it does not consume the value.");
("println", "println [T ...] ()", ("println", "println [T ...] ()",
"print, with a newline after it — (println) alone is the newline."); "print, with a newline after it — (println) alone is the newline.");
("watch", "watch [string T] ()", ("watch", "watch [str T] ()",
"Writes the value, rendered as print renders it, into the dev session's \ "Writes the value, rendered as print renders it, into the dev session's \
watch table under the name, where M-x flan-watch shows it. Does nothing \ watch table under the name, where M-x flan-watch shows it. Does nothing \
when no watch buffer is open. Outside a dev build it only evaluates \ when no watch buffer is open. Outside a dev build it only evaluates \
@ -14623,7 +14664,7 @@ let builtins : (string * string * string) list =
("exit", "exit [i32] never", ("exit", "exit [i32] never",
"Ends the process with this status. It has no value, so nothing written \ "Ends the process with this status. It has no value, so nothing written \
after it runs."); after it runs.");
("argv", "argv [] [string]", "The command line, as a slice of strings."); ("argv", "argv [] [str]", "The command line, as a slice of strings.");
(* the names rather than calls — [var]'s arms. Their (* the names rather than calls — [var]'s arms. Their
signature is the [name type] shape [Dev.defs] gives a global, because signature is the [name type] shape [Dev.defs] gives a global, because
@ -16507,7 +16548,7 @@ let check_main env decls =
in in
if not ok_params then if not ok_params then
fail at fail at
"main takes no parameters or one [string], not (%s)" "main takes no parameters or one [str], not (%s)"
(String.concat (if Source.indented_at at then ", " else " ") (String.concat (if Source.indented_at at then ", " else " ")
(List.map (tyname at) params)); (List.map (tyname at) params));
if not (Types.equal ret Types.Unit || Types.equal ret (Types.Int Types.I32)) if not (Types.equal ret Types.Unit || Types.equal ret (Types.Int Types.I32))

View File

@ -552,7 +552,7 @@ let param_ty env (s : string) : Ast.texpr =
let inner = String.trim (String.sub s 0 (String.length s - 1)) in let inner = String.trim (String.sub s 0 (String.length s - 1)) in
let is_const = strip_prefix "const " inner <> None in let is_const = strip_prefix "const " inner <> None in
match bare inner with match bare inner with
| "char" when is_const -> tname "string" | "char" when is_const -> tname "str"
| "char" -> | "char" ->
refuse refuse
"char * is a parameter C may write through, and a Flan string crosses \ "char * is a parameter C may write through, and a Flan string crosses \
@ -573,7 +573,7 @@ let param_ty env (s : string) : Ast.texpr =
[const char *] is text the caller only reads — a static buffer, a pointer [const char *] is text the caller only reads — a static buffer, a pointer
into an argument, a string literal — and [Shim] copies it into the context into an argument, a string literal — and [Shim] copies it into the context
allocator, so it becomes a [string]. [char *] without the const is text the allocator, so it becomes a [str]. [char *] without the const is text the
caller owns and must hand back through the library's own release function caller owns and must hand back through the library's own release function
([LoadFileText] and [UnloadFileText]); copying it would leak the original ([LoadFileText] and [UnloadFileText]); copying it would leak the original
with nothing left to release it through, so it is refused and a with nothing left to release it through, so it is refused and a
@ -585,7 +585,7 @@ let ret_ty env (s : string) : Ast.texpr option =
let inner = String.trim (String.sub s 0 (String.length s - 1)) in let inner = String.trim (String.sub s 0 (String.length s - 1)) in
let is_const = strip_prefix "const " inner <> None in let is_const = strip_prefix "const " inner <> None in
match bare inner with match bare inner with
| "char" when is_const -> Some (tname "string") | "char" when is_const -> Some (tname "str")
| "char" -> | "char" ->
refuse refuse
"returns char *, which the caller owns and releases through the \ "returns char *, which the caller owns and releases through the \
@ -927,7 +927,7 @@ let agrees env (a : Ast.texpr) (b : Ast.texpr) =
The second tolerance, and the one [agrees] cannot express on its own. By the The second tolerance, and the one [agrees] cannot express on its own. By the
time a C parameter has become a Flan type, what the header actually said time a C parameter has become a Flan type, what the header actually said
about it is gone: [param_ty] renders [const char *] as [string] and about it is gone: [param_ty] renders [const char *] as [str] and
[value_ty] renders [void *] as [(Ptr u8)], and both of those are the [value_ty] renders [void *] as [(Ptr u8)], and both of those are the
importer's own choice rather than the header's word. A hand-written line importer's own choice rather than the header's word. A hand-written line
that says [(Ptr u8)] over a [const char *] is not disagreeing with the that says [(Ptr u8)] over a [const char *] is not disagreeing with the
@ -966,7 +966,7 @@ let ptr_agrees_elem env ~inner (elem : Ast.texpr) =
reporting [value_ty]'s guess of [u8] back at the author as if the header reporting [value_ty]'s guess of [u8] back at the author as if the header
had said it. What is *not* given up is that it is a pointer at all: the had said it. What is *not* given up is that it is a pointer at all: the
match above requires [(Ptr _)] on the Flan side, so an [i32] or a match above requires [(Ptr _)] on the Flan side, so an [i32] or a
[string] declared against a [void *] is still a finding. That [str] declared against a [void *] is still a finding. That
asymmetry is the point — raylib spells thirty-odd parameters [void *] asymmetry is the point — raylib spells thirty-odd parameters [void *]
and none of them is a scalar. *) and none of them is a scalar. *)
let b = bare inner in let b = bare inner in
@ -1580,7 +1580,7 @@ let decl_source (d : Ast.decl) =
- a [(Ptr T)] where the header says [T *] and the hand-written line chose - a [(Ptr T)] where the header says [T *] and the hand-written line chose
something more specific for a reason it recorded. That is [ptr_agrees], something more specific for a reason it recorded. That is [ptr_agrees],
and it needs the C spelling because the rendering has already thrown the and it needs the C spelling because the rendering has already thrown the
answer away: [param_ty] turns [const char *] into [string] and [value_ty] answer away: [param_ty] turns [const char *] into [str] and [value_ty]
turns [void *] into [(Ptr u8)], neither of which is what the header said. turns [void *] into [(Ptr u8)], neither of which is what the header said.
[(Ptr u8)] over a [char *], and [(Ptr] anything[)] over a [void *], now [(Ptr u8)] over a [char *], and [(Ptr] anything[)] over a [void *], now
agree; [(Ptr A)] over a [B *] does not. agree; [(Ptr A)] over a [B *] does not.
@ -1642,15 +1642,15 @@ let diff_bound ~env ~(bound : (Ast.fn * string) list) (d : dump) =
| None -> | None ->
(match (try Ok (ret_ty env c.cret) with Refused w -> Error w) with (match (try Ok (ret_ty env c.cret) with Refused w -> Error w) with
(* A return the importer refuses says nothing, as a parameter (* A return the importer refuses says nothing, as a parameter
does — except a [string] over a [char *] without const. That does — except a [str] over a [char *] without const. That
one is a real disagreement: the shim would copy the text and one is a real disagreement: the shim would copy the text and
leave the library's buffer with no owner. *) leave the library's buffer with no owner. *)
| Error _ -> | Error _ ->
(match fn.Ast.ret with (match fn.Ast.ret with
| Some { Ast.t = Ast.Tname "string"; _ } -> | Some { Ast.t = Ast.Tname "str"; _ } ->
say say
(Printf.sprintf (Printf.sprintf
"returns string and the header says %s, which the \ "returns str and the header says %s, which the \
caller owns and releases through the library — \ caller owns and releases through the library — \
declare it (Ptr u8)" declare it (Ptr u8)"
c.cret) c.cret)

View File

@ -1092,7 +1092,7 @@ let rec dty m d (t : Types.t) : int =
(* ptr+len, and shown as ptr+len. There is no hidden owner and no (* ptr+len, and shown as ptr+len. There is no hidden owner and no
capacity, so two members are the whole truth about a slice. *) capacity, so two members are the whole truth about a slice. *)
| Types.String -> | Types.String ->
composite "string" composite "str"
[ ("ptr", Types.Ptr (Types.Mut, (Types.Int Types.U8))); ("len", Types.Int Types.I64) ] [ ("ptr", Types.Ptr (Types.Mut, (Types.Int Types.U8))); ("len", Types.Int Types.I64) ]
| Types.Slice (_, e) -> | Types.Slice (_, e) ->
composite (Types.to_string t) composite (Types.to_string t)
@ -3989,7 +3989,7 @@ and prim f (e : Tast.expr) (p : Tast.prim) (args : Tast.expr list) =
no copy (plan.org, Milestone-2 primitives). *) no copy (plan.org, Milestone-2 primitives). *)
| Tast.Bytes, [ x ] -> value f x | Tast.Bytes, [ x ] -> value f x
(* (string b), and the same non-instruction for the same reason: String and (* (string b), and the same non-instruction for the same reason: String and
Slice _ are both %slice. See check.ml's "string" case. *) Slice _ are both %slice. See check.ml's "str" case. *)
| Tast.StrOfBytes, [ x ] -> value f x | Tast.StrOfBytes, [ x ] -> value f x
| Tast.BytesToF64, [ x ] -> shim_in f "@flan_bytes_to_f64" "double" x | Tast.BytesToF64, [ x ] -> shim_in f "@flan_bytes_to_f64" "double" x
| Tast.BytesToI64, [ x ] -> shim_in f "@flan_bytes_to_i64" "i64" x | Tast.BytesToI64, [ x ] -> shim_in f "@flan_bytes_to_i64" "i64" x

View File

@ -36,7 +36,7 @@
- A struct — a plain object, [{x: 1, y: 2}], keyed by the field's own name. - A struct — a plain object, [{x: 1, y: 2}], keyed by the field's own name.
Readable output is a goal, and this is most of it. Readable output is a goal, and this is most of it.
- [[n T]], a fixed array — a JS array, or a [Uint8Array] when [T] is [u8]. - [[n T]], a fixed array — a JS array, or a [Uint8Array] when [T] is [u8].
- [[T]], a slice, and [string] — [{a, o, n}]: the backing store, an offset - [[T]], a slice, and [str] — [{a, o, n}]: the backing store, an offset
into it and a length. A slice is a non-owning view in Flan and this is a into it and a length. A slice is a non-owning view in Flan and this is a
non-owning view here: writing through one writes the original, and a non-owning view here: writing through one writes the original, and a
copy of a slice shares its storage, both of which are what the other two copy of a slice shares its storage, both of which are what the other two
@ -47,11 +47,11 @@
- [(Fn [..] R)] — a JS function. Function {e values} come along; they carry - [(Fn [..] R)] — a JS function. Function {e values} come along; they carry
no capture in Flan either. no capture in Flan either.
{b [string] and [[u8]] are the same representation, and that is not a {b [str] and [[u8]] are the same representation, and that is not a
shortcut.} [Tast.Bytes] and [Tast.StrOfBytes] are documented there as shortcut.} [Tast.Bytes] and [Tast.StrOfBytes] are documented there as
non-instructions — in both existing backends a [string] {e is} a (ptr, len) non-instructions — in both existing backends a [str] {e is} a (ptr, len)
pair over bytes, and the two prims reinterpret rather than convert. Making pair over bytes, and the two prims reinterpret rather than convert. Making
[string] a JS string would make [(length s)] count UTF-16 code units where [str] a JS string would make [(length s)] count UTF-16 code units where
every other backend counts bytes, and the prelude is byte-oriented every other backend counts bytes, and the prelude is byte-oriented
throughout ([rune-count], [valid-utf8?], [decode-rune] all take [[u8]]). throughout ([rune-count], [valid-utf8?], [decode-rune] all take [[u8]]).
So the JS string appears in exactly one place: the argument of So the JS string appears in exactly one place: the argument of

View File

@ -1507,7 +1507,7 @@ let rec decl (f : Form.t) : Ast.decl =
(* An empty field vector is the same category as none. *) (* An empty field vector is the same category as none. *)
| [ n; { v = Kw "parent"; _ }; p ] | [ n; { v = Kw "parent"; _ }; p; { v = Vec []; _ } ] -> | [ n; { v = Kw "parent"; _ }; p ] | [ n; { v = Kw "parent"; _ }; p; { v = Vec []; _ } ] ->
let str name = let str name =
{ Ast.fname = name; fty = { Ast.t = Ast.Tname "string"; tloc = f.loc }; { Ast.fname = name; fty = { Ast.t = Ast.Tname "str"; tloc = f.loc };
floc = f.loc } floc = f.loc }
in in
mk (Ast.Defstruct (tname n, [ str "name"; str "message" ], Some (texpr p))) mk (Ast.Defstruct (tname n, [ str "name"; str "message" ], Some (texpr p)))
@ -1765,8 +1765,8 @@ let rec decl (f : Form.t) : Ast.decl =
[declare]'s signature IS the C signature, already flattened by whoever [declare]'s signature IS the C signature, already flattened by whoever
wrote the C; [declare-c]'s is the *library's* — structs by value — and wrote the C; [declare-c]'s is the *library's* — structs by value — and
[Shim] generates the flattening. The two cannot be one form, because [Shim] generates the flattening. The two cannot be one form, because
(declare f [p string] ...) already means the symbol takes ptr+len and (declare f [p str] ...) already means the symbol takes ptr+len and
(declare-c f [p string] ...) means it takes a NUL-terminated char *. *) (declare-c f [p str] ...) means it takes a NUL-terminated char *. *)
let mkd fn csym = let mkd fn csym =
if String.equal which "declare-c" then Ast.DeclareC (fn, csym) if String.equal which "declare-c" then Ast.DeclareC (fn, csym)
else Ast.Declare (fn, csym) else Ast.Declare (fn, csym)

View File

@ -60,7 +60,7 @@ let source = {flan|
;; condition has an empty sentence, since its fields say what it is. ;; condition has an empty sentence, since its fields say what it is.
;; ;;
;; (pause) and warnings are not under Error: a breakpoint is not a failure. ;; (pause) and warnings are not under Error: a breakpoint is not a failure.
(defstruct Error [name string message string]) (defstruct Error [name str message str])
;; The condition every allocating operation signals when the allocator cannot ;; The condition every allocating operation signals when the allocator cannot
;; satisfy a request — spec-memory.md, "Allocation failure". It is here rather ;; satisfy a request — spec-memory.md, "Allocation failure". It is here rather
@ -195,7 +195,7 @@ let source = {flan|
;; agreement flan_bounds_cond keeps with BoundsError. No restart is ;; agreement flan_bounds_cond keeps with BoundsError. No restart is
;; established at the call, BoundsError's decision for BoundsError's reason: ;; established at the call, BoundsError's decision for BoundsError's reason:
;; nothing a handler supplies makes the old arguments fit the new body. ;; nothing a handler supplies makes the old arguments fit the new body.
(defstruct StaleCall :parent Error [callee string compiled string current string]) (defstruct StaleCall :parent Error [callee str compiled str current str])
;; A call through a (CFn ...) that holds no function. A CFn may be a struct ;; A call through a (CFn ...) that holds no function. A CFn may be a struct
;; field, a fixed array's element or a global, and each of those starts out ;; field, a fixed array's element or a global, and each of those starts out
@ -206,7 +206,7 @@ let source = {flan|
;; Signalled from the runtime — flan_null_call in runtime/flan_rt.c — so ;; Signalled from the runtime — flan_null_call in runtime/flan_rt.c — so
;; **this field is a C struct that has to agree with this one**. No restart is ;; **this field is a C struct that has to agree with this one**. No restart is
;; established at the call, BoundsError's decision for BoundsError's reason. ;; established at the call, BoundsError's decision for BoundsError's reason.
(defstruct NullCall :parent Error [type string]) (defstruct NullCall :parent Error [type str])
;; What a generic function signals when no method answers. `generic` is the ;; What a generic function signals when no method answers. `generic` is the
;; name written at the defgeneric or defmulti, and `value` is what the ;; name written at the defgeneric or defmulti, and `value` is what the
@ -228,7 +228,7 @@ let source = {flan|
;; ;;
;; No restart is established at the miss, which is BoundsError's decision ;; No restart is established at the miss, which is BoundsError's decision
;; taken for BoundsError's reason -- see the note above it. ;; taken for BoundsError's reason -- see the note above it.
(defstruct NoMethod :parent Error [generic string value dyn]) (defstruct NoMethod :parent Error [generic str value dyn])
;; A breakpoint. (pause) stops the program where it stands and hands it to the ;; A breakpoint. (pause) stops the program where it stands and hands it to the
;; break loop, with the whole stack under it readable — C-c C-b lists the ;; break loop, with the whole stack under it readable — C-c C-b lists the
@ -1282,9 +1282,9 @@ let source = {flan|
;; something. flan_getenv answers a length of -1 and a pointer at a valid empty ;; something. flan_getenv answers a length of -1 and a pointer at a valid empty
;; string, so the test below is arithmetic and the pointer is never dereferenced ;; string, so the test below is arithmetic and the pointer is never dereferenced
;; on the absent path. ;; on the absent path.
(declare getenv-raw [name string out-len (Ptr i64)] (Ptr u8) "flan_getenv") (declare getenv-raw [name str out-len (Ptr i64)] (Ptr u8) "flan_getenv")
(defn getenv [name string] (Option [u8]) (defn getenv [name str] (Option [u8])
(let [n (i64 0) (let [n (i64 0)
p (getenv-raw name (addr n))] p (getenv-raw name (addr n))]
(if (< n 0) (if (< n 0)
@ -1951,7 +1951,7 @@ let source = {flan|
;; stopped being true when `Vec` landed, and most of the list has moved up into ;; stopped being true when `Vec` landed, and most of the list has moved up into
;; the building section above: join, concat, split, to-lower, to-upper, repeat ;; the building section above: join, concat, split, to-lower, to-upper, repeat
;; and replace are all written now, and `string-from-bytes` turned out to be ;; and replace are all written now, and `string-from-bytes` turned out to be
;; the `string` builtin all along — (string (slice v)) is the round trip, ;; the `str` builtin all along — (str (slice v)) is the round trip,
;; and the layouts being identical is exactly why it is free. ;; and the layouts being identical is exactly why it is free.
;; ;;
;; What is left is refused for four *different* reasons, which is why they are ;; What is left is refused for four *different* reasons, which is why they are
@ -1992,7 +1992,7 @@ let source = {flan|
;; does, so an embed costs nothing at run time and nothing at startup. It is ;; does, so an embed costs nothing at run time and nothing at startup. It is
;; also read-only, so `data` is a [const u8] and a store through it is refused ;; also read-only, so `data` is a [const u8] and a store through it is refused
;; at compile time. To get a writable copy, copy the bytes into a Vec. ;; at compile time. To get a writable copy, copy the bytes into a Vec.
(defstruct EmbedFile [name string data [const u8]]) (defstruct EmbedFile [name str data [const u8]])
;; A linear scan, deliberately. A directory embed is tens of entries, the scan ;; A linear scan, deliberately. A directory embed is tens of entries, the scan
;; is over names already in cache-warm .rodata, and the alternative — a ;; is over names already in cache-warm .rodata, and the alternative — a
@ -2003,7 +2003,7 @@ let source = {flan|
;; It takes a slice rather than the array (embed-dir) answers, because an array ;; 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 ;; length is part of its type and there are no generics: write
;; (embed-find (slice assets 0 (length assets)) "brush.png"). ;; (embed-find (slice assets 0 (length assets)) "brush.png").
(defn embed-find [files [EmbedFile] name string] (Option [const u8]) (defn embed-find [files [EmbedFile] name str] (Option [const u8])
(dotimes [i (length files)] (dotimes [i (length files)]
(when (bytes=? (bytes-view (.name (at files i))) (bytes-view name)) (when (bytes=? (bytes-view (.name (at files i))) (bytes-view name))
(return (Some (.data (at files i)))))) (return (Some (.data (at files i))))))
@ -2021,7 +2021,7 @@ let source = {flan|
;; parent link, not class inheritance", decides on is the ;; parent link, not class inheritance", decides on is the
;; answer to that, and it is not built; when it is, these reasons can become ;; answer to that, and it is not built; when it is, these reasons can become
;; types without any call site changing. ;; types without any call site changing.
(defstruct FileError :parent Error [path string op i32 reason i32]) (defstruct FileError :parent Error [path str op i32 reason i32])
(defconst file-op-read i32 0) (defconst file-op-read i32 0)
(defconst file-op-write i32 1) (defconst file-op-write i32 1)
@ -2051,7 +2051,7 @@ let source = {flan|
;; each signals FileError with those two restarts. "Is it there" and "how big ;; each signals FileError with those two restarts. "Is it there" and "how big
;; is it" have no such answer: absence is the reply, not a fault, and a ;; is it" have no such answer: absence is the reply, not a fault, and a
;; condition would make the ordinary case cost a handler search. ;; condition would make the ordinary case cost a handler search.
(declare file-stat-raw [path string out-size (Ptr i64)] i8 "flan_file_stat") (declare file-stat-raw [path str out-size (Ptr i64)] i8 "flan_file_stat")
;; True for anything the path resolves to — a file, a directory, a device — ;; True for anything the path resolves to — a file, a directory, a device —
;; because that is what the question asks and a caller wanting "and it is a ;; because that is what the question asks and a caller wanting "and it is a
@ -2063,7 +2063,7 @@ let source = {flan|
;; first. Reach for it when the answer is the point — choosing a config path, ;; first. Reach for it when the answer is the point — choosing a config path,
;; deciding whether to write a default — and not as a guard in front of an ;; deciding whether to write a default — and not as a guard in front of an
;; operation that already reports its own failure properly. ;; operation that already reports its own failure properly.
(defn file-exists? [path string] bool (defn file-exists? [path str] bool
(let [n (i64 0)] (let [n (i64 0)]
(= (file-stat-raw path (addr n)) 1))) (= (file-stat-raw path (addr n)) 1)))
@ -2071,7 +2071,7 @@ let source = {flan|
;; answer — that is the trade a caller makes by asking a question with no ;; answer — that is the trade a caller makes by asking a question with no
;; restart on it. A caller that needs to tell "missing" from "denied" wants ;; restart on it. A caller that needs to tell "missing" from "denied" wants
;; `slurp`, whose FileError carries the reason. ;; `slurp`, whose FileError carries the reason.
(defn file-size [path string] (Option i64) (defn file-size [path str] (Option i64)
(let [n (i64 0)] (let [n (i64 0)]
(if (= (file-stat-raw path (addr n)) 1) (Some n) None))) (if (= (file-stat-raw path (addr n)) 1) (Some n) None)))
@ -2100,10 +2100,10 @@ let source = {flan|
;; process rather than to any source file — nothing else knows better, and ;; process rather than to any source file — nothing else knows better, and
;; every program links this runtime. It is not a file API and `slurp` is; this ;; every program links this runtime. It is not a file API and `slurp` is; this
;; exists so a macro can look at data at compile time. ;; exists so a macro can look at data at compile time.
(declare macro-slurp-raw [path string out-len (Ptr i64)] (Ptr u8) (declare macro-slurp-raw [path str out-len (Ptr i64)] (Ptr u8)
"flan_macro_slurp") "flan_macro_slurp")
(defn macro-slurp [path string] (Option [u8]) (defn macro-slurp [path str] (Option [u8])
(let [n (i64 0) (let [n (i64 0)
p (macro-slurp-raw path (addr n))] p (macro-slurp-raw path (addr n))]
(if (< n 0) (if (< n 0)
@ -2131,11 +2131,11 @@ let source = {flan|
;; Case order is the tag order (docs/BUILT.md, data types), so this list is a layout ;; Case order is the tag order (docs/BUILT.md, data types), so this list is a layout
;; contract with lib/expand.ml's marshaller and may not be reordered. ;; contract with lib/expand.ml's marshaller and may not be reordered.
(defdata Form (defdata Form
[(Sym [s string]) [(Sym [s str])
(Kw [s string]) (Kw [s str])
(Int [i i64]) (Int [i i64])
(Float [x f64]) (Float [x f64])
(Str [s string]) (Str [s str])
(Byte [b i32]) (Byte [b i32])
(List [xs [Form]]) (List [xs [Form]])
(Vec [xs [Form]]) (Vec [xs [Form]])
@ -2182,7 +2182,7 @@ let source = {flan|
;; (head x) for every x, which is what ~~@xs splices into an inner template: ;; (head x) for every x, which is what ~~@xs splices into an inner template:
;; one unquote per element, as SBCL's unquote* builds (src/code/backq.lisp). ;; one unquote per element, as SBCL's unquote* builds (src/code/backq.lisp).
(defn form-wrap-each [head string xs [Form]] [Form] (defn form-wrap-each [head str xs [Form]] [Form]
(let [v (vec-new Form)] (let [v (vec-new Form)]
(dotimes [i (length xs)] (dotimes [i (length xs)]
(push v (Form.List {.xs (form-pair (Form.Sym {.s head}) (at xs i))}))) (push v (Form.List {.xs (form-pair (Form.Sym {.s head}) (at xs i))})))
@ -2219,7 +2219,7 @@ let source = {flan|
(let [d (i64->bytes (gensym-next))] (let [d (i64->bytes (gensym-next))]
(dotimes [i (length d)] (dotimes [i (length d)]
(push v (at d i)))) (push v (at d i))))
(Form.Sym {.s (string (slice v))}))) (Form.Sym {.s (str (slice v))})))
;; ── The first special form to stop being one ────────────────────────── ;; ── The first special form to stop being one ──────────────────────────
;; ;;
@ -2443,7 +2443,7 @@ let source = {flan|
(Form.List xs) xs (Form.List xs) xs
_ (form-nil))) _ (form-nil)))
(defn form-sym? [f Form name string] bool (defn form-sym? [f Form name str] bool
(match f (match f
(Form.Sym s) (bytes=? (bytes-view s) (bytes-view name)) (Form.Sym s) (bytes=? (bytes-view s) (bytes-view name))
_ false)) _ false))

View File

@ -307,7 +307,7 @@ let rec render ?(refuse = print_refusal) c depth (e : Tast.expr) : Tast.expr lis
Not squeamishness about indeterminate bytes — a printer that showed a Not squeamishness about indeterminate bytes — a printer that showed a
number nobody stored would be fine, and every member of a union is a number nobody stored would be fine, and every member of a union is a
legal read by this language's own rule. It is that one of those members legal read by this language's own rule. It is that one of those members
may be a [string] or a [Ptr], and rendering it would dereference may be a [str] or a [Ptr], and rendering it would dereference
whatever bytes happen to be in the union's storage. A tagged data type whatever bytes happen to be in the union's storage. A tagged data type
is safe to print because its tag says which case is live; there is no is safe to print because its tag says which case is live; there is no
such fact here, so the printer would be following a pointer it invented. such fact here, so the printer would be following a pointer it invented.

View File

@ -9,8 +9,8 @@
[declare-c] says the other thing: the signature is the *library's*, structs [declare-c] says the other thing: the signature is the *library's*, structs
by value and all, and the compiler is to produce whatever flattening makes by value and all, and the compiler is to produce whatever flattening makes
it crossable. The two cannot be one form — it crossable. The two cannot be one form —
[(declare start-raw [path string] i32 "flan_agent_start")] and [(declare start-raw [path str] i32 "flan_agent_start")] and
[(declare-c init-window [w i32 h i32 title string] "InitWindow")] are the [(declare-c init-window [w i32 h i32 title str] "InitWindow")] are the
same shape and mean opposite things about who NUL-terminates the string. same shape and mean opposite things about who NUL-terminates the string.
Why the crossing is still C, and not [emit.ml]: a small aggregate's calling Why the crossing is still C, and not [emit.ml]: a small aggregate's calling
@ -320,7 +320,7 @@ let rec cty env ~needed ~loc ~what (t : Ast.texpr) : string =
"%s is %s, a union, and the shim generator writes structs only. \ "%s is %s, a union, and the shim generator writes structs only. \
Pass (Ptr %s) and let the C side read it" Pass (Ptr %s) and let the C side read it"
what n n what n n
else if String.equal n "string" then else if String.equal n "str" then
fail loc fail loc
"%s is a string, and a string crosses only as a parameter or a \ "%s is a string, and a string crosses only as a parameter or a \
return value — declare (Ptr u8) and read it in Flan" return value — declare (Ptr u8) and read it in Flan"
@ -379,7 +379,7 @@ type pkind =
let classify env ~needed ~loc ~what (t : Ast.texpr) = let classify env ~needed ~loc ~what (t : Ast.texpr) =
let t' = unalias env t in let t' = unalias env t in
match t'.Ast.t with match t'.Ast.t with
| Ast.Tname "string" -> (Pstr, "const char *") | Ast.Tname "str" -> (Pstr, "const char *")
(* A copy crosses behind a pointer only: by value, the Flan half this (* A copy crosses behind a pointer only: by value, the Flan half this
generator writes would have to spell the copy's type, and it builds its generator writes would have to spell the copy's type, and it builds its
wrapper from struct names. *) wrapper from struct names. *)
@ -891,7 +891,7 @@ let flan_wrapper ?track (fn : Ast.fn) (s : shim) raw : Ast.decl_kind =
out ], out ],
Some (ty loc (Ast.Tname n)) ) Some (ty loc (Ast.Tname n)) )
| `Str -> | `Str ->
(* (string (bytes (string (slice-from p n)))): a view of C's bytes, (* (str (bytes (str (slice-from p n)))): a view of C's bytes,
copied by [bytes] into the context allocator, and seen as a string copied by [bytes] into the context allocator, and seen as a string
again. The length is bound before the pointer is, so its address again. The length is bound before the pointer is, so its address
exists to be written through. *) exists to be written through. *)
@ -903,9 +903,9 @@ let flan_wrapper ?track (fn : Ast.fn) (s : shim) raw : Ast.decl_kind =
bval = ex loc (Ast.Int 0L); bloc = loc }; bval = ex loc (Ast.Int 0L); bloc = loc };
{ Ast.bname = ptr_tmp; bty = None; { Ast.bname = ptr_tmp; bty = None;
bval = call (args @ [ app "addr" [ v len_tmp ] ]); bloc = loc } ]; bval = call (args @ [ app "addr" [ v len_tmp ] ]); bloc = loc } ];
( [ app "string" ( [ app "str"
[ app "bytes" [ app "bytes"
[ app "string" [ app "str"
[ app "slice-from" [ v ptr_tmp; v len_tmp ] ] ] ] ], [ app "slice-from" [ v ptr_tmp; v len_tmp ] ] ] ] ],
fn.Ast.ret ) fn.Ast.ret )
| _ -> ([ call args ], fn.Ast.ret) | _ -> ([ call args ], fn.Ast.ret)
@ -957,7 +957,7 @@ let one env ~taken ~tracks (fn : Ast.fn) csym loc =
let t' = unalias env t in let t' = unalias env t in
(match t'.Ast.t with (match t'.Ast.t with
| Ast.Tname "Unit" -> `Void | Ast.Tname "Unit" -> `Void
| Ast.Tname "string" -> `Str | Ast.Tname "str" -> `Str
| Ast.Tname n when Hashtbl.mem env.structs n -> | Ast.Tname n when Hashtbl.mem env.structs n ->
ignore (cty env ~needed ~loc ~what t'); ignore (cty env ~needed ~loc ~what t');
`Struct (n, ctype_name n) `Struct (n, ctype_name n)

View File

@ -36,7 +36,7 @@ type prim =
calc-me's tokenizer and the prelude's printers each need. *) calc-me's tokenizer and the prelude's printers each need. *)
| Bytes | BytesToF64 | BytesToI64 | F64ToBytes | I64ToBytes | Bytes | BytesToF64 | BytesToI64 | F64ToBytes | I64ToBytes
(* (string b): the other direction of [Bytes], and the same non-instruction. (* (string b): the other direction of [Bytes], and the same non-instruction.
See check.ml's "string" case for why it is unchecked. *) See check.ml's "str" case for why it is unchecked. *)
| StrOfBytes | StrOfBytes
(* No surface name: the structural printer is the only thing that builds (* No surface name: the structural printer is the only thing that builds
these. U64ToBytes because u64 is not i64 with a flag, EscapeBytes for a these. U64ToBytes because u64 is not i64 with a flag, EscapeBytes for a

View File

@ -44,7 +44,7 @@ type t =
| Array of int64 * t (* [n T] inline, a value, copies *) | Array of int64 * t (* [n T] inline, a value, copies *)
| Map of t * t (* (Map K V) *) | Map of t * t (* (Map K V) *)
| Ptr of access * t (* (Ptr T) (Ptr const T) *) | Ptr of access * t (* (Ptr T) (Ptr const T) *)
(* [Allocator]: a builtin opaque type, the way [string] is a builtin (* [Allocator]: a builtin opaque type, the way [str] is a builtin
ptr+len. It is a [Types.t] case with no user-writable constructor, which ptr+len. It is a [Types.t] case with no user-writable constructor, which
is what lets spec-memory.md's "procedure plus an opaque data pointer" be is what lets spec-memory.md's "procedure plus an opaque data pointer" be
expressed with none of milestone 5's function values — the procedure is a expressed with none of milestone 5's function values — the procedure is a
@ -145,7 +145,7 @@ let bits_f = function F32 -> 32 | F64 -> 64
that already accepts [i32] accepts [int] without learning the word. that already accepts [i32] accepts [int] without learning the word.
Only these two. The rest of the foreign spellings — [long], [double], Only these two. The rest of the foreign spellings — [long], [double],
[uint], [str] — stay refusals that teach the Flan name; see [uint], [string] — stay refusals that teach the Flan name; see
[Check.foreign_spelling] for why the line is drawn where it is. [Check.foreign_spelling] for why the line is drawn where it is.
The mapping is one-way on purpose: [ikind_name] and [fkind_name] below The mapping is one-way on purpose: [ikind_name] and [fkind_name] below
@ -175,7 +175,7 @@ let fkind_of_name = function
and the identity is a half one. *) and the identity is a half one. *)
let primitive_names = let primitive_names =
[ "i8"; "i16"; "i32"; "i64"; "u8"; "u16"; "u32"; "u64"; [ "i8"; "i16"; "i32"; "i64"; "u8"; "u16"; "u32"; "u64";
"f32"; "f64"; "bool"; "string"; "dyn"; "Unit"; "Never"; "Allocator"; "f32"; "f64"; "bool"; "str"; "dyn"; "Unit"; "Never"; "Allocator";
"int"; "float" ] "int"; "float" ]
let ikind_name k = let ikind_name k =
@ -272,7 +272,7 @@ and to_string = function
| Int k -> ikind_name k | Int k -> ikind_name k
| Float k -> fkind_name k | Float k -> fkind_name k
| Bool -> "bool" | Bool -> "bool"
| String -> "string" | String -> "str"
| Unit -> "()" | Unit -> "()"
| Never -> "Never" | Never -> "Never"
| Named n -> (match Hashtbl.find_opt display n with Some d -> d | None -> n) | Named n -> (match Hashtbl.find_opt display n with Some d -> d | None -> n)

View File

@ -778,7 +778,7 @@ void flan_dev_watch_end(void) {
/* These are the whole feature for a scalar, and they are what a program can /* These are the whole feature for a scalar, and they are what a program can
* use today: * use today:
* *
* (declare-c watch-i64 [name string x i64] i32 "flan_dev_watch_i64") * (declare-c watch-i64 [name str x i64] i32 "flan_dev_watch_i64")
* ... * ...
* (watch-i64 "ticks" ticks) * (watch-i64 "ticks" ticks)
* *
@ -901,7 +901,7 @@ void flan_dev_watch_reset(void) {
/* Reachable from a program by [declare-c], the same as the four scalars and /* Reachable from a program by [declare-c], the same as the four scalars and
* for the same reason — no arm in the checker, nothing the compiler learns: * for the same reason — no arm in the checker, nothing the compiler learns:
* *
* (declare-c watch-num-i64 [name string x i64] i32 "flan_dev_watch_num_i64") * (declare-c watch-num-i64 [name str x i64] i32 "flan_dev_watch_num_i64")
* ... * ...
* (watch-num-i64 "cell" (at grid i)) * (watch-num-i64 "cell" (at grid i))
* *

View File

@ -1716,7 +1716,7 @@ static slot_type slot_type_of(const uint8_t *w, int64_t n) {
const char *w; uint8_t kind, fbits; int64_t lo, hi; const char *w; uint8_t kind, fbits; int64_t lo, hi;
} known[] = { } known[] = {
{ "bool", ST_BOOL, 0, 0, 0 }, { "bool", ST_BOOL, 0, 0, 0 },
{ "string", ST_TEXT, 0, 0, 0 }, { "str", ST_TEXT, 0, 0, 0 },
{ "f32", ST_FLOAT, 24, 0, 0 }, { "f32", ST_FLOAT, 24, 0, 0 },
{ "f64", ST_FLOAT, 53, 0, 0 }, { "f64", ST_FLOAT, 53, 0, 0 },
{ "i8", ST_INT, 0, INT8_MIN, INT8_MAX }, { "i8", ST_INT, 0, INT8_MIN, INT8_MAX },
@ -2682,7 +2682,7 @@ flan_dyn flan_dyn_rem(flan_dyn a, flan_dyn b, const uint8_t *loc,
* *
* Numbers against numbers, text against text, and nothing else. Text orders * Numbers against numbers, text against text, and nothing else. Text orders
* bytewise, which is [memcmp] with the shorter one first on a tie — the same * bytewise, which is [memcmp] with the shorter one first on a tie — the same
* order a sort of a [(Vec string)] would want and the only order that needs no * order a sort of a [(Vec str)] would want and the only order that needs no
* locale, no collation table and no argument. * locale, no collation table and no argument.
* *
* A number against a text traps rather than answering. The temptation is to * A number against a text traps rather than answering. The temptation is to

View File

@ -71,7 +71,7 @@ void flan_handler_pop(flan_handler *h) {
* *
* A handler that matched through a parent link is not handed the condition, * A handler that matched through a parent link is not handed the condition,
* whose layout is its own type's, but a view laid out as the prelude's * whose layout is its own type's, but a view laid out as the prelude's
* (defstruct Error [name string message string]) — the parent's type is * (defstruct Error [name str message str]) — the parent's type is
* Error-shaped, which the checker requires. The view's message is the * Error-shaped, which the checker requires. The view's message is the
* condition with its values: [message] for the runtime's own conditions, * condition with its values: [message] for the runtime's own conditions,
* which format their sentence before signalling; what [render] prints for a * which format their sentence before signalling; what [render] prints for a
@ -388,7 +388,7 @@ typedef struct { const uint8_t *ptr; int64_t len; } flan_slice;
static int rt_argc; static int rt_argc;
static char **rt_argv; static char **rt_argv;
static flan_slice *rt_args; /* argv as [string], built once, never freed */ static flan_slice *rt_args; /* argv as [str], built once, never freed */
void flan_rt_init(int32_t argc, char **argv) { void flan_rt_init(int32_t argc, char **argv) {
rt_argc = (int)argc; rt_argc = (int)argc;
@ -1487,7 +1487,7 @@ void flan_arith_error(const uint8_t *loc, int64_t loclen, int32_t op,
* is the price of never pinning a module for it. * is the price of never pinning a module for it.
* *
* The condition must agree field for field with the prelude's * The condition must agree field for field with the prelude's
* (defstruct StaleCall [callee string compiled string current string]), * (defstruct StaleCall [callee str compiled str current str]),
* the same hand-kept agreement flan_bounds_cond has with BoundsError. */ * the same hand-kept agreement flan_bounds_cond has with BoundsError. */
typedef struct { flan_slice callee, compiled, current; } flan_stale_cond; typedef struct { flan_slice callee, compiled, current; } flan_stale_cond;
@ -1551,7 +1551,7 @@ void flan_stale_call(const char *site, const char *callee, const char *want,
* `type` is copied and never freed, for flan_stale_call's reason: the text * `type` is copied and never freed, for flan_stale_call's reason: the text
* lives in the image of the module that compiled the call, which may be a * lives in the image of the module that compiled the call, which may be a
* thunk that is unloaded once it returns. Must agree with the prelude's * thunk that is unloaded once it returns. Must agree with the prelude's
* (defstruct NullCall :parent Error [type string]). */ * (defstruct NullCall :parent Error [type str]). */
typedef struct { flan_slice type; } flan_nullcall_cond; typedef struct { flan_slice type; } flan_nullcall_cond;
@ -3529,7 +3529,7 @@ int8_t flan_map_init(flan_map *m, flan_allocator *a, int64_t ksize,
m->alloc = a; m->alloc = a;
m->epoch = (int64_t)a->epoch; m->epoch = (int64_t)a->epoch;
/* No block until something is put in it: an empty map that is never written /* No block until something is put in it: an empty map that is never written
* costs nothing, which is what makes a (defonce m (Map string i32)) free. */ * costs nothing, which is what makes a (defonce m (Map str i32)) free. */
return 1; return 1;
} }

View File

@ -24,7 +24,7 @@ The consequence is visible in the syntax. A `restart-case` in value position
must produce its type on the *fall-through* path too: must produce its type on the *fall-through* path too:
``` ```
(defn load-texture [path string] (Handle Texture) (defn load-texture [path str] (Handle Texture)
(if (file-exists? path) (if (file-exists? path)
(rl/load-texture path) (rl/load-texture path)
(restart-case (restart-case

View File

@ -53,7 +53,7 @@ it is usually written in — a `let` binding — has no type slot for it to take
them from: them from:
``` ```
(let [enemies (map-new string Enemy)] (let [enemies (map-new str Enemy)]
...) ...)
``` ```

View File

@ -134,7 +134,7 @@
;; `try` unwraps Ok, else early-returns Err, widening this function's error ;; `try` unwraps Ok, else early-returns Err, widening this function's error
;; set. Option→Result conversion is explicit — no implicit From, no anyhow. ;; set. Option→Result conversion is explicit — no implicit From, no anyhow.
(defn load-config [path string] (Result Config) (defn load-config [path str] (Result Config)
(let [text (try (read-file path)) (let [text (try (read-file path))
table (try (parse-toml text)) table (try (parse-toml text))
port (try (ok-or (get table "port") port (try (ok-or (get table "port")
@ -143,7 +143,7 @@
;; errdefer runs only on the Result failure path — NOT on a restart transfer ;; errdefer runs only on the Result failure path — NOT on a restart transfer
;; (spec-conditions.md §5). Pairs with explicit allocation. ;; (spec-conditions.md §5). Pairs with explicit allocation.
(defn load-atlas [path string] (Result Atlas) (defn load-atlas [path str] (Result Atlas)
(let [buf (alloc-image context/allocator)] (let [buf (alloc-image context/allocator)]
(errdefer (free buf)) (errdefer (free buf))
(try (decode-png path buf)) (try (decode-png path buf))
@ -155,13 +155,13 @@
;; file is fixed on disk. So it offers a menu and the caller chooses. ;; file is fixed on disk. So it offers a menu and the caller chooses.
;; A condition type is an ordinary struct — there is no defcondition, and no ;; A condition type is an ordinary struct — there is no defcondition, and no
;; class hierarchy to put one in. Matching is by type plus a predicate. ;; class hierarchy to put one in. Matching is by type plus a predicate.
(defstruct AssetMissing [path string]) (defstruct AssetMissing [path str])
;; `signal` has type () and RETURNS if every handler returns normally, so the ;; `signal` has type () and RETURNS if every handler returns normally, so the
;; fall-through path of a restart-case in value position must still produce the ;; fall-through path of a restart-case in value position must still produce the
;; type. `abort` has type Never, which unifies with (Handle Texture). ;; type. `abort` has type Never, which unifies with (Handle Texture).
;; Every clause body and the restart-case body share one type. ;; Every clause body and the restart-case body share one type.
(defn load-texture [path string] (Handle Texture) (defn load-texture [path str] (Handle Texture)
(if (file-exists? path) (if (file-exists? path)
(rl/load-texture path) (rl/load-texture path)
(restart-case (restart-case
@ -176,7 +176,7 @@
;; then the body. It is not a type paired with an `fn`, and a handler closes ;; then the body. It is not a type paired with an `fn`, and a handler closes
;; over nothing: it is lifted into its own function, so a value it wants to keep ;; over nothing: it is lifted into its own function, so a value it wants to keep
;; goes on the condition or into a global. ;; goes on the condition or into a global.
(defn load-level [path string] Level (defn load-level [path str] Level
(handler-bind [(AssetMissing [c] (handler-bind [(AssetMissing [c]
(log "missing asset:" (.path c)) (log "missing asset:" (.path c))
(invoke-restart 'use-placeholder))] (invoke-restart 'use-placeholder))]
@ -195,7 +195,7 @@
(skip-form [] (skip-to-next-delimiter p)))) (skip-form [] (skip-to-next-delimiter p))))
forms)) forms))
(defn collect-parse-errors [src string] (Result Ast) (defn collect-parse-errors [src str] (Result Ast)
(let [errors (make-vec ParseError)] (let [errors (make-vec ParseError)]
(handler-bind [(ParseError [c] (handler-bind [(ParseError [c]
(push errors c) ; value struct: copies out of (push errors c) ; value struct: copies out of
@ -218,7 +218,7 @@
;; ── defer for explicit resources ────────────────────────────────────── ;; ── defer for explicit resources ──────────────────────────────────────
;; defer DOES run when a restart transfer passes through this frame. ;; defer DOES run when a restart transfer passes through this frame.
(defn save-world [w (Ptr World) path string] () (defn save-world [w (Ptr World) path str] ()
(let [f (open path :write)] (let [f (open path :write)]
(defer (close f)) (defer (close f))
(write-bytes f (serialize w)))) (write-bytes f (serialize w))))
@ -235,7 +235,7 @@
;; An imperative loop, not (each (fn [p] (try ...))): `try` and `return` inside a ;; An imperative loop, not (each (fn [p] (try ...))): `try` and `return` inside a
;; `fn` exit the FN, so a callback would swallow the Err instead of propagating ;; `fn` exit the FN, so a callback would swallow the Err instead of propagating
;; it out of preload. ;; it out of preload.
(defn ^:async preload [paths [string]] (Result ()) (defn ^:async preload [paths [str]] (Result ())
(for [p paths] (for [p paths]
(try (await (load-texture-async p)))) (try (await (load-texture-async p))))
(Ok unit)) (Ok unit))

View File

@ -19,7 +19,7 @@
(set ticks (+ ticks 1)) (set ticks (+ ticks 1))
ticks) ticks)
(defn main [args [string]] i32 (defn main [args [str]] i32
(if (< (length args) 2) (if (< (length args) 2)
(do (println "usage: agent-queue <socket>") 2) (do (println "usage: agent-queue <socket>") 2)
(do (do

View File

@ -19,7 +19,7 @@
(set ticks (+ ticks 1)) (set ticks (+ ticks 1))
ticks) ticks)
(defn main [args [string]] i32 (defn main [args [str]] i32
(if (< (length args) 2) (if (< (length args) 2)
(do (println "usage: agent <socket>") 2) (do (println "usage: agent <socket>") 2)
(do (do

View File

@ -15,7 +15,7 @@
(defn show-fields [s [[const u8]]] () (defn show-fields [s [[const u8]]] ()
(dotimes [i (length s)] (dotimes [i (length s)]
(print (string (at s i))) (print (str (at s i)))
(print " ")) (print " "))
(println "")) (println ""))
@ -114,7 +114,7 @@
(let [f (split (bytes-view "delta,alpha,charlie,bravo") \,)] (let [f (split (bytes-view "delta,alpha,charlie,bravo") \,)]
(sort-bytes (slice f)) (sort-bytes (slice f))
(let [j (join (slice f) (bytes-view " < "))] (let [j (join (slice f) (bytes-view " < "))]
(println (string (slice j))) ; alpha < bravo < charlie < delta (println (str (slice j))) ; alpha < bravo < charlie < delta
(free j)) (free j))
(free f)) (free f))
0) 0)

View File

@ -23,7 +23,7 @@
(defdata Value [Nil (Int [n i64]) (List [items (Vec Value)])]) (defdata Value [Nil (Int [n i64]) (List [items (Vec Value)])])
(defn main [args [string]] i32 (defn main [args [str]] i32
(set frame (arena-new 4096)) (set frame (arena-new 4096))
(let [which (if (> (length 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 (cond

View File

@ -19,9 +19,9 @@
(defdata Value (defdata Value
[(Nil []) [(Nil [])
(Int [n i64]) (Int [n i64])
(Text [s string]) (Text [s str])
(List [items (Vec Value)]) (List [items (Vec Value)])
(Table [entries (Map string Value)])]) (Table [entries (Map str Value)])])
;; [0 1 .. n-1] as a dynamic list. No allocator is named: with-allocator in ;; [0 1 .. n-1] as a dynamic list. No allocator is named: with-allocator in
;; main has rebound the context, and spec-memory.md puts the allocator in the ;; main has rebound the context, and spec-memory.md puts the allocator in the
@ -36,7 +36,7 @@
;; A table whose values are themselves lists, so the graph is three levels ;; A table whose values are themselves lists, so the graph is three levels
;; deep before it reaches a leaf: Table -> Vec -> List -> Vec -> Int. ;; deep before it reaches a leaf: Table -> Vec -> List -> Vec -> Int.
(defn a-table [] Value (defn a-table [] Value
(let [entries (map-new string Value)] (let [entries (map-new str Value)]
(put entries "xs" (number-list 3)) (put entries "xs" (number-list 3))
(put entries "ys" (number-list 5)) (put entries "ys" (number-list 5))
(put entries "name" (Value.Text {.s "edn"})) (put entries "name" (Value.Text {.s "edn"}))

View File

@ -61,7 +61,7 @@
(defonce small f64 -1e300) (defonce small f64 -1e300)
(defonce uz u32) (defonce uz u32)
(defn show [name string n i64] () (defn show [name str n i64] ()
(print name) (print " ") (print n) (println "")) (print name) (print " ") (print n) (println ""))
;;; Two frames deep with a defer on the way, so the transfer has something to ;;; Two frames deep with a defer on the way, so the transfer has something to

View File

@ -30,7 +30,7 @@
(defonce m1-32 i32 -1) (defonce m1-32 i32 -1)
(defonce wide f32 1e30) (defonce wide f32 1e30)
(defn main [args [string]] i32 (defn main [args [str]] i32
(let [n (i32 (bytes->i64 (bytes-view (at args 1)))) (let [n (i32 (bytes->i64 (bytes-view (at args 1))))
;; The most negative i64. No literal spells it — the reader parses the ;; The most negative i64. No literal spells it — the reader parses the
;; digits and then negates, and the positive half does not fit — so it ;; digits and then negates, and the positive half does not fit — so it

View File

@ -58,7 +58,7 @@
;;; Two frames deep, with a defer on the way, so the transfer has something to ;;; Two frames deep, with a defer on the way, so the transfer has something to
;;; cross and something to run on its way out. ;;; cross and something to run on its way out.
(defn show [name string n i64] () (defn show [name str n i64] ()
(print name) (print " ") (print n) (println "")) (print name) (print " ") (print n) (println ""))
(defn read-cell [i i32] i32 (defn read-cell [i i32] i32

View File

@ -15,7 +15,7 @@
;;;; (that is a separate job) and lets LLVM fold the branch away here. ;;;; (that is a separate job) and lets LLVM fold the branch away here.
(defonce arr [3 i32]) (defonce arr [3 i32])
(defn main [args [string]] i32 (defn main [args [str]] i32
(let [n (i32 (bytes->i64 (bytes-view (at args 1)))) (let [n (i32 (bytes->i64 (bytes-view (at args 1))))
s (bytes-view "hello")] ; len 5 s (bytes-view "hello")] ; len 5
(cond (cond

View File

@ -37,7 +37,7 @@
;;; one. ;;; one.
;;; Both additions are qualified too, since + is shadowed below and this ;;; Both additions are qualified too, since + is shadowed below and this
;;; function wants the arithmetic and not the 99. ;;; function wants the arithmetic and not the 99.
(defn length [s string] i32 (builtin/+ 1 (builtin/length s))) (defn length [s str] i32 (builtin/+ 1 (builtin/length s)))
(defn + [a i32 b i32] i32 99) (defn + [a i32 b i32] i32 99)

View File

@ -13,7 +13,7 @@
(let [s "INSERTIONSORT" (let [s "INSERTIONSORT"
b (bytes s)] b (bytes s)]
(set (at b 0) \Z) (set (at b 0) \Z)
(println (string b)) ; ZNSERTIONSORT (println (str b)) ; ZNSERTIONSORT
(println s) ; INSERTIONSORT (println s) ; INSERTIONSORT
;; The copy's block came from the context allocator, and free hands it ;; The copy's block came from the context allocator, and free hands it
;; back there — which is what keeps this program leak-free. ;; back there — which is what keeps this program leak-free.
@ -23,7 +23,7 @@
;; at -O0 and silently do nothing at -O2. ;; at -O0 and silently do nothing at -O2.
(let [b (bytes "hi")] (let [b (bytes "hi")]
(set (at b 0) \H) (set (at b 0) \H)
(println (string b)) ; Hi (println (str b)) ; Hi
(free b)) (free b))
;; 3. The view still costs nothing and reads the string's own storage. ;; 3. The view still costs nothing and reads the string's own storage.
@ -36,7 +36,7 @@
(set frame (arena-new 4096)) (set frame (arena-new 4096))
(let [b (bytes "arena" frame)] (let [b (bytes "arena" frame)]
(set (at b 4) \A) (set (at b 4) \A)
(println (string b))) ; arenA (println (str b))) ; arenA
(free-all frame) (free-all frame)
(arena-destroy frame) (arena-destroy frame)

View File

@ -7,5 +7,5 @@
(defn main [] i32 (defn main [] i32
(let [v (bytes-view "INSERTIONSORT")] (let [v (bytes-view "INSERTIONSORT")]
(set (at v 0) \Z) (set (at v 0) \Z)
(print (string v)) (print (str v))
0)) 0))

View File

@ -21,7 +21,7 @@
;; Brackets around the result so an empty trim is visible as [] rather than ;; Brackets around the result so an empty trim is visible as [] rather than
;; as nothing at all — the all-whitespace case is otherwise indistinguishable ;; as nothing at all — the all-whitespace case is otherwise indistinguishable
;; from a trim that printed the wrong slice of length zero. ;; from a trim that printed the wrong slice of length zero.
(defn show-trim [s string] () (defn show-trim [s str] ()
(print "[") (print "[")
(print (trim (bytes-view s))) (print (trim (bytes-view s)))
(print "]")) (print "]"))

View File

@ -16,12 +16,12 @@
;; Prints its tag and answers its value. Every operand below is one of these, ;; Prints its tag and answers its value. Every operand below is one of these,
;; so the tag line is a transcript of the evaluation. ;; so the tag line is a transcript of the evaluation.
(defn mark [tag string v i32] i32 (defn mark [tag str v i32] i32
(set calls (+ calls 1)) (set calls (+ calls 1))
(print tag) (print tag)
v) v)
(defn markf [tag string v f64] f64 (defn markf [tag str v f64] f64
(print tag) (print tag)
v) v)

View File

@ -5,7 +5,7 @@
;;;; before 1100 two-byte characters, so a cut at a fixed byte count would ;;;; before 1100 two-byte characters, so a cut at a fixed byte count would
;;;; land inside one. ;;;; land inside one.
(defstruct Wide :parent Error [code i32 why string]) (defstruct Wide :parent Error [code i32 why str])
(defstruct Longs :parent Error) (defstruct Longs :parent Error)
(defn main [] i32 (defn main [] i32

View File

@ -5,7 +5,7 @@
(defstruct IoError :parent Error) (defstruct IoError :parent Error)
(defstruct Empty :parent Error []) (defstruct Empty :parent Error [])
(defstruct MyErr :parent Error [code i32 why string]) (defstruct MyErr :parent Error [code i32 why str])
(defonce zero i64) (defonce zero i64)

View File

@ -7,7 +7,7 @@
(defstruct MyErr :parent Error [code i32]) (defstruct MyErr :parent Error [code i32])
(defn why [] string (defn why [] str
(handler-case (do (error (MyErr {.code 3})) "") (handler-case (do (error (MyErr {.code 3})) "")
[(Error [e] (.message e))])) [(Error [e] (.message e))]))

View File

@ -39,7 +39,7 @@
(sort w) (sort w)
(println (total w) (total (slice w 1))) (println (total w) (total (slice w 1)))
(println (first-of w) (first-of (bytes-view "z"))) (println (first-of w) (first-of (bytes-view "z")))
(println (string head) (string tail) (length head)) (println (str head) (str tail) (length head))
(println (bytes=? head (bytes-view "hello")) (starts-with? r head)) (println (bytes=? head (bytes-view "hello")) (starts-with? r head))
(push names head) (push names head)
(push names tail) (push names tail)
@ -51,15 +51,15 @@
both [a b]] both [a b]]
(println (widths (slice both))) (println (widths (slice both)))
(set (at a 0) \A) (set (at a 0) \A)
(println (string a))) (println (str a)))
(let [f (split (bytes-view "b,a,c") \,)] (let [f (split (bytes-view "b,a,c") \,)]
(sort-bytes (slice f)) (sort-bytes (slice f))
(println (string (slice (join (slice f) (bytes-view "-")))))) (println (str (slice (join (slice f) (bytes-view "-"))))))
(println (at r 0)) (println (at r 0))
(println (call-rd rd) (call-bare rd) (call-mk mk)) (println (call-rd rd) (call-bare rd) (call-mk mk))
(let [b (bytes "q")] (let [b (bytes "q")]
(println (peek (addr (at r 1))) (peek (addr (at "abc" 2))) (println (peek (addr (at r 1))) (peek (addr (at "abc" 2)))
(peek (addr (at b 0))) (peek (addr (at b 0)))
(string (slice-from (addr (at r 7)) 5)))) (str (slice-from (addr (at r 7)) 5))))
(free names)) (free names))
0) 0)

View File

@ -4,7 +4,7 @@
;;;; destroyed arena's record. Allocating from the context must then trap, ;;;; destroyed arena's record. Allocating from the context must then trap,
;;;; not quietly allocate from the new arena. Argument 1 reads context/allocator ;;;; not quietly allocate from the new arena. Argument 1 reads context/allocator
;;;; as a value first and uses that instead, which traps the same way. ;;;; as a value first and uses that instead, which traps the same way.
(defn main [args [string]] i32 (defn main [args [str]] i32
(let [which (if (> (length args) 1) (bytes->i64 (bytes-view (at args 1))) 0) (let [which (if (> (length args) 1) (bytes->i64 (bytes-view (at args 1))) 0)
a (arena-new 4096)] a (arena-new 4096)]
(with-allocator a (with-allocator a

View File

@ -19,8 +19,8 @@
(let [path (vec-new u8)] (let [path (vec-new u8)]
(append (addr path) (bytes-view "dir/")) (append (addr path) (bytes-view "dir/"))
(dotimes [i 300] (push path \x)) (dotimes [i 300] (push path \x))
(let [tail (cret/after-slash (string (slice path))) (let [tail (cret/after-slash (str (slice path)))
long-name (string (slice path 4))] long-name (str (slice path 4))]
(println (length tail)) (println (length tail))
(println (= tail long-name)))) (println (= tail long-name))))
(println (length (cret/nothing))) (println (length (cret/nothing)))

View File

@ -13,7 +13,7 @@
[Empty [Empty
(Dot [x f64 y f64]) (Dot [x f64 y f64])
(Rect [w i32 h i32]) (Rect [w i32 h i32])
(Tag [name string n u8])]) (Tag [name str n u8])])
;; A union crosses a call boundary in both directions, as a parameter and as a ;; A union crosses a call boundary in both directions, as a parameter and as a
;; return type -- a value that cannot do that is not a value. ;; return type -- a value that cannot do that is not a value.
@ -29,7 +29,7 @@
;; alignment visible to something other than a slot. ;; alignment visible to something other than a slot.
(defstruct Cell [id i32 s Shape]) (defstruct Cell [id i32 s Shape])
(defn describe [s Shape] string (defn describe [s Shape] str
(match s (match s
Empty "empty" Empty "empty"
(Dot x y) (if (= x y) "dot on the diagonal" "dot") (Dot x y) (if (= x y) "dot on the diagonal" "dot")
@ -100,7 +100,7 @@
;; and not unions, so (vec-new Shape) was refused for not saying what it ;; and not unions, so (vec-new Shape) was refused for not saying what it
;; held -- by a program that had said. ;; held -- by a program that had said.
(let [vs (vec-new Shape) (let [vs (vec-new Shape)
ms (map-new string Shape)] ms (map-new str Shape)]
(push vs (Shape.Rect {.w 2 .h 3})) (push vs (Shape.Rect {.w 2 .h 3}))
(push vs Shape.Empty) (push vs Shape.Empty)
(put ms "only" (Shape.Tag {.name "in a map" .n 1})) (put ms "only" (Shape.Tag {.name "in a map" .n 1}))

View File

@ -7,7 +7,7 @@
;;;; members here, which is what makes the pair a test rather than an ;;;; members here, which is what makes the pair a test rather than an
;;;; observation. ;;;; observation.
(defstruct Cell [name string id i32 alive bool heat f64]) (defstruct Cell [name str id i32 alive bool heat f64])
(defn tick [c (Ptr Cell) n i32] i32 (defn tick [c (Ptr Cell) n i32] i32
(let [bump (+ n 1)] (let [bump (+ n 1)]

View File

@ -12,7 +12,7 @@
;;;; wrong, because it is the only one whose own alignment exceeds its first ;;;; wrong, because it is the only one whose own alignment exceeds its first
;;;; member's size. ;;;; member's size.
(defstruct Cell [alive bool heat f64 id i32 name string]) (defstruct Cell [alive bool heat f64 id i32 name str])
(defn tick [c (Ptr Cell) n i32] i32 (defn tick [c (Ptr Cell) n i32] i32
(let [bump (+ n 1)] (let [bump (+ n 1)]

View File

@ -21,7 +21,7 @@
;;;; Argument 3 makes and destroys arenas in a loop, as many times as the ;;;; Argument 3 makes and destroys arenas in a loop, as many times as the
;;;; second argument says. A retired allocator is reused rather than kept, so ;;;; second argument says. A retired allocator is reused rather than kept, so
;;;; the loop's memory stays flat however long it runs. ;;;; the loop's memory stays flat however long it runs.
(defn main [args [string]] i32 (defn main [args [str]] i32
(let [which (if (> (length 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)
a (arena-new 4096)] a (arena-new 4096)]
(cond (cond

View File

@ -22,7 +22,7 @@
(set calls (+ calls 1)) (set calls (+ calls 1))
(Point {.x 3 .y 4})) (Point {.x 3 .y 4}))
(defn show2 [label string a i32 b i32] () (defn show2 [label str a i32 b i32] ()
(print label) (print label)
(print " ") (print " ")
(print a) (print a)

View File

@ -16,7 +16,7 @@
(defstruct Boom [why i32]) (defstruct Boom [why i32])
(defonce label string) (defonce label str)
(defonce grid [4 i32]) (defonce grid [4 i32])
(defonce pressure i64) (defonce pressure i64)
(defonce untouched i64 99) (defonce untouched i64 99)

View File

@ -11,7 +11,7 @@
(defstruct Point [x f32 y f32]) (defstruct Point [x f32 y f32])
(defstruct Boom [why i32]) (defstruct Boom [why i32])
(defn look [n i64 label string] i64 (defn look [n i64 label str] i64
(let [p (Point {.x 1.5 .y 2.5}) (let [p (Point {.x 1.5 .y 2.5})
xs [10 20 30] xs [10 20 30]
flag (> n 0) flag (> n 0)

View File

@ -18,7 +18,7 @@
(defonce ticks i64) (defonce ticks i64)
(defn parity [n i64] string (defn parity [n i64] str
(let [out "even"] (let [out "even"]
(unless (= 0 (% n 2)) (unless (= 0 (% n 2))
(set out "odd")) (set out "odd"))

View File

@ -34,7 +34,7 @@
(defonce odd f64) (defonce odd f64)
(defonce yes bool) (defonce yes bool)
(defonce byte u8) (defonce byte u8)
(defonce text string) (defonce text str)
(defonce colour Colour) (defonce colour Colour)
(defonce stray Colour) (defonce stray Colour)
(defonce some (Option Point)) (defonce some (Option Point))

View File

@ -20,7 +20,7 @@
(defn hold [] i32 (defn hold [] i32
(set tv context/temp) (set tv context/temp)
(let [r (fetch)] (let [r (fetch)]
(println (string (i64->bytes 31))) (println (str (i64->bytes 31)))
r)) r))
(defn main [] i32 (defn main [] i32

View File

@ -20,7 +20,7 @@
(defn hold [] i32 (defn hold [] i32
(set keep (vec-new u8 context/temp)) (set keep (vec-new u8 context/temp))
(push keep (u8 5)) (push keep (u8 5))
(let [s (string (i64->bytes 4242)) (let [s (str (i64->bytes 4242))
r (fetch)] r (fetch)]
(println s) (println s)
(println (length keep)) (println (length keep))

View File

@ -10,13 +10,13 @@
;;;; at frame rate and the editor reads memory. ;;;; at frame rate and the editor reads memory.
(import agent "vendor:agent") (import agent "vendor:agent")
(declare-c watch-i64 [name string x i64] i32 "flan_dev_watch_i64") (declare-c watch-i64 [name str x i64] i32 "flan_dev_watch_i64")
(declare-c watch-f64 [name string x f64] i32 "flan_dev_watch_f64") (declare-c watch-f64 [name str x f64] i32 "flan_dev_watch_f64")
(declare-c watch-str [name string s string] i32 "flan_dev_watch_str") (declare-c watch-str [name str s str] i32 "flan_dev_watch_str")
;; The accumulator, for a value sampled from inside a hot loop. A scalar watch ;; The accumulator, for a value sampled from inside a hot loop. A scalar watch
;; there shows whichever iteration happened to run last, which is the case this ;; there shows whichever iteration happened to run last, which is the case this
;; exists for; see flan_dev.c, "A number sampled thousands of times a frame". ;; exists for; see flan_dev.c, "A number sampled thousands of times a frame".
(declare-c watch-num-i64 [name string x i64] i32 "flan_dev_watch_num_i64") (declare-c watch-num-i64 [name str x i64] i32 "flan_dev_watch_num_i64")
(defonce ticks i64) (defonce ticks i64)

View File

@ -10,7 +10,7 @@
;;;; exists and (get s :pause) is a place, where a plain map's absent key is ;;;; exists and (get s :pause) is a place, where a plain map's absent key is
;;;; not. dyn-slot-trap.flan has the refusals. ;;;; not. dyn-slot-trap.flan has the refusals.
(defclass state [pause bool step i32 speed f64 name string tag]) (defclass state [pause bool step i32 speed f64 name str tag])
;; A class is a slot type, and (Option T) admits nil beside a T. ;; A class is a slot type, and (Option T) admits nil beside a T.
(defclass node [owner state next (Option node) weight (Option f32)]) (defclass node [owner state next (Option node) weight (Option f32)])

View File

@ -45,7 +45,7 @@
(defn pick [x dyn n i64] dyn x) (defn pick [x dyn n i64] dyn x)
(defn pick-t [t T n i64] dyn (.d t)) (defn pick-t [t T n i64] dyn (.d t))
(defn show [label string v dyn] () (defn show [label str v dyn] ()
(churn) (churn)
(println label (at v 0) (at v 1))) (println label (at v 0) (at v 1)))

View File

@ -10,7 +10,7 @@
(defonce tv (Vec i64)) (defonce tv (Vec i64))
(defn main [args [string]] i32 (defn main [args [str]] i32
(let [which (if (> (length 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)
v (vec-new dyn) v (vec-new dyn)
ar (arena-new 4096)] ar (arena-new 4096)]

View File

@ -3,7 +3,7 @@
;; ;;
;; With an argument, the last slice runs past the end and traps. ;; With an argument, the last slice runs past the end and traps.
(defn main [args [string]] i32 (defn main [args [str]] i32
(let [d (the dyn "hello")] (let [d (the dyn "hello")]
(println (slice d)) (println (slice d))
(println (slice d 1)) (println (slice d 1))

View File

@ -6,7 +6,7 @@
(defn as-dyn [d dyn] dyn d) (defn as-dyn [d dyn] dyn d)
(defn main [args [string]] i32 (defn main [args [str]] i32
(let [which (if (> (length 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)
s (state false 3 nil)] s (state false 3 nil)]
(println "before") (println "before")

View File

@ -14,7 +14,7 @@
(let [n (the i32 7) (let [n (the i32 7)
f (the f32 1.5) f (the f32 1.5)
b (the bool true) b (the bool true)
s (the string "typed")] s (the str "typed")]
(println (type-of nil)) (println (type-of nil))
(println (type-of true)) (println (type-of true))
(println (type-of 42)) (println (type-of 42))

View File

@ -54,7 +54,7 @@
;; carries that pair, because a refusal cannot run. ;; carries that pair, because a refusal cannot run.
(defonce rows [2 (Vec i64)]) (defonce rows [2 (Vec i64)])
(defn main [args [string]] i32 (defn main [args [str]] i32
(let [n (i32 (bytes->i64 (bytes-view (at args 1))))] (let [n (i32 (bytes->i64 (bytes-view (at args 1))))]
(cond (cond
(= n 0) (= n 0)

View File

@ -66,7 +66,7 @@
;; The struct says :name :hp :speed :boss? :drops :hitbox. These bytes have no ;; The struct says :name :hp :speed :boss? :drops :hitbox. These bytes have no
;; :speed and have a :level the struct has never heard of, which is what a ;; :speed and have a :level the struct has never heard of, which is what a
;; tuning file looks like a month after the program was built. ;; tuning file looks like a month after the program was built.
(defconst drifted string (defconst drifted str
"{:name \"imp\" :hp 3 :level 7 :boss? true :drops [1] :hitbox {:w 1 :h 1 :offset {:x 0 :y 0}}}") "{:name \"imp\" :hp 3 :level 7 :boss? true :drops [1] :hitbox {:w 1 :h 1 :offset {:x 0 :y 0}}}")
(defn show-drift [a Allocator] () (defn show-drift [a Allocator] ()
@ -93,7 +93,7 @@
;; that — read-file answers an Option so that a malformed document is ;; that — read-file answers an Option so that a malformed document is
;; distinguishable from one that is literally nil — and a derived reader has to ;; distinguishable from one that is literally nil — and a derived reader has to
;; be at least as honest. ;; be at least as honest.
(defconst broken string "{:name \"orc\" :hp }") (defconst broken str "{:name \"orc\" :hp }")
(defn show-broken [a Allocator] () (defn show-broken [a Allocator] ()
(handler-bind (handler-bind

View File

@ -54,12 +54,12 @@
;; The size of a set after the dedup, which is the whole of what the dedup can ;; The size of a set after the dedup, which is the whole of what the dedup can
;; be asked for. ;; be asked for.
(defn set-size [src string] () (defn set-size [src str] ()
(println (length (edn/read (bytes-view src))))) (println (length (edn/read (bytes-view src)))))
;; Malformed input, told apart from the document `nil` by the cursor — the ;; 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. ;; return value alone cannot say it, and this is the spelling that can.
(defn malformed? [src string] bool (defn malformed? [src str] bool
(let [c (edn/cursor (bytes-view src)) (let [c (edn/cursor (bytes-view src))
t (edn/next (addr c)) t (edn/next (addr c))
v (edn/read-value (addr c) t)] ; the value is not the question here v (edn/read-value (addr c) t)] ; the value is not the question here

View File

@ -18,7 +18,7 @@
;; wrong — off by the quote, off by the colon — would pass on the letters ;; wrong — off by the quote, off by the colon — would pass on the letters
;; alone. ;; alone.
(defn kind-letter [k i32] string (defn kind-letter [k i32] str
(cond (cond
(= k edn/tok-eof) "." (= k edn/tok-eof) "."
(= k edn/tok-error) "!" (= k edn/tok-error) "!"
@ -40,7 +40,7 @@
(= k edn/tok-set-open) "#" (= k edn/tok-set-open) "#"
:else "?")) :else "?"))
(defn dump [src string] () (defn dump [src str] ()
(let [b (bytes-view src) (let [b (bytes-view src)
c (edn/cursor b) c (edn/cursor b)
t (edn/next (addr c))] t (edn/next (addr c))]
@ -58,7 +58,7 @@
;; The refusals. Asserted on the *reason*, not on the fact of failing: a ;; The refusals. Asserted on the *reason*, not on the fact of failing: a
;; tokenizer that answered err-unexpected-byte for every one of these would ;; tokenizer that answered err-unexpected-byte for every one of these would
;; pass a test that only checked that it failed. ;; pass a test that only checked that it failed.
(defn refusal [src string] () (defn refusal [src str] ()
(let [b (bytes-view src) (let [b (bytes-view src)
c (edn/cursor b)] c (edn/cursor b)]
(while (and (edn/ok? (addr c)) (while (and (edn/ok? (addr c))
@ -121,7 +121,7 @@
(return e))))) (return e)))))
e)) e))
(defn show-enemy [src string] () (defn show-enemy [src str] ()
(let [b (bytes-view src) (let [b (bytes-view src)
c (edn/cursor b) c (edn/cursor b)
e (read-enemy (addr c))] e (read-enemy (addr c))]

View File

@ -20,13 +20,13 @@
;; The default answer is a [u8]: bytes, because that is what an asset is. ;; The default answer is a [u8]: bytes, because that is what an asset is.
(let [a (embed "assets/a.txt")] (let [a (embed "assets/a.txt")]
(println (length a)) ; 13 (println (length a)) ; 13
(print (string a))) ; hello from a (print (str a))) ; hello from a
;; `string` is the second spelling, not a different meaning for the same ;; `string` is the second spelling, not a different meaning for the same
;; text. With structural equality and nothing that converts one container ;; text. With structural equality and nothing that converts one container
;; into another -- implicit widening is numbers only -- one form that changes ;; into another -- implicit widening is numbers only -- one form that changes
;; type with its context would be a wart. ;; type with its context would be a wart.
(println (embed "assets/b.bin" string)) ; BBB (println (embed "assets/b.bin" str)) ; BBB
;; Byte-exact, including bytes no text encoding would survive: emit.ml's ;; Byte-exact, including bytes no text encoding would survive: emit.ml's
;; escape hex-escapes everything outside printable ASCII, so a PNG makes the ;; escape hex-escapes everything outside printable ASCII, so a PNG makes the
@ -50,7 +50,7 @@
;; there are no generics. ;; there are no generics.
(let [all (slice assets 0 (length assets))] (let [all (slice assets 0 (length assets))]
(match (embed-find all "b.bin") (match (embed-find all "b.bin")
(Some b) (println (string b)) ; BBB (Some b) (println (str b)) ; BBB
None (println "missing")) None (println "missing"))
(match (embed-find all "nope.txt") (match (embed-find all "nope.txt")
(Some _) (println "found") (Some _) (println "found")

View File

@ -18,7 +18,7 @@
;; An enum parameter driven by a loop variable, which is the shape this exists ;; An enum parameter driven by a loop variable, which is the shape this exists
;; for: the caller has an index, not a member. ;; for: the caller has an index, not a member.
(defn name-at [i i32] string (defn name-at [i i32] str
(let [k (K i)] (let [k (K i)]
(cond (= k :lo) "lo" (cond (= k :lo) "lo"
(= k :mid) "mid" (= k :mid) "mid"

View File

@ -10,13 +10,13 @@
(defn double [x i32] i32 (* x 2)) (defn double [x i32] i32 (* x 2))
(defn negate [x i32] i32 (- 0 x)) (defn negate [x i32] i32 (- 0 x))
(defstruct Ops [name string run (CFn [i32] i32)]) (defstruct Ops [name str run (CFn [i32] i32)])
(defonce table [3 (CFn [i32] i32)]) (defonce table [3 (CFn [i32] i32)])
(defonce hook (CFn [i32] i32)) (defonce hook (CFn [i32] i32))
(defonce evaluated i32 0) (defonce evaluated i32 0)
(defonce caught i32 0) (defonce caught i32 0)
(defonce seen string "") (defonce seen str "")
(defn arg [x i32] i32 (defn arg [x i32] i32
(set evaluated (+ evaluated 1)) (set evaluated (+ evaluated 1))
@ -27,7 +27,7 @@
(do (print (f (arg x))) (println "")) (do (print (f (arg x))) (println ""))
(continue [] (println "empty")))) (continue [] (println "empty"))))
(defn main [args [string]] i32 (defn main [args [str]] i32
(set (at table 0) double) (set (at table 0) double)
(set (at table 2) negate) (set (at table 2) negate)
(let [ops (Ops {.name "half-built"})] (let [ops (Ops {.name "half-built"})]

View File

@ -51,7 +51,7 @@
(push msg who) (push msg who)
(fn [] (i64 (length msg))))) (fn [] (i64 (length msg)))))
(defstruct Button [label string on-click (Option (Fn [] i64))]) (defstruct Button [label str on-click (Option (Fn [] i64))])
(defonce handler (Option (Fn [i64] i64))) (defonce handler (Option (Fn [i64] i64)))

View File

@ -17,7 +17,7 @@
(push junk true)))) (push junk true))))
(defn main [] i32 (defn main [] i32
(let [ops (map-new string (Fn [i32] i32)) (let [ops (map-new str (Fn [i32] i32))
many (map-new i32 (Fn [i32] i32)) many (map-new i32 (Fn [i32] i32))
rows (map-new i32 dyn)] rows (map-new i32 dyn)]
(put ops "one" (adder 1)) (put ops "one" (adder 1))

View File

@ -11,7 +11,7 @@
(defn show [x f64 p i32] () (defn show [x f64 p i32] ()
(let [v (format-f64 x p)] (let [v (format-f64 x p)]
(println (string (slice v))) (println (str (slice v)))
(free v))) (free v)))
(defn main [] i32 (defn main [] i32
@ -95,6 +95,6 @@
(let [f (format-f64 0.0166667 4)] (let [f (format-f64 0.0166667 4)]
(append (addr b) (slice f)) (append (addr b) (slice f))
(free f)) (free f))
(println (string (slice b))) ; fps 59.9 / frame 0.0167 (println (str (slice b))) ; fps 59.9 / frame 0.0167
(free b)) (free b))
0) 0)

View File

@ -109,7 +109,7 @@
(when (!= (.brush world) (.brush world-backup)) (set d (+ d 1))) (when (!= (.brush world) (.brush world-backup)) (set d (+ d 1)))
d)) d))
(defn show [name string n i64] () (defn show [name str n i64] ()
(print name) (print " ") (print n) (println "")) (print name) (print " ") (print n) (println ""))
(defn dump [] () (defn dump [] ()

View File

@ -6,7 +6,7 @@
;;;; 3 frees a view of an array, which no allocator handed out; 4 frees a ;;;; 3 frees a view of an array, which no allocator handed out; 4 frees a
;;;; Vec's storage through a let-bound view of it; 5 frees a formatted ;;;; Vec's storage through a let-bound view of it; 5 frees a formatted
;;;; number's text, which the temp allocator holds. ;;;; number's text, which the temp allocator holds.
(defn main [args [string]] i32 (defn main [args [str]] i32
(let [which (if (> (length args) 1) (bytes->i64 (bytes-view (at args 1))) 0) (let [which (if (> (length args) 1) (bytes->i64 (bytes-view (at args 1))) 0)
a (arena-new 4096)] a (arena-new 4096)]
(cond (cond
@ -26,7 +26,7 @@
(let [b (bytes "heap") (let [b (bytes "heap")
c (bytes "arena" a) c (bytes "arena" a)
d (clone (slice [1.5 2.5]) (heap-allocator))] d (clone (slice [1.5 2.5]) (heap-allocator))]
(println (string b) (string c) (at d 1)) (println (str b) (str c) (at d 1))
(free b) (free b)
(free c a) (free c a)
(free d (heap-allocator)))) (free d (heap-allocator))))

View File

@ -6,7 +6,7 @@
;;;; position that takes i32 takes int, the cast head included, and nothing ;;;; position that takes i32 takes int, the cast head included, and nothing
;;;; downstream of the checker ever hears the word. ;;;; downstream of the checker ever hears the word.
;;;; ;;;;
;;;; The rest of the foreign spellings -- long, double, integer, str -- are ;;;; The rest of the foreign spellings -- long, double, integer, string -- are
;;;; still refusals that teach the Flan name, which a program cannot show ;;;; still refusals that teach the Flan name, which a program cannot show
;;;; because it would not compile; that half is in test_flan.ml. ;;;; because it would not compile; that half is in test_flan.ml.
@ -41,7 +41,7 @@
;; Generic type arguments: the element type of a Vec and both halves of a ;; Generic type arguments: the element type of a Vec and both halves of a
;; Map, named with the alias. ;; Map, named with the alias.
(let [v (vec-new int) (let [v (vec-new int)
m (map-new string int)] m (map-new str int)]
(push v 10) (push v 10)
(push v 20) (push v 20)
(put m "k" 30) (put m "k" 30)

View File

@ -44,7 +44,7 @@
;; its backslash still on it; that is the divergence between the tokenizer and ;; its backslash still on it; that is the divergence between the tokenizer and
;; string-of, and this is where it is visible. ;; string-of, and this is where it is visible.
(defn kind-letter [k i32] string (defn kind-letter [k i32] str
(cond (cond
(= k json/tok-eof) "." (= k json/tok-eof) "."
(= k json/tok-error) "!" (= k json/tok-error) "!"
@ -61,7 +61,7 @@
(= k json/tok-comma) "," (= k json/tok-comma) ","
:else "?")) :else "?"))
(defn dump [src string] () (defn dump [src str] ()
(let [b (bytes-view src) (let [b (bytes-view src)
c (json/cursor b) c (json/cursor b)
t (json/next (addr c))] t (json/next (addr c))]
@ -79,7 +79,7 @@
;; The refusals. Asserted on the *reason* and not on the fact of failing: a ;; The refusals. Asserted on the *reason* and not on the fact of failing: a
;; tokenizer answering one generic error for all of these would pass a test ;; tokenizer answering one generic error for all of these would pass a test
;; that only checked that it stopped. ;; that only checked that it stopped.
(defn refusal [src string] () (defn refusal [src str] ()
(let [b (bytes-view src) (let [b (bytes-view src)
c (json/cursor b)] c (json/cursor b)]
(while (and (json/ok? (addr c)) (while (and (json/ok? (addr c))
@ -96,9 +96,9 @@
(Bool [b bool]) (Bool [b bool])
(Int [n i64]) (Int [n i64])
(Float [x f64]) (Float [x f64])
(Text [s string]) (Text [s str])
(Array [items (Vec Value)]) (Array [items (Vec Value)])
(Object [entries (Map string Value)])]) (Object [entries (Map str Value)])])
;; One token in hand, and the cursor for whatever the token opens. An array and ;; One token in hand, and the cursor for whatever the token opens. An array and
;; an object recurse; everything else is a leaf. ;; an object recurse; everything else is a leaf.
@ -160,7 +160,7 @@
;; same string-of the values use, so the map owns its keys and the source ;; same string-of the values use, so the map owns its keys and the source
;; buffer is not in the picture. ;; buffer is not in the picture.
(= (.kind t) json/tok-object-open) (= (.kind t) json/tok-object-open)
(let [entries (map-new string Value) (let [entries (map-new str Value)
k (json/next c) k (json/next c)
more (and (json/ok? c) (!= (.kind k) json/tok-object-close))] more (and (json/ok? c) (!= (.kind k) json/tok-object-close))]
(while more (while more
@ -234,14 +234,14 @@
(match (get entries "xs") (Some x) (sum-ints x) None (i64 0)) (match (get entries "xs") (Some x) (sum-ints x) None (i64 0))
_ (i64 0))) _ (i64 0)))
(defn describe [v Value] string (defn describe [v Value] str
(match v (match v
Null "null" (Bool _b) "bool" (Int _n) "int" (Float _x) "float" Null "null" (Bool _b) "bool" (Int _n) "int" (Float _x) "float"
(Text _s) "string" (Array _i) "array" (Object _e) "object")) (Text _s) "string" (Array _i) "array" (Object _e) "object"))
;; The text at a top-level key, or a marker. Used after the source buffer has ;; The text at a top-level key, or a marker. Used after the source buffer has
;; been scribbled over, which is the whole reason it exists. ;; been scribbled over, which is the whole reason it exists.
(defn text-at [v Value key string] string (defn text-at [v Value key str] str
(match v (match v
(Object entries) (Object entries)
(match (get entries key) (match (get entries key)
@ -257,7 +257,7 @@
;; A reader's own refusals, driven end to end: read the whole document and then ;; A reader's own refusals, driven end to end: read the whole document and then
;; report what the cursor says. The position matters as much as the message — ;; report what the cursor says. The position matters as much as the message —
;; a trailing comma reported at the opening brace would be useless. ;; a trailing comma reported at the opening brace would be useless.
(defn reject [src string] () (defn reject [src str] ()
(let [b (bytes-view src) (let [b (bytes-view src)
c (json/cursor b) c (json/cursor b)
t (json/next (addr c))] t (json/next (addr c))]

View File

@ -55,7 +55,7 @@
(defn inf-f32? [x f32] bool (defn inf-f32? [x f32] bool
(and (= x (* x (f32 2.0))) (!= x (f32 0.0)))) (and (= x (* x (f32 2.0))) (!= x (f32 0.0))))
(defn say [name string ok bool] () (defn say [name str ok bool] ()
(print name) (print name)
(print " ") (print " ")
(println (if ok "ok" "WRONG"))) (println (if ok "ok" "WRONG")))

View File

@ -10,7 +10,7 @@
(defmacro thru [& body] (defmacro thru [& body]
`(do ~@body)) `(do ~@body))
(defn g [s string] i32 0) (defn g [s str] i32 0)
(defn main [] i32 (defn main [] i32
(thru (thru

View File

@ -9,7 +9,7 @@
;;;; the move, which is the point: the test for the feature is the corpus that ;;;; the move, which is the point: the test for the feature is the corpus that
;;;; was written against the special form. ;;;; was written against the special form.
(defn classify [n i32] string (defn classify [n i32] str
(let [out "even"] (let [out "even"]
(unless (= 0 (% n 2)) (unless (= 0 (% n 2))
(set out "odd")) (set out "odd"))

View File

@ -2,13 +2,13 @@
;; Each element is hashed and compared by its own pair, so two keys built from ;; Each element is hashed and compared by its own pair, so two keys built from
;; different storage with the same bytes are the same key. ;; different storage with the same bytes are the same key.
(defstruct Tag [name string n i32]) (defstruct Tag [name str n i32])
(defn main [] i32 (defn main [] i32
(let [m (map-new [2 string] i32) (let [m (map-new [2 str] i32)
a (the [2 string] ["ab" "cd"]) a (the [2 str] ["ab" "cd"])
b (the [2 string] [(slice "xab" 1) (slice "cdx" 0 2)]) b (the [2 str] [(slice "xab" 1) (slice "cdx" 0 2)])
c (the [2 string] ["ab" "ce"])] c (the [2 str] ["ab" "ce"])]
(put m a 1) (put m a 1)
(put m c 3) (put m c 3)
(println (or-else (get m b) -1)) (println (or-else (get m b) -1))

View File

@ -64,7 +64,7 @@
(set failures (+ failures 1)) (set failures (+ failures 1))
(set-alloc-budget tight (* 8 (alloc-budget tight))) (set-alloc-budget tight (* 8 (alloc-budget tight)))
(invoke-restart 'retry))] (invoke-restart 'retry))]
(let [m (map-new string i32 tight)] (let [m (map-new str i32 tight)]
(reserve m 200) (reserve m 200)
(println (length m)) ; 0 (println (length m)) ; 0
(put m "a" 1) (put m "a" 1)

View File

@ -65,7 +65,7 @@
(defstruct Point [x i32 y i32]) (defstruct Point [x i32 y i32])
(defn wider-entries [] () (defn wider-entries [] ()
(let [m (map-new string Point)] (let [m (map-new str Point)]
(put m "a" (Point {.x 1 .y 2})) (put m "a" (Point {.x 1 .y 2}))
(put m "bb" (Point {.x 3 .y 4})) (put m "bb" (Point {.x 3 .y 4}))
(put m "ccc" (Point {.x 5 .y 6})) (put m "ccc" (Point {.x 5 .y 6}))

View File

@ -28,8 +28,8 @@
(free vs)) (free vs))
(free m)) (free m))
;; A string key, and a map that never allocated. ;; A string key, and a map that never allocated.
(let [names (map-new string i32) (let [names (map-new str i32)
none (map-new string i32)] none (map-new str i32)]
(put names "b" 2) (put names "b" 2)
(put names "a" 1) (put names "a" 1)
(let [ks (map-keys names) (let [ks (map-keys names)

View File

@ -88,7 +88,7 @@
(print (length g)) (println "") ; 399 (print (length g)) (println "") ; 399
(free g)) (free g))
(let [s (map-new string i32)] (let [s (map-new str i32)]
(put s "alpha" 1) (put s "alpha" 1)
(put s "beta" 2) (put s "beta" 2)
(match (map-remove s "alpha") (match (map-remove s "alpha")

View File

@ -5,7 +5,7 @@
;;;; version gets wrong, and the numbers differ per failure so a single wrong ;;;; version gets wrong, and the numbers differ per failure so a single wrong
;;;; answer names its own cause. ;;;; answer names its own cause.
(defstruct Cell [x i32 y i32]) (defstruct Cell [x i32 y i32])
(defstruct Named [tag string n i32]) (defstruct Named [tag str n i32])
(defenum Suit [hearts 0 spades 1 clubs 2]) (defenum Suit [hearts 0 spades 1 clubs 2])
;;;; (8) A map crosses a function boundary in both directions. Returning one ;;;; (8) A map crosses a function boundary in both directions. Returning one
@ -103,7 +103,7 @@
;; (6) Upsert replaces and does not grow the length, and reserve means room ;; (6) Upsert replaces and does not grow the length, and reserve means room
;; for n *entries* — n still under the load factor — not n slots. ;; for n *entries* — n still under the load factor — not n slots.
(let [u (map-new string i32)] (let [u (map-new str i32)]
(reserve u 100) (reserve u 100)
(put u "k" 1) (put u "k" 1)
(put u "k" 2) (put u "k" 2)

Some files were not shown because too many files have changed in this diff Show More