println prints a char as the character itself, and a char inside a value, in the inspector or at the REPL prints as its literal.
This commit is contained in:
parent
fd8620516c
commit
92fbeb4fc9
7
TODO.org
7
TODO.org
@ -33,13 +33,14 @@ Decision 127: a char literal is the number typed code wants there, and a =char=
|
|||||||
an untyped array of char literals included; a =char= crosses into dyn as a dyn char, and
|
an untyped array of char literals included; a =char= crosses into dyn as a dyn char, and
|
||||||
only a dyn char unboxes into one. No arithmetic: =(i32 c)= and =(char n)= convert, the
|
only a dyn char unboxes into one. No arithmetic: =(i32 c)= and =(char n)= convert, the
|
||||||
latter checked. An untyped defconst of one is that literal where a number is wanted.
|
latter checked. An untyped defconst of one is that literal where a number is wanted.
|
||||||
Printed as its literal, as dyn prints one. =runes-next= and =rune-at= give a char; the
|
Printed as dyn prints one (129a). =runes-next= and =rune-at= give a char; the
|
||||||
UTF-8 codec (=decode-rune=, =encode-rune=) stays on i32. Rules out the f(\a) fork.
|
UTF-8 codec (=decode-rune=, =encode-rune=) stays on i32. Rules out the f(\a) fork.
|
||||||
|
|
||||||
** DONE Dyn has a char, and dyn text counts characters
|
** DONE Dyn has a char, and dyn text counts characters
|
||||||
CLOSED: [2026-09-26]
|
CLOSED: [2026-09-26]
|
||||||
Only a char literal, =at= on a text and =chars= make one, and it prints as its
|
Only a char literal, =at= on a text and =chars= make one. =println= prints it as the
|
||||||
literal, bare too, a control character as \\uXXXX. Into any integer width it gives its
|
character itself (129a); inside a value, in the inspector and the REPL it is its
|
||||||
|
literal, a control character as \\uXXXX. Into any integer width it gives its
|
||||||
code point where that fits, into a byte only when ASCII; a dyn int into any width is
|
code point where that fits, into a byte only when ASCII; a dyn int into any width is
|
||||||
range-checked, while a cast on either wraps as a typed cast does. length, at and slice
|
range-checked, while a cast on either wraps as a typed cast does. length, at and slice
|
||||||
on dyn text count code points, a malformed byte counting as one U+FFFD. A non-ASCII
|
on dyn text count code points, a malformed byte counting as one U+FFFD. A non-ASCII
|
||||||
|
|||||||
14
lib/check.ml
14
lib/check.ml
@ -5185,7 +5185,8 @@ let condition_desc ctx loc name =
|
|||||||
ei64 = (fun x -> emit (to_bytes hctx loc Tast.I64ToBytes x));
|
ei64 = (fun x -> emit (to_bytes hctx loc Tast.I64ToBytes x));
|
||||||
eu64 = (fun x -> emit (to_bytes hctx loc Tast.U64ToBytes x));
|
eu64 = (fun x -> emit (to_bytes hctx loc Tast.U64ToBytes x));
|
||||||
ef64 = (fun x -> emit (to_bytes hctx loc Tast.F64ToBytes x));
|
ef64 = (fun x -> emit (to_bytes hctx loc Tast.F64ToBytes x));
|
||||||
edyn = (fun x -> mk loc Types.Unit (Tast.Prim (Tast.Rt "flan_dyn_emit_msg", [ x ]))) }
|
edyn = (fun x -> mk loc Types.Unit (Tast.Prim (Tast.Rt "flan_dyn_emit_msg", [ x ])));
|
||||||
|
enested = (fun x -> mk loc Types.Unit (Tast.Prim (Tast.Rt "flan_dyn_emit_msg", [ x ]))) }
|
||||||
in
|
in
|
||||||
let value =
|
let value =
|
||||||
mk loc ty (Tast.Deref (mk loc (Types.Ptr (Types.Mut, ty)) (Tast.Local pslot)))
|
mk loc ty (Tast.Deref (mk loc (Types.Ptr (Types.Mut, ty)) (Tast.Local pslot)))
|
||||||
@ -14486,13 +14487,19 @@ and named_call ?(qualified = false) ctx ~want loc name args =
|
|||||||
ei64 = (fun x -> write (conv Tast.I64ToBytes x));
|
ei64 = (fun x -> write (conv Tast.I64ToBytes x));
|
||||||
eu64 = (fun x -> write (conv Tast.U64ToBytes x));
|
eu64 = (fun x -> write (conv Tast.U64ToBytes x));
|
||||||
ef64 = (fun x -> write (conv Tast.F64ToBytes x));
|
ef64 = (fun x -> write (conv Tast.F64ToBytes x));
|
||||||
edyn = (fun x -> mk loc Types.Unit (Tast.Prim (Tast.Rt "flan_dyn_print_at", [ x; here loc ]))) }
|
edyn = (fun x -> mk loc Types.Unit (Tast.Prim (Tast.Rt "flan_dyn_print_at", [ x; here loc ])));
|
||||||
|
enested = (fun x -> mk loc Types.Unit (Tast.Prim (Tast.Rt "flan_dyn_print_nested_at", [ x; here loc ]))) }
|
||||||
in
|
in
|
||||||
let rc = render_ctx ctx emitter in
|
let rc = render_ctx ctx emitter in
|
||||||
|
let c_top = emitter.Render.edyn in
|
||||||
let render_one a =
|
let render_one a =
|
||||||
match a.Tast.ty with
|
match a.Tast.ty with
|
||||||
| Types.String | Types.Slice (_, (Types.Int Types.U8)) ->
|
| Types.String | Types.Slice (_, (Types.Int Types.U8)) ->
|
||||||
[ write (mk loc bslice (Tast.Prim (Tast.Bytes, [ a ]))) ]
|
[ write (mk loc bslice (Tast.Prim (Tast.Bytes, [ a ]))) ]
|
||||||
|
(* A char at the top prints as the character itself (129a), through the
|
||||||
|
runtime's dyn printer so the two sides agree. *)
|
||||||
|
| Types.Char ->
|
||||||
|
[ c_top (box loc a) ]
|
||||||
(* A String prints as its text, raw at the top as a str does. *)
|
(* A String prints as its text, raw at the top as a str does. *)
|
||||||
| t when is_string_ty t ->
|
| t when is_string_ty t ->
|
||||||
[ write (mk loc bslice (Tast.Prim (Tast.Bytes, [ string_bytes ctx loc a ]))) ]
|
[ write (mk loc bslice (Tast.Prim (Tast.Bytes, [ string_bytes ctx loc a ]))) ]
|
||||||
@ -14562,7 +14569,8 @@ and named_call ?(qualified = false) ctx ~want loc name args =
|
|||||||
ei64 = (fun x -> unit_rt "flan_dev_watch_emit_i64" [ x ]);
|
ei64 = (fun x -> unit_rt "flan_dev_watch_emit_i64" [ x ]);
|
||||||
eu64 = (fun x -> unit_rt "flan_dev_watch_emit_u64" [ x ]);
|
eu64 = (fun x -> unit_rt "flan_dev_watch_emit_u64" [ x ]);
|
||||||
ef64 = (fun x -> unit_rt "flan_dev_watch_emit_f64" [ x ]);
|
ef64 = (fun x -> unit_rt "flan_dev_watch_emit_f64" [ x ]);
|
||||||
edyn = (fun x -> unit_rt "flan_dyn_emit_watch" [ x ]) }
|
edyn = (fun x -> unit_rt "flan_dyn_emit_watch" [ x ]);
|
||||||
|
enested = (fun x -> unit_rt "flan_dyn_emit_watch" [ x ]) }
|
||||||
in
|
in
|
||||||
(* A place is read where it stands; anything else is bound to a slot of
|
(* A place is read where it stands; anything else is bound to a slot of
|
||||||
this frame first, so the walk — which names its argument once per
|
this frame first, so the walk — which names its argument once per
|
||||||
|
|||||||
@ -5122,6 +5122,7 @@ declare void @flan_dyn_set_at(i64, i64, i64, ptr, i64)
|
|||||||
declare void @flan_dyn_push(i64, i64, ptr, i64)
|
declare void @flan_dyn_push(i64, i64, ptr, i64)
|
||||||
declare void @flan_dyn_print(i64)
|
declare void @flan_dyn_print(i64)
|
||||||
declare void @flan_dyn_print_at(i64, ptr, i64)
|
declare void @flan_dyn_print_at(i64, ptr, i64)
|
||||||
|
declare void @flan_dyn_print_nested_at(i64, ptr, i64)
|
||||||
declare void @flan_dyn_emit_dev(i64)
|
declare void @flan_dyn_emit_dev(i64)
|
||||||
declare void @flan_dyn_emit_watch(i64)
|
declare void @flan_dyn_emit_watch(i64)
|
||||||
; The watch table, which (watch "name" v) renders into. flan_dev.c is linked
|
; The watch table, which (watch "name" v) renders into. flan_dev.c is linked
|
||||||
|
|||||||
@ -166,7 +166,7 @@ let refusal c (ty : Types.t) : string option =
|
|||||||
{ Render.structs = c.structs; datas = c.datas; unions = c.unions;
|
{ Render.structs = c.structs; datas = c.datas; unions = c.unions;
|
||||||
enums = c.enums;
|
enums = c.enums;
|
||||||
emit = { Render.ebytes = emit; estr = emit; ei64 = emit; eu64 = emit;
|
emit = { Render.ebytes = emit; estr = emit; ei64 = emit; eu64 = emit;
|
||||||
ef64 = emit; edyn = emit };
|
ef64 = emit; edyn = emit; enested = emit };
|
||||||
ptrs = Some { Render.live = (fun _ -> { unit_ with ty = Types.Bool });
|
ptrs = Some { Render.live = (fun _ -> { unit_ with ty = Types.Bool });
|
||||||
bytechar = emit; epitaph = emit };
|
bytechar = emit; epitaph = emit };
|
||||||
alloc = (fun _ -> 0) }
|
alloc = (fun _ -> 0) }
|
||||||
|
|||||||
@ -35,6 +35,10 @@ type emitter = {
|
|||||||
runtime's to read — so the runtime renders it, into the same place the
|
runtime's to read — so the runtime renders it, into the same place the
|
||||||
other four write to. *)
|
other four write to. *)
|
||||||
edyn : Tast.expr -> Tast.expr;
|
edyn : Tast.expr -> Tast.expr;
|
||||||
|
(* The same, for a dyn inside a larger value: a text quoted and a char as
|
||||||
|
its literal. The same as [edyn] on the inspecting side, which quotes at
|
||||||
|
the top too; [println]'s differs, since its top level is raw. *)
|
||||||
|
enested : Tast.expr -> Tast.expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
(* What a walk is allowed to do with a pointer, and it is exactly two
|
(* What a walk is allowed to do with a pointer, and it is exactly two
|
||||||
@ -155,10 +159,12 @@ let rec render ?(refuse = print_refusal) c depth (e : Tast.expr) : Tast.expr lis
|
|||||||
| Types.Float _ -> [ c.emit.ef64 (cast (Types.Float Types.F64) e) ]
|
| Types.Float _ -> [ c.emit.ef64 (cast (Types.Float Types.F64) e) ]
|
||||||
| Types.Bool ->
|
| Types.Bool ->
|
||||||
[ unit_ (Tast.If (e, lit "true", lit "false")) ]
|
[ unit_ (Tast.If (e, lit "true", lit "false")) ]
|
||||||
(* A char prints as a dyn char does, as its literal: the runtime's one
|
(* A char inside a value, or in the inspector, prints as its literal, by
|
||||||
spelling, so the typed and dyn sides cannot drift apart. *)
|
the runtime's one spelling so the typed and dyn sides cannot drift
|
||||||
|
apart. [println]'s top level is the character itself, and check.ml's
|
||||||
|
print arm takes that case before the walk. *)
|
||||||
| Types.Char ->
|
| Types.Char ->
|
||||||
[ c.emit.edyn
|
[ c.emit.enested
|
||||||
{ Tast.e = Tast.Prim (Tast.Rt "flan_dyn_from_char", [ cast (Types.Int Types.I32) e ]);
|
{ Tast.e = Tast.Prim (Tast.Rt "flan_dyn_from_char", [ cast (Types.Int Types.I32) e ]);
|
||||||
ty = Types.Dyn; loc } ]
|
ty = Types.Dyn; loc } ]
|
||||||
(* Evaluated *and then* reported. A Unit expression is almost always a call
|
(* Evaluated *and then* reported. A Unit expression is almost always a call
|
||||||
@ -443,7 +449,7 @@ let rec render ?(refuse = print_refusal) c depth (e : Tast.expr) : Tast.expr lis
|
|||||||
does not — so the printing belongs on the side that can see the tag, and
|
does not — so the printing belongs on the side that can see the tag, and
|
||||||
the walk hands the whole value to [c.emit.edyn], which names the runtime
|
the walk hands the whole value to [c.emit.edyn], which names the runtime
|
||||||
entry point that renders into this emitter's sink. *)
|
entry point that renders into this emitter's sink. *)
|
||||||
| Types.Dyn -> [ c.emit.edyn e ]
|
| Types.Dyn -> [ (if depth = 0 then c.emit.edyn else c.emit.enested) e ]
|
||||||
(* Reachable: [(println m)] on a Map. Everything else in [Types.t] has an
|
(* Reachable: [(println m)] on a Map. Everything else in [Types.t] has an
|
||||||
arm above, and a [Var] never reaches a backend. So this names the fix
|
arm above, and a [Var] never reaches a backend. So this names the fix
|
||||||
rather than only the refusal. *)
|
rather than only the refusal. *)
|
||||||
|
|||||||
@ -1556,6 +1556,10 @@ let dev_emitter : Render.emitter =
|
|||||||
(* Into the value buffer, not stdout: a dyn expression's value belongs in
|
(* Into the value buffer, not stdout: a dyn expression's value belongs in
|
||||||
the reply's value like any other. *)
|
the reply's value like any other. *)
|
||||||
edyn =
|
edyn =
|
||||||
|
(fun x ->
|
||||||
|
{ Tast.e = Tast.Prim (Tast.Rt "flan_dyn_emit_dev", [ x ]);
|
||||||
|
ty = Types.Unit; loc = x.Tast.loc });
|
||||||
|
enested =
|
||||||
(fun x ->
|
(fun x ->
|
||||||
{ Tast.e = Tast.Prim (Tast.Rt "flan_dyn_emit_dev", [ x ]);
|
{ Tast.e = Tast.Prim (Tast.Rt "flan_dyn_emit_dev", [ x ]);
|
||||||
ty = Types.Unit; loc = x.Tast.loc }) }
|
ty = Types.Unit; loc = x.Tast.loc }) }
|
||||||
|
|||||||
@ -855,9 +855,12 @@ static void render(dyn_sink w, flan_dyn v, int depth, int nested) {
|
|||||||
else emit_n(w, obj_text_bytes(o), o->len);
|
else emit_n(w, obj_text_bytes(o), o->len);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* A char prints as the literal that reads back as it, at every depth. */
|
/* A char at the top of a println is the character itself, and anywhere a
|
||||||
|
* reader must tell it apart — inside a value, in the inspector — the
|
||||||
|
* literal that reads back as it: Clojure's println against its pr. */
|
||||||
case FLAN_DYN_TAG_CHAR:
|
case FLAN_DYN_TAG_CHAR:
|
||||||
char_spell((uint32_t)dyn_payload(v), buf);
|
if (nested) char_spell((uint32_t)dyn_payload(v), buf);
|
||||||
|
else buf[utf8_encode((uint32_t)dyn_payload(v), (uint8_t *)buf)] = '\0';
|
||||||
emit(w, buf);
|
emit(w, buf);
|
||||||
return;
|
return;
|
||||||
/* A keyword prints with its colon, bare, at every depth: :a is its own
|
/* A keyword prints with its colon, bare, at every depth: :a is its own
|
||||||
@ -4384,6 +4387,15 @@ void flan_dyn_print_at(flan_dyn v, const uint8_t *loc, int64_t loclen) {
|
|||||||
|
|
||||||
void flan_dyn_print(flan_dyn v) { flan_dyn_print_at(v, NULL, 0); }
|
void flan_dyn_print(flan_dyn v) { flan_dyn_print_at(v, NULL, 0); }
|
||||||
|
|
||||||
|
/* The same onto stdout, as a value inside a larger one prints: a text
|
||||||
|
* quoted and a char as its literal. println's walk calls it for a dyn or a
|
||||||
|
* char inside a typed struct, array or slice. */
|
||||||
|
void flan_dyn_print_nested_at(flan_dyn v, const uint8_t *loc, int64_t loclen) {
|
||||||
|
walk_site was = walk_enter(loc, loclen, "print");
|
||||||
|
render(flan_write_stdout, v, 0, 1);
|
||||||
|
walk_leave(was);
|
||||||
|
}
|
||||||
|
|
||||||
flan_dyn flan_dyn_eq_at(flan_dyn a, flan_dyn b, const uint8_t *loc,
|
flan_dyn flan_dyn_eq_at(flan_dyn a, flan_dyn b, const uint8_t *loc,
|
||||||
int64_t loclen) {
|
int64_t loclen) {
|
||||||
walk_site was = walk_enter(loc, loclen, "=");
|
walk_site was = walk_enter(loc, loclen, "=");
|
||||||
|
|||||||
@ -399,6 +399,8 @@ void flan_dyn_need_as(flan_dyn v, const uint8_t *want, int64_t wantlen,
|
|||||||
/* print, =, length and has-key? with the site they were written at: a view
|
/* print, =, length and has-key? with the site they were written at: a view
|
||||||
* that traps inside one names it. */
|
* that traps inside one names it. */
|
||||||
void flan_dyn_print_at(flan_dyn v, const uint8_t *loc, int64_t loclen);
|
void flan_dyn_print_at(flan_dyn v, const uint8_t *loc, int64_t loclen);
|
||||||
|
void flan_dyn_print_nested_at(flan_dyn v, const uint8_t *loc,
|
||||||
|
int64_t loclen);
|
||||||
flan_dyn flan_dyn_eq_at(flan_dyn a, flan_dyn b, const uint8_t *loc,
|
flan_dyn flan_dyn_eq_at(flan_dyn a, flan_dyn b, const uint8_t *loc,
|
||||||
int64_t loclen);
|
int64_t loclen);
|
||||||
flan_dyn flan_dyn_len_at(flan_dyn v, const uint8_t *loc, int64_t loclen);
|
flan_dyn flan_dyn_len_at(flan_dyn v, const uint8_t *loc, int64_t loclen);
|
||||||
|
|||||||
@ -1,19 +1,20 @@
|
|||||||
;;;; Every ASCII code point, the C1 controls, and a few past them, as dyn chars printed one per
|
;;;; Every ASCII code point, the C1 controls, and a few past them, as dyn chars printed one per
|
||||||
;;;; line. The test reads each line back with the reader and wants the same
|
;;;; line, each inside a vector, where a char prints as its literal. The test
|
||||||
;;;; code point, so what a char prints as is what reads as it.
|
;;;; reads each back with the reader and wants the same code point, so what a
|
||||||
|
;;;; char prints as there is what reads as it.
|
||||||
|
|
||||||
(defn main [] i32
|
(defn main [] i32
|
||||||
(let [v (vec-new u8)]
|
(let [v (vec-new u8)]
|
||||||
(dotimes [i 128] (push v (u8 i)))
|
(dotimes [i 128] (push v (u8 i)))
|
||||||
(let [t (the dyn (str (slice v)))]
|
(let [t (the dyn (str (slice v)))]
|
||||||
(dotimes [i (length t)] (println (at t i))))
|
(dotimes [i (length t)] (println [(at t i)])))
|
||||||
;; the C1 controls, U+0080 to U+009F, and U+00A0, each C2 then one byte
|
;; the C1 controls, U+0080 to U+009F, and U+00A0, each C2 then one byte
|
||||||
(let [w (vec-new u8)]
|
(let [w (vec-new u8)]
|
||||||
(dotimes [i 33] (push w (u8 0xC2)) (push w (u8 (+ 0x80 i))))
|
(dotimes [i 33] (push w (u8 0xC2)) (push w (u8 (+ 0x80 i))))
|
||||||
(let [c1 (the dyn (str (slice w)))]
|
(let [c1 (the dyn (str (slice w)))]
|
||||||
(dotimes [i (length c1)] (println (at c1 i))))
|
(dotimes [i (length c1)] (println [(at c1 i)])))
|
||||||
(free w))
|
(free w))
|
||||||
(let [u (the dyn "é日😀")]
|
(let [u (the dyn "é日😀")]
|
||||||
(dotimes [i (length u)] (println (at u i))))
|
(dotimes [i (length u)] (println [(at u i)])))
|
||||||
(free v))
|
(free v))
|
||||||
0)
|
0)
|
||||||
|
|||||||
@ -5518,8 +5518,8 @@ level "1"
|
|||||||
code point in a typed i32. With an argument, a dyn int at the i32
|
code point in a typed i32. With an argument, a dyn int at the i32
|
||||||
traps at the call. *)
|
traps at the call. *)
|
||||||
let dyn_char_out =
|
let dyn_char_out =
|
||||||
"\\I\n\\é\n\\日\n\\😀\n[\\a \\space \\( \\newline]\n:char\n\
|
"I\né\n日\n😀\n[\\a \\space \\( \\newline]\n:char\n\
|
||||||
6\n\\é\n\\日\n\\😀\n\\o\n日😀\n\
|
6\né\n日\n😀\no\n日😀\n\
|
||||||
[\\é \\日 \\😀 \\space \\o \\k]\n6\né日😀 ok\ntrue\n日\nok\n\
|
[\\é \\日 \\😀 \\space \\o \\k]\n6\né日😀 ok\ntrue\n日\nok\n\
|
||||||
true\nfalse\nfalse\nfalse\ntrue\nfalse\ntrue\n2\n128512\n5\n97\n"
|
true\nfalse\nfalse\nfalse\ntrue\nfalse\ntrue\n2\n128512\n5\n97\n"
|
||||||
in
|
in
|
||||||
@ -5531,9 +5531,9 @@ level "1"
|
|||||||
both ways. Then a dyn int at a char parameter and (char n) on a
|
both ways. Then a dyn int at a char parameter and (char n) on a
|
||||||
surrogate, each trapping at its own form. *)
|
surrogate, each trapping at its own form. *)
|
||||||
let char_out =
|
let char_out =
|
||||||
"\\a\n\\a\n\\é [\\é \\z \\space]\n233 233\n\
|
"a\na\né [\\é \\z \\space]\n233 233\n\
|
||||||
true true true true \\c \\q\n\\A \\😀 65 65 \\z\n\\B\n\
|
true true true true c q\nA 😀 65 65 z\nB\n\
|
||||||
(some 1) (some 2) false\n2\ntrue true\n122\né日\n\\q\n[\\x \\y]\n\\y\n"
|
(some 1) (some 2) false\n2\ntrue true\n122\né日\nq\n[\\x \\y]\ny\n"
|
||||||
in
|
in
|
||||||
outputs "char: a typed char" "programs/char.flan" char_out;
|
outputs "char: a typed char" "programs/char.flan" char_out;
|
||||||
outputs ~opt:"-O0" "char: a typed char, -O0" "programs/char.flan" char_out;
|
outputs ~opt:"-O0" "char: a typed char, -O0" "programs/char.flan" char_out;
|
||||||
@ -5560,8 +5560,8 @@ level "1"
|
|||||||
(* A String, and a str made from one, cross into dyn as text measured
|
(* A String, and a str made from one, cross into dyn as text measured
|
||||||
like any other: characters counted, ASCII or not. *)
|
like any other: characters counted, ASCII or not. *)
|
||||||
let string_char_out =
|
let string_char_out =
|
||||||
"ab 2 \\b\né日😀! 4 \\日 \\!\n日😀 [\\é \\日 \\😀 \\!]\n\
|
"ab 2 b\né日😀! 4 日 !\n日😀 [\\é \\日 \\😀 \\!]\n\
|
||||||
é日😀! 4 \\😀 true\n10 4\n"
|
é日😀! 4 😀 true\n10 4\n"
|
||||||
in
|
in
|
||||||
outputs "dyn: a String crossing counts chars"
|
outputs "dyn: a String crossing counts chars"
|
||||||
"programs/dyn-char-string.flan" string_char_out;
|
"programs/dyn-char-string.flan" string_char_out;
|
||||||
@ -5572,7 +5572,7 @@ level "1"
|
|||||||
(* A text pinned by crossing into a str keeps counting characters (the
|
(* A text pinned by crossing into a str keeps counting characters (the
|
||||||
pin's stamp and the text's measure live in different header fields),
|
pin's stamp and the text's measure live in different header fields),
|
||||||
and a char writes through a view of typed storage. *)
|
and a char writes through a view of typed storage. *)
|
||||||
let pinned_out = "9\n9\n9\n3\n\\日\né日\n122 26085 3\n97 2\n" in
|
let pinned_out = "9\n9\n9\n3\n日\né日\n122 26085 3\n97 2\n" in
|
||||||
outputs "dyn: a pinned text counts chars" "programs/dyn-char-pinned.flan"
|
outputs "dyn: a pinned text counts chars" "programs/dyn-char-pinned.flan"
|
||||||
pinned_out;
|
pinned_out;
|
||||||
outputs ~opt:"-O0" "dyn: a pinned text counts chars, -O0"
|
outputs ~opt:"-O0" "dyn: a pinned text counts chars, -O0"
|
||||||
@ -5648,7 +5648,8 @@ level "1"
|
|||||||
("x", "52:34: dyn: an i64 is wanted here, and this is a text, \
|
("x", "52:34: dyn: an i64 is wanted here, and this is a text, \
|
||||||
\"x\". An i64 takes an int or a char's code point") ])
|
\"x\". An i64 takes an int or a char's code point") ])
|
||||||
[ false; true ];
|
[ false; true ];
|
||||||
(* Print, then read: each char dyn-char-spell.flan prints — every ASCII
|
(* Print, then read: each char dyn-char-spell.flan prints inside a vector,
|
||||||
|
where a char shows its literal — every ASCII
|
||||||
code point, the C1 controls, then four past them — reads back as the code point it was,
|
code point, the C1 controls, then four past them — reads back as the code point it was,
|
||||||
and so does the compiler's own spelling of the same literal, which is
|
and so does the compiler's own spelling of the same literal, which is
|
||||||
what flan convert writes. *)
|
what flan convert writes. *)
|
||||||
@ -5676,7 +5677,12 @@ level "1"
|
|||||||
let code, text = run exe None in
|
let code, text = run exe None in
|
||||||
let lines = String.split_on_char '\n' text in
|
let lines = String.split_on_char '\n' text in
|
||||||
let lines = List.filteri (fun i _ -> i < List.length spelled) lines in
|
let lines = List.filteri (fun i _ -> i < List.length spelled) lines in
|
||||||
let got = List.map read_char lines in
|
let unwrap l =
|
||||||
|
let n = String.length l in
|
||||||
|
if n >= 2 && l.[0] = '[' && l.[n - 1] = ']' then String.sub l 1 (n - 2)
|
||||||
|
else l
|
||||||
|
in
|
||||||
|
let got = List.map (fun l -> read_char (unwrap l)) lines in
|
||||||
if code <> 0 || got <> List.map Option.some spelled then begin
|
if code <> 0 || got <> List.map Option.some spelled then begin
|
||||||
incr failures;
|
incr failures;
|
||||||
Printf.printf "FAIL dyn chars read back as printed%s\n \
|
Printf.printf "FAIL dyn chars read back as printed%s\n \
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user