A field label is a dot now, and the colon is refused where one was
The delimiter is what disambiguates: (.x v) is a call and therefore an
access, {.x 1.0} is a brace form and therefore a construction. The colon
kept two jobs -- field label and enum member -- and this leaves it with
one, keys, which is what a map literal will want.
The old spelling is refused rather than quietly accepted, and the refusal
names the new one. Two accepted spellings is how two spellings become
permanent, and this repo rejects what it does not support and says why.
:keys keeps its colon. It names no field -- it is an instruction to the
compiler that happens to sit in the same brace -- so leaving it alone is
what lets the dot mean exactly one thing.
render.ml prints the dot too, or a struct the daemon shows would not be
Flan anyone could paste back.
This commit is contained in:
parent
9a820d86cd
commit
8e47356592
@ -13,7 +13,7 @@
|
||||
;;;; It is a top-level `defvar` and not a local, which is NOT a stylistic
|
||||
;;;; choice. A `let` binding takes no type annotation, so the only way to make
|
||||
;;;; a fixed array inside a function is to initialise it from a literal with
|
||||
;;;; every element written out — ten `(rl/Vector2 {:x 0.0 :y 0.0})`s here, and
|
||||
;;;; every element written out — ten `(rl/Vector2 {.x 0.0 .y 0.0})`s here, and
|
||||
;;;; thirty-two in the gestures testbed. A zeroed local array of a given type
|
||||
;;;; cannot be spelled. Static storage is what the C's `= { 0 }` gets anyway.
|
||||
;;;;
|
||||
|
||||
52
lib/parse.ml
52
lib/parse.ml
@ -111,7 +111,7 @@ let rec expr (f : Form.t) : Ast.expr =
|
||||
(* In value position brackets are a fixed-array literal; in type position
|
||||
they are a slice or array type. Position disambiguates, as with {}. *)
|
||||
| Vec items -> mk (Ast.Arr (List.map expr items))
|
||||
| Map _ -> fail f "a bare map is not an expression; write (Type {:field v})"
|
||||
| Map _ -> fail f "a bare map is not an expression; write (Type {.field v})"
|
||||
| List [] -> fail f "() is not an expression"
|
||||
| List (head :: args) -> form f mk head args
|
||||
|
||||
@ -343,11 +343,11 @@ and form f mk (head : Form.t) (args : Form.t list) : Ast.expr =
|
||||
| [ target ] -> mk (Ast.Field (expr target, field))
|
||||
| _ -> fail f "field access is (.%s value)" field)
|
||||
|
||||
(* ── struct literal: (Cursor {:src s :pos 0}) ───────────────────── *)
|
||||
(* ── struct literal: (Cursor {.src s .pos 0}) ───────────────────── *)
|
||||
| Sym name when args <> [] && is_map (List.hd args) ->
|
||||
(match args with
|
||||
| [ { v = Map kvs; _ } ] -> mk (Ast.Struct (name, struct_fields f kvs))
|
||||
| _ -> fail f "a struct literal is (%s {:field value ...})" name)
|
||||
| _ -> fail f "a struct literal is (%s {.field value ...})" name)
|
||||
|
||||
(* ── anything else is a call ────────────────────────────────────── *)
|
||||
| _ -> mk (Ast.Call (expr head, List.map expr args))
|
||||
@ -410,11 +410,18 @@ and destructure (p : Form.t) (v : Ast.expr) : Ast.binding list =
|
||||
{:keys [x y]} over a struct or [a b] over a fixed array"
|
||||
(Form.to_string p)
|
||||
|
||||
(* {:keys [x y]} and {inner :field}, over a struct. Clojure's map destructuring
|
||||
(* {:keys [x y]} and {inner .field}, over a struct. Clojure's map destructuring
|
||||
with Flan's structs standing in for its maps: [:keys] is the common case and
|
||||
the pair form is what nests, since a [:keys] entry is a name and never a
|
||||
pattern. Everything else Clojure puts in this position — [:as], [:or],
|
||||
[:strs], [:syms] — is refused by name where it is written. *)
|
||||
[:strs], [:syms] — is refused by name where it is written.
|
||||
|
||||
[:keys] keeps its colon while [.field] takes the dot, and the split is the
|
||||
point rather than an inconsistency: [.field] names a field of the struct,
|
||||
[:keys] names no field at all — it is an instruction to the compiler that
|
||||
happens to sit in the same brace. Keeping them apart leaves the dot meaning
|
||||
exactly one thing, "this names a field", which is the whole reason the
|
||||
colon was given up here. *)
|
||||
and dmap (p : Form.t) (t : Ast.expr) (items : Form.t list) : Ast.binding list =
|
||||
let ex loc e : Ast.expr = { Ast.e; loc } in
|
||||
let field loc name = ex loc (Ast.Field (t, name)) in
|
||||
@ -438,7 +445,7 @@ and dmap (p : Form.t) (t : Ast.expr) (items : Form.t list) : Ast.binding list =
|
||||
| _ ->
|
||||
Loc.fail n.loc
|
||||
":keys binds field names, and %s is not one — a nested pattern \
|
||||
is written {%s :field}"
|
||||
is written {%s .field}"
|
||||
(Form.to_string n) (Form.to_string n)
|
||||
in
|
||||
{ Ast.bname = name; bty = None; bval = field n.loc name; bloc = n.loc }
|
||||
@ -449,15 +456,22 @@ and dmap (p : Form.t) (t : Ast.expr) (items : Form.t list) : Ast.binding list =
|
||||
ignore rest;
|
||||
Loc.fail bad.loc
|
||||
":%s is not implemented in a destructuring pattern — a struct pattern \
|
||||
is {:keys [x y]} or {name :field}, and nothing else" k
|
||||
| pat :: ({ v = Kw fld; _ } as fform) :: rest ->
|
||||
destructure pat (field fform.loc fld) @ go rest
|
||||
is {:keys [x y]} or {name .field}, and nothing else" k
|
||||
| pat :: ({ v = Sym s; _ } as fform) :: rest
|
||||
when String.length s > 1 && s.[0] = '.' ->
|
||||
destructure pat (field fform.loc (String.sub s 1 (String.length s - 1)))
|
||||
@ go rest
|
||||
| pat :: ({ v = Kw fld; _ } as bad) :: _ ->
|
||||
ignore pat;
|
||||
Loc.fail bad.loc
|
||||
"a field label is written .%s, not :%s — the colon is for keys, and a \
|
||||
struct pattern binds {name .%s}" fld fld fld
|
||||
| pat :: other :: _ ->
|
||||
Loc.fail other.loc
|
||||
"expected :field after %s, found %s — a struct pattern binds \
|
||||
{name :field}" (Form.to_string pat) (Form.to_string other)
|
||||
"expected .field after %s, found %s — a struct pattern binds \
|
||||
{name .field}" (Form.to_string pat) (Form.to_string other)
|
||||
| [ odd ] ->
|
||||
Loc.fail odd.loc "%s has no :field — a struct pattern comes in pairs"
|
||||
Loc.fail odd.loc "%s has no .field — a struct pattern comes in pairs"
|
||||
(Form.to_string odd)
|
||||
in
|
||||
if items = [] then
|
||||
@ -543,12 +557,22 @@ and no_duplicates (p : Form.t) (bs : Ast.binding list) =
|
||||
in
|
||||
ignore p; go [] bs
|
||||
|
||||
(* A field label is a dot, never a colon. The delimiter is what disambiguates:
|
||||
[(.x v)] is a call and therefore an access, [{.x 1.0}] is a brace form and
|
||||
therefore a construction. The colon is left for keys — map keys and enum
|
||||
members — so the two never share a spelling. *)
|
||||
and struct_fields f (items : Form.t list) : (string * Ast.expr) list =
|
||||
let rec go = function
|
||||
| [] -> []
|
||||
| { v = Kw k; _ } :: value :: rest -> (k, expr value) :: go rest
|
||||
| { v = Sym s; _ } :: value :: rest
|
||||
when String.length s > 1 && s.[0] = '.' ->
|
||||
(String.sub s 1 (String.length s - 1), expr value) :: go rest
|
||||
| ({ v = Kw k; _ } as bad) :: _ :: _ ->
|
||||
Loc.fail bad.loc
|
||||
"a field label is written .%s, not :%s — the colon is for keys, and a \
|
||||
struct value is (Type {.%s value ...})" k k k
|
||||
| other :: _ :: _ ->
|
||||
Loc.fail other.loc "expected :field, found %s" (Form.to_string other)
|
||||
Loc.fail other.loc "expected .field, found %s" (Form.to_string other)
|
||||
| [ odd ] -> Loc.fail odd.loc "field %s has no value" (Form.to_string odd)
|
||||
in
|
||||
ignore f; go items
|
||||
|
||||
@ -147,7 +147,7 @@ let rec render c depth (e : Tast.expr) : Tast.expr list =
|
||||
(fun i (f : Tast.field) ->
|
||||
let v = { Tast.e = Tast.Field (e, i); ty = f.Tast.fty; loc } in
|
||||
(if i = 0 then [] else [ lit " " ])
|
||||
@ [ lit (":" ^ f.Tast.fname ^ " ") ]
|
||||
@ [ lit ("." ^ f.Tast.fname ^ " ") ]
|
||||
@ render c (depth + 1) v)
|
||||
shown)
|
||||
in
|
||||
|
||||
@ -20,8 +20,10 @@
|
||||
;; {string i32} owning hashmap — move-only, shorthand for (Map string i32)
|
||||
;;
|
||||
;; Braces are read by position: in a TYPE position {K V} is a map type; in a
|
||||
;; VALUE position {:field v ...} is a struct or condition literal. There is no
|
||||
;; map literal — a map is built with make-map and an allocator.
|
||||
;; VALUE position {.field v ...} is a struct or condition literal — a field
|
||||
;; label is a dot, and the colon is left for keys. There is no map literal yet;
|
||||
;; a map is built with make-map and an allocator, and when a literal arrives it
|
||||
;; takes {:key value}, which is why the dot is what struct construction uses.
|
||||
;; (Ptr World) pointer
|
||||
;; (Fn [f32] bool) function pointer, no captured environment
|
||||
;; (Option a) union from the stdlib
|
||||
|
||||
@ -953,10 +953,10 @@ let () =
|
||||
~needle:"an empty struct pattern {} binds nothing";
|
||||
rejects_check "a field name with no pattern before it"
|
||||
(pt ^ "(defn f [p Point] i32 (let [{.x} p] 0))")
|
||||
~needle:"has no :field";
|
||||
~needle:"has no .field";
|
||||
rejects_check "a pattern with no field name after it"
|
||||
(pt ^ "(defn f [p Point] i32 (let [{a b} p] 0))")
|
||||
~needle:"expected :field after a";
|
||||
~needle:"expected .field after a";
|
||||
|
||||
(* Clojure's other map-destructuring keys. Each is refused by its own name:
|
||||
"unexpected form" would leave the author guessing which of the four they
|
||||
|
||||
@ -195,8 +195,10 @@ def walk(paths):
|
||||
for p in paths:
|
||||
if os.path.isdir(p):
|
||||
for root, dirs, files in os.walk(p):
|
||||
# vendor/ is not third-party: edn, raylib and agent are this
|
||||
# repo's own packages, written in Flan, and they convert too.
|
||||
dirs[:] = [d for d in dirs
|
||||
if d not in ('_build', '.git', 'vendor', 'node_modules')]
|
||||
if d not in ('_build', '.git', 'node_modules')]
|
||||
for f in sorted(files):
|
||||
if f.endswith('.flan'):
|
||||
yield os.path.join(root, f)
|
||||
|
||||
8
vendor/edn/edn.flan
vendored
8
vendor/edn/edn.flan
vendored
@ -158,7 +158,7 @@
|
||||
;; ── Construction ────────────────────────────────────────────────────
|
||||
|
||||
(defn cursor [src [u8]] Cursor
|
||||
(Cursor {:src src :pos 0 :err err-none :err-pos 0 :depth 0}))
|
||||
(Cursor {.src src .pos 0 .err err-none .err-pos 0 .depth 0}))
|
||||
|
||||
(defn ok? [c (Ptr Cursor)] bool
|
||||
(= (.err c) err-none))
|
||||
@ -248,10 +248,10 @@
|
||||
(slice (.src c) p p))
|
||||
|
||||
(defn token [c (Ptr Cursor) kind i32 lo i32 hi i32 p i32] Token
|
||||
(Token {:kind kind :text (slice (.src c) lo hi) :pos p}))
|
||||
(Token {.kind kind .text (slice (.src c) lo hi) .pos p}))
|
||||
|
||||
(defn error-token [c (Ptr Cursor)] Token
|
||||
(Token {:kind tok-error :text (empty-at c (.err-pos c)) :pos (.err-pos c)}))
|
||||
(Token {.kind tok-error .text (empty-at c (.err-pos c)) .pos (.err-pos c)}))
|
||||
|
||||
;; Whitespace, commas, and `;` comments, which run to the newline or to the end
|
||||
;; of input — a comment on the last line of a file with no trailing newline is
|
||||
@ -376,7 +376,7 @@
|
||||
(when (> (.depth c) 0)
|
||||
(fail c err-unbalanced (.pos c))
|
||||
(return (error-token c)))
|
||||
(return (Token {:kind tok-eof :text (empty-at c (.pos c)) :pos (.pos c)})))
|
||||
(return (Token {.kind tok-eof .text (empty-at c (.pos c)) .pos (.pos c)})))
|
||||
|
||||
(let [s (.src c)
|
||||
lo (.pos c)
|
||||
|
||||
52
vendor/raylib/raylib.flan
vendored
52
vendor/raylib/raylib.flan
vendored
@ -133,8 +133,8 @@
|
||||
;; directions at once.
|
||||
(declare-c fade [color Color alpha f32] Color "Fade")
|
||||
|
||||
(defconst black (Color {:r 0 :g 0 :b 0 :a 255}))
|
||||
(defconst white (Color {:r 255 :g 255 :b 255 :a 255}))
|
||||
(defconst black (Color {.r 0 .g 0 .b 0 .a 255}))
|
||||
(defconst white (Color {.r 255 .g 255 .b 255 .a 255}))
|
||||
|
||||
;; raylib's own named palette, from raylib.h's CLITERAL macros. These are the
|
||||
;; only entries in this file that are not a function, a layout or an enum, and
|
||||
@ -143,32 +143,32 @@
|
||||
;; hex that cannot be diffed against the C it came from. They are values and
|
||||
;; not calls — a Color is four bytes with no packing question — so unlike
|
||||
;; get-color they cost nothing at run time and need no window.
|
||||
(defconst lightgray (Color {:r 200 :g 200 :b 200 :a 255}))
|
||||
(defconst gray (Color {:r 130 :g 130 :b 130 :a 255}))
|
||||
(defconst darkgray (Color {:r 80 :g 80 :b 80 :a 255}))
|
||||
(defconst yellow (Color {:r 253 :g 249 :b 0 :a 255}))
|
||||
(defconst gold (Color {:r 255 :g 203 :b 0 :a 255}))
|
||||
(defconst orange (Color {:r 255 :g 161 :b 0 :a 255}))
|
||||
(defconst pink (Color {:r 255 :g 109 :b 194 :a 255}))
|
||||
(defconst red (Color {:r 230 :g 41 :b 55 :a 255}))
|
||||
(defconst maroon (Color {:r 190 :g 33 :b 55 :a 255}))
|
||||
(defconst green (Color {:r 0 :g 228 :b 48 :a 255}))
|
||||
(defconst lime (Color {:r 0 :g 158 :b 47 :a 255}))
|
||||
(defconst darkgreen (Color {:r 0 :g 117 :b 44 :a 255}))
|
||||
(defconst skyblue (Color {:r 102 :g 191 :b 255 :a 255}))
|
||||
(defconst blue (Color {:r 0 :g 121 :b 241 :a 255}))
|
||||
(defconst darkblue (Color {:r 0 :g 82 :b 172 :a 255}))
|
||||
(defconst purple (Color {:r 200 :g 122 :b 255 :a 255}))
|
||||
(defconst violet (Color {:r 135 :g 60 :b 190 :a 255}))
|
||||
(defconst darkpurple (Color {:r 112 :g 31 :b 126 :a 255}))
|
||||
(defconst beige (Color {:r 211 :g 176 :b 131 :a 255}))
|
||||
(defconst brown (Color {:r 127 :g 106 :b 79 :a 255}))
|
||||
(defconst darkbrown (Color {:r 76 :g 63 :b 47 :a 255}))
|
||||
(defconst magenta (Color {:r 255 :g 0 :b 255 :a 255}))
|
||||
(defconst lightgray (Color {.r 200 .g 200 .b 200 .a 255}))
|
||||
(defconst gray (Color {.r 130 .g 130 .b 130 .a 255}))
|
||||
(defconst darkgray (Color {.r 80 .g 80 .b 80 .a 255}))
|
||||
(defconst yellow (Color {.r 253 .g 249 .b 0 .a 255}))
|
||||
(defconst gold (Color {.r 255 .g 203 .b 0 .a 255}))
|
||||
(defconst orange (Color {.r 255 .g 161 .b 0 .a 255}))
|
||||
(defconst pink (Color {.r 255 .g 109 .b 194 .a 255}))
|
||||
(defconst red (Color {.r 230 .g 41 .b 55 .a 255}))
|
||||
(defconst maroon (Color {.r 190 .g 33 .b 55 .a 255}))
|
||||
(defconst green (Color {.r 0 .g 228 .b 48 .a 255}))
|
||||
(defconst lime (Color {.r 0 .g 158 .b 47 .a 255}))
|
||||
(defconst darkgreen (Color {.r 0 .g 117 .b 44 .a 255}))
|
||||
(defconst skyblue (Color {.r 102 .g 191 .b 255 .a 255}))
|
||||
(defconst blue (Color {.r 0 .g 121 .b 241 .a 255}))
|
||||
(defconst darkblue (Color {.r 0 .g 82 .b 172 .a 255}))
|
||||
(defconst purple (Color {.r 200 .g 122 .b 255 .a 255}))
|
||||
(defconst violet (Color {.r 135 .g 60 .b 190 .a 255}))
|
||||
(defconst darkpurple (Color {.r 112 .g 31 .b 126 .a 255}))
|
||||
(defconst beige (Color {.r 211 .g 176 .b 131 .a 255}))
|
||||
(defconst brown (Color {.r 127 .g 106 .b 79 .a 255}))
|
||||
(defconst darkbrown (Color {.r 76 .g 63 .b 47 .a 255}))
|
||||
(defconst magenta (Color {.r 255 .g 0 .b 255 .a 255}))
|
||||
;; Alpha 0, so it is invisible rather than a colour — raylib's own name for it.
|
||||
(defconst blank (Color {:r 0 :g 0 :b 0 :a 0}))
|
||||
(defconst blank (Color {.r 0 .g 0 .b 0 .a 0}))
|
||||
;; raylib's off-white background, which is what every example clears to.
|
||||
(defconst raywhite (Color {:r 245 :g 245 :b 245 :a 255}))
|
||||
(defconst raywhite (Color {.r 245 .g 245 .b 245 .a 255}))
|
||||
|
||||
;; ── Drawing ─────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user