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
(declare-c set-seed [seed u32] "set_seed")
(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
;; 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
** NEXT str and String
Decided 2026-09-25: the typed read-only text is =str= (renamed from =string=); =String=
is new, owned, growable, always valid UTF-8, through an allocator. The prelude's text
Decided 2026-09-25: the typed read-only text is =str= (the rename from =string= is
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
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
@ -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.
** 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
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.

View File

@ -118,7 +118,7 @@
;; 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
;; 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)
(do (println "usage: calc-me \"1 + 2 * 3\"") 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 `declare-c` may return `string`. 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
A `declare-c` may return `str`. The C wrapper answers the pointer and its length, and the generated Flan wrapper is
`(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
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
@ -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
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
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
@ -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:
`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-`.
### rlgl is its own package
@ -1167,7 +1167,7 @@ i8..i64 / u8..u64 i8..i64 signedness lives in the ops
f32 f64 float double
bool i1
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
(Ptr T) ptr opaque pointers
(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.
**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
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,
@ -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
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.
- 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
@ -3564,7 +3564,7 @@ the payload at offset 8, and 40/8 for a struct holding one.
[Empty
(Dot [x f64 y f64])
(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}) ; 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
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
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
*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.
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
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
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
`(string (slice v))` is the round trip, free precisely because the layouts are identical.
have moved up into the code; `string-from-bytes` turned out to be the `str` builtin all along, and
`(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*
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
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
(`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
@ -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
`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
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
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
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
`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.
**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:
```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)
```
@ -7079,7 +7079,7 @@ expression the checker walks can live.
### 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
`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
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
@ -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.
**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.*
**`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
one parameter on three functions.
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
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
> `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;
> `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
> `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
@ -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
*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",
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 "いろはに"
b (bytes text)
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))
```
@ -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
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
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:
```
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
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
@ -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
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
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
`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)))`
is the C's `char *ptr` and compiles to nothing: a `string` and a `[u8]` are the same two
**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 `str` and a `[u8]` are the same two
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
@ -852,7 +852,7 @@ example.
> 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
`[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
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.
**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
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
@ -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
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
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
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
@ -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:
```flan
(declare-c watch-num-i64 [name string 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-i64 [name str x i64] i32 "flan_dev_watch_num_i64")
(declare-c watch-num-f64 [name str x f64] i32 "flan_dev_watch_num_f64")
(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:
```flan
(declare-c watch-i64 [name string x i64] i32 "flan_dev_watch_i64")
(declare-c watch-f64 [name string x f64] i32 "flan_dev_watch_f64")
(declare-c watch-str [name string s string] i32 "flan_dev_watch_str")
(declare-c watch-i64 [name str x i64] i32 "flan_dev_watch_i64")
(declare-c watch-f64 [name str x f64] i32 "flan_dev_watch_f64")
(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

View File

@ -717,7 +717,7 @@ visits the source of the one it lands on."
(defun flan-cnr-param-types (params)
"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."
(let ((form (and (stringp 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)
;; The package half of a qualified name, as `flan-mode' draws it.
("\\_<\\([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)
("\\_<\\$[^][ \t\n(){},;\":]*" . font-lock-type-face)
;; A character literal, `\c' or `\space'.

View File

@ -175,15 +175,15 @@ face says.")
;; containers and memory
"length" "at" "slice" "slice-from" "addr" "deref"
;; options, bytes, the host
"Some" "bytes" "bytes-view" "string"
"Some" "bytes" "bytes-view" "str"
"bytes->f64" "bytes->i64" "f64->bytes" "i64->bytes"
"write-stdout" "print" "println" "exit" "argv")
"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
builtins and not as keywords. `string' is in this list and in the type rule
below and means a different thing in each: `(string b)' converts and a bare
`string' names a type, which the rules tell apart by the paren.
builtins and not as keywords. `str' is in this list and in the type rule
below and means a different thing in each: `(str b)' converts and a bare
`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
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
;; advertise a spelling the parser rejects, which is the same reason
;; `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)
;; A type variable, `$t', which is what a generic `defn' names its
;; 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 "()")
(:report "Try again with another path"
:at "/src/files.flan:12:3" :arity 1
:params "(string)")
:params "(str)")
(:report "" :at nil :arity 0 :params "()")))))
(text (with-current-buffer buf (buffer-string))))
(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"
(string-match-p "again (/src/files.flan:12:3)" text))
(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))
(test-flan--check "one with no report and no source shows its name alone"
(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.
(test-flan--check "a restart's parameter types are read from their spelling"
(equal (flan-cnr-param-types "(i64 (Option string))")
'("i64" "(Option string)")))
(equal (flan-cnr-param-types "(i64 (Option str))")
'("i64" "(Option str)")))
(let ((sent nil) (asked nil))
(let ((flan-cnr-request-function
(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.
(let ((text (with-current-buffer
(test-flan--cnr (list :condition "Missing" :restarts '("retry")
:fields '(("path" "string" nil)
:fields '(("path" "str" nil)
("tried" "i32" "3"))
:fields-why "the stop's value was not readable"))
(buffer-string))))
(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"
(and (string-match-p "not read — the stop's value" text)
(= 1 (seq-count
@ -1696,7 +1696,7 @@ would be overwritten. Look again and re-do the edit")
(let ((text (with-current-buffer
(test-flan--cnr
(list :condition "Missing" :restarts '("retry" "a" "retry")
:fields '(("path" "string" nil))
:fields '(("path" "str" nil))
:stack (list (list :fn "f" :fetched t :locals nil))))
(buffer-string))))
(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"
:restarts ("retry")))
("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))
(buffer-string))))
(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)
"sim/Missing"))
(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
;; daemon answers only the first — so the values fall back to the
;; 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.
:globals '(("grid" "[4 i32]" "[7 5 0 0]" (0 1))
("pressure" "i64" "12" (0))
("label" "string" "\"running\"" (1)))))
("label" "str" "\"running\"" (1)))))
(buf (test-flan--cnr state))
(text (with-current-buffer buf (buffer-string))))
(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
(list :condition "Boom" :restarts '("retry")
: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-skipped
'(("0: sim/inner" "this frame's body was redefined since it was entered"))))

View File

@ -111,13 +111,13 @@
;; there.
(test-flan-mode--check
"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)
n)")
(test-flan-mode--check
"and a defn without one indents it the same"
"(defn show-trim [s string] ()
"(defn show-trim [s str] ()
(print s)
(println \"\"))")

View File

@ -67,7 +67,7 @@
(defconst gesture-log-size 20)
(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,
;; 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
@ -103,7 +103,7 @@
;; belongs under a :gesture-none guard. The C's switch over single members
;; hides both; a range test cannot.
(defn gesture-name [g rl/Gesture] string
(defn gesture-name [g rl/Gesture] str
(cond
(= g :gesture-none) "None"
(= g :gesture-tap) "Tap"

View File

@ -30,7 +30,7 @@
(defconst max-gesture-strings 20)
(defonce gesture-log [max-gesture-strings string])
(defonce gesture-log [max-gesture-strings str])
(defonce gestures-count i32)
(defonce current-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
;; gesture logs the empty string, which draws nothing, which is what falling
;; 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
(= g :gesture-tap) "GESTURE TAP"
(= g :gesture-double-tap) "GESTURE DOUBLETAP"

View File

@ -60,7 +60,7 @@
;; and a table of one-character strings; (string b) makes the
;; number a string with no instructions, so it is one draw-text and
;; 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
rl/black))))

View File

@ -188,7 +188,7 @@
;; Not in the C: which button the search picked, as a number, so the
;; headless case and the window agree about the same thing.
(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
rl/lightgray))

View File

@ -47,7 +47,7 @@
;; 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
;; 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)
(let [x (+ 10 (rl/measure-text label 10))]
(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
;; 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/measure-text s size))
@ -74,7 +74,7 @@
;; the old note about i32's most negative value — it is widened to i64 before
;; formatting, so there is no negation to wrap.
(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
;; wider than `width` is drawn in full rather than truncated, which is what

View File

@ -135,7 +135,7 @@
(let [b (bytes-view text)]
(if (>= off (length b))
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.
;;

View File

@ -122,7 +122,7 @@
(i32 (.width text-box)) (i32 (.height text-box))
(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)
40 rl/maroon)

View File

@ -82,7 +82,7 @@
i 0]
(while (< i length)
(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
;; stop; this walks on one byte at a time so the bad bytes get drawn.
(when (= cp (i32 \?)) (set size 1))

View File

@ -68,7 +68,7 @@
;; the C — see the header comment.
(let [b (bytes-view message)
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 "HOLD [SPACE] to SPEED UP!" 239 300 20 rl/lightgray))))

View File

@ -80,7 +80,7 @@
(defconst proc-flip-vertical 7)
(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.
(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,
strings as strings. [Shim] generates the C that flattens it and rewrites
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. *)
| DeclareC of fn * string

View File

@ -570,7 +570,7 @@ let foreign_spelling = function
| "byte" -> Some "u8"
| "double" -> Some "f64"
| "boolean" -> Some "bool"
| "str" -> Some "string"
| "string" -> Some "str"
| _ -> None
(* 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 ->
match n with
| "bool" -> Types.Bool
| "string" -> Types.String
| "str" -> Types.String
(* 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
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
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]
...)] has no good reading: taken as written it is a parameter called [i64],
which shadows nothing but confuses everything, and the likelier intent is a
pair written backwards. Refusing here costs a rename in the one program that
meant it and closes the one place where this rule could still hand somebody
a signature they did not write. *)
A name that *is* a type name pairs only when a type follows it: [[str str]]
has one reading. [(defn f [i64 x] ...)] has two — a dyn parameter called
[i64], or a pair written backwards — so it is refused rather than handed
back as a signature nobody wrote. *)
let is_type_name env n =
Types.ikind_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.structs 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
second parameter and needs no word about parameter vectors. *)
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
| Some m when fln ->
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
"a parameter's name was expected here, and this is a type. Parameters \
are [name Type ...]"
| Ast.Pname (n, loc) :: rest when is_type_name env n ->
ignore rest;
(* A parameter may share a type's name — [str] is a common name for text —
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
"%s names a type, so it cannot also be this parameter's name. Write \
[name %s], or rename the parameter" n n
"%s names a type, and no type follows this parameter called %s. Give \
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. *)
| Ast.Pname (n, _) :: Ast.Pname ("_", 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. *)
|| n <> tyvar_bare n
|| 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.datas n
|| Hashtbl.mem ctx.env.unions n
|| Hashtbl.mem ctx.env.enums 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
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
@ -10427,6 +10459,7 @@ and vec_new_elem ctx ~want loc args =
(match want with
| Some (Types.Vec t) -> t, args
| _ ->
shadowed_type_arg ctx loc "vec-new" args;
fail loc
"nothing here says what (vec-new) is a Vec of — write the element \
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 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 —
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. *)
and map_new_types ctx ~want loc args =
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
| a :: _ when type_of_expr ~generic:(Hashtbl.mem ctx.env.gstructs) a <> None ->
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)"
| { Ast.e = Ast.Var k; _ } :: rest when is_type k && rest = [] ->
fail loc
@ -10517,9 +10550,10 @@ and map_new_types ctx ~want loc args =
(match want with
| Some (Types.Map (k, v)) -> k, v, args
| _ ->
shadowed_type_arg ctx loc "map-new" args;
fail loc
"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. *)
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
after fixing it. *)
(match args with
| [ _; { Ast.e = Ast.Var "string"; _ } ] | [ _ ] -> ()
| [ _; { Ast.e = Ast.Var "str"; _ } ] | [ _ ] -> ()
| [ _; t ] ->
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 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
depends on it. *)
(match args with
| [ _; { Ast.e = Ast.Var "string"; _ } ] ->
| [ _; { Ast.e = Ast.Var "str"; _ } ] ->
expect ctx loc ~want (as_string ())
| _ ->
(match want with
@ -12096,7 +12130,7 @@ and named_call ?(qualified = false) ctx ~want loc name args =
| _ -> expect ctx loc ~want (as_bytes ())))
| _ ->
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 ───────────────────
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
@ -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)
| _ -> 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
unlike the numeric casts, which go through [is_cast] and really do
convert.
@ -12680,7 +12714,7 @@ and named_call ?(qualified = false) ctx ~want loc name args =
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
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
@ -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
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.
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
kept past the frame is cloned first. *)
| "string" ->
| "str" ->
arity ctx loc name 1 args;
prim Tast.StrOfBytes Types.String [ byte_slice ctx (List.hd args) ]
| "bytes->f64" ->
@ -13362,6 +13396,13 @@ and ordinary_call ctx ~want loc name args =
not (List.mem m [ "true"; "false"; "nil"; "None";
"context/allocator"; "context/temp" ])
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
| Some m when not (callable m) ->
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 \
class's name as class-of answers it. A typed value answers the kind it \
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 \
exists at run time — a reader building :texture-path out of a token's \
text. A literal :foo is already one.");
(* 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; \
(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 \
read-only data.");
("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.");
(* 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 \
signals FileError under retry and use-value, and a failure to allocate \
signals StorageExhausted.");
("barf", "barf [string [const u8]] ()",
("barf", "barf [str [const u8]] ()",
"Writes a whole file. On the web target it signals FileError every \
time, with the path — there is no conditional compilation, so the \
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 \
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 \
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 \
use-value names a different source for the same destination, which is \
the direction a handler can act on.");
(* 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, \
a string, a Vec and a Map.");
("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 \
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.");
("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 \
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 \
@ -14580,20 +14621,20 @@ let builtins : (string * string * string) list =
written as a name rather than as a call.");
(* 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 \
one named. It allocates like vec-new does — a failure signals \
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, \
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 \
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 \
copy.");
("string", "string [[const u8]] string",
"A byte slice seen as a string, and free at run time. It does not check \
UTF-8, because `string` does not claim UTF-8 — valid-utf8? is an \
("str", "str [[const u8]] str",
"A byte slice seen as a str, and free at run time. It does not check \
UTF-8, because `str` does not claim UTF-8 — valid-utf8? is an \
ordinary function you call when you care.");
("bytes->f64", "bytes->f64 [[const u8]] f64", "Parses a float out of the bytes.");
("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.");
("println", "println [T ...] ()",
"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 \
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 \
@ -14623,7 +14664,7 @@ let builtins : (string * string * string) list =
("exit", "exit [i32] never",
"Ends the process with this status. It has no value, so nothing written \
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
signature is the [name type] shape [Dev.defs] gives a global, because
@ -16507,7 +16548,7 @@ let check_main env decls =
in
if not ok_params then
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 " ")
(List.map (tyname at) params));
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 is_const = strip_prefix "const " inner <> None in
match bare inner with
| "char" when is_const -> tname "string"
| "char" when is_const -> tname "str"
| "char" ->
refuse
"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
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
([LoadFileText] and [UnloadFileText]); copying it would leak the original
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 is_const = strip_prefix "const " inner <> None in
match bare inner with
| "char" when is_const -> Some (tname "string")
| "char" when is_const -> Some (tname "str")
| "char" ->
refuse
"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
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
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
@ -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
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
[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 *]
and none of them is a scalar. *)
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
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
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.
[(Ptr u8)] over a [char *], and [(Ptr] anything[)] over a [void *], now
agree; [(Ptr A)] over a [B *] does not.
@ -1642,15 +1642,15 @@ let diff_bound ~env ~(bound : (Ast.fn * string) list) (d : dump) =
| None ->
(match (try Ok (ret_ty env c.cret) with Refused w -> Error w) with
(* 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
leave the library's buffer with no owner. *)
| Error _ ->
(match fn.Ast.ret with
| Some { Ast.t = Ast.Tname "string"; _ } ->
| Some { Ast.t = Ast.Tname "str"; _ } ->
say
(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 — \
declare it (Ptr u8)"
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
capacity, so two members are the whole truth about a slice. *)
| Types.String ->
composite "string"
composite "str"
[ ("ptr", Types.Ptr (Types.Mut, (Types.Int Types.U8))); ("len", Types.Int Types.I64) ]
| Types.Slice (_, e) ->
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). *)
| Tast.Bytes, [ x ] -> value f x
(* (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.BytesToF64, [ x ] -> shim_in f "@flan_bytes_to_f64" "double" 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.
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].
- [[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
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
@ -47,11 +47,11 @@
- [(Fn [..] R)] — a JS function. Function {e values} come along; they carry
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
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
[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
throughout ([rune-count], [valid-utf8?], [decode-rune] all take [[u8]]).
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. *)
| [ n; { v = Kw "parent"; _ }; p ] | [ n; { v = Kw "parent"; _ }; p; { v = Vec []; _ } ] ->
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 }
in
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
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
(declare f [p string] ...) already means the symbol takes ptr+len and
(declare-c f [p string] ...) means it takes a NUL-terminated char *. *)
(declare f [p str] ...) already means the symbol takes ptr+len and
(declare-c f [p str] ...) means it takes a NUL-terminated char *. *)
let mkd fn csym =
if String.equal which "declare-c" then Ast.DeclareC (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.
;;
;; (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
;; 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
;; established at the call, BoundsError's decision for BoundsError's reason:
;; 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
;; 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
;; **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.
(defstruct NullCall :parent Error [type string])
(defstruct NullCall :parent Error [type str])
;; What a generic function signals when no method answers. `generic` is 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
;; 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
;; 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
;; string, so the test below is arithmetic and the pointer is never dereferenced
;; 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)
p (getenv-raw name (addr n))]
(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
;; 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
;; 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.
;;
;; 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
;; 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.
(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
;; 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
;; length is part of its type and there are no generics: write
;; (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)]
(when (bytes=? (bytes-view (.name (at files i))) (bytes-view name))
(return (Some (.data (at files i))))))
@ -2021,7 +2021,7 @@ let source = {flan|
;; parent link, not class inheritance", decides on is the
;; answer to that, and it is not built; when it is, these reasons can become
;; 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-write i32 1)
@ -2051,7 +2051,7 @@ let source = {flan|
;; 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
;; 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 —
;; 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,
;; deciding whether to write a default — and not as a guard in front of an
;; operation that already reports its own failure properly.
(defn file-exists? [path string] bool
(defn file-exists? [path str] bool
(let [n (i64 0)]
(= (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
;; restart on it. A caller that needs to tell "missing" from "denied" wants
;; `slurp`, whose FileError carries the reason.
(defn file-size [path string] (Option i64)
(defn file-size [path str] (Option i64)
(let [n (i64 0)]
(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
;; 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.
(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")
(defn macro-slurp [path string] (Option [u8])
(defn macro-slurp [path str] (Option [u8])
(let [n (i64 0)
p (macro-slurp-raw path (addr n))]
(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
;; contract with lib/expand.ml's marshaller and may not be reordered.
(defdata Form
[(Sym [s string])
(Kw [s string])
[(Sym [s str])
(Kw [s str])
(Int [i i64])
(Float [x f64])
(Str [s string])
(Str [s str])
(Byte [b i32])
(List [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:
;; 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)]
(dotimes [i (length xs)]
(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))]
(dotimes [i (length d)]
(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 ──────────────────────────
;;
@ -2443,7 +2443,7 @@ let source = {flan|
(Form.List xs) xs
_ (form-nil)))
(defn form-sym? [f Form name string] bool
(defn form-sym? [f Form name str] bool
(match f
(Form.Sym s) (bytes=? (bytes-view s) (bytes-view name))
_ 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
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
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
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.

View File

@ -9,8 +9,8 @@
[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
it crossable. The two cannot be one form —
[(declare start-raw [path string] i32 "flan_agent_start")] and
[(declare-c init-window [w i32 h i32 title string] "InitWindow")] are the
[(declare start-raw [path str] i32 "flan_agent_start")] and
[(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.
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. \
Pass (Ptr %s) and let the C side read it"
what n n
else if String.equal n "string" then
else if String.equal n "str" then
fail loc
"%s is a string, and a string crosses only as a parameter or a \
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 t' = unalias env t in
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
generator writes would have to spell the copy's type, and it builds its
wrapper from struct names. *)
@ -891,7 +891,7 @@ let flan_wrapper ?track (fn : Ast.fn) (s : shim) raw : Ast.decl_kind =
out ],
Some (ty loc (Ast.Tname n)) )
| `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
again. The length is bound before the pointer is, so its address
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 };
{ Ast.bname = ptr_tmp; bty = None;
bval = call (args @ [ app "addr" [ v len_tmp ] ]); bloc = loc } ];
( [ app "string"
( [ app "str"
[ app "bytes"
[ app "string"
[ app "str"
[ app "slice-from" [ v ptr_tmp; v len_tmp ] ] ] ] ],
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
(match t'.Ast.t with
| Ast.Tname "Unit" -> `Void
| Ast.Tname "string" -> `Str
| Ast.Tname "str" -> `Str
| Ast.Tname n when Hashtbl.mem env.structs n ->
ignore (cty env ~needed ~loc ~what t');
`Struct (n, ctype_name n)

View File

@ -36,7 +36,7 @@ type prim =
calc-me's tokenizer and the prelude's printers each need. *)
| Bytes | BytesToF64 | BytesToI64 | F64ToBytes | I64ToBytes
(* (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
(* 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

View File

@ -44,7 +44,7 @@ type t =
| Array of int64 * t (* [n T] inline, a value, copies *)
| Map of t * t (* (Map K V) *)
| 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
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
@ -145,7 +145,7 @@ let bits_f = function F32 -> 32 | F64 -> 64
that already accepts [i32] accepts [int] without learning the word.
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.
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. *)
let primitive_names =
[ "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" ]
let ikind_name k =
@ -272,7 +272,7 @@ and to_string = function
| Int k -> ikind_name k
| Float k -> fkind_name k
| Bool -> "bool"
| String -> "string"
| String -> "str"
| Unit -> "()"
| Never -> "Never"
| 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
* 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)
*
@ -901,7 +901,7 @@ void flan_dev_watch_reset(void) {
/* 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:
*
* (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))
*

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;
} known[] = {
{ "bool", ST_BOOL, 0, 0, 0 },
{ "string", ST_TEXT, 0, 0, 0 },
{ "str", ST_TEXT, 0, 0, 0 },
{ "f32", ST_FLOAT, 24, 0, 0 },
{ "f64", ST_FLOAT, 53, 0, 0 },
{ "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
* 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.
*
* 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,
* 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
* condition with its values: [message] for the runtime's own conditions,
* 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 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) {
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.
*
* 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. */
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
* 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
* (defstruct NullCall :parent Error [type string]). */
* (defstruct NullCall :parent Error [type str]). */
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->epoch = (int64_t)a->epoch;
/* 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;
}

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:
```
(defn load-texture [path string] (Handle Texture)
(defn load-texture [path str] (Handle Texture)
(if (file-exists? path)
(rl/load-texture path)
(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:
```
(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
;; 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))
table (try (parse-toml text))
port (try (ok-or (get table "port")
@ -143,7 +143,7 @@
;; errdefer runs only on the Result failure path — NOT on a restart transfer
;; (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)]
(errdefer (free buf))
(try (decode-png path buf))
@ -155,13 +155,13 @@
;; 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
;; 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
;; fall-through path of a restart-case in value position must still produce the
;; type. `abort` has type Never, which unifies with (Handle Texture).
;; 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)
(rl/load-texture path)
(restart-case
@ -176,7 +176,7 @@
;; 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
;; goes on the condition or into a global.
(defn load-level [path string] Level
(defn load-level [path str] Level
(handler-bind [(AssetMissing [c]
(log "missing asset:" (.path c))
(invoke-restart 'use-placeholder))]
@ -195,7 +195,7 @@
(skip-form [] (skip-to-next-delimiter p))))
forms))
(defn collect-parse-errors [src string] (Result Ast)
(defn collect-parse-errors [src str] (Result Ast)
(let [errors (make-vec ParseError)]
(handler-bind [(ParseError [c]
(push errors c) ; value struct: copies out of
@ -218,7 +218,7 @@
;; ── defer for explicit resources ──────────────────────────────────────
;; 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)]
(defer (close f))
(write-bytes f (serialize w))))
@ -235,7 +235,7 @@
;; 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
;; it out of preload.
(defn ^:async preload [paths [string]] (Result ())
(defn ^:async preload [paths [str]] (Result ())
(for [p paths]
(try (await (load-texture-async p))))
(Ok unit))

View File

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

View File

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

View File

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

View File

@ -23,7 +23,7 @@
(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))
(let [which (if (> (length args) 1) (i32 (bytes->i64 (bytes-view (at args 1)))) 0)]
(cond

View File

@ -19,9 +19,9 @@
(defdata Value
[(Nil [])
(Int [n i64])
(Text [s string])
(Text [s str])
(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
;; 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
;; deep before it reaches a leaf: Table -> Vec -> List -> Vec -> Int.
(defn a-table [] Value
(let [entries (map-new string Value)]
(let [entries (map-new str Value)]
(put entries "xs" (number-list 3))
(put entries "ys" (number-list 5))
(put entries "name" (Value.Text {.s "edn"}))

View File

@ -61,7 +61,7 @@
(defonce small f64 -1e300)
(defonce uz u32)
(defn show [name string n i64] ()
(defn show [name str n i64] ()
(print name) (print " ") (print n) (println ""))
;;; 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 wide f32 1e30)
(defn main [args [string]] i32
(defn main [args [str]] i32
(let [n (i32 (bytes->i64 (bytes-view (at args 1))))
;; 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

View File

@ -58,7 +58,7 @@
;;; Two frames deep, with a defer on the way, so the transfer has something to
;;; 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 ""))
(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.
(defonce arr [3 i32])
(defn main [args [string]] i32
(defn main [args [str]] i32
(let [n (i32 (bytes->i64 (bytes-view (at args 1))))
s (bytes-view "hello")] ; len 5
(cond

View File

@ -37,7 +37,7 @@
;;; one.
;;; Both additions are qualified too, since + is shadowed below and this
;;; 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)

View File

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

View File

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

View File

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

View File

@ -16,12 +16,12 @@
;; Prints its tag and answers its value. Every operand below is one of these,
;; 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))
(print tag)
v)
(defn markf [tag string v f64] f64
(defn markf [tag str v f64] f64
(print tag)
v)

View File

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

View File

@ -5,7 +5,7 @@
(defstruct IoError :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)

View File

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

View File

@ -39,7 +39,7 @@
(sort w)
(println (total w) (total (slice w 1)))
(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))
(push names head)
(push names tail)
@ -51,15 +51,15 @@
both [a b]]
(println (widths (slice both)))
(set (at a 0) \A)
(println (string a)))
(println (str a)))
(let [f (split (bytes-view "b,a,c") \,)]
(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 (call-rd rd) (call-bare rd) (call-mk mk))
(let [b (bytes "q")]
(println (peek (addr (at r 1))) (peek (addr (at "abc" 2)))
(peek (addr (at b 0)))
(string (slice-from (addr (at r 7)) 5))))
(str (slice-from (addr (at r 7)) 5))))
(free names))
0)

View File

@ -4,7 +4,7 @@
;;;; destroyed arena's record. Allocating from the context must then trap,
;;;; 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.
(defn main [args [string]] i32
(defn main [args [str]] i32
(let [which (if (> (length args) 1) (bytes->i64 (bytes-view (at args 1))) 0)
a (arena-new 4096)]
(with-allocator a

View File

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

View File

@ -13,7 +13,7 @@
[Empty
(Dot [x f64 y f64])
(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
;; return type -- a value that cannot do that is not a value.
@ -29,7 +29,7 @@
;; alignment visible to something other than a slot.
(defstruct Cell [id i32 s Shape])
(defn describe [s Shape] string
(defn describe [s Shape] str
(match s
Empty "empty"
(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
;; held -- by a program that had said.
(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.Empty)
(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
;;;; 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
(let [bump (+ n 1)]

View File

@ -12,7 +12,7 @@
;;;; wrong, because it is the only one whose own alignment exceeds its first
;;;; 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
(let [bump (+ n 1)]

View File

@ -21,7 +21,7 @@
;;;; 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
;;;; 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)
a (arena-new 4096)]
(cond

View File

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

View File

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

View File

@ -11,7 +11,7 @@
(defstruct Point [x f32 y f32])
(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})
xs [10 20 30]
flag (> n 0)

View File

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

View File

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

View File

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

View File

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

View File

@ -10,13 +10,13 @@
;;;; at frame rate and the editor reads memory.
(import agent "vendor:agent")
(declare-c watch-i64 [name string x i64] i32 "flan_dev_watch_i64")
(declare-c watch-f64 [name string x f64] i32 "flan_dev_watch_f64")
(declare-c watch-str [name string s string] i32 "flan_dev_watch_str")
(declare-c watch-i64 [name str x i64] i32 "flan_dev_watch_i64")
(declare-c watch-f64 [name str x f64] i32 "flan_dev_watch_f64")
(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
;; 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".
(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)

View File

@ -10,7 +10,7 @@
;;;; exists and (get s :pause) is a place, where a plain map's absent key is
;;;; 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.
(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-t [t T n i64] dyn (.d t))
(defn show [label string v dyn] ()
(defn show [label str v dyn] ()
(churn)
(println label (at v 0) (at v 1)))

View File

@ -10,7 +10,7 @@
(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)
v (vec-new dyn)
ar (arena-new 4096)]

View File

@ -3,7 +3,7 @@
;;
;; 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")]
(println (slice d))
(println (slice d 1))

View File

@ -6,7 +6,7 @@
(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)
s (state false 3 nil)]
(println "before")

View File

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

View File

@ -54,7 +54,7 @@
;; carries that pair, because a refusal cannot run.
(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))))]
(cond
(= n 0)

View File

@ -66,7 +66,7 @@
;; 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
;; 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}}}")
(defn show-drift [a Allocator] ()
@ -93,7 +93,7 @@
;; 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
;; be at least as honest.
(defconst broken string "{:name \"orc\" :hp }")
(defconst broken str "{:name \"orc\" :hp }")
(defn show-broken [a Allocator] ()
(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
;; be asked for.
(defn set-size [src string] ()
(defn set-size [src str] ()
(println (length (edn/read (bytes-view src)))))
;; Malformed input, told apart from the document `nil` by the cursor — the
;; return value alone cannot say it, and this is the spelling that can.
(defn malformed? [src string] bool
(defn malformed? [src str] bool
(let [c (edn/cursor (bytes-view src))
t (edn/next (addr c))
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
;; alone.
(defn kind-letter [k i32] string
(defn kind-letter [k i32] str
(cond
(= k edn/tok-eof) "."
(= k edn/tok-error) "!"
@ -40,7 +40,7 @@
(= k edn/tok-set-open) "#"
:else "?"))
(defn dump [src string] ()
(defn dump [src str] ()
(let [b (bytes-view src)
c (edn/cursor b)
t (edn/next (addr c))]
@ -58,7 +58,7 @@
;; 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
;; pass a test that only checked that it failed.
(defn refusal [src string] ()
(defn refusal [src str] ()
(let [b (bytes-view src)
c (edn/cursor b)]
(while (and (edn/ok? (addr c))
@ -121,7 +121,7 @@
(return e)))))
e))
(defn show-enemy [src string] ()
(defn show-enemy [src str] ()
(let [b (bytes-view src)
c (edn/cursor b)
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.
(let [a (embed "assets/a.txt")]
(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
;; text. With structural equality and nothing that converts one container
;; into another -- implicit widening is numbers only -- one form that changes
;; 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
;; escape hex-escapes everything outside printable ASCII, so a PNG makes the
@ -50,7 +50,7 @@
;; there are no generics.
(let [all (slice assets 0 (length assets))]
(match (embed-find all "b.bin")
(Some b) (println (string b)) ; BBB
(Some b) (println (str b)) ; BBB
None (println "missing"))
(match (embed-find all "nope.txt")
(Some _) (println "found")

View File

@ -18,7 +18,7 @@
;; An enum parameter driven by a loop variable, which is the shape this exists
;; 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)]
(cond (= k :lo) "lo"
(= k :mid) "mid"

View File

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

View File

@ -51,7 +51,7 @@
(push msg who)
(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)))

View File

@ -17,7 +17,7 @@
(push junk true))))
(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))
rows (map-new i32 dyn)]
(put ops "one" (adder 1))

View File

@ -11,7 +11,7 @@
(defn show [x f64 p i32] ()
(let [v (format-f64 x p)]
(println (string (slice v)))
(println (str (slice v)))
(free v)))
(defn main [] i32
@ -95,6 +95,6 @@
(let [f (format-f64 0.0166667 4)]
(append (addr b) (slice 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))
0)

View File

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

View File

@ -6,7 +6,7 @@
;;;; 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
;;;; 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)
a (arena-new 4096)]
(cond
@ -26,7 +26,7 @@
(let [b (bytes "heap")
c (bytes "arena" a)
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 c a)
(free d (heap-allocator))))

View File

@ -6,7 +6,7 @@
;;;; position that takes i32 takes int, the cast head included, and nothing
;;;; 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
;;;; 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
;; Map, named with the alias.
(let [v (vec-new int)
m (map-new string int)]
m (map-new str int)]
(push v 10)
(push v 20)
(put m "k" 30)

View File

@ -44,7 +44,7 @@
;; its backslash still on it; that is the divergence between the tokenizer and
;; string-of, and this is where it is visible.
(defn kind-letter [k i32] string
(defn kind-letter [k i32] str
(cond
(= k json/tok-eof) "."
(= k json/tok-error) "!"
@ -61,7 +61,7 @@
(= k json/tok-comma) ","
:else "?"))
(defn dump [src string] ()
(defn dump [src str] ()
(let [b (bytes-view src)
c (json/cursor b)
t (json/next (addr c))]
@ -79,7 +79,7 @@
;; 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
;; that only checked that it stopped.
(defn refusal [src string] ()
(defn refusal [src str] ()
(let [b (bytes-view src)
c (json/cursor b)]
(while (and (json/ok? (addr c))
@ -96,9 +96,9 @@
(Bool [b bool])
(Int [n i64])
(Float [x f64])
(Text [s string])
(Text [s str])
(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
;; 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
;; buffer is not in the picture.
(= (.kind t) json/tok-object-open)
(let [entries (map-new string Value)
(let [entries (map-new str Value)
k (json/next c)
more (and (json/ok? c) (!= (.kind k) json/tok-object-close))]
(while more
@ -234,14 +234,14 @@
(match (get entries "xs") (Some x) (sum-ints x) None (i64 0))
_ (i64 0)))
(defn describe [v Value] string
(defn describe [v Value] str
(match v
Null "null" (Bool _b) "bool" (Int _n) "int" (Float _x) "float"
(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
;; 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
(Object entries)
(match (get entries key)
@ -257,7 +257,7 @@
;; 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 —
;; a trailing comma reported at the opening brace would be useless.
(defn reject [src string] ()
(defn reject [src str] ()
(let [b (bytes-view src)
c (json/cursor b)
t (json/next (addr c))]

View File

@ -55,7 +55,7 @@
(defn inf-f32? [x f32] bool
(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 " ")
(println (if ok "ok" "WRONG")))

View File

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

View File

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

View File

@ -2,13 +2,13 @@
;; 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.
(defstruct Tag [name string n i32])
(defstruct Tag [name str n i32])
(defn main [] i32
(let [m (map-new [2 string] i32)
a (the [2 string] ["ab" "cd"])
b (the [2 string] [(slice "xab" 1) (slice "cdx" 0 2)])
c (the [2 string] ["ab" "ce"])]
(let [m (map-new [2 str] i32)
a (the [2 str] ["ab" "cd"])
b (the [2 str] [(slice "xab" 1) (slice "cdx" 0 2)])
c (the [2 str] ["ab" "ce"])]
(put m a 1)
(put m c 3)
(println (or-else (get m b) -1))

View File

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

View File

@ -65,7 +65,7 @@
(defstruct Point [x i32 y i32])
(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 "bb" (Point {.x 3 .y 4}))
(put m "ccc" (Point {.x 5 .y 6}))

View File

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

View File

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

View File

@ -5,7 +5,7 @@
;;;; version gets wrong, and the numbers differ per failure so a single wrong
;;;; answer names its own cause.
(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])
;;;; (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
;; 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)
(put u "k" 1)
(put u "k" 2)

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