xmmath 1.0.9 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1381 @@
1
+ // 归一化阶段:分词树(ast)之后的语义处理,全部为纯数据操作,不依赖 DOM。
2
+ import type { tree, vtype } from "./ast.js";
3
+ import emoji from "./emoji.json?raw";
4
+ import symbols from "./symbols.json?raw";
5
+
6
+ type fdic = { [id: string]: tree };
7
+
8
+ /** @see https://github.com/typst/codex/blob/v0.3.0/src/modules/sym.txt (typst v0.15.1) */
9
+ const s = JSON.parse(symbols);
10
+
11
+ // symbols路径简写
12
+ function simple_dot(s: any) {
13
+ const ss: { [id: string]: string } = {};
14
+ for (const i in s) {
15
+ if (typeof s[i] === "string") {
16
+ ss[i] = s[i];
17
+ } else {
18
+ for (const objOrStr of s[i]) {
19
+ // 第二层的array
20
+ if (typeof objOrStr === "string") {
21
+ ss[i] = objOrStr;
22
+ } else {
23
+ if (!ss[i]) ss[i] = objOrStr[Object.keys(objOrStr)[0]];
24
+ for (const j in objOrStr) {
25
+ // 第三层的obj
26
+ ss[`${i}.${j}`] = objOrStr[j];
27
+ // 允许部分索引,但要保证唯一
28
+ const l = j.split(".");
29
+ for (let n = 1; n < l.length; n++) {
30
+ const shotKey = l.slice(0, n).join(".");
31
+ if (!objOrStr[shotKey]) {
32
+ ss[`${i}.${shotKey}`] = objOrStr[j];
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
38
+ }
39
+ }
40
+ return ss;
41
+ }
42
+ const ss = simple_dot(s);
43
+
44
+ /** @see https://github.com/typst/codex/blob/v0.3.0/src/modules/emoji.txt (typst v0.15.1) */
45
+ const emojix = simple_dot(JSON.parse(emoji));
46
+
47
+ const shorthand = {
48
+ "->": "arrow.r",
49
+ "|->": "arrow.r.bar",
50
+ "=>": "arrow.r.double",
51
+ "|=>": "arrow.r.double.bar",
52
+ "==>": "arrow.r.double.long",
53
+ "-->": "arrow.r.long",
54
+ "~~>": "arrow.r.long.squiggly",
55
+ "~>": "arrow.r.squiggly",
56
+ ">->": "arrow.r.tail",
57
+ "->>": "arrow.r.twohead",
58
+ "<-": "arrow.l",
59
+ "<==": "arrow.l.double.long",
60
+ "<--": "arrow.l.long",
61
+ "<~~": "arrow.l.long.squiggly",
62
+ "<~": "arrow.l.squiggly",
63
+ "<-<": "arrow.l.tail",
64
+ "<<-": "arrow.l.twohead",
65
+ "<->": "arrow.l.r",
66
+ "<=>": "arrow.l.r.double",
67
+ "<==>": "arrow.l.r.double.long",
68
+ "<-->": "arrow.l.r.long",
69
+ "*": "convolve",
70
+ "||": "bar.v.double",
71
+ "[|": "bracket.l.stroked",
72
+ "|]": "bracket.r.stroked",
73
+ ":=": "colon.eq",
74
+ "::=": "colon.double.eq",
75
+ "--": "dash.en",
76
+ "---": "dash.em",
77
+ "...": "dots.h",
78
+ "=:": "eq.colon",
79
+ "!=": "eq.not",
80
+ ">>": "gt.double",
81
+ ">=": "gt.eq",
82
+ ">>>": "gt.triple",
83
+ "-?": "hyph.soft",
84
+ "<<": "lt.double",
85
+ "<=": "lt.eq",
86
+ "<<<": "lt.triple",
87
+ "-": "minus",
88
+ "'": "prime",
89
+ "~": "space.nobreak",
90
+ };
91
+ let max_shorthand_len = 0;
92
+ for (const i in shorthand) {
93
+ if (i.length > max_shorthand_len) {
94
+ max_shorthand_len = i.length;
95
+ }
96
+ }
97
+
98
+ // 直接跟参数的符号(分词归一化用;渲染实现见 main 的 ff,需与这里的键保持一致)
99
+ const direct_arg_sy = ["√", "∛", "∜"];
100
+
101
+ const opl: { id: string; str?: string; limits?: boolean }[] = [
102
+ { id: "arccos" },
103
+ { id: "arcsin" },
104
+ { id: "arctan" },
105
+ { id: "arg" },
106
+ { id: "cos" },
107
+ { id: "cosh" },
108
+ { id: "cot" },
109
+ { id: "ctg" },
110
+ { id: "coth" },
111
+ { id: "csc" },
112
+ { id: "csch" },
113
+ { id: "deg" },
114
+ { id: "det", limits: true },
115
+ { id: "dim" },
116
+ { id: "exp" },
117
+ { id: "gcd", limits: true },
118
+ { id: "hom" },
119
+ { id: "mod" },
120
+ { id: "id" },
121
+ { id: "im" },
122
+ { id: "inf", limits: true },
123
+ { id: "ker" },
124
+ { id: "lg" },
125
+ { id: "lim", limits: true },
126
+ { id: "ln" },
127
+ { id: "log" },
128
+ { id: "max", limits: true },
129
+ { id: "min", limits: true },
130
+ { id: "Pr", limits: true },
131
+ { id: "sec" },
132
+ { id: "sech" },
133
+ { id: "sin" },
134
+ { id: "sinc" },
135
+ { id: "sinh" },
136
+ { id: "sup", limits: true },
137
+ { id: "tan" },
138
+ { id: "tg" },
139
+ { id: "tr" },
140
+ { id: "tanh" },
141
+ { id: "liminf", str: "lim inf", limits: true },
142
+ { id: "limsup", str: "lim sup", limits: true },
143
+ { id: "dif", str: "d" },
144
+ { id: "Dif", str: "D" },
145
+ ];
146
+
147
+ const limits_f = [];
148
+
149
+ // 官方核对(typst v0.11.1;语法解析一律以 0.11.1 为准):
150
+ // 依据 typst v0.11.1 `crates/typst/src/math/attach.rs::Limits::for_char` —— 底座字符的 Unicode
151
+ // 数学类为 Relation 时 `Limits::Always`(`_`/`^` 落正上/正下,与 display 无关);Large 类为
152
+ // `Limits::Display`(积分特例 Never);其余一律 scripts。类表即 v0.11.1 Cargo.lock 锁定的
153
+ // unicode-math-class 0.1.0(Unicode MathClass-15.txt,REVISION 15)。
154
+ // 注意:typst 会在 fragment.rs::Glyph::with_id 里把 ':' 特例成 Relation、把 '⋯'/'⋮'/'⋱'/'⋰' 特例成
155
+ // Normal,但那只改间距用的 class;limits 查的是 for_char 里未经特例的原始类,故 ':' 仍走 scripts。
156
+ // 实测核对(2026-10-05):rel_names ∪ 官方全部 Relation 名共 426 个字符,用 v0.11.1 二进制
157
+ // `measure()` 量帧宽差 —— limits 取 max(底座, 附着) 宽、scripts 另加 space_after_script,
158
+ // 故 d = a - w - wref 在 limits 时 ≈ -(w+space) < -w/2、scripts 时 ≈ 0;实测 425 个 Relation 全走
159
+ // limits(ratio ≥ 2.07)、唯一非 Relation 的 ':' 全走 scripts(ratio 0.00),与类表 100% 吻合。
160
+ // 据此修正:移除误写的 `colon`,补入 22 项漏写的官方 Relation(bag*/bot/bowtie/dots*/
161
+ // harpoon(s)/mustache*/or.dot/parallel.slanted/prec.curly/succ.curly/tack/tack.t/tilde)。
162
+ // `arrow.*` 官方同为 Relation,但由下面 is_limit 的前缀规则覆盖,不在此列;
163
+ // `arrows.*` 前缀不同(split('.')[0] 是 "arrows"),必须靠本表生效。
164
+ // 版本敏感:同一探针在 typst 0.15.1 上已漂移 11 个字符 —— `:` 由 scripts 翻成 limits、
165
+ // ⟆⊥⋯⋱⋰⋮⎱⟇(8 个)由 limits 翻成 scripts、⟅⎰ 直接禁止附角标;故升级 typst 属"版本升级任务",
166
+ // 必须用新版本二进制重跑核对;平时 symbols.json 随 0.15.1 增长,本表判定仍按 0.11.1。
167
+ // 本表按 名称 -> 字符 -> 官方类 三段判定,名称集来自 symbols.json(codex v0.15.1),
168
+ // 上游新增 Relation 名时需同步补入本表。
169
+ const rel_names = new Set([
170
+ "angle.azimuth",
171
+ "angzarr",
172
+ "approx",
173
+ "approx.eq",
174
+ "approx.hat",
175
+ "approx.not",
176
+ "arrows.bb",
177
+ "arrows.bt",
178
+ "arrows.ll",
179
+ "arrows.lll",
180
+ "arrows.lr",
181
+ "arrows.rl",
182
+ "arrows.rr",
183
+ "arrows.rrr",
184
+ "arrows.tb",
185
+ "arrows.tt",
186
+ "asymp",
187
+ "asymp.not",
188
+ "bag",
189
+ "bag.l",
190
+ "bag.r",
191
+ "because",
192
+ "bot",
193
+ "bowtie",
194
+ "bowtie.filled",
195
+ "bowtie.filled.l",
196
+ "bowtie.filled.r",
197
+ "bowtie.stroked",
198
+ "colon.double",
199
+ "colon.double.eq",
200
+ "colon.eq",
201
+ "dagger",
202
+ "dagger.double",
203
+ "dash.colon",
204
+ "divides",
205
+ "divides.not",
206
+ "divides.not.rev",
207
+ "divides.struck",
208
+ "dots",
209
+ "dots.down",
210
+ "dots.h.c",
211
+ "dots.up",
212
+ "dots.v",
213
+ "eq",
214
+ "eq.ast",
215
+ "eq.colon",
216
+ "eq.def",
217
+ "eq.delta",
218
+ "eq.dot",
219
+ "eq.dots",
220
+ "eq.dots.down",
221
+ "eq.dots.up",
222
+ "eq.equi",
223
+ "eq.est",
224
+ "eq.gt",
225
+ "eq.lt",
226
+ "eq.m",
227
+ "eq.not",
228
+ "eq.prec",
229
+ "eq.quad",
230
+ "eq.quest",
231
+ "eq.star",
232
+ "eq.succ",
233
+ "eq.triple",
234
+ "eq.triple.not",
235
+ "equiv",
236
+ "equiv.not",
237
+ "forces",
238
+ "forces.not",
239
+ "frown",
240
+ "gt",
241
+ "gt.approx",
242
+ "gt.arc",
243
+ "gt.arc.eq",
244
+ "gt.closed",
245
+ "gt.closed.eq",
246
+ "gt.closed.eq.not",
247
+ "gt.closed.not",
248
+ "gt.dot",
249
+ "gt.double",
250
+ "gt.double.nested",
251
+ "gt.eq",
252
+ "gt.eq.lt",
253
+ "gt.eq.not",
254
+ "gt.eq.slant",
255
+ "gt.equiv",
256
+ "gt.lt",
257
+ "gt.lt.not",
258
+ "gt.napprox",
259
+ "gt.neq",
260
+ "gt.nequiv",
261
+ "gt.not",
262
+ "gt.ntilde",
263
+ "gt.quest",
264
+ "gt.tilde",
265
+ "gt.tilde.not",
266
+ "gt.tri",
267
+ "gt.tri.eq",
268
+ "gt.tri.eq.not",
269
+ "gt.tri.not",
270
+ "gt.triple",
271
+ "gt.triple.nested",
272
+ "harpoon",
273
+ "harpoon.bl",
274
+ "harpoon.bl.bar",
275
+ "harpoon.bl.stop",
276
+ "harpoon.br",
277
+ "harpoon.br.bar",
278
+ "harpoon.br.stop",
279
+ "harpoon.lb",
280
+ "harpoon.lb.bar",
281
+ "harpoon.lb.rb",
282
+ "harpoon.lb.rt",
283
+ "harpoon.lb.stop",
284
+ "harpoon.lt",
285
+ "harpoon.lt.bar",
286
+ "harpoon.lt.rb",
287
+ "harpoon.lt.rt",
288
+ "harpoon.lt.stop",
289
+ "harpoon.rb",
290
+ "harpoon.rb.bar",
291
+ "harpoon.rb.stop",
292
+ "harpoon.rt",
293
+ "harpoon.rt.bar",
294
+ "harpoon.rt.stop",
295
+ "harpoon.tl",
296
+ "harpoon.tl.bar",
297
+ "harpoon.tl.bl",
298
+ "harpoon.tl.br",
299
+ "harpoon.tl.stop",
300
+ "harpoon.tr",
301
+ "harpoon.tr.bar",
302
+ "harpoon.tr.bl",
303
+ "harpoon.tr.br",
304
+ "harpoon.tr.stop",
305
+ "harpoons",
306
+ "harpoons.blbr",
307
+ "harpoons.bltr",
308
+ "harpoons.lbrb",
309
+ "harpoons.ltlb",
310
+ "harpoons.ltrb",
311
+ "harpoons.ltrt",
312
+ "harpoons.rblb",
313
+ "harpoons.rtlb",
314
+ "harpoons.rtlt",
315
+ "harpoons.rtrb",
316
+ "harpoons.tlbr",
317
+ "harpoons.tltr",
318
+ "image",
319
+ "in",
320
+ "in.not",
321
+ "in.rev",
322
+ "in.rev.not",
323
+ "in.rev.small",
324
+ "in.small",
325
+ "lat",
326
+ "lat.eq",
327
+ "lt",
328
+ "lt.approx",
329
+ "lt.arc",
330
+ "lt.arc.eq",
331
+ "lt.closed",
332
+ "lt.closed.eq",
333
+ "lt.closed.eq.not",
334
+ "lt.closed.not",
335
+ "lt.dot",
336
+ "lt.double",
337
+ "lt.double.nested",
338
+ "lt.eq",
339
+ "lt.eq.gt",
340
+ "lt.eq.not",
341
+ "lt.eq.slant",
342
+ "lt.equiv",
343
+ "lt.gt",
344
+ "lt.gt.not",
345
+ "lt.napprox",
346
+ "lt.neq",
347
+ "lt.nequiv",
348
+ "lt.not",
349
+ "lt.ntilde",
350
+ "lt.quest",
351
+ "lt.tilde",
352
+ "lt.tilde.not",
353
+ "lt.tri",
354
+ "lt.tri.eq",
355
+ "lt.tri.eq.not",
356
+ "lt.tri.not",
357
+ "lt.triple",
358
+ "lt.triple.nested",
359
+ "mapsfrom",
360
+ "mapsfrom.long",
361
+ "mapsto",
362
+ "mapsto.long",
363
+ "minus.tilde",
364
+ "models",
365
+ "multimap",
366
+ "multimap.double",
367
+ "mustache",
368
+ "mustache.l",
369
+ "mustache.r",
370
+ "or.dot",
371
+ "original",
372
+ "parallel",
373
+ "parallel.eq",
374
+ "parallel.equiv",
375
+ "parallel.not",
376
+ "parallel.slanted",
377
+ "parallel.slanted.eq",
378
+ "parallel.slanted.eq.tilde",
379
+ "parallel.slanted.equiv",
380
+ "parallel.struck",
381
+ "parallel.tilde",
382
+ "perp",
383
+ "plus.o.arrow",
384
+ "prec",
385
+ "prec.approx",
386
+ "prec.curly",
387
+ "prec.curly.eq",
388
+ "prec.curly.eq.not",
389
+ "prec.double",
390
+ "prec.eq",
391
+ "prec.equiv",
392
+ "prec.napprox",
393
+ "prec.neq",
394
+ "prec.nequiv",
395
+ "prec.not",
396
+ "prec.ntilde",
397
+ "prec.tilde",
398
+ "prop",
399
+ "ratio",
400
+ "semi.rev",
401
+ "smile",
402
+ "smt",
403
+ "smt.eq",
404
+ "subset",
405
+ "subset.approx",
406
+ "subset.closed",
407
+ "subset.closed.eq",
408
+ "subset.dot",
409
+ "subset.double",
410
+ "subset.eq",
411
+ "subset.eq.dot",
412
+ "subset.eq.not",
413
+ "subset.eq.sq",
414
+ "subset.eq.sq.not",
415
+ "subset.equiv",
416
+ "subset.neq",
417
+ "subset.nequiv",
418
+ "subset.not",
419
+ "subset.plus",
420
+ "subset.sq",
421
+ "subset.sq.neq",
422
+ "subset.tilde",
423
+ "subset.times",
424
+ "succ",
425
+ "succ.approx",
426
+ "succ.curly",
427
+ "succ.curly.eq",
428
+ "succ.curly.eq.not",
429
+ "succ.double",
430
+ "succ.eq",
431
+ "succ.equiv",
432
+ "succ.napprox",
433
+ "succ.neq",
434
+ "succ.nequiv",
435
+ "succ.not",
436
+ "succ.ntilde",
437
+ "succ.tilde",
438
+ "supset",
439
+ "supset.approx",
440
+ "supset.closed",
441
+ "supset.closed.eq",
442
+ "supset.dot",
443
+ "supset.double",
444
+ "supset.eq",
445
+ "supset.eq.dot",
446
+ "supset.eq.not",
447
+ "supset.eq.sq",
448
+ "supset.eq.sq.not",
449
+ "supset.equiv",
450
+ "supset.neq",
451
+ "supset.nequiv",
452
+ "supset.not",
453
+ "supset.plus",
454
+ "supset.sq",
455
+ "supset.sq.neq",
456
+ "supset.tilde",
457
+ "supset.times",
458
+ "tack",
459
+ "tack.b.double",
460
+ "tack.b.short",
461
+ "tack.bb",
462
+ "tack.l",
463
+ "tack.l.double",
464
+ "tack.l.long",
465
+ "tack.l.r",
466
+ "tack.l.short",
467
+ "tack.ll",
468
+ "tack.r",
469
+ "tack.r.double",
470
+ "tack.r.double.not",
471
+ "tack.r.long",
472
+ "tack.r.not",
473
+ "tack.r.short",
474
+ "tack.rr",
475
+ "tack.rr.not",
476
+ "tack.rrr",
477
+ "tack.t",
478
+ "tack.t.double",
479
+ "tack.t.short",
480
+ "tack.tt",
481
+ "therefore",
482
+ "tilde",
483
+ "tilde.dot",
484
+ "tilde.eq",
485
+ "tilde.eq.not",
486
+ "tilde.eq.rev",
487
+ "tilde.equiv",
488
+ "tilde.equiv.not",
489
+ "tilde.nequiv",
490
+ "tilde.not",
491
+ "tilde.op",
492
+ "tilde.rev",
493
+ "tilde.rev.equiv",
494
+ "tilde.triple",
495
+ ]);
496
+ // 大算符:0.11.1 实测 inline 走角标、block(display) 才走上下
497
+ const limits_sy = [
498
+ // 大算符:0.11.1 实测 inline 走角标、block(display) 才走上下
499
+ "∏",
500
+ "∐",
501
+ "∑",
502
+ "⋀",
503
+ "⋁",
504
+ "⋂",
505
+ "⋃",
506
+ "⟕",
507
+ "⟖",
508
+ "⟗",
509
+ "⧸",
510
+ "⨀",
511
+ "⨁",
512
+ "⨂",
513
+ "⨃",
514
+ "⨄",
515
+ "⨅",
516
+ "⨆",
517
+ "⨉",
518
+ "⨝",
519
+ "⨼",
520
+ "⟘",
521
+ "⨊",
522
+ ];
523
+ for (const i in ss) {
524
+ for (const j of limits_sy) {
525
+ if (ss[i] === j) {
526
+ limits_f.push(i);
527
+ }
528
+ }
529
+ }
530
+
531
+ function out_kh(x: tree[0]) {
532
+ if (x.type === "group") {
533
+ if (x.kh === "()") return x.children;
534
+ else return [x];
535
+ } else {
536
+ return [x];
537
+ }
538
+ }
539
+
540
+ function transfer_kh(list: tree) {
541
+ // 未闭合组(kh 只有开括号)原样保留:由 render 的 group 分支只画开括号,
542
+ // 这里补 v_f(kh[1]) 会造出 value=undefined 的假右括号。
543
+ if (list.length === 1 && list[0].type === "group" && list[0].kh.length > 1)
544
+ list = [v_f(list[0].kh[0]), ...list[0].children, v_f(list[0].kh[1])];
545
+ return list;
546
+ }
547
+
548
+ function in_kh(x: tree) {
549
+ const k: tree = [{ type: "group", value: "", children: x, kh: "()" }];
550
+ return k;
551
+ }
552
+
553
+ function is_sup(x: tree[0]) {
554
+ return eqq(x, { type: "v", value: "^" });
555
+ }
556
+
557
+ function is_sub(x: tree[0]) {
558
+ return eqq(x, { type: "v", value: "_" });
559
+ }
560
+
561
+ function is_frac(x: tree[0]) {
562
+ return eqq(x, { type: "v", value: "/" });
563
+ }
564
+
565
+ function is_br(x: tree[0]) {
566
+ return x && x.value === "br" && x.esc;
567
+ }
568
+
569
+ const rel_sy = new Set<string>();
570
+ {
571
+ const taken = new Set<string>();
572
+ for (const k in ss) {
573
+ const v = ss[k];
574
+ if (!rel_names.has(k)) {
575
+ if (rel_sy.has(v)) taken.add(v); // 角标类符号抢占了关系类的字形
576
+ continue;
577
+ }
578
+ rel_sy.add(v);
579
+ }
580
+ for (const c of taken) rel_sy.delete(c);
581
+ }
582
+
583
+ // display/inline 是整条公式的属性,不是单个节点的:由 toMMLV 在渲染前设置一次,
584
+ // 嵌套子树(如 lr(sum_1^2) 里的 sum)自动继承,无需把标志穿过 40 个 render 调用点。
585
+ let display = true;
586
+ export function setDisplay(d: boolean) {
587
+ display = d;
588
+ }
589
+
590
+ export function isDisplay() {
591
+ return display;
592
+ }
593
+
594
+ function is_limit(tree: tree) {
595
+ if (tree.length === 1) {
596
+ const x = tree[0];
597
+ if (x.type === "f") {
598
+ // 只有裸符号才算子;`tilde(x)` 这类调用里 x.value 同名但不是运算符本体
599
+ if (!x.children || x.children.length === 0) {
600
+ if (rel_names.has(x.value)) return true;
601
+ if (limits_f.includes(x.value)) return display;
602
+ if (x.value.split(".").at(0) === "arrow") return true;
603
+ for (const i of opl) {
604
+ if (i.limits && x.value === i.id) return display;
605
+ }
606
+ }
607
+ if (x.value === "scripts") return false;
608
+ if (x.value === "limits") return true;
609
+ if (x.value === "op") {
610
+ const { dic } = f_attr(x);
611
+ return is_true(dic.limits) && display;
612
+ }
613
+ }
614
+ if (x.type === "v") {
615
+ if (rel_sy.has(x.value)) return true;
616
+ if (limits_sy.includes(x.value)) return display;
617
+ }
618
+ } else {
619
+ return false;
620
+ }
621
+ }
622
+
623
+ function is_dot(x: tree[0]) {
624
+ return eq(x, { type: "v", value: "." });
625
+ }
626
+
627
+ function is_dot_f(x: tree[0]) {
628
+ return x && (x.type === "f" || (x.type === "v" && x.value.match(/[a-z]/)));
629
+ }
630
+
631
+ function is_factorial(x: tree[0]) {
632
+ return x && x.type === "v" && x.value === "!" && !x.esc;
633
+ }
634
+
635
+ const dh: tree[0] = { type: "v", value: "," };
636
+
637
+ function v_f(str: string): tree[0] {
638
+ return { type: "v", value: str };
639
+ }
640
+
641
+ function eq(x0: tree[0], x1: tree[0]) {
642
+ if (!x0 || !x1) return false;
643
+ return x0.type === x1.type && x0.value === x1.value;
644
+ }
645
+
646
+ function eqq(x0: tree[0], x1: tree[0]) {
647
+ if (!x0 || !x1) return false;
648
+ return x0.type === x1.type && x0.value === x1.value && x0?.esc === x1?.esc;
649
+ }
650
+
651
+ function is_type(x: tree[0], ...type: vtype[]) {
652
+ return type.includes(x?.type);
653
+ }
654
+
655
+ function trim(tree: tree) {
656
+ if (!tree?.[0]) return [];
657
+ let start = 0;
658
+ let end = tree.length;
659
+ if (tree[0].type === "blank") start = 1;
660
+ if (tree.at(-1).type === "blank") end--;
661
+ return tree.slice(start, end);
662
+ }
663
+
664
+ function dic_to_ast(dic: { [id: string]: tree }) {
665
+ const l: tree = [];
666
+ for (const i in dic) {
667
+ l.push({ type: "f", value: i });
668
+ l.push({ type: "v", value: ":" });
669
+ for (const x of dic[i]) {
670
+ if (eq(x, dh)) x.esc = true;
671
+ }
672
+ l.push(...dic[i]);
673
+ if (Number(i) + 1 !== Object.keys(dic).length) {
674
+ l.push(dh);
675
+ }
676
+ }
677
+ return l;
678
+ }
679
+
680
+ function is_true(t: tree) {
681
+ return eqq(trim(t)?.[0], { type: "sharp", value: "true" });
682
+ }
683
+
684
+ function ast2(tree: tree) {
685
+ // 处理数字和小数
686
+ {
687
+ const t: tree = [];
688
+ const num = /[0-9]/;
689
+ let number = "";
690
+ for (let n = 0; n < tree.length; n++) {
691
+ const x = tree[n];
692
+ if (
693
+ x.value.match(num) ||
694
+ (tree[n - 1]?.value.match(num) &&
695
+ is_dot(x) &&
696
+ tree[n + 1]?.value.match(num))
697
+ ) {
698
+ number += x.value;
699
+ } else {
700
+ t.push(x);
701
+ }
702
+ if (
703
+ x.value.match(num) &&
704
+ (!tree[n + 1] || !tree[n + 1].value.match(num)) &&
705
+ !(is_dot(tree[n + 1]) && tree[n + 2]?.value.match(num))
706
+ ) {
707
+ t.push({ type: "v", value: number });
708
+ number = "";
709
+ }
710
+ }
711
+ tree = t;
712
+ }
713
+
714
+ // 处理\转义
715
+ {
716
+ const t: tree = [];
717
+ for (let n = 0; n < tree.length; n++) {
718
+ const x = tree[n];
719
+
720
+ if (eqq(x, v_f("\\"))) {
721
+ if (tree?.[n + 1]) {
722
+ const next = tree[n + 1];
723
+ if (eq(next, v_f("\\"))) {
724
+ t.push({ type: "v", value: "\\", esc: true });
725
+ n++;
726
+ } else if (next.type === "blank") {
727
+ t.push({ type: "v", value: "br", esc: true });
728
+ n++;
729
+ } else if (next.type === "f") {
730
+ const v = next.value;
731
+ t.push({ type: "v", value: v[0] });
732
+ t.push({ type: v.length === 2 ? "v" : "f", value: v.slice(1) });
733
+ n++;
734
+ } else if (next.type === "sharp") {
735
+ t.push({ type: "v", value: "#" });
736
+ n++;
737
+ } else {
738
+ const v = next;
739
+ v.esc = true;
740
+ t.push(v);
741
+ n++;
742
+ }
743
+ }
744
+ } else {
745
+ t.push(x);
746
+ }
747
+ }
748
+ tree = t;
749
+ }
750
+
751
+ return tree;
752
+ }
753
+
754
+ function ast3(tree: tree) {
755
+ // 处理符号简写(shorthand)
756
+ {
757
+ const t: tree = [];
758
+ for (let n = 0; n < tree.length; n++) {
759
+ const x = tree[n];
760
+
761
+ if (!x.esc) {
762
+ if (x.type === "v") {
763
+ let nn = n;
764
+ let shortkey = x.value;
765
+ const l = [];
766
+ if (shorthand[shortkey]) l.push(shortkey);
767
+ // 连起来的字符
768
+ while (
769
+ tree?.[nn + 1]?.type === "v" &&
770
+ shortkey.length <= max_shorthand_len
771
+ ) {
772
+ shortkey += tree[nn + 1].value;
773
+ if (shorthand[shortkey]) l.push(shortkey);
774
+ nn++;
775
+ }
776
+ if (l.length) {
777
+ const nx: tree[0] = {
778
+ type: "f",
779
+ value: shorthand[l.at(-1)],
780
+ src: l.at(-1),
781
+ };
782
+ t.push(nx);
783
+
784
+ // 不处理已经后面连起来的字符
785
+ n += l.at(-1).length - 1;
786
+ } else {
787
+ t.push(x);
788
+ }
789
+ } else {
790
+ t.push(x);
791
+ }
792
+ } else {
793
+ t.push(x);
794
+ }
795
+ }
796
+ tree = t;
797
+ }
798
+
799
+ // 处理字符与v之间空格
800
+ {
801
+ const t: tree = [];
802
+ for (let n = 0; n < tree.length; n++) {
803
+ const x = tree[n];
804
+
805
+ if (
806
+ x.type === "v" &&
807
+ tree?.[n + 1]?.type === "blank" &&
808
+ tree?.[n + 2]?.type === "str"
809
+ ) {
810
+ t.push(x);
811
+ t.push({ type: "f", value: "space" });
812
+ } else if (
813
+ x.type === "str" &&
814
+ tree?.[n + 1]?.type === "blank" &&
815
+ tree?.[n + 2]?.type === "v"
816
+ ) {
817
+ t.push(x);
818
+ t.push({ type: "f", value: "space" });
819
+ } else {
820
+ t.push(x);
821
+ }
822
+ }
823
+ tree = t;
824
+ }
825
+
826
+ // 处理!
827
+ {
828
+ const t: tree = [];
829
+ let tmpx: tree = [];
830
+ for (let n = 0; n < tree.length; n++) {
831
+ const x = tree[n];
832
+
833
+ if (
834
+ is_factorial(tree?.[n + 1]) &&
835
+ !is_sup(x) &&
836
+ !is_sub(x) &&
837
+ !is_frac(x)
838
+ ) {
839
+ tmpx.push(x);
840
+ } else {
841
+ if (tmpx.length) {
842
+ tmpx.push(x);
843
+ t.push({ type: "group1", value: "", children: tmpx });
844
+ tmpx = [];
845
+ } else {
846
+ t.push(x);
847
+ }
848
+ }
849
+ }
850
+
851
+ tree = t;
852
+ }
853
+
854
+ // 处理带.的f
855
+ {
856
+ const t: tree = [];
857
+ let f = "";
858
+ for (let n = 0; n < tree.length; n++) {
859
+ const x = tree[n];
860
+
861
+ if (is_dot_f(x) && is_dot(tree[n + 1])) {
862
+ if (f || (!f && x.type === "f")) {
863
+ f += `${x.value}.`;
864
+ n++;
865
+ } else {
866
+ t.push(x);
867
+ }
868
+ } else if (f && is_dot_f(x)) {
869
+ f += x.value;
870
+ if (!is_dot(tree[n + 1])) {
871
+ t.push({ type: "f", value: f });
872
+ f = "";
873
+ }
874
+ } else {
875
+ t.push(x);
876
+ }
877
+ }
878
+ tree = t;
879
+ }
880
+ // 处理后面直接跟参数的f(√ ∛ ∜)
881
+ // typst v0.11.1 实测:根号吃掉一个「原子」,该原子自己的附着(_/^ 与素号)都算在根号里面 ——
882
+ // √2^3=√(2³)、√2_1^2=√(2带1带2)、√f'=√(f')、√a_1^2 b^3=√(a带1带2)·b³、∛x^2=³√(x²);
883
+ // 后面再接别的原子(√2 3、√a b^2)或二元运算符(√2+3)则到此为止。
884
+ // 括号组:无附着时沿用原来的去括号路径(√(2)=√2、√(x+y) 根号直接覆盖 x+y,与 typst 一致),
885
+ // 有附着时把整组(连括号)当基底收进参数(√(2)^3=√((2)³),typst 同样保留括号)。
886
+ {
887
+ const t: tree = [];
888
+ for (let n = 0; n < tree.length; n++) {
889
+ const x = tree[n];
890
+ if (x.type === "v" && direct_arg_sy.includes(x.value) && tree[n + 1]) {
891
+ x.type = "f";
892
+ const next = tree[n + 1];
893
+ // 向后吞附着链:_值、^值、素号,可混排可重复,中间允许空格
894
+ const att: tree = [];
895
+ let end = n + 1;
896
+ let j = n + 2;
897
+ for (;;) {
898
+ let k = j;
899
+ while (tree[k]?.type === "blank") k++;
900
+ const y = tree[k];
901
+ if (!y) break;
902
+ if ((is_sup(y) || is_sub(y)) && tree[k + 1] !== undefined) {
903
+ att.push(y, tree[k + 1]);
904
+ end = k + 1;
905
+ j = k + 2;
906
+ } else if (eqq(y, { type: "f", value: "prime" })) {
907
+ att.push(y);
908
+ end = k;
909
+ j = k + 1;
910
+ } else break;
911
+ }
912
+ const is_paren = is_type(next, "group") && next.kh === "()";
913
+ if (!is_paren || att.length) {
914
+ t.push(x);
915
+ t.push({
916
+ type: "group",
917
+ children: [next, ...att],
918
+ value: "",
919
+ kh: "()",
920
+ });
921
+ n = end;
922
+ continue;
923
+ }
924
+ }
925
+ t.push(x);
926
+ }
927
+ tree = t;
928
+ }
929
+ // 处理f
930
+ {
931
+ const t: tree = [];
932
+ for (let n = 0; n < tree.length; n++) {
933
+ const x = tree[n];
934
+ const next = tree[n + 1];
935
+
936
+ // 带有括号(参数)的函数
937
+ // prime 是后缀算子(稍后单独转成 attach),不能吞掉后面的 (..) 组
938
+ if (
939
+ x.type === "f" &&
940
+ x.value !== "prime" &&
941
+ next &&
942
+ next.type === "group"
943
+ ) {
944
+ if (next.kh === "()") {
945
+ x.children = tree[n + 1].children;
946
+ t.push(x);
947
+
948
+ // 不处理group
949
+ n++;
950
+ continue;
951
+ } else {
952
+ // 未闭合的开括号组(kh 只有开括号)不能当参数收编:typst
953
+ // 那里是 unclosed delimiter 错误,本库保持原来的并列渲染,
954
+ // 组本身留给下一轮出栈,否则内容会被吞进 f 再渲染不出来。
955
+ if (next.kh.length > 1 && next.kh[0] === "(") {
956
+ // 向后找
957
+ const tt: tree = [];
958
+ tt.push(...next.children);
959
+ tt.push(v_f(next.kh[1]));
960
+ for (let i = n + 2; i < tree.length; i++) {
961
+ if (tree[i]) {
962
+ if (eqq(tree[i], v_f(")"))) {
963
+ n = i;
964
+ break;
965
+ } else {
966
+ tt.push(tree[i]);
967
+ }
968
+ }
969
+ }
970
+ x.children = tt;
971
+ t.push(x);
972
+ } else {
973
+ t.push(x);
974
+ }
975
+ }
976
+ for (const i in x.children || []) {
977
+ if (x.children[i].value === "\\" && x.children[i].type === "v") {
978
+ if (x.children[Number(i) + 1]) {
979
+ x.children[Number(i) + 1].esc = true;
980
+ }
981
+ }
982
+ }
983
+ } else {
984
+ t.push(x);
985
+ }
986
+ }
987
+ tree = t;
988
+ }
989
+
990
+ // sin[]/2=(sin[])/2 a_i()=a_(i()) 之类的
991
+ {
992
+ const t: tree = [];
993
+ const un_list_str = [
994
+ "`",
995
+ "~",
996
+ "!",
997
+ "@",
998
+ "%",
999
+ "*",
1000
+ "/",
1001
+ "(",
1002
+ ")",
1003
+ "-",
1004
+ "+",
1005
+ "=",
1006
+ "[",
1007
+ "]",
1008
+ "{",
1009
+ "}",
1010
+ "|",
1011
+ ":",
1012
+ ";",
1013
+ "<",
1014
+ ",",
1015
+ ">",
1016
+ ".",
1017
+ "?",
1018
+ ]; // typst 似乎是直接排除了这些字符,不排除转义,shorthand之类的
1019
+ // `/` 也必须排除(unicode-math-class=Binary,typst continuable=false):
1020
+ // 否则 `1/(2 (x)` 里的 `/` 会和后面的未闭合组粘成一个 token,`/` 就不再是分数算子。
1021
+ for (let n = 0; n < tree.length; n++) {
1022
+ const x = tree[n];
1023
+ const next = tree[n + 1];
1024
+ if (
1025
+ is_type(next, "group") &&
1026
+ next.kh !== "()" &&
1027
+ is_type(x, "f", "str", "v") &&
1028
+ !x.value.match(/[0-9]/) &&
1029
+ !is_sub(x) &&
1030
+ !is_sup(x) &&
1031
+ // `#box(...)` 的名字归下面的「处理#」(它吃 tree[n+1] 的值),
1032
+ // 这里抢先粘成 group1 会让 sharp 变成空值、名字连同参数被丢掉
1033
+ tree[n - 1]?.type !== "sharp" &&
1034
+ (!(
1035
+ (is_type(x, "f") && un_list_str.includes(x.src)) ||
1036
+ (is_type(x, "v") && un_list_str.includes(x.value))
1037
+ ) ||
1038
+ x.esc)
1039
+ ) {
1040
+ t.push({ type: "group1", value: "", children: [x, next] });
1041
+ n++;
1042
+ continue;
1043
+ }
1044
+ t.push(x);
1045
+ }
1046
+ tree = t;
1047
+ }
1048
+
1049
+ // 处理#
1050
+ {
1051
+ const t: tree = [];
1052
+ for (let n = 0; n < tree.length; n++) {
1053
+ const x = tree[n];
1054
+ if (x.type === "sharp") {
1055
+ if (!x.value)
1056
+ if (tree[n + 1]?.type === "group") {
1057
+ x.children = tree[n + 1].children;
1058
+ for (const i in x.children) {
1059
+ if (x.children[i].value === "\\" && x.children[i].type === "v") {
1060
+ if (x.children[Number(i) + 1]) {
1061
+ x.children[Number(i) + 1].esc = true;
1062
+ }
1063
+ }
1064
+ }
1065
+ const v = x.children.map((i) => i.value).join("");
1066
+ if (v.match(/^[0-9+\-*/()]+$/)) {
1067
+ x.value = v; // todo 数学运算
1068
+ } else {
1069
+ x.value = v;
1070
+ }
1071
+ } else {
1072
+ x.value = tree[n + 1]?.value;
1073
+ if (tree[n + 2]?.type === "f") {
1074
+ x.value += tree[n + 2].value;
1075
+ n++;
1076
+ }
1077
+ }
1078
+ t.push(x);
1079
+ n++;
1080
+ } else {
1081
+ t.push(x);
1082
+ }
1083
+ }
1084
+ tree = t;
1085
+ }
1086
+
1087
+ // '->^':素号是后缀算子,等价于 `^<素号串>`,交给后面的 ^/_ 合并逻辑处理
1088
+ {
1089
+ const t: tree = [];
1090
+ for (let n = 0; n < tree.length; n++) {
1091
+ const x = tree[n];
1092
+ const next = tree[n + 1];
1093
+ if (!eqq(next, { type: "f", value: "prime" })) {
1094
+ t.push(x);
1095
+ continue;
1096
+ }
1097
+ let nn = n + 1;
1098
+ while (tree[nn] && eqq(tree[nn], { type: "f", value: "prime" })) nn++;
1099
+ const primes = nn - (n + 1);
1100
+ if (eqq(x, { type: "f", value: "prime" })) {
1101
+ // 整段素号前面没有基底(如 `'''''''`):平铺成一个后缀串,不做附着
1102
+ t.push({ type: "v", value: "'".repeat(primes + 1) });
1103
+ } else if (!is_sup(x) && !is_sub(x) && x.type !== "blank") {
1104
+ t.push({
1105
+ type: "f",
1106
+ value: "attach",
1107
+ children: [
1108
+ x,
1109
+ dh,
1110
+ ...dic_to_ast({
1111
+ tr: [{ type: "v", value: "'".repeat(primes) }],
1112
+ }),
1113
+ ],
1114
+ });
1115
+ } else {
1116
+ t.push(x, { type: "v", value: "'".repeat(primes) });
1117
+ }
1118
+ n += primes;
1119
+ }
1120
+ tree = t;
1121
+ }
1122
+
1123
+ // 移除blank
1124
+ {
1125
+ const t: tree = [];
1126
+ for (const i of tree) {
1127
+ if (i.type === "blank") continue;
1128
+ t.push(i);
1129
+ }
1130
+ tree = t;
1131
+ }
1132
+
1133
+ // 处理^_
1134
+ {
1135
+ // 获取^_嵌套索引
1136
+ let index: [number, number][] = [];
1137
+ let start = NaN;
1138
+ for (let n = 0; n < tree.length; n++) {
1139
+ const x = tree[n];
1140
+ if (!is_sup(x) && !is_sub(x)) {
1141
+ if (
1142
+ (is_sup(tree[n + 1]) || is_sub(tree[n + 1])) &&
1143
+ !(is_sup(tree[n - 1]) || is_sub(tree[n - 1]))
1144
+ ) {
1145
+ if (!start) start = n;
1146
+ }
1147
+ if (
1148
+ (is_sup(tree[n - 1]) || is_sub(tree[n - 1])) &&
1149
+ !(is_sup(tree[n + 1]) || is_sub(tree[n + 1]))
1150
+ ) {
1151
+ index.push([start, n]);
1152
+ start = NaN;
1153
+ }
1154
+ }
1155
+ }
1156
+
1157
+ const t: tree = [];
1158
+ for (let n = 0; n < tree.length; n++) {
1159
+ if (n === index?.[0]?.[0]) {
1160
+ {
1161
+ let tmp: tree[0] = tree[index[0][1]];
1162
+ for (let i = index[0][1]; i >= index[0][0]; i--) {
1163
+ if (is_sup(tree[i - 1]) && is_sub(tree[i - 3])) {
1164
+ const o = is_limit([tree[i - 4]])
1165
+ ? { t: out_kh(tmp), b: out_kh(tree[i - 2]) }
1166
+ : { tr: out_kh(tmp), br: out_kh(tree[i - 2]) };
1167
+ tmp = {
1168
+ type: "f",
1169
+ value: "attach",
1170
+ children: [tree[i - 4], dh, ...dic_to_ast(o)],
1171
+ };
1172
+ i -= 4 - 1;
1173
+ continue;
1174
+ }
1175
+ if (is_sub(tree[i - 1]) && is_sup(tree[i - 3])) {
1176
+ const o = is_limit([tree[i - 4]])
1177
+ ? { t: out_kh(tree[i - 2]), b: out_kh(tmp) }
1178
+ : { tr: out_kh(tree[i - 2]), br: out_kh(tmp) };
1179
+ tmp = {
1180
+ type: "f",
1181
+ value: "attach",
1182
+ children: [tree[i - 4], dh, ...dic_to_ast(o)],
1183
+ };
1184
+ i -= 4 - 1;
1185
+ continue;
1186
+ }
1187
+ if (is_sup(tree[i - 1])) {
1188
+ const o = is_limit([tree[i - 2]])
1189
+ ? { t: out_kh(tmp) }
1190
+ : { tr: out_kh(tmp) };
1191
+ tmp = {
1192
+ type: "f",
1193
+ value: "attach",
1194
+ children: [tree[i - 2], dh, ...dic_to_ast(o)],
1195
+ };
1196
+ i -= 2 - 1;
1197
+ continue;
1198
+ }
1199
+ if (is_sub(tree[i - 1])) {
1200
+ const o = is_limit([tree[i - 2]])
1201
+ ? { b: out_kh(tmp) }
1202
+ : { br: out_kh(tmp) };
1203
+ tmp = {
1204
+ type: "f",
1205
+ value: "attach",
1206
+ children: [tree[i - 2], dh, ...dic_to_ast(o)],
1207
+ };
1208
+ i -= 2 - 1;
1209
+ }
1210
+ }
1211
+ t.push(tmp);
1212
+ }
1213
+ n += index[0][1] - index[0][0];
1214
+ index = index.slice(1);
1215
+ continue;
1216
+ }
1217
+
1218
+ t.push(tree[n]);
1219
+ }
1220
+ tree = t;
1221
+ }
1222
+
1223
+ // 处理/
1224
+ {
1225
+ // 获取/嵌套索引
1226
+ const index: [number, number][] = [];
1227
+ let start = NaN;
1228
+ for (let n = 0; n < tree.length; n++) {
1229
+ const x = tree[n];
1230
+ if (!is_frac(x)) {
1231
+ if (is_frac(tree[n + 1]) && !is_frac(tree[n - 1])) {
1232
+ if (!start) start = n;
1233
+ }
1234
+ if (is_frac(tree[n - 1]) && !is_frac(tree[n + 1])) {
1235
+ index.push([start, n]);
1236
+ start = NaN;
1237
+ }
1238
+ }
1239
+ }
1240
+
1241
+ const t: tree = [];
1242
+ for (let n = 0; n < tree.length; n++) {
1243
+ if (n === index?.[0]?.[0]) {
1244
+ {
1245
+ let tmp: tree[0] = tree[index[0][0]];
1246
+ for (let i = index[0][0]; i <= index[0][1]; i++) {
1247
+ if (is_frac(tree[i + 1])) {
1248
+ tmp = {
1249
+ type: "f",
1250
+ value: "frac",
1251
+ children: [...out_kh(tmp), dh, ...out_kh(tree[i + 2])],
1252
+ };
1253
+ i += 2 - 1;
1254
+ }
1255
+ }
1256
+ t.push(tmp);
1257
+ }
1258
+ n += index[0][1] - index[0][0];
1259
+ index.splice(0, 1);
1260
+ continue;
1261
+ }
1262
+
1263
+ t.push(tree[n]);
1264
+ }
1265
+ tree = t;
1266
+ }
1267
+ return tree;
1268
+ }
1269
+
1270
+ function f_attr(x: tree[0]) {
1271
+ const list: (tree | "," | ";")[] = [[]];
1272
+ for (const i of x.children) {
1273
+ if (
1274
+ eqq(i, { type: "v", value: "," }) ||
1275
+ eqq(i, { type: "v", value: ";" })
1276
+ ) {
1277
+ list.push(i.value as "," | ";");
1278
+ list.push([]);
1279
+ } else {
1280
+ (list.at(-1) as tree).push(i);
1281
+ }
1282
+ }
1283
+ const dicl: tree[] = [];
1284
+ const xattr: typeof list = [];
1285
+ for (const x of list) {
1286
+ if (typeof x === "string") {
1287
+ xattr.push(x);
1288
+ } else {
1289
+ if (
1290
+ x.find(
1291
+ (v, i) =>
1292
+ is_type(x[i - 1], "f", "v") && eqq(v, { type: "v", value: ":" }),
1293
+ )
1294
+ ) {
1295
+ dicl.push(x);
1296
+ } else {
1297
+ xattr.push(x);
1298
+ }
1299
+ }
1300
+ }
1301
+
1302
+ let nl: typeof xattr = [];
1303
+ for (let i = 0; i < xattr.length; i++) {
1304
+ const n = xattr[i],
1305
+ next = xattr[i + 1];
1306
+ if ((n === "," && next === ";") || (n === ";" && next === ",")) {
1307
+ nl.push(";");
1308
+ i++;
1309
+ } else {
1310
+ nl.push(xattr[i]);
1311
+ }
1312
+ }
1313
+
1314
+ let attr: tree[] | tree[][];
1315
+ if (typeof nl.at(-1) === "string") nl = nl.slice(0, -1);
1316
+ const has_fenhao = nl.includes(";");
1317
+ if (has_fenhao) {
1318
+ let x = array_split(nl, (x) => x === ";");
1319
+ if (x.at(-1)[0].length === 0) x = x.slice(0, -1);
1320
+ attr = x.map((x) => x.filter((i) => typeof i !== "string")) as tree[][];
1321
+ } else {
1322
+ attr = (nl.filter((i) => typeof i !== "string") as tree[]).map((i) =>
1323
+ trim(i),
1324
+ );
1325
+ }
1326
+
1327
+ // 将dicl的tree转为键对
1328
+ const dic: fdic = {};
1329
+ for (const i of dicl) {
1330
+ let x = false;
1331
+ let n = "";
1332
+ const t: tree = [];
1333
+ for (const el of i) {
1334
+ if (eqq(el, { type: "v", value: ":" })) {
1335
+ x = true;
1336
+ continue;
1337
+ }
1338
+ if (!x) {
1339
+ n += el.value;
1340
+ } else {
1341
+ t.push(el);
1342
+ }
1343
+ }
1344
+ dic[n] = ast3(ast2(t));
1345
+ }
1346
+ return { attr, dic };
1347
+ }
1348
+
1349
+ function array_split<i>(list: i[], f: (i: i) => boolean) {
1350
+ const l: i[][] = [[]];
1351
+ for (const x of list) {
1352
+ if (f(x)) {
1353
+ l.push([]);
1354
+ } else {
1355
+ l.at(-1).push(x);
1356
+ }
1357
+ }
1358
+ return l;
1359
+ }
1360
+
1361
+ export {
1362
+ ast2,
1363
+ ast3,
1364
+ dh,
1365
+ emojix,
1366
+ eq,
1367
+ eqq,
1368
+ f_attr,
1369
+ type fdic,
1370
+ in_kh,
1371
+ is_br,
1372
+ is_limit,
1373
+ is_true,
1374
+ is_type,
1375
+ opl,
1376
+ out_kh,
1377
+ ss,
1378
+ transfer_kh,
1379
+ trim,
1380
+ v_f,
1381
+ };