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 }