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
|
;;;; 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
|
;;;; 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
|
;;;; 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
|
;;;; 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.
|
;;;; 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
|
(* In value position brackets are a fixed-array literal; in type position
|
||||||
they are a slice or array type. Position disambiguates, as with {}. *)
|
they are a slice or array type. Position disambiguates, as with {}. *)
|
||||||
| Vec items -> mk (Ast.Arr (List.map expr items))
|
| 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 [] -> fail f "() is not an expression"
|
||||||
| List (head :: args) -> form f mk head args
|
| 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))
|
| [ target ] -> mk (Ast.Field (expr target, field))
|
||||||
| _ -> fail f "field access is (.%s value)" 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) ->
|
| Sym name when args <> [] && is_map (List.hd args) ->
|
||||||
(match args with
|
(match args with
|
||||||
| [ { v = Map kvs; _ } ] -> mk (Ast.Struct (name, struct_fields f kvs))
|
| [ { 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 ────────────────────────────────────── *)
|
(* ── anything else is a call ────────────────────────────────────── *)
|
||||||
| _ -> mk (Ast.Call (expr head, List.map expr args))
|
| _ -> 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"
|
{:keys [x y]} over a struct or [a b] over a fixed array"
|
||||||
(Form.to_string p)
|
(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
|
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
|
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],
|
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 =
|
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 ex loc e : Ast.expr = { Ast.e; loc } in
|
||||||
let field loc name = ex loc (Ast.Field (t, name)) 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
|
Loc.fail n.loc
|
||||||
":keys binds field names, and %s is not one — a nested pattern \
|
":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)
|
(Form.to_string n) (Form.to_string n)
|
||||||
in
|
in
|
||||||
{ Ast.bname = name; bty = None; bval = field n.loc name; bloc = n.loc }
|
{ 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;
|
ignore rest;
|
||||||
Loc.fail bad.loc
|
Loc.fail bad.loc
|
||||||
":%s is not implemented in a destructuring pattern — a struct pattern \
|
":%s is not implemented in a destructuring pattern — a struct pattern \
|
||||||
is {:keys [x y]} or {name :field}, and nothing else" k
|
is {:keys [x y]} or {name .field}, and nothing else" k
|
||||||
| pat :: ({ v = Kw fld; _ } as fform) :: rest ->
|
| pat :: ({ v = Sym s; _ } as fform) :: rest
|
||||||
destructure pat (field fform.loc fld) @ go 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 :: _ ->
|
| pat :: other :: _ ->
|
||||||
Loc.fail other.loc
|
Loc.fail other.loc
|
||||||
"expected :field after %s, found %s — a struct pattern binds \
|
"expected .field after %s, found %s — a struct pattern binds \
|
||||||
{name :field}" (Form.to_string pat) (Form.to_string other)
|
{name .field}" (Form.to_string pat) (Form.to_string other)
|
||||||
| [ odd ] ->
|
| [ 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)
|
(Form.to_string odd)
|
||||||
in
|
in
|
||||||
if items = [] then
|
if items = [] then
|
||||||
@ -543,12 +557,22 @@ and no_duplicates (p : Form.t) (bs : Ast.binding list) =
|
|||||||
in
|
in
|
||||||
ignore p; go [] bs
|
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 =
|
and struct_fields f (items : Form.t list) : (string * Ast.expr) list =
|
||||||
let rec go = function
|
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 :: _ :: _ ->
|
| 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)
|
| [ odd ] -> Loc.fail odd.loc "field %s has no value" (Form.to_string odd)
|
||||||
in
|
in
|
||||||
ignore f; go items
|
ignore f; go items
|
||||||
|
|||||||
@ -147,7 +147,7 @@ let rec render c depth (e : Tast.expr) : Tast.expr list =
|
|||||||
(fun i (f : Tast.field) ->
|
(fun i (f : Tast.field) ->
|
||||||
let v = { Tast.e = Tast.Field (e, i); ty = f.Tast.fty; loc } in
|
let v = { Tast.e = Tast.Field (e, i); ty = f.Tast.fty; loc } in
|
||||||
(if i = 0 then [] else [ lit " " ])
|
(if i = 0 then [] else [ lit " " ])
|
||||||
@ [ lit (":" ^ f.Tast.fname ^ " ") ]
|
@ [ lit ("." ^ f.Tast.fname ^ " ") ]
|
||||||
@ render c (depth + 1) v)
|
@ render c (depth + 1) v)
|
||||||
shown)
|
shown)
|
||||||
in
|
in
|
||||||
|
|||||||
@ -20,8 +20,10 @@
|
|||||||
;; {string i32} owning hashmap — move-only, shorthand for (Map string i32)
|
;; {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
|
;; 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
|
;; VALUE position {.field v ...} is a struct or condition literal — a field
|
||||||
;; map literal — a map is built with make-map and an allocator.
|
;; 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
|
;; (Ptr World) pointer
|
||||||
;; (Fn [f32] bool) function pointer, no captured environment
|
;; (Fn [f32] bool) function pointer, no captured environment
|
||||||
;; (Option a) union from the stdlib
|
;; (Option a) union from the stdlib
|
||||||
|
|||||||
@ -953,10 +953,10 @@ let () =
|
|||||||
~needle:"an empty struct pattern {} binds nothing";
|
~needle:"an empty struct pattern {} binds nothing";
|
||||||
rejects_check "a field name with no pattern before it"
|
rejects_check "a field name with no pattern before it"
|
||||||
(pt ^ "(defn f [p Point] i32 (let [{.x} p] 0))")
|
(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"
|
rejects_check "a pattern with no field name after it"
|
||||||
(pt ^ "(defn f [p Point] i32 (let [{a b} p] 0))")
|
(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:
|
(* 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
|
"unexpected form" would leave the author guessing which of the four they
|
||||||
|
|||||||
@ -195,8 +195,10 @@ def walk(paths):
|
|||||||
for p in paths:
|
for p in paths:
|
||||||
if os.path.isdir(p):
|
if os.path.isdir(p):
|
||||||
for root, dirs, files in os.walk(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
|
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):
|
for f in sorted(files):
|
||||||
if f.endswith('.flan'):
|
if f.endswith('.flan'):
|
||||||
yield os.path.join(root, f)
|
yield os.path.join(root, f)
|
||||||
|
|||||||
8
vendor/edn/edn.flan
vendored
8
vendor/edn/edn.flan
vendored
@ -158,7 +158,7 @@
|
|||||||
;; ── Construction ────────────────────────────────────────────────────
|
;; ── Construction ────────────────────────────────────────────────────
|
||||||
|
|
||||||
(defn cursor [src [u8]] Cursor
|
(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
|
(defn ok? [c (Ptr Cursor)] bool
|
||||||
(= (.err c) err-none))
|
(= (.err c) err-none))
|
||||||
@ -248,10 +248,10 @@
|
|||||||
(slice (.src c) p p))
|
(slice (.src c) p p))
|
||||||
|
|
||||||
(defn token [c (Ptr Cursor) kind i32 lo i32 hi i32 p i32] Token
|
(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
|
(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
|
;; 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
|
;; of input — a comment on the last line of a file with no trailing newline is
|
||||||
@ -376,7 +376,7 @@
|
|||||||
(when (> (.depth c) 0)
|
(when (> (.depth c) 0)
|
||||||
(fail c err-unbalanced (.pos c))
|
(fail c err-unbalanced (.pos c))
|
||||||
(return (error-token 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)
|
(let [s (.src c)
|
||||||
lo (.pos c)
|
lo (.pos c)
|
||||||
|
|||||||
52
vendor/raylib/raylib.flan
vendored
52
vendor/raylib/raylib.flan
vendored
@ -133,8 +133,8 @@
|
|||||||
;; directions at once.
|
;; directions at once.
|
||||||
(declare-c fade [color Color alpha f32] Color "Fade")
|
(declare-c fade [color Color alpha f32] Color "Fade")
|
||||||
|
|
||||||
(defconst black (Color {:r 0 :g 0 :b 0 :a 255}))
|
(defconst black (Color {.r 0 .g 0 .b 0 .a 255}))
|
||||||
(defconst white (Color {:r 255 :g 255 :b 255 :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
|
;; 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
|
;; 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
|
;; 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
|
;; 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.
|
;; get-color they cost nothing at run time and need no window.
|
||||||
(defconst lightgray (Color {:r 200 :g 200 :b 200 :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 gray (Color {.r 130 .g 130 .b 130 .a 255}))
|
||||||
(defconst darkgray (Color {:r 80 :g 80 :b 80 :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 yellow (Color {.r 253 .g 249 .b 0 .a 255}))
|
||||||
(defconst gold (Color {:r 255 :g 203 :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 orange (Color {.r 255 .g 161 .b 0 .a 255}))
|
||||||
(defconst pink (Color {:r 255 :g 109 :b 194 :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 red (Color {.r 230 .g 41 .b 55 .a 255}))
|
||||||
(defconst maroon (Color {:r 190 :g 33 :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 green (Color {.r 0 .g 228 .b 48 .a 255}))
|
||||||
(defconst lime (Color {:r 0 :g 158 :b 47 :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 darkgreen (Color {.r 0 .g 117 .b 44 .a 255}))
|
||||||
(defconst skyblue (Color {:r 102 :g 191 :b 255 :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 blue (Color {.r 0 .g 121 .b 241 .a 255}))
|
||||||
(defconst darkblue (Color {:r 0 :g 82 :b 172 :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 purple (Color {.r 200 .g 122 .b 255 .a 255}))
|
||||||
(defconst violet (Color {:r 135 :g 60 :b 190 :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 darkpurple (Color {.r 112 .g 31 .b 126 .a 255}))
|
||||||
(defconst beige (Color {:r 211 :g 176 :b 131 :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 brown (Color {.r 127 .g 106 .b 79 .a 255}))
|
||||||
(defconst darkbrown (Color {:r 76 :g 63 :b 47 :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 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.
|
;; 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.
|
;; 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 ─────────────────────────────────────────────────────────
|
;; ── Drawing ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user