tex2typst 0.3.27-beta.1 → 0.3.28

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/convert.ts CHANGED
@@ -188,6 +188,41 @@ function convert_tex_array_align_literal(alignLiteral: string): TypstNamedParams
188
188
  return np;
189
189
  }
190
190
 
191
+ const TYPST_LEFT_PARENTHESIS: TypstToken = new TypstToken(TypstTokenType.ELEMENT, '(');
192
+ const TYPST_RIGHT_PARENTHESIS: TypstToken = new TypstToken(TypstTokenType.ELEMENT, ')');
193
+
194
+ function is_delimiter(c: TypstNode): boolean {
195
+ return c.head.type === TypstTokenType.ELEMENT && ['(', ')', '[', ']', '{', '}', '|', '⌊', '⌋', '⌈', '⌉'].includes(c.head.value);
196
+ }
197
+
198
+ function appendWithBracketsIfNeeded(node: TypstNode): TypstNode {
199
+ let need_to_wrap = ['group', 'supsub', 'matrixLike', 'fraction','empty'].includes(node.type);
200
+
201
+ if (node.type === 'group') {
202
+ const group = node as TypstGroup;
203
+ if (group.items.length === 0) {
204
+ // e.g. TeX `P_{}` converts to Typst `P_()`
205
+ need_to_wrap = true;
206
+ } else {
207
+ const first = group.items[0];
208
+ const last = group.items[group.items.length - 1];
209
+ if (is_delimiter(first) && is_delimiter(last)) {
210
+ need_to_wrap = false;
211
+ }
212
+ }
213
+ }
214
+
215
+ if (need_to_wrap) {
216
+ return new TypstLeftright(null, {
217
+ left: TYPST_LEFT_PARENTHESIS,
218
+ right: TYPST_RIGHT_PARENTHESIS,
219
+ body: node,
220
+
221
+ });
222
+ } else {
223
+ return node;
224
+ }
225
+ }
191
226
 
