1-- Formatting: a template whose placeholders take values of any type that
2-- can write itself into a builder. Nothing runs at compile time, so the
3-- template is checked against its values when it is formatted.
4
5--- The template does not fit its values: its `{}` placeholders and the
6--- values differ in number, or a brace is neither part of a `{}` nor
7--- doubled.
8public invalid_format: atom
9
10--- A value that writes its own text into a builder, which `format` fills
11--- a placeholder with. The library supplies it for the ten integer types
12--- in decimal, `bool` as `true` or `false`, `utf8`, `[]u8` and host
13--- strings as their bytes. A type of the program's own conforms with a
14--- function that appends its text; a different reading of a supplied type,
15--- hexadecimal for one, is a typed `append` call, or a `distinct` wrapper
16--- with its own conformance [1280].
17public formattable: type = concept (item: type)
18 --- Append the value's text, or report `no_space` and leave the
19 --- builder as it was.
20 append: (self: ptr item, inout into: builder) -> none ! no_space
21end formattable
22
23-- Check the template against the number of values: every `{` begins `{}`
24-- or `{{`, every `}` not closing a placeholder is doubled, and there are
25-- as many placeholders as values.
26checked: (source: []u8, count: usize) -> none ! invalid_format =
27 mut wanted: usize = 0
28 mut at: usize = 0
29 while at < lenof source do
30 current: u8 = source[at]
31 if current == 123 or current == 125 then
32 fail invalid_format when at + 1 >= lenof source
33 following: u8 = source[at + 1]
34 if current == 123 and following == 125 then
35 inc wanted
36 elsif following <> current then
37 fail invalid_format
38 end if
39 at += 2
40 else
41 inc at
42 end if
43 end while
44 fail invalid_format when wanted <> count
45end checked
46
47-- Write a checked template: each run of plain bytes, one brace of each
48-- doubled pair, and each value where its placeholder stands.
49written: (inout into: builder, source: []u8, values: []any formattable)
50 -> none ! no_space =
51 mut run: usize = 0
52 mut at: usize = 0
53 mut next: usize = 0
54 while at < lenof source do
55 current: u8 = source[at]
56 if current == 123 or current == 125 then
57 if current == 123 and source[at + 1] == 125 then
58 try append(into, source[run..<at])
59 value: any formattable = values[next]
60 try value.append(into)
61 inc next
62 else
63 try append(into, source[run..<at + 1])
64 end if
65 at += 2
66 run = at
67 else
68 inc at
69 end if
70 end while
71 try append(into, source[run..<at])
72end written
73
74--- Append `template` with each `{}` replaced by the next of `values`, in
75--- order, written by its own `formattable` conformance; `{{` writes `{`
76--- and `}}` writes `}`. A placeholder takes nothing else: width, padding
77--- and hexadecimal are the typed `append` and `align` calls. The template
78--- is checked first, so `invalid_format` writes nothing, and a builder that
79--- fills partway is put back as it was before `no_space` is reported. No
80--- locale is consulted: the text is the same everywhere.
81---
82--- The values are an array literal of erased addresses, which the call
83--- lends as a slice: `text.format(line, "{} {}\n", [any(addr count),
84--- any(addr name)])`.
85public format: (inout into: builder, template: utf8,
86 values: []any formattable)
87 -> none ! no_space | invalid_format =
88 source: []u8 = bytes(template)
89 try checked(source, lenof values)
90 start: usize = into.used
91 written(into, source, values) else (problem)
92 into.used = start
93 fail problem
94 end
95end format
96
97append_u8_value: (self: ptr u8, inout into: builder) -> none ! no_space =
98 try append_u32(into, u32(self.val))
99end append_u8_value
100append_u16_value: (self: ptr u16, inout into: builder) -> none ! no_space =
101 try append_u32(into, u32(self.val))
102end append_u16_value
103append_u32_value: (self: ptr u32, inout into: builder) -> none ! no_space =
104 try append_u32(into, self.val)
105end append_u32_value
106append_u64_value: (self: ptr u64, inout into: builder) -> none ! no_space =
107 try append_u64(into, self.val)
108end append_u64_value
109append_usize_value: (self: ptr usize, inout into: builder)
110 -> none ! no_space =
111 try append_usize(into, self.val)
112end append_usize_value
113append_i8_value: (self: ptr i8, inout into: builder) -> none ! no_space =
114 try append_i32(into, i32(self.val))
115end append_i8_value
116append_i16_value: (self: ptr i16, inout into: builder) -> none ! no_space =
117 try append_i32(into, i32(self.val))
118end append_i16_value
119append_i32_value: (self: ptr i32, inout into: builder) -> none ! no_space =
120 try append_i32(into, self.val)
121end append_i32_value
122append_i64_value: (self: ptr i64, inout into: builder) -> none ! no_space =
123 try append_i64(into, self.val)
124end append_i64_value
125append_isize_value: (self: ptr isize, inout into: builder)
126 -> none ! no_space =
127 try append_isize(into, self.val)
128end append_isize_value
129append_bool_value: (self: ptr bool, inout into: builder) -> none ! no_space =
130 if self.val then
131 try append(into, "true")
132 else
133 try append(into, "false")
134 end if
135end append_bool_value
136append_text_value: (self: ptr utf8, inout into: builder) -> none ! no_space =
137 try append_text(into, self.val)
138end append_text_value
139append_bytes_value: (self: ptr []u8, inout into: builder) -> none ! no_space =
140 try append(into, self.val)
141end append_bytes_value
142append_host_value: (self: ptr host_string, inout into: builder)
143 -> none ! no_space =
144 try append_host(into, self.val)
145end append_host_value
146
147u8 is formattable (append: append_u8_value)
148u16 is formattable (append: append_u16_value)
149u32 is formattable (append: append_u32_value)
150u64 is formattable (append: append_u64_value)
151usize is formattable (append: append_usize_value)
152i8 is formattable (append: append_i8_value)
153i16 is formattable (append: append_i16_value)
154i32 is formattable (append: append_i32_value)
155i64 is formattable (append: append_i64_value)
156isize is formattable (append: append_isize_value)
157bool is formattable (append: append_bool_value)
158utf8 is formattable (append: append_text_value)
159[]u8 is formattable (append: append_bytes_value)
160host_value is formattable (append: append_host_value)