tex2typst 0.3.27-beta.1 → 0.3.27

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) {
@@ -234,6 +269,14 @@ export function convert_tex_node_to_typst(abstractNode: TexNode, options: Tex2Ty
234
269
  sub: sub? convert_tex_node_to_typst(sub, options) : null,
235
270
  };
236
271
 
272
+ data.base = appendWithBracketsIfNeeded(data.base);
273
+ if (data.sup) {
274
+ data.sup = appendWithBracketsIfNeeded(data.sup);
275
+ }
276
+ if (data.sub) {
277
+ data.sub = appendWithBracketsIfNeeded(data.sub);
278
+ }
279
+
237
280
  return new TypstSupsub(data);
238
281
  }
239
282
  case 'leftright': {
@@ -348,12 +391,20 @@ export function convert_tex_node_to_typst(abstractNode: TexNode, options: Tex2Ty
348
391
  return res;
349
392
  }
350
393
 
351
- // \substack{a \\ b} -> `a \ b`
394
+ // \substack{a \\ b} -> a \ b
352
395
  // as in translation from \sum_{\substack{a \\ b}} to sum_(a \ b)
353
396
  if (node.head.value === '\\substack') {
354
397
  return arg0;
355
398
  }
356
399
 
400
+ // \set{a, b, c} -> {a, b, c}
401
+ if (node.head.value === '\\set') {
402
+ return new TypstLeftright(
403
+ null,
404
+ { body: arg0, left: TypstToken.LEFT_BRACE, right: TypstToken.RIGHT_BRACE }
405
+ );
406
+ }
407
+
357
408
  if (node.head.value === '\\overset') {
358
409
  return convert_overset(node, options);
359
410
  }
@@ -364,7 +415,7 @@ export function convert_tex_node_to_typst(abstractNode: TexNode, options: Tex2Ty
364
415
  // \frac{a}{b} -> a / b
365
416
  if (node.head.value === '\\frac') {
366
417
  if (options.fracToSlash) {
367
- return new TypstFraction(node.args.map((n) => convert_tex_node_to_typst(n, options)));
418
+ return new TypstFraction(node.args.map((n) => convert_tex_node_to_typst(n, options)).map(appendWithBracketsIfNeeded));
368
419
  }
369
420
  }
370
421
 
@@ -1,24 +1,23 @@
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
+ nonAsciiWrapper?: string; /** default is "" */
16
+ customTexMacros?: { [key: string]: string; };
17
+ }
18
+
19
+ export declare function tex2typst(tex: string, options?: Tex2TypstOptions): string;
20
+ export declare function typst2tex(typst: string): string;
21
+
22
+ export declare const symbolMap: Map<string, string>;
23
+ export declare const shorthandMap: Map<string, string>;
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";
@@ -21,9 +22,12 @@ export function tex2typst(tex: string, options?: Tex2TypstOptions): string {
21
22
  };
22
23
 
23
24
  if(options !== undefined) {
25
+ if (typeof options !== 'object') {
26
+ throw new Error("options must be an object");
27
+ }
24
28
  for (const key in opt) {
25
- if (options[key] !== undefined) {
26
- opt[key] = options[key];
29
+ if (key in options) {
30
+ opt[key as keyof Tex2TypstOptions] = options[key as keyof Tex2TypstOptions] as any;
27
31
  }
28
32
  }
29
33
  }
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> {
@@ -35,6 +35,7 @@ export const TEX_UNARY_COMMANDS = [
35
35
  'overrightarrow',
36
36
  'hspace',
37
37
  'substack',
38
+ 'set',
38
39
  ]
39
40
 
40
41
  export const TEX_BINARY_COMMANDS = [
package/src/tex2typst.ts CHANGED
@@ -4,7 +4,5 @@
4
4
 
5
5
  import { tex2typst, typst2tex } from './index';
6
6
 
7
- if(typeof window !== 'undefined') {
8
- (window as any).tex2typst = tex2typst;
9
- (window as any).typst2tex = typst2tex;
10
- }
7
+ (window as any).tex2typst = tex2typst;
8
+ (window as any).typst2tex = typst2tex;
@@ -53,8 +53,8 @@ function find_closing_delim(tokens: TypstToken[], start: number): number {
53
53
  return _find_closing_match(
54
54
  tokens,
55
55
  start,
56
- [LEFT_PARENTHESES, LEFT_BRACKET, LEFT_CURLY_BRACKET, VERTICAL_BAR],
57
- [RIGHT_PARENTHESES, RIGHT_BRACKET, RIGHT_CURLY_BRACKET, VERTICAL_BAR]
56
+ TypstToken.LEFT_DELIMITERS,
57
+ TypstToken.RIGHT_DELIMITERS
58
58
  );
59
59
  }
60
60
 
@@ -218,7 +218,6 @@ const LEFT_BRACKET: TypstToken = new TypstToken(TypstTokenType.ELEMENT, '[');
218
218
  const RIGHT_BRACKET: TypstToken = new TypstToken(TypstTokenType.ELEMENT, ']');
219
219
  const LEFT_CURLY_BRACKET: TypstToken = new TypstToken(TypstTokenType.ELEMENT, '{');
220
220
  const RIGHT_CURLY_BRACKET: TypstToken = new TypstToken(TypstTokenType.ELEMENT, '}');
221
- const VERTICAL_BAR = new TypstToken(TypstTokenType.ELEMENT, '|');
222
221
  const COMMA = new TypstToken(TypstTokenType.ELEMENT, ',');
223
222
  const SEMICOLON = new TypstToken(TypstTokenType.ELEMENT, ';');
224
223
  const SINGLE_SPACE = new TypstToken(TypstTokenType.SPACE, ' ');
@@ -373,20 +372,20 @@ export class TypstParser {
373
372
  // start: the position of the left parentheses
374
373
  parseLrArguments(tokens: TypstToken[], start: number): [TypstNode, number] {
375
374
  const lr_token = tokens[start];
376
- if (tokens[start + 1].isOneOf([LEFT_PARENTHESES, LEFT_BRACKET, LEFT_CURLY_BRACKET, VERTICAL_BAR])) {
377
- const end = find_closing_match(tokens, start);
375
+ const end = find_closing_match(tokens, start);
376
+ if (tokens[start + 1].isOneOf(TypstToken.LEFT_DELIMITERS)) {
378
377
  const inner_start = start + 1;
379
378
  const inner_end = find_closing_delim(tokens, inner_start);
380
- const inner_args= this.parseArgumentsWithSeparator(tokens, inner_start + 1, inner_end, COMMA);
379
+ const [inner_args, _]= this.parseGroup(tokens, inner_start + 1, inner_end);
381
380
  return [
382
- new TypstLeftright(lr_token, { body: new TypstGroup(inner_args), left: tokens[inner_start], right: tokens[inner_end]}),
381
+ new TypstLeftright(lr_token, { body: inner_args, left: tokens[inner_start], right: tokens[inner_end]}),
383
382
  end + 1,
384
383
  ];
385
384
  } else {
386
- const [args, end] = this.parseArguments(tokens, start);
385
+ const [inner_args, _] = this.parseGroup(tokens, start + 1, end - 1);
387
386
  return [
388
- new TypstLeftright(lr_token, { body: new TypstGroup(args), left: null, right: null }),
389
- end,
387
+ new TypstLeftright(lr_token, { body: inner_args, left: null, right: null }),
388
+ end + 1,
390
389
  ];
391
390
  }
392
391
  }