lib/termtex/src/flat.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const alloc_arena = @import("alloc_arena");
3 const ast = @import("ast.zig");
4 const layout = @import("layout.zig");
5 const parse = @import("parse.zig");
6
7 pub fn renderAlloc(allocator: std.mem.Allocator, source: []const u8) ![]u8 {
8 var arena = alloc_arena.Arena.init(allocator);
9 defer arena.deinit();
10 const expr = try parse.parse(arena.allocator(), source);
11 var out: std.ArrayListUnmanaged(u8) = .empty;
12 errdefer out.deinit(allocator);
13 try appendFlat(allocator, arena.allocator(), &out, expr);
14 return out.toOwnedSlice(allocator);
15 }
16
17 fn appendFlat(
18 allocator: std.mem.Allocator,
19 scratch: std.mem.Allocator,
20 out: *std.ArrayListUnmanaged(u8),
21 expr: *const ast.Expr,
22 ) std.mem.Allocator.Error!void {
23 switch (expr.*) {
24 .row => |items| for (items) |item| try appendFlat(allocator, scratch, out, item),
25 .text => |value| try out.appendSlice(allocator, value),
26 .operator => |value| try appendFlat(allocator, scratch, out, value.body),
27 .space => |value| try appendFlatSpace(allocator, out, value),
28 .fraction => |value| {
29 try appendFlatGrouped(allocator, scratch, out, value.numerator);
30 try out.appendSlice(allocator, "⁄");
31 try appendFlatGrouped(allocator, scratch, out, value.denominator);
32 },
33 .sqrt => |value| {
34 try out.appendSlice(allocator, "√");
35 if (value.index) |index| {
36 try out.append(allocator, '[');
37 try appendFlat(allocator, scratch, out, index);
38 try out.append(allocator, ']');
39 }
40 try appendFlatGrouped(allocator, scratch, out, value.body);
41 },
42 .scripts => |value| {
43 try appendFlat(allocator, scratch, out, value.base);
44 if (value.sub) |sub| try appendScript(allocator, scratch, out, sub, .sub);
45 if (value.sup) |sup| try appendScript(allocator, scratch, out, sup, .sup);
46 },
47 .accent => |value| {
48 switch (value.mark) {
49 .bar => try out.appendSlice(allocator, "bar"),
50 .hat => try out.appendSlice(allocator, "hat"),
51 .vec => try out.appendSlice(allocator, "vec"),
52 .dot => try out.appendSlice(allocator, "dot"),
53 .underline => try out.appendSlice(allocator, "underline"),
54 .tilde => try out.appendSlice(allocator, "tilde"),
55 .check => try out.appendSlice(allocator, "check"),
56 .breve => try out.appendSlice(allocator, "breve"),
57 .ddot => try out.appendSlice(allocator, "ddot"),
58 .acute => try out.appendSlice(allocator, "acute"),
59 .grave => try out.appendSlice(allocator, "grave"),
60 .ring => try out.appendSlice(allocator, "ring"),
61 .overleft => try out.appendSlice(allocator, "overleftarrow"),
62 .overleftright => try out.appendSlice(allocator, "overleftrightarrow"),
63 }
64 try appendFlatGrouped(allocator, scratch, out, value.body);
65 },
66 .grid => |value| try appendFlatGrid(allocator, scratch, out, value),
67 .annotation => |value| try appendFlatAnnotation(allocator, scratch, out, value),
68 .delimited => |value| try appendFlatDelimited(allocator, scratch, out, value),
69 }
70 }
71
72 fn appendFlatSpace(
73 allocator: std.mem.Allocator,
74 out: *std.ArrayListUnmanaged(u8),
75 value: ast.Space,
76 ) std.mem.Allocator.Error!void {
77 if (out.items.len == 0) return;
78 for (0..layout.spaceCells(value)) |_| try out.append(allocator, ' ');
79 }
80
81 fn appendFlatDelimited(
82 allocator: std.mem.Allocator,
83 scratch: std.mem.Allocator,
84 out: *std.ArrayListUnmanaged(u8),
85 value: ast.Delimited,
86 ) std.mem.Allocator.Error!void {
87 if (flatDelimiter(value.left)) |delimiter| try out.appendSlice(allocator, delimiter);
88 try appendFlat(allocator, scratch, out, value.body);
89 if (flatDelimiter(value.right)) |delimiter| try out.appendSlice(allocator, delimiter);
90 }
91
92 fn appendFlatAnnotation(
93 allocator: std.mem.Allocator,
94 scratch: std.mem.Allocator,
95 out: *std.ArrayListUnmanaged(u8),
96 value: ast.Annotation,
97 ) std.mem.Allocator.Error!void {
98 switch (value.kind) {
99 .plain => {
100 if (value.over) |over| {
101 try out.appendSlice(allocator, "overset(");
102 try appendFlat(allocator, scratch, out, over);
103 try out.append(allocator, ',');
104 try appendFlat(allocator, scratch, out, value.base);
105 try out.append(allocator, ')');
106 } else if (value.under) |under| {
107 try out.appendSlice(allocator, "underset(");
108 try appendFlat(allocator, scratch, out, under);
109 try out.append(allocator, ',');
110 try appendFlat(allocator, scratch, out, value.base);
111 try out.append(allocator, ')');
112 } else {
113 try appendFlat(allocator, scratch, out, value.base);
114 }
115 },
116 .overbrace => {
117 try out.appendSlice(allocator, "overbrace");
118 try appendFlatGrouped(allocator, scratch, out, value.base);
119 },
120 .underbrace => {
121 try out.appendSlice(allocator, "underbrace");
122 try appendFlatGrouped(allocator, scratch, out, value.base);
123 },
124 .boxed => {
125 try out.append(allocator, '[');
126 try appendFlat(allocator, scratch, out, value.base);
127 try out.append(allocator, ']');
128 },
129 }
130 }
131
132 fn appendFlatGrid(
133 allocator: std.mem.Allocator,
134 scratch: std.mem.Allocator,
135 out: *std.ArrayListUnmanaged(u8),
136 value: ast.Grid,
137 ) std.mem.Allocator.Error!void {
138 if (flatLeftFence(value.fence)) |fence| try out.appendSlice(allocator, fence);
139 for (value.rows, 0..) |row, row_index| {
140 if (row_index != 0) try out.append(allocator, ';');
141 for (row.cells, 0..) |cell, cell_index| {
142 if (cell_index != 0) try out.append(allocator, ',');
143 try appendFlat(allocator, scratch, out, cell);
144 }
145 }
146 if (flatRightFence(value.fence)) |fence| try out.appendSlice(allocator, fence);
147 }
148
149 fn appendFlatGrouped(
150 allocator: std.mem.Allocator,
151 scratch: std.mem.Allocator,
152 out: *std.ArrayListUnmanaged(u8),
153 expr: *const ast.Expr,
154 ) std.mem.Allocator.Error!void {
155 const grouped = switch (expr.*) {
156 .text => false,
157 .operator => false,
158 .space => false,
159 .scripts => false,
160 else => true,
161 };
162 if (grouped) try out.append(allocator, '(');
163 try appendFlat(allocator, scratch, out, expr);
164 if (grouped) try out.append(allocator, ')');
165 }
166
167 fn flatLeftFence(fence: ast.GridFence) ?[]const u8 {
168 return switch (fence) {
169 .none => null,
170 .paren => "(",
171 .bracket => "[",
172 .brace, .left_brace => "{",
173 .bar => "|",
174 .double_bar => "‖",
175 };
176 }
177
178 fn flatRightFence(fence: ast.GridFence) ?[]const u8 {
179 return switch (fence) {
180 .none, .left_brace => null,
181 .paren => ")",
182 .bracket => "]",
183 .brace => "}",
184 .bar => "|",
185 .double_bar => "‖",
186 };
187 }
188
189 fn flatDelimiter(delimiter: ast.Delimiter) ?[]const u8 {
190 return switch (delimiter) {
191 .none => null,
192 .shape => |shape| flatDelimiterShape(shape),
193 .text => |value| value,
194 };
195 }
196
197 fn flatDelimiterShape(shape: ast.DelimiterShape) []const u8 {
198 return switch (shape) {
199 .left_paren => "(",
200 .right_paren => ")",
201 .left_bracket => "[",
202 .right_bracket => "]",
203 .left_brace => "{",
204 .right_brace => "}",
205 .bar => "|",
206 .double_bar => "‖",
207 .left_angle => "⟨",
208 .right_angle => "⟩",
209 .left_double_angle => "⟪",
210 .right_double_angle => "⟫",
211 .left_double_bracket => "⟦",
212 .right_double_bracket => "⟧",
213 .left_floor => "⌊",
214 .right_floor => "⌋",
215 .left_ceil => "⌈",
216 .right_ceil => "⌉",
217 };
218 }
219
220 const ScriptMode = enum {
221 sub,
222 sup,
223 };
224
225 fn appendScript(
226 allocator: std.mem.Allocator,
227 scratch: std.mem.Allocator,
228 out: *std.ArrayListUnmanaged(u8),
229 expr: *const ast.Expr,
230 mode: ScriptMode,
231 ) std.mem.Allocator.Error!void {
232 var raw: std.ArrayListUnmanaged(u8) = .empty;
233 defer raw.deinit(scratch);
234 try appendFlat(scratch, scratch, &raw, expr);
235 if (try appendMappedScript(allocator, out, raw.items, mode)) return;
236 try out.append(allocator, switch (mode) {
237 .sup => '^',
238 .sub => '_',
239 });
240 try out.append(allocator, '(');
241 try out.appendSlice(allocator, raw.items);
242 try out.append(allocator, ')');
243 }
244
245 fn appendMappedScript(
246 allocator: std.mem.Allocator,
247 out: *std.ArrayListUnmanaged(u8),
248 raw: []const u8,
249 mode: ScriptMode,
250 ) std.mem.Allocator.Error!bool {
251 var index: usize = 0;
252 while (index < raw.len) {
253 const len = std.unicode.utf8ByteSequenceLength(raw[index]) catch return false;
254 const end = index + @min(len, raw.len - index);
255 if (scriptGlyph(raw[index..end], mode) == null) return false;
256 index = end;
257 }
258 index = 0;
259 while (index < raw.len) {
260 const len = std.unicode.utf8ByteSequenceLength(raw[index]) catch unreachable;
261 const end = index + @min(len, raw.len - index);
262 try out.appendSlice(allocator, scriptGlyph(raw[index..end], mode).?);
263 index = end;
264 }
265 return true;
266 }
267
268 fn scriptGlyph(value: []const u8, mode: ScriptMode) ?[]const u8 {
269 return switch (mode) {
270 .sup => superscriptGlyph(value),
271 .sub => subscriptGlyph(value),
272 };
273 }
274
275 fn superscriptGlyph(value: []const u8) ?[]const u8 {
276 if (std.mem.eql(u8, value, "0")) return "⁰";
277 if (std.mem.eql(u8, value, "1")) return "¹";
278 if (std.mem.eql(u8, value, "2")) return "²";
279 if (std.mem.eql(u8, value, "3")) return "³";
280 if (std.mem.eql(u8, value, "4")) return "⁴";
281 if (std.mem.eql(u8, value, "5")) return "⁵";
282 if (std.mem.eql(u8, value, "6")) return "⁶";
283 if (std.mem.eql(u8, value, "7")) return "⁷";
284 if (std.mem.eql(u8, value, "8")) return "⁸";
285 if (std.mem.eql(u8, value, "9")) return "⁹";
286 if (std.mem.eql(u8, value, "+")) return "⁺";
287 if (std.mem.eql(u8, value, "-")) return "⁻";
288 if (std.mem.eql(u8, value, "=")) return "⁼";
289 if (std.mem.eql(u8, value, "(")) return "⁽";
290 if (std.mem.eql(u8, value, ")")) return "⁾";
291 if (std.mem.eql(u8, value, "n")) return "ⁿ";
292 if (std.mem.eql(u8, value, "i")) return "ⁱ";
293 return null;
294 }
295
296 fn subscriptGlyph(value: []const u8) ?[]const u8 {
297 if (std.mem.eql(u8, value, "0")) return "₀";
298 if (std.mem.eql(u8, value, "1")) return "₁";
299 if (std.mem.eql(u8, value, "2")) return "₂";
300 if (std.mem.eql(u8, value, "3")) return "₃";
301 if (std.mem.eql(u8, value, "4")) return "₄";
302 if (std.mem.eql(u8, value, "5")) return "₅";
303 if (std.mem.eql(u8, value, "6")) return "₆";
304 if (std.mem.eql(u8, value, "7")) return "₇";
305 if (std.mem.eql(u8, value, "8")) return "₈";
306 if (std.mem.eql(u8, value, "9")) return "₉";
307 if (std.mem.eql(u8, value, "+")) return "₊";
308 if (std.mem.eql(u8, value, "-")) return "₋";
309 if (std.mem.eql(u8, value, "=")) return "₌";
310 if (std.mem.eql(u8, value, "(")) return "₍";
311 if (std.mem.eql(u8, value, ")")) return "₎";
312 if (std.mem.eql(u8, value, "a")) return "ₐ";
313 if (std.mem.eql(u8, value, "e")) return "ₑ";
314 if (std.mem.eql(u8, value, "h")) return "ₕ";
315 if (std.mem.eql(u8, value, "i")) return "ᵢ";
316 if (std.mem.eql(u8, value, "j")) return "ⱼ";
317 if (std.mem.eql(u8, value, "k")) return "ₖ";
318 if (std.mem.eql(u8, value, "l")) return "ₗ";
319 if (std.mem.eql(u8, value, "m")) return "ₘ";
320 if (std.mem.eql(u8, value, "n")) return "ₙ";
321 if (std.mem.eql(u8, value, "o")) return "ₒ";
322 if (std.mem.eql(u8, value, "p")) return "ₚ";
323 if (std.mem.eql(u8, value, "r")) return "ᵣ";
324 if (std.mem.eql(u8, value, "s")) return "ₛ";
325 if (std.mem.eql(u8, value, "t")) return "ₜ";
326 if (std.mem.eql(u8, value, "u")) return "ᵤ";
327 if (std.mem.eql(u8, value, "v")) return "ᵥ";
328 if (std.mem.eql(u8, value, "x")) return "ₓ";
329 return null;
330 }