tex2typst 0.2.7 → 0.2.9

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.
package/src/parser.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { TexNode, TexSupsubData } from "./types";
1
+ import { symbolMap } from "./map";
2
+ import { TexNode, TexSupsubData, Token, TokenType } from "./types";
2
3
 
3
4
 
4
5
  const UNARY_COMMANDS = [
@@ -42,7 +43,7 @@ const BINARY_COMMANDS = [
42
43
  'tbinom',
43
44
  ]
44
45
 
45
- const EMPTY_NODE = { 'type': 'empty', 'content': '' }
46
+ const EMPTY_NODE: TexNode = { type: 'empty', content: '' };
46
47
 
47
48
  function assert(condition: boolean, message: string = ''): void {
48
49
  if (!condition) {
@@ -60,8 +61,8 @@ function get_command_param_num(command: string): number {
60
61
  }
61
62
  }
62
63
 
63
- const LEFT_CURLY_BRACKET: Token = {type: 'control', value: '{'};
64
- const RIGHT_CURLY_BRACKET: Token = {type: 'control', value: '}'};
64
+ const LEFT_CURLY_BRACKET: Token = {type: TokenType.CONTROL, value: '{'};
65
+ const RIGHT_CURLY_BRACKET: Token = {type: TokenType.CONTROL, value: '}'};
65
66
 
66
67
  function find_closing_curly_bracket(tokens: Token[], start: number): number {
67
68
  assert(token_eq(tokens[start], LEFT_CURLY_BRACKET));
@@ -83,8 +84,8 @@ function find_closing_curly_bracket(tokens: Token[], start: number): number {
83
84
  return pos - 1;
84
85
  }
85
86
 
86
- const LEFT_SQUARE_BRACKET: Token = {type: 'element', value: '['};
87
- const RIGHT_SQUARE_BRACKET: Token = {type: 'element', value: ']'};
87
+ const LEFT_SQUARE_BRACKET: Token = {type: TokenType.ELEMENT, value: '['};
88
+ const RIGHT_SQUARE_BRACKET: Token = {type: TokenType.ELEMENT, value: ']'};
88
89
 
89
90
  function find_closing_square_bracket(tokens: Token[], start: number): number {
90
91
  assert(token_eq(tokens[start], LEFT_SQUARE_BRACKET));
@@ -117,7 +118,7 @@ function isdigit(char: string): boolean {
117
118
 
118
119
  function eat_whitespaces(tokens: Token[], start: number): Token[] {
119
120
  let pos = start;
120
- while (pos < tokens.length && ['whitespace', 'newline'].includes(tokens[pos].type)) {
121
+ while (pos < tokens.length && [TokenType.WHITESPACE, TokenType.NEWLINE].includes(tokens[pos].type)) {
121
122
  pos++;
122
123
  }
123
124
  return tokens.slice(start, pos);
@@ -126,9 +127,9 @@ function eat_whitespaces(tokens: Token[], start: number): Token[] {
126
127
 
127
128
  function eat_parenthesis(tokens: Token[], start: number): Token | null {
128
129
  const firstToken = tokens[start];
129
- if (firstToken.type === 'element' && ['(', ')', '[', ']', '|', '\\{', '\\}'].includes(firstToken.value)) {
130
+ if (firstToken.type === TokenType.ELEMENT && ['(', ')', '[', ']', '|', '\\{', '\\}'].includes(firstToken.value)) {
130
131
  return firstToken;
131
- } else if (firstToken.type === 'command' && ['lfloor', 'rfloor', 'lceil', 'rceil', 'langle', 'rangle'].includes(firstToken.value.slice(1))) {
132
+ } else if (firstToken.type === TokenType.COMMAND && ['lfloor', 'rfloor', 'lceil', 'rceil', 'langle', 'rangle'].includes(firstToken.value.slice(1))) {
132
133
  return firstToken;
133
134
  } else {
134
135
  return null;
@@ -137,7 +138,7 @@ function eat_parenthesis(tokens: Token[], start: number): Token | null {
137
138
 
138
139
  function eat_primes(tokens: Token[], start: number): number {
139
140
  let pos = start;
140
- while (pos < tokens.length && token_eq(tokens[pos], { type: 'element', value: "'" })) {
141
+ while (pos < tokens.length && token_eq(tokens[pos], { type: TokenType.ELEMENT, value: "'" })) {
141
142
  pos += 1;
142
143
  }
143
144
  return pos - start;
@@ -155,8 +156,8 @@ function eat_command_name(latex: string, start: number): string {
155
156
 
156
157
 
157
158
 
158
- const LEFT_COMMAND: Token = { type: 'command', value: '\\left' };
159
- const RIGHT_COMMAND: Token = { type: 'command', value: '\\right' };
159
+ const LEFT_COMMAND: Token = { type: TokenType.COMMAND, value: '\\left' };
160
+ const RIGHT_COMMAND: Token = { type: TokenType.COMMAND, value: '\\right' };
160
161
 
161
162
  function find_closing_right_command(tokens: Token[], start: number): number {
162
163
  let count = 1;
@@ -178,8 +179,8 @@ function find_closing_right_command(tokens: Token[], start: number): number {
178
179
  }
179
180
 
180
181
 
181
- const BEGIN_COMMAND: Token = { type: 'command', value: '\\begin' };
182
- const END_COMMAND: Token = { type: 'command', value: '\\end' };
182
+ const BEGIN_COMMAND: Token = { type: TokenType.COMMAND, value: '\\begin' };
183
+ const END_COMMAND: Token = { type: TokenType.COMMAND, value: '\\end' };
183
184
 
184
185
 
185
186
  function find_closing_end_command(tokens: Token[], start: number): number {
@@ -226,11 +227,6 @@ function find_closing_curly_bracket_char(latex: string, start: number): number {
226
227
  }
227
228
 
228
229
 
229
- export interface Token {
230
- type: 'element' | 'command' | 'text' | 'comment' | 'whitespace' | 'newline' | 'control' | 'unknown';
231
- value: string;
232
- }
233
-
234
230
  export function tokenize(latex: string): Token[] {
235
231
  const tokens: Token[] = [];
236
232
  let pos = 0;
@@ -244,7 +240,7 @@ export function tokenize(latex: string): Token[] {
244
240
  while (newPos < latex.length && latex[newPos] !== '\n') {
245
241
  newPos += 1;
246
242
  }
247
- token = { type: 'comment', value: latex.slice(pos + 1, newPos) };
243
+ token = { type: TokenType.COMMENT, value: latex.slice(pos + 1, newPos) };
248
244
  pos = newPos;
249
245
  break;
250
246
  }
@@ -253,19 +249,19 @@ export function tokenize(latex: string): Token[] {
253
249
  case '_':
254
250
  case '^':
255
251
  case '&':
256
- token = { type: 'control', value: firstChar};
252
+ token = { type: TokenType.CONTROL, value: firstChar};
257
253
  pos++;
258
254
  break;
259
255
  case '\n':
260
- token = { type: 'newline', value: firstChar};
256
+ token = { type: TokenType.NEWLINE, value: firstChar};
261
257
  pos++;
262
258
  break;
263
259
  case '\r': {
264
260
  if (pos + 1 < latex.length && latex[pos + 1] === '\n') {
265
- token = { type: 'newline', value: '\n' };
261
+ token = { type: TokenType.NEWLINE, value: '\n' };
266
262
  pos += 2;
267
263
  } else {
268
- token = { type: 'newline', value: '\n' };
264
+ token = { type: TokenType.NEWLINE, value: '\n' };
269
265
  pos ++;
270
266
  }
271
267
  break;
@@ -275,7 +271,7 @@ export function tokenize(latex: string): Token[] {
275
271
  while (newPos < latex.length && latex[newPos] === ' ') {
276
272
  newPos += 1;
277
273
  }
278
- token = {type: 'whitespace', value: latex.slice(pos, newPos)};
274
+ token = {type: TokenType.WHITESPACE, value: latex.slice(pos, newPos)};
279
275
  pos = newPos;
280
276
  break;
281
277
  }
@@ -285,12 +281,12 @@ export function tokenize(latex: string): Token[] {
285
281
  }
286
282
  const firstTwoChars = latex.slice(pos, pos + 2);
287
283
  if (['\\\\', '\\,'].includes(firstTwoChars)) {
288
- token = { type: 'control', value: firstTwoChars };
284
+ token = { type: TokenType.CONTROL, value: firstTwoChars };
289
285
  } else if (['\\{','\\}', '\\%', '\\$', '\\&', '\\#', '\\_'].includes(firstTwoChars)) {
290
- token = { type: 'element', value: firstTwoChars };
286
+ token = { type: TokenType.ELEMENT, value: firstTwoChars };
291
287
  } else {
292
288
  const command = eat_command_name(latex, pos + 1);
293
- token = { type: 'command', value: '\\' + command};
289
+ token = { type: TokenType.COMMAND, value: '\\' + command};
294
290
  }
295
291
  pos += token.value.length;
296
292
  break;
@@ -301,13 +297,13 @@ export function tokenize(latex: string): Token[] {
301
297
  while (newPos < latex.length && isdigit(latex[newPos])) {
302
298
  newPos += 1;
303
299
  }
304
- token = { type: 'element', value: latex.slice(pos, newPos) }
300
+ token = { type: TokenType.ELEMENT, value: latex.slice(pos, newPos) }
305
301
  } else if (isalpha(firstChar)) {
306
- token = { type: 'element', value: firstChar };
302
+ token = { type: TokenType.ELEMENT, value: firstChar };
307
303
  } else if ('+-*/=\'<>!.,;?()[]|'.includes(firstChar)) {
308
- token = { type: 'element', value: firstChar }
304
+ token = { type: TokenType.ELEMENT, value: firstChar }
309
305
  } else {
310
- token = { type: 'unknown', value: firstChar };
306
+ token = { type: TokenType.UNKNOWN, value: firstChar };
311
307
  }
312
308
  pos += token.value.length;
313
309
  }
@@ -315,11 +311,11 @@ export function tokenize(latex: string): Token[] {
315
311
 
316
312
  tokens.push(token);
317
313
 
318
- if (token.type === 'command' && ['\\text', '\\begin', '\\end'].includes(token.value)) {
314
+ if (token.type === TokenType.COMMAND && ['\\text', '\\operatorname', '\\begin', '\\end'].includes(token.value)) {
319
315
  if (pos >= latex.length || latex[pos] !== '{') {
320
316
  throw new LatexParserError(`No content for ${token.value} command`);
321
317
  }
322
- tokens.push({ type: 'control', value: '{' });
318
+ tokens.push({ type: TokenType.CONTROL, value: '{' });
323
319
  const posClosingBracket = find_closing_curly_bracket_char(latex, pos);
324
320
  pos++;
325
321
  let textInside = latex.slice(pos, posClosingBracket);
@@ -328,8 +324,8 @@ export function tokenize(latex: string): Token[] {
328
324
  for (const char of chars) {
329
325
  textInside = textInside.replaceAll('\\' + char, char);
330
326
  }
331
- tokens.push({ type: 'text', value: textInside });
332
- tokens.push({ type: 'control', value: '}' });
327
+ tokens.push({ type: TokenType.TEXT, value: textInside });
328
+ tokens.push({ type: TokenType.CONTROL, value: '}' });
333
329
  pos = posClosingBracket + 1;
334
330
  }
335
331
  }
@@ -351,8 +347,8 @@ export class LatexParserError extends Error {
351
347
 
352
348
  type ParseResult = [TexNode, number];
353
349
 
354
- const SUB_SYMBOL:Token = { type: 'control', value: '_' };
355
- const SUP_SYMBOL:Token = { type: 'control', value: '^' };
350
+ const SUB_SYMBOL:Token = { type: TokenType.CONTROL, value: '_' };
351
+ const SUP_SYMBOL:Token = { type: TokenType.CONTROL, value: '^' };
356
352
 
357
353
  export class LatexParser {
358
354
  space_sensitive: boolean;
@@ -443,7 +439,7 @@ export class LatexParser {
443
439
  if (num_prime > 0) {
444
440
  res.sup = { type: 'ordgroup', content: '', args: [] };
445
441
  for (let i = 0; i < num_prime; i++) {
446
- res.sup.args!.push({ type: 'symbol', content: '\\prime' });
442
+ res.sup.args!.push({ type: 'element', content: "'" });
447
443
  }
448
444
  if (sup) {
449
445
  res.sup.args!.push(sup);
@@ -464,13 +460,17 @@ export class LatexParser {
464
460
  const firstToken = tokens[start];
465
461
  const tokenType = firstToken.type;
466
462
  switch (tokenType) {
467
- case 'element':
468
- case 'text':
469
- case 'comment':
470
- case 'whitespace':
471
- case 'newline':
472
- return [{ type: tokenType, content: firstToken.value }, start + 1];
473
- case 'command':
463
+ case TokenType.ELEMENT:
464
+ return [{ type: 'element', content: firstToken.value }, start + 1];
465
+ case TokenType.TEXT:
466
+ return [{ type: 'text', content: firstToken.value }, start + 1];
467
+ case TokenType.COMMENT:
468
+ return [{ type: 'comment', content: firstToken.value }, start + 1];
469
+ case TokenType.WHITESPACE:
470
+ return [{ type: 'whitespace', content: firstToken.value }, start + 1];
471
+ case TokenType.NEWLINE:
472
+ return [{ type: 'newline', content: firstToken.value }, start + 1];
473
+ case TokenType.COMMAND:
474
474
  if (token_eq(firstToken, BEGIN_COMMAND)) {
475
475
  return this.parseBeginEndExpr(tokens, start);
476
476
  } else if (token_eq(firstToken, LEFT_COMMAND)) {
@@ -478,7 +478,7 @@ export class LatexParser {
478
478
  } else {
479
479
  return this.parseCommandExpr(tokens, start);
480
480
  }
481
- case 'control':
481
+ case TokenType.CONTROL:
482
482
  const controlChar = firstToken.value;
483
483
  switch (controlChar) {
484
484
  case '{':
@@ -508,7 +508,7 @@ export class LatexParser {
508
508
  }
509
509
 
510
510
  parseCommandExpr(tokens: Token[], start: number): ParseResult {
511
- assert(tokens[start].type === 'command');
511
+ assert(tokens[start].type === TokenType.COMMAND);
512
512
 
513
513
  const command = tokens[start].value; // command name starts with a \
514
514
 
@@ -518,35 +518,42 @@ export class LatexParser {
518
518
  throw new LatexParserError('Unexpected command: ' + command);
519
519
  }
520
520
 
521
+
521
522
  const paramNum = get_command_param_num(command.slice(1));
522
- if (paramNum === 0) {
523
- return [{ type: 'symbol', content: command }, pos];
524
- } else if (paramNum === 1) {
525
- if (command === '\\sqrt' && pos < tokens.length && token_eq(tokens[pos], LEFT_SQUARE_BRACKET)) {
526
- const posLeftSquareBracket = pos;
527
- const posRightSquareBracket = find_closing_square_bracket(tokens, pos);
528
- const exprInside = tokens.slice(posLeftSquareBracket + 1, posRightSquareBracket);
529
- const exponent = this.parse(exprInside);
530
- const [arg1, newPos] = this.parseNextExprWithoutSupSub(tokens, posRightSquareBracket + 1);
531
- return [{ type: 'unaryFunc', content: command, args: [arg1], data: exponent }, newPos];
532
- } else if (command === '\\text') {
533
- if (pos + 2 >= tokens.length) {
534
- throw new LatexParserError('Expecting content for \\text command');
523
+ switch (paramNum) {
524
+ case 0:
525
+ if (!symbolMap.has(command.slice(1))) {
526
+ return [{ type: 'unknownMacro', content: command }, pos];
527
+ }
528
+ return [{ type: 'symbol', content: command }, pos];
529
+ case 1: {
530
+ if (command === '\\sqrt' && pos < tokens.length && token_eq(tokens[pos], LEFT_SQUARE_BRACKET)) {
531
+ const posLeftSquareBracket = pos;
532
+ const posRightSquareBracket = find_closing_square_bracket(tokens, pos);
533
+ const exprInside = tokens.slice(posLeftSquareBracket + 1, posRightSquareBracket);
534
+ const exponent = this.parse(exprInside);
535
+ const [arg1, newPos] = this.parseNextExprWithoutSupSub(tokens, posRightSquareBracket + 1);
536
+ return [{ type: 'unaryFunc', content: command, args: [arg1], data: exponent }, newPos];
537
+ } else if (command === '\\text') {
538
+ if (pos + 2 >= tokens.length) {
539
+ throw new LatexParserError('Expecting content for \\text command');
540
+ }
541
+ assert(token_eq(tokens[pos], LEFT_CURLY_BRACKET));
542
+ assert(tokens[pos + 1].type === TokenType.TEXT);
543
+ assert(token_eq(tokens[pos + 2], RIGHT_CURLY_BRACKET));
544
+ const text = tokens[pos + 1].value;
545
+ return [{ type: 'text', content: text }, pos + 3];
535
546
  }
536
- assert(token_eq(tokens[pos], LEFT_CURLY_BRACKET));
537
- assert(tokens[pos + 1].type === 'text');
538
- assert(token_eq(tokens[pos + 2], RIGHT_CURLY_BRACKET));
539
- const text = tokens[pos + 1].value;
540
- return [{ type: 'text', content: text }, pos + 3];
547
+ let [arg1, newPos] = this.parseNextExprWithoutSupSub(tokens, pos);
548
+ return [{ type: 'unaryFunc', content: command, args: [arg1] }, newPos];
541
549
  }
542
- let [arg1, newPos] = this.parseNextExprWithoutSupSub(tokens, pos);
543
- return [{ type: 'unaryFunc', content: command, args: [arg1] }, newPos];
544
- } else if (paramNum === 2) {
545
- const [arg1, pos1] = this.parseNextExprWithoutSupSub(tokens, pos);
546
- const [arg2, pos2] = this.parseNextExprWithoutSupSub(tokens, pos1);
547
- return [{ type: 'binaryFunc', content: command, args: [arg1, arg2] }, pos2];
548
- } else {
549
- throw new Error( 'Invalid number of parameters');
550
+ case 2: {
551
+ const [arg1, pos1] = this.parseNextExprWithoutSupSub(tokens, pos);
552
+ const [arg2, pos2] = this.parseNextExprWithoutSupSub(tokens, pos1);
553
+ return [{ type: 'binaryFunc', content: command, args: [arg1, arg2] }, pos2];
554
+ }
555
+ default:
556
+ throw new Error( 'Invalid number of parameters');
550
557
  }
551
558
  }
552
559
 
@@ -586,12 +593,12 @@ export class LatexParser {
586
593
 
587
594
  const exprInside = tokens.slice(exprInsideStart, exprInsideEnd);
588
595
  const body = this.parse(exprInside);
589
- const args = [
596
+ const args: TexNode[] = [
590
597
  { type: 'element', content: leftDelimiter.value },
591
598
  body,
592
599
  { type: 'element', content: rightDelimiter.value }
593
600
  ]
594
- const res = { type: 'leftright', content: '', args: args };
601
+ const res: TexNode = { type: 'leftright', content: '', args: args };
595
602
  return [res, pos];
596
603
  }
597
604
 
@@ -600,7 +607,7 @@ export class LatexParser {
600
607
 
601
608
  let pos = start + 1;
602
609
  assert(token_eq(tokens[pos], LEFT_CURLY_BRACKET));
603
- assert(tokens[pos + 1].type === 'text');
610
+ assert(tokens[pos + 1].type === TokenType.TEXT);
604
611
  assert(token_eq(tokens[pos + 2], RIGHT_CURLY_BRACKET));
605
612
  const envName = tokens[pos + 1].value;
606
613
  pos += 3;
@@ -617,7 +624,7 @@ export class LatexParser {
617
624
  pos = endIdx + 1;
618
625
 
619
626
  assert(token_eq(tokens[pos], LEFT_CURLY_BRACKET));
620
- assert(tokens[pos + 1].type === 'text');
627
+ assert(tokens[pos + 1].type === TokenType.TEXT);
621
628
  assert(token_eq(tokens[pos + 2], RIGHT_CURLY_BRACKET));
622
629
  if (tokens[pos + 1].value !== envName) {
623
630
  throw new LatexParserError('Mismatched \\begin and \\end environments');
@@ -626,11 +633,11 @@ export class LatexParser {
626
633
 
627
634
  const exprInside = tokens.slice(exprInsideStart, exprInsideEnd);
628
635
  // ignore whitespaces and '\n' before \end{envName}
629
- while(exprInside.length > 0 && ['whitespace', 'newline'].includes(exprInside[exprInside.length - 1].type)) {
636
+ while(exprInside.length > 0 && [TokenType.WHITESPACE, TokenType.NEWLINE].includes(exprInside[exprInside.length - 1].type)) {
630
637
  exprInside.pop();
631
638
  }
632
639
  const body = this.parseAligned(exprInside);
633
- const res = { type: 'beginend', content: envName, data: body };
640
+ const res: TexNode = { type: 'beginend', content: envName, data: body };
634
641
  return [res, pos];
635
642
  }
636
643
 
@@ -665,15 +672,15 @@ export class LatexParser {
665
672
  }
666
673
  }
667
674
 
668
- // Remove all whitespace before and after _ or ^
675
+ // Remove all whitespace before or after _ or ^
669
676
  function passIgnoreWhitespaceBeforeScriptMark(tokens: Token[]): Token[] {
670
677
  const is_script_mark = (token: Token) => token_eq(token, SUB_SYMBOL) || token_eq(token, SUP_SYMBOL);
671
678
  let out_tokens: Token[] = [];
672
679
  for (let i = 0; i < tokens.length; i++) {
673
- if (tokens[i].type === 'whitespace' && i + 1 < tokens.length && is_script_mark(tokens[i + 1])) {
680
+ if (tokens[i].type === TokenType.WHITESPACE && i + 1 < tokens.length && is_script_mark(tokens[i + 1])) {
674
681
  continue;
675
682
  }
676
- if (tokens[i].type === 'whitespace' && i - 1 >= 0 && is_script_mark(tokens[i - 1])) {
683
+ if (tokens[i].type === TokenType.WHITESPACE && i - 1 >= 0 && is_script_mark(tokens[i - 1])) {
677
684
  continue;
678
685
  }
679
686
  out_tokens.push(tokens[i]);
@@ -685,7 +692,7 @@ function passIgnoreWhitespaceBeforeScriptMark(tokens: Token[]): Token[] {
685
692
  function passExpandCustomTexMacros(tokens: Token[], customTexMacros: {[key: string]: string}): Token[] {
686
693
  let out_tokens: Token[] = [];
687
694
  for (const token of tokens) {
688
- if (token.type === 'command' && customTexMacros[token.value]) {
695
+ if (token.type === TokenType.COMMAND && customTexMacros[token.value]) {
689
696
  const expanded_tokens = tokenize(customTexMacros[token.value]);
690
697
  out_tokens = out_tokens.concat(expanded_tokens);
691
698
  } else {
package/src/types.ts CHANGED
@@ -1,3 +1,20 @@
1
+ export enum TokenType {
2
+ ELEMENT,
3
+ COMMAND,
4
+ TEXT,
5
+ COMMENT,
6
+ WHITESPACE,
7
+ NEWLINE,
8
+ CONTROL,
9
+ UNKNOWN,
10
+ }
11
+
12
+ export interface Token {
13
+ type: TokenType;
14
+ value: string;
15
+ }
16
+
17
+
1
18
  export interface TexSupsubData {
2
19
  base: TexNode;
3
20
  sup?: TexNode;
@@ -9,7 +26,8 @@ export type TexSqrtData = TexNode;
9
26
  export type TexArrayData = TexNode[][];
10
27
 
11
28
  export interface TexNode {
12
- type: string;
29
+ type: 'element' | 'text' | 'comment' | 'whitespace' | 'newline' | 'control' | 'ordgroup' | 'supsub'
30
+ | 'unaryFunc' | 'binaryFunc' | 'leftright' | 'beginend' | 'symbol' | 'empty' | 'unknownMacro';
13
31
  content: string;
14
32
  args?: TexNode[];
15
33
  // position?: Position;
@@ -19,10 +37,20 @@ export interface TexNode {
19
37
  data?: TexSqrtData | TexSupsubData | TexArrayData;
20
38
  }
21
39
 
40
+ export interface TypstSupsubData {
41
+ base: TypstNode;
42
+ sup?: TypstNode;
43
+ sub?: TypstNode;
44
+ }
45
+
46
+ export type TypstArrayData = TypstNode[][];
47
+
22
48
  export interface TypstNode {
23
- type: 'atom' | 'symbol' | 'text' | 'softSpace' | 'comment' | 'newline',
49
+ type: 'atom' | 'symbol' | 'text' | 'softSpace' | 'comment' | 'newline'
50
+ | 'empty' | 'group' | 'supsub' | 'unaryFunc' | 'binaryFunc' | 'align' | 'matrix' | 'unknown';
24
51
  content: string;
25
52
  args?: TypstNode[];
53
+ data?: TypstSupsubData | TypstArrayData;
26
54
  }
27
55
 
28
56
  export interface Tex2TypstOptions {