192
227
  export function convert_tex_node_to_typst(abstractNode: TexNode, options: Tex2TypstOptions = {}): TypstNode {
193
228
  switch (abstractNode.type) {
@@ -197,12 +232,6 @@ export function convert_tex_node_to_typst(abstractNode: TexNode, options: Tex2Ty
197
232
  }
198
233
  case 'text': {
199
234
  const node = abstractNode as TexText;
200
- if ((/[^\x00-\x7F]+/).test(node.head.value) && options.nonAsciiWrapper !== "") {
201
- return new TypstFuncCall(
202
- new TypstToken(TypstTokenType.SYMBOL, options.nonAsciiWrapper!),
203
- [new TypstToken(TypstTokenType.TEXT, node.head.value).toNode()]
204
- );
205
- }
206
235
  return new TypstToken(TypstTokenType.TEXT, node.head.value).toNode();
207
236
  }
208
237
  case 'ordgroup':
@@ -234,6 +263,14 @@ export function convert_tex_node_to_typst(abstractNode: TexNode, options: Tex2Ty
234
263
  sub: sub? convert_tex_node_to_typst(sub, options) : null,
235
264
  };
236
265
 
266
+ data.base = appendWithBracketsIfNeeded(data.base);
267
+ if (data.sup) {
268
+ data.sup = appendWithBracketsIfNeeded(data.sup);
269
+ }
270
+ if (data.sub) {
271
+ data.sub = appendWithBracketsIfNeeded(data.sub);
272
+ }
273
+
237
274
  return new TypstSupsub(data);
238
275
  }
239
276
  case 'leftright': {
@@ -348,12 +385,54 @@ export function convert_tex_node_to_typst(abstractNode: TexNode, options: Tex2Ty
348
385
  return res;
349
386
  }
350
387
 
351
- // \substack{a \\ b} -> `a \ b`
388
+ // \substack{a \\ b} -> a \ b
352
389
  // as in translation from \sum_{\substack{a \\ b}} to sum_(a \ b)
353
390
  if (node.head.value === '\\substack') {
354
391
  return arg0;
355
392
  }
356
393
 
394
+ // \displaylines{...} -> ...
395
+ if (node.head.value === '\\displaylines') {
396
+ return arg0;
397
+ }
398
+
399
+ // \mathinner{...} -> ...
400
+ if (node.head.value === '\\mathinner') {
401
+ return arg0;
402
+ }
403
+
404
+ // \mathrel{X} -> class("relation", X)
405
+ if (node.head.value === '\\mathrel') {
406
+ return new TypstFuncCall(
407
+ new TypstToken(TypstTokenType.SYMBOL, 'class'),
408
+ [new TypstToken(TypstTokenType.TEXT, 'relation').toNode(), arg0]
409
+ );
410
+ }
411
+
412
+ // \mathbin{X} -> class("binary", X)
413
+ if (node.head.value === '\\mathbin') {
414
+ return new TypstFuncCall(
415
+ new TypstToken(TypstTokenType.SYMBOL, 'class'),
416
+ [new TypstToken(TypstTokenType.TEXT, 'binary').toNode(), arg0]
417
+ );
418
+ }
419
+
420
+ // \mathop{X} -> class("large", X)
421
+ if (node.head.value === '\\mathop') {
422
+ return new TypstFuncCall(
423
+ new TypstToken(TypstTokenType.SYMBOL, 'class'),
424
+ [new TypstToken(TypstTokenType.TEXT, 'large').toNode(), arg0]
425
+ );
426
+ }
427
+
428
+ // \set{a, b, c} -> {a, b, c}
429
+ if (node.head.value === '\\set') {
430
+ return new TypstLeftright(
431
+ null,
432
+ { body: arg0, left: TypstToken.LEFT_BRACE, right: TypstToken.RIGHT_BRACE }
433
+ );
434
+ }
435
+
357
436
  if (node.head.value === '\\overset') {
358
437
  return convert_overset(node, options);
359
438
  }
@@ -364,7 +443,7 @@ export function convert_tex_node_to_typst(abstractNode: TexNode, options: Tex2Ty
364
443
  // \frac{a}{b} -> a / b
365
444
  if (node.head.value === '\\frac') {
366
445
  if (options.fracToSlash) {
367
- return new TypstFraction(node.args.map((n) => convert_tex_node_to_typst(n, options)));
446
+ return new TypstFraction(node.args.map((n) => convert_tex_node_to_typst(n, options)).map(appendWithBracketsIfNeeded));
368
447
  }
369
448
  }
370
449
 
@@ -697,6 +776,31 @@ export function convert_typst_node_to_tex(abstractNode: TypstNode): TexNode {
697
776
  assert(arg0.head.type === TypstTokenType.TEXT);
698
777
  return new TexFuncCall(typst_token_to_tex(node.head), [new TexToken(TexTokenType.LITERAL, arg0.head.value).toNode()]);
699
778
  }
779
+ case 'class': {
780
+ const arg0 = node.args[0];
781
+ assert(arg0.head.type === TypstTokenType.TEXT);
782
+ let command: string;
783
+ switch (arg0.head.value) {
784
+ // \mathrel{X} <- class("relation", X)
785
+ case 'relation':
786
+ command = '\\mathrel';
787
+ break;
788
+ // \mathbin{X} <- class("binary", X)
789
+ case 'binary':
790
+ command = '\\mathbin';
791
+ break;
792
+ // \mathop{X} <- class("large", X)
793
+ case 'large':
794
+ command = '\\mathop';
795
+ break;
796
+ default:
797
+ throw new Error(`Unimplemented class: ${arg0.head.value}`);
798
+ }
799
+ return new TexFuncCall(
800
+ new TexToken(TexTokenType.COMMAND, command),
801
+ [convert_typst_node_to_tex(node.args[1])]
802
+ );
803
+ }
700
804
  // general case
701
805
  default: {
702
806
  const func_name_tex = typst_token_to_tex(node.head);
@@ -1,24 +1,22 @@
1
-
2
- /**
3
- * ATTENTION:
4
- * Don't use any options except those explicitly documented in
5
- * https://github.com/qwinsi/tex2typst/blob/main/docs/api-reference.md
6
- * Any undocumented options may be not working at present or break in the future!
7
- */
8
-
9
- export interface Tex2TypstOptions {
10
- nonStrict?: boolean; /** default is true */
11
- preferShorthands?: boolean; /** default is true */
12
- keepSpaces?: boolean; /** default is false */
13
- fracToSlash?: boolean; /** default is true */
14
- inftyToOo?: boolean; /** default is false */
15
- optimize?: boolean; /** default is true */
16
- nonAsciiWrapper?: string; /** default is "" */
17
- customTexMacros?: { [key: string]: string; };
18
- }
19
-
20
- export declare function tex2typst(tex: string, options?: Tex2TypstOptions): string;
21
- export declare function typst2tex(typst: string): string;
22
-
23
- export declare const symbolMap: Map<string, string>;
24
- export declare const shorthandMap: Map<string, string>;
1
+
2
+ /**
3
+ * ATTENTION:
4
+ * Don't use any options except those explicitly documented in
5
+ * https://github.com/qwinsi/tex2typst/blob/main/docs/api-reference.md
6
+ * Any undocumented options may be not working at present or break in the future!
7
+ */
8
+ export interface Tex2TypstOptions {
9
+ nonStrict?: boolean; /** default is true */
10
+ preferShorthands?: boolean; /** default is true */
11
+ keepSpaces?: boolean; /** default is false */
12
+ fracToSlash?: boolean; /** default is true */
13
+ inftyToOo?: boolean; /** default is false */
14
+ optimize?: boolean; /** default is true */
15
+ customTexMacros?: { [key: string]: string; };
16
+ }
17
+
18
+ export declare function tex2typst(tex: string, options?: Tex2TypstOptions): string;
19
+ export declare function typst2tex(typst: string): string;
20
+
21
+ export declare const symbolMap: Map<string, string>;
22
+ export declare const shorthandMap: Map<string, string>;
package/src/generic.ts CHANGED
@@ -59,6 +59,22 @@ export function array_split<T extends IEquatable>(array: T[], sep: T): T[][] {
59
59
  return res;
60
60
  }
61
61
 
62
+ // e.g. input array=[['a', 'b'], ['c'], ['d', 'e']], sep = '+'
63
+ // return ['a', 'b', '+', 'c', '+', 'd', 'e']
64
+ export function array_join<T>(arrays: T[][], sep: T): T[] {
65
+ /*
66
+ const res: T[] = [];
67
+ for (let i = 0; i < arrays.length; i++) {
68
+ res.push(...arrays[i]);
69
+ if (i !== arrays.length - 1) {
70
+ res.push(sep);
71
+ }
72
+ }
73
+ return res;
74
+ */
75
+ return arrays.flatMap((arr, i) => i !== arrays.length - 1? [...arr, sep]: arr);
76
+ }
77
+
62
78
  // e.g. input array=['a', 'b', 'c'], sep = '+'
63
79
  // return ['a','+', 'b', '+','c']
64
80
  export function array_intersperse<T>(array: T[], sep: T): T[] {
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { parseTex } from "./tex-parser";
2
2
  import type { Tex2TypstOptions } from "./exposed-types";
3
- import { TypstWriter, type TypstWriterOptions } from "./typst-writer";
3
+ import { TypstWriter } from "./typst-writer";
4
+ import { type TypstWriterOptions } from "./typst-types";
4
5
  import { convert_tex_node_to_typst, convert_typst_node_to_tex } from "./convert";
5
6
  import { symbolMap } from "./map";
6
7
  import { parseTypst } from "./typst-parser";
@@ -16,14 +17,16 @@ export function tex2typst(tex: string, options?: Tex2TypstOptions): string {
16
17
  fracToSlash: true,
17
18
  inftyToOo: false,
18
19
  optimize: true,
19
- nonAsciiWrapper: "",
20
20
  customTexMacros: {}
21
21
  };
22
22
 
23
23
  if(options !== undefined) {
24
+ if (typeof options !== 'object') {
25
+ throw new Error("options must be an object");
26
+ }
24
27
  for (const key in opt) {
25
- if (options[key] !== undefined) {
26
- opt[key] = options[key];
28
+ if (key in options) {
29
+ opt[key as keyof Tex2TypstOptions] = options[key as keyof Tex2TypstOptions] as any;
27
30
  }
28
31
  }
29
32
  }
package/src/jslex.ts CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
 
7
7
  interface ILexSpec<T> {
8
- start: Map<string, (arg0: Scanner<T>) => T | T[]>;
8
+ [key: string]: Map<string, (arg0: Scanner<T>) => T | T[]>;
9
9
  }
10
10
 
11
11
  interface IRule<T> {
package/src/map.ts CHANGED
@@ -56,6 +56,7 @@ const symbolMap = new Map<string, string>([
56
56
  ['mathbb', 'bb'],
57
57
  ['mathbf', 'bold'],
58
58
  ['mathcal', 'cal'],
59
+ ['mathscr', 'scr'],
59
60
  ['mathit', 'italic'],
60
61
  ['mathfrak', 'frak'],
61
62
  ['mathrm', 'upright'],
@@ -260,7 +261,6 @@ const symbolMap = new Map<string, string>([
260
261
  ['intop', 'limits(integral)'],
261
262
 
262
263
  // extended
263
- ['mathscr', 'scr'],
264
264
  ['LaTeX', '#LaTeX'],
265
265
  ['TeX', '#TeX'],
266
266
  ]);