tex2typst 0.6.1 → 0.6.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.
package/dist/index.js CHANGED
@@ -324,7 +324,7 @@ function array_intersperse(array, sep) {
324
324
  }
325
325
 
326
326
  // src/lex.ts
327
- var EOF = {};
327
+ var EOF = null;
328
328
  function matchcompare(m1, m2) {
329
329
  const m1_len = m1.reMatchArray[0].length;
330
330
  const m2_len = m2.reMatchArray[0].length;
@@ -334,114 +334,31 @@ function matchcompare(m1, m2) {
334
334
  return m1.index - m2.index;
335
335
  }
336
336
  }
337
+ var ScanResult = class _ScanResult {
338
+ result;
339
+ status;
340
+ constructor(status, result) {
341
+ this.result = result;
342
+ this.status = status;
343
+ }
344
+ static Accepted(result) {
345
+ return new _ScanResult(0 /* ACCEPTED */, result);
346
+ }
347
+ static Rejected() {
348
+ return new _ScanResult(1 /* REJECTED */, []);
349
+ }
350
+ static Error(message) {
351
+ return new _ScanResult(2 /* ERROR */, []);
352
+ }
353
+ };
337
354
  var Scanner = class {
338
355
  _input;
339
- _lexer;
356
+ rules;
340
357
  // position within input stream
341
358
  _pos = 0;
342
- // current line number
343
- _line = 0;
344
- // current column number
345
- _col = 0;
346
- _offset = 0;
347
- _less = null;
348
- _go = false;
349
- _newstate = null;
350
- _state;
351
- _text = null;
352
- _leng = null;
353
- _reMatchArray = null;
354
- constructor(input, lexer) {
359
+ constructor(input, rules) {
355
360
  this._input = input;
356
- this._lexer = lexer;
357
- this._state = lexer.states[0];
358
- }
359
- /**
360
- * Analogous to yytext and yyleng in lex - will be set during scan.
361
- */
362
- text() {
363
- return this._text;
364
- }
365
- leng() {
366
- return this._leng;
367
- }
368
- reMatchArray() {
369
- return this._reMatchArray;
370
- }
371
- /**
372
- * Position of in stream, line number and column number of match.
373
- */
374
- pos() {
375
- return this._pos;
376
- }
377
- line() {
378
- return this._line;
379
- }
380
- column() {
381
- return this._col;
382
- }
383
- /**
384
- * Analogous to input() in lex.
385
- * @return {string} The next character in the stream.
386
- */
387
- input() {
388
- return this._input.charAt(this._pos + this._leng + this._offset++);
389
- }
390
- /**
391
- * Similar to unput() in lex, but does not allow modifying the stream.
392
- * @return {int} The offset position after the operation.
393
- */
394
- unput() {
395
- return this._offset = this._offset > 0 ? this._offset-- : 0;
396
- }
397
- /**
398
- * Analogous to yyless(n) in lex - retains the first n characters from this pattern, and returns
399
- * the rest to the input stream, such that they will be used in the next pattern-matching operation.
400
- * @param {int} n Number of characters to retain.
401
- * @return {int} Length of the stream after the operation has completed.
402
- */
403
- less(n) {
404
- this._less = n;
405
- this._offset = 0;
406
- this._text = this._text.substring(0, n);
407
- return this._leng = this._text.length;
408
- }
409
- /**
410
- * Like less(), but instead of retaining the first n characters, it chops off the last n.
411
- * @param {int} n Number of characters to chop.
412
- * @return {int} Length of the stream after the operation has completed.
413
- */
414
- pushback(n) {
415
- return this.less(this._leng - n);
416
- }
417
- /**
418
- * Similar to REJECT in lex, except it doesn't break the current execution context.
419
- * TIP: reject() should be the last instruction in a spec callback.
420
- */
421
- reject() {
422
- this._go = true;
423
- }
424
- /**
425
- * Analogous to BEGIN in lex - sets the named state (start condition).
426
- * @param {string|int} state Name of state to switch to, or ordinal number (0 is first, etc).
427
- * @return {string} The new state on successful switch, throws exception on failure.
428
- */
429
- begin(state) {
430
- if (this._lexer.specification[state]) {
431
- return this._newstate = state;
432
- }
433
- const s = this._lexer.states[parseInt(state)];
434
- if (s) {
435
- return this._newstate = s;
436
- }
437
- throw "Unknown state '" + state + "' requested";
438
- }
439
- /**
440
- * Simple accessor for reading in the current state.
441
- * @return {string} The current state.
442
- */
443
- state() {
444
- return this._state;
361
+ this.rules = rules;
445
362
  }
446
363
  /**
447
364
  * Scan method to be returned to caller - grabs the next token and fires appropriate calback.
@@ -452,7 +369,7 @@ var Scanner = class {
452
369
  return EOF;
453
370
  }
454
371
  const str = this._input.substring(this._pos);
455
- const rules = this._lexer.specification[this._state];
372
+ const rules = this.rules;
456
373
  const matches = [];
457
374
  for (let i = 0; i < rules.length; i++) {
458
375
  const rule = rules[i];
@@ -469,87 +386,50 @@ var Scanner = class {
469
386
  throw new Error("No match found for input '" + str + "'");
470
387
  }
471
388
  matches.sort(matchcompare);
472
- this._go = true;
473
- let result;
474
- let matched_text;
475
- for (let j = 0, n = matches.length; j < n && this._go; j++) {
476
- this._offset = 0;
477
- this._less = null;
478
- this._go = false;
479
- this._newstate = null;
480
- const m = matches[j];
481
- matched_text = m.reMatchArray[0];
482
- this._text = matched_text;
483
- this._leng = matched_text.length;
484
- this._reMatchArray = m.reMatchArray;
485
- result = m.rule.action(this);
486
- if (this._newstate && this._newstate != this._state) {
487
- this._state = this._newstate;
488
- break;
389
+ for (const m of matches) {
390
+ const matched_text = m.reMatchArray[0];
391
+ const result = m.rule.action({
392
+ pos: this._pos,
393
+ text: matched_text,
394
+ reMatchArray: m.reMatchArray
395
+ });
396
+ if (result.status === 0 /* ACCEPTED */) {
397
+ this._pos += matched_text.length;
398
+ return result.result;
489
399
  }
490
400
  }
491
- const text = this._less === null ? matched_text : matched_text.substring(0, this._less);
492
- const len = text.length;
493
- this._pos += len + this._offset;
494
- const nlm = text.match(/\n/g);
495
- if (nlm !== null) {
496
- this._line += nlm.length;
497
- this._col = len - text.lastIndexOf("\n") - 1;
498
- } else {
499
- this._col += len;
500
- }
501
- return result;
401
+ throw new Error("No match found for input '" + str + "'");
502
402
  }
503
403
  };
504
404
  var JSLex = class {
505
- states;
506
- specification;
507
- constructor(spec3) {
508
- this.states = Object.keys(spec3);
509
- this.specification = {};
510
- for (const s of this.states) {
511
- const rule_map = spec3[s];
512
- if (s in this.specification) {
513
- throw "Duplicate state declaration encountered for state '" + s + "'";
514
- }
515
- this.specification[s] = [];
516
- for (const [k, v] of rule_map.entries()) {
517
- let re;
518
- try {
519
- re = new RegExp("^" + k);
520
- } catch (err) {
521
- throw "Invalid regexp '" + k + "' in state '" + s + "' (" + err.message + ")";
522
- }
523
- this.specification[s].push({
524
- re,
525
- action: v
526
- });
405
+ rules = [];
406
+ constructor(ruleMap) {
407
+ for (const [k, v] of ruleMap.entries()) {
408
+ let re;
409
+ try {
410
+ re = new RegExp("^" + k);
411
+ } catch (err) {
412
+ throw "Invalid regexp '" + k + "' (" + err.message + ")";
527
413
  }
414
+ this.rules.push({
415
+ re,
416
+ action: v
417
+ });
528
418
  }
529
419
  }
530
- /**
531
- * Scanner function - makes a new scanner object which is used to get tokens one at a time.
532
- * @param {string} input Input text to tokenize.
533
- * @return {function} Scanner function.
534
- */
535
- scanner(input) {
536
- return new Scanner(input, this);
537
- }
538
420
  /**
539
421
  * Similar to lex's yylex() function, consumes all input, calling calback for each token.
540
422
  * @param {string} input Text to lex.
541
423
  * @param {function} callback Function to execute for each token.
542
424
  */
543
425
  lex(input, callback) {
544
- const scanner = this.scanner(input);
426
+ const scanner = new Scanner(input, this.rules);
545
427
  while (true) {
546
428
  const token = scanner.scan();
547
429
  if (token === EOF) {
548
- return;
549
- }
550
- if (token !== void 0) {
551
- callback(token);
430
+ break;
552
431
  }
432
+ callback(token);
553
433
  }
554
434
  }
555
435
  /**
@@ -647,8 +527,8 @@ var rules_map = /* @__PURE__ */ new Map([
647
527
  [
648
528
  String.raw`\\begin{(array|subarry)}{(.+?)}`,
649
529
  (s) => {
650
- const match = s.reMatchArray();
651
- return [
530
+ const match = s.reMatchArray;
531
+ return ScanResult.Accepted([
652
532
  new TexToken(2 /* COMMAND */, "\\begin"),
653
533
  new TexToken(7 /* CONTROL */, "{"),
654
534
  new TexToken(3 /* LITERAL */, match[1]),
@@ -656,74 +536,69 @@ var rules_map = /* @__PURE__ */ new Map([
656
536
  new TexToken(7 /* CONTROL */, "{"),
657
537
  new TexToken(3 /* LITERAL */, match[2]),
658
538
  new TexToken(7 /* CONTROL */, "}")
659
- ];
539
+ ]);
660
540
  }
661
541
  ],
662
542
  [
663
543
  String.raw`\\(text|operatorname\*?|textcolor|begin|end|hspace|array)\s*{(.+?)}`,
664
544
  (s) => {
665
- const match = s.reMatchArray();
666
- return [
545
+ const match = s.reMatchArray;
546
+ return ScanResult.Accepted([
667
547
  new TexToken(2 /* COMMAND */, "\\" + match[1]),
668
548
  new TexToken(7 /* CONTROL */, "{"),
669
549
  new TexToken(3 /* LITERAL */, unescape(match[2])),
670
550
  new TexToken(7 /* CONTROL */, "}")
671
- ];
551
+ ]);
672
552
  }
673
553
  ],
674
- [String.raw`%[^\n]*`, (s) => new TexToken(4 /* COMMENT */, s.text().substring(1))],
675
- [String.raw`[{}_^&]`, (s) => new TexToken(7 /* CONTROL */, s.text())],
676
- [String.raw`\\[\\,:;!> ]`, (s) => new TexToken(7 /* CONTROL */, s.text())],
677
- [String.raw`~`, (s) => new TexToken(7 /* CONTROL */, s.text())],
678
- [String.raw`\r?\n`, (_s) => new TexToken(6 /* NEWLINE */, "\n")],
679
- [String.raw`\s+`, (s) => new TexToken(5 /* SPACE */, s.text())],
680
- [String.raw`\\[{}%$&#_|]`, (s) => new TexToken(1 /* ELEMENT */, s.text())],
554
+ [String.raw`%[^\n]*`, (s) => ScanResult.Accepted(new TexToken(4 /* COMMENT */, s.text.substring(1)))],
555
+ [String.raw`[{}_^&]`, (s) => ScanResult.Accepted([new TexToken(7 /* CONTROL */, s.text)])],
556
+ [String.raw`\\[\\,:;!> ]`, (s) => ScanResult.Accepted([new TexToken(7 /* CONTROL */, s.text)])],
557
+ [String.raw`~`, (s) => ScanResult.Accepted([new TexToken(7 /* CONTROL */, s.text)])],
558
+ [String.raw`\r?\n`, (_s) => ScanResult.Accepted([new TexToken(6 /* NEWLINE */, "\n")])],
559
+ [String.raw`\s+`, (s) => ScanResult.Accepted([new TexToken(5 /* SPACE */, s.text)])],
560
+ [String.raw`\\[{}%$&#_|]`, (s) => ScanResult.Accepted([new TexToken(1 /* ELEMENT */, s.text)])],
681
561
  // e.g. match `\frac13`, `\frac1 b`, `\frac a b`
682
562
  [String.raw`(\\[a-zA-Z]+)(\s*\d|\s+[a-zA-Z])\s*([0-9a-zA-Z])`, (s) => {
683
- const match = s.reMatchArray();
563
+ const match = s.reMatchArray;
684
564
  const command = match[1];
685
565
  if (TEX_BINARY_COMMANDS.includes(command.substring(1))) {
686
566
  const arg1 = match[2].trimStart();
687
567
  const arg2 = match[3];
688
- return [
568
+ return ScanResult.Accepted([
689
569
  new TexToken(2 /* COMMAND */, command),
690
570
  new TexToken(1 /* ELEMENT */, arg1),
691
571
  new TexToken(1 /* ELEMENT */, arg2)
692
- ];
572
+ ]);
693
573
  } else {
694
- s.reject();
695
- return [];
574
+ return ScanResult.Rejected();
696
575
  }
697
576
  }],
698
577
  // e.g. match `\sqrt3`, `\sqrt a`
699
578
  [String.raw`(\\[a-zA-Z]+)(\s*\d|\s+[a-zA-Z])`, (s) => {
700
- const match = s.reMatchArray();
579
+ const match = s.reMatchArray;
701
580
  const command = match[1];
702
581
  if (TEX_UNARY_COMMANDS.includes(command.substring(1))) {
703
582
  const arg1 = match[2].trimStart();
704
- return [
583
+ return ScanResult.Accepted([
705
584
  new TexToken(2 /* COMMAND */, command),
706
585
  new TexToken(1 /* ELEMENT */, arg1)
707
- ];
586
+ ]);
708
587
  } else {
709
- s.reject();
710
- return [];
588
+ return ScanResult.Rejected();
711
589
  }
712
590
  }],
713
- [String.raw`\\[a-zA-Z]+`, (s) => new TexToken(2 /* COMMAND */, s.text())],
591
+ [String.raw`\\[a-zA-Z]+`, (s) => ScanResult.Accepted(new TexToken(2 /* COMMAND */, s.text))],
714
592
  // Numbers like "123", "3.14"
715
- [String.raw`[0-9]+(\.[0-9]+)?`, (s) => new TexToken(1 /* ELEMENT */, s.text())],
716
- [String.raw`[a-zA-Z]`, (s) => new TexToken(1 /* ELEMENT */, s.text())],
717
- [String.raw`[+\-*/='<>!.,;:?()\[\]|]`, (s) => new TexToken(1 /* ELEMENT */, s.text())],
593
+ [String.raw`[0-9]+(\.[0-9]+)?`, (s) => ScanResult.Accepted(new TexToken(1 /* ELEMENT */, s.text))],
594
+ [String.raw`[a-zA-Z]`, (s) => ScanResult.Accepted(new TexToken(1 /* ELEMENT */, s.text))],
595
+ [String.raw`[+\-*/='<>!.,;:?()\[\]|]`, (s) => ScanResult.Accepted(new TexToken(1 /* ELEMENT */, s.text))],
718
596
  // non-ASCII characters
719
- [String.raw`[^\x00-\x7F]`, (s) => new TexToken(1 /* ELEMENT */, s.text())],
720
- [String.raw`.`, (s) => new TexToken(8 /* UNKNOWN */, s.text())]
597
+ [String.raw`[^\x00-\x7F]`, (s) => ScanResult.Accepted(new TexToken(1 /* ELEMENT */, s.text))],
598
+ [String.raw`.`, (s) => ScanResult.Accepted([new TexToken(8 /* UNKNOWN */, s.text)])]
721
599
  ]);
722
- var spec = {
723
- "start": rules_map
724
- };
725
600
  function tokenize_tex(input) {
726
- const lexer = new JSLex(spec);
601
+ const lexer = new JSLex(rules_map);
727
602
  return lexer.collect(input);
728
603
  }
729
604
 
@@ -3065,8 +2940,6 @@ function tex_token_to_typst(token, options) {
3065
2940
  case 7 /* CONTROL */: {
3066
2941
  if (token.value === "\\\\") {
3067
2942
  return new TypstToken(7 /* CONTROL */, "\\");
3068
- } else if (token.value === "\\!") {
3069
- return new TypstToken(1 /* SYMBOL */, "#h(-math.thin.amount)");
3070
2943
  } else if (token.value === "~") {
3071
2944
  const typst_symbol = symbolMap.get("~");
3072
2945
  return new TypstToken(1 /* SYMBOL */, typst_symbol);
@@ -3166,6 +3039,12 @@ function convert_tex_node_to_typst(abstractNode, options) {
3166
3039
  switch (abstractNode.type) {
3167
3040
  case "terminal": {
3168
3041
  const node2 = abstractNode;
3042
+ if (node2.head.eq(new TexToken(7 /* CONTROL */, "\\!"))) {
3043
+ return new TypstFuncCall(
3044
+ new TypstToken(1 /* SYMBOL */, "#h"),
3045
+ [new TypstToken(3 /* LITERAL */, "-math.thin.amount").toNode()]
3046
+ );
3047
+ }
3169
3048
  return tex_token_to_typst(node2.head, options).toNode();
3170
3049
  }
3171
3050
  case "text": {
@@ -3880,64 +3759,69 @@ function generate_regex_for_shorthands() {
3880
3759
  }
3881
3760
  var REGEX_SHORTHANDS = generate_regex_for_shorthands();
3882
3761
  var rules_map2 = /* @__PURE__ */ new Map([
3883
- [String.raw`//[^\n]*`, (s) => new TypstToken(5 /* COMMENT */, s.text().substring(2))],
3884
- [String.raw`/`, (s) => new TypstToken(2 /* ELEMENT */, s.text())],
3885
- [String.raw`[_^&]`, (s) => new TypstToken(7 /* CONTROL */, s.text())],
3886
- [String.raw`\r?\n`, (_s) => new TypstToken(8 /* NEWLINE */, "\n")],
3887
- [String.raw`\s+`, (s) => new TypstToken(6 /* SPACE */, s.text())],
3888
- [String.raw`\\[$&#_]`, (s) => new TypstToken(2 /* ELEMENT */, s.text())],
3889
- [String.raw`\\\n`, (s) => {
3890
- return [
3762
+ [String.raw`//[^\n]*`, (s) => ScanResult.Accepted(new TypstToken(5 /* COMMENT */, s.text.substring(2)))],
3763
+ [String.raw`/`, (s) => ScanResult.Accepted(new TypstToken(2 /* ELEMENT */, s.text))],
3764
+ [String.raw`[_^&]`, (s) => ScanResult.Accepted(new TypstToken(7 /* CONTROL */, s.text))],
3765
+ [String.raw`\r?\n`, (_s) => ScanResult.Accepted(new TypstToken(8 /* NEWLINE */, "\n"))],
3766
+ [String.raw`\s+`, (s) => ScanResult.Accepted(new TypstToken(6 /* SPACE */, s.text))],
3767
+ [String.raw`\\[$&#_]`, (s) => ScanResult.Accepted(new TypstToken(2 /* ELEMENT */, s.text))],
3768
+ [
3769
+ String.raw`\\\n`,
3770
+ (s) => ScanResult.Accepted([
3891
3771
  new TypstToken(7 /* CONTROL */, "\\"),
3892
3772
  new TypstToken(8 /* NEWLINE */, "\n")
3893
- ];
3894
- }],
3773
+ ])
3774
+ ],
3895
3775
  [String.raw`\\\s`, (s) => {
3896
- return [
3776
+ return ScanResult.Accepted([
3897
3777
  new TypstToken(7 /* CONTROL */, "\\"),
3898
3778
  new TypstToken(6 /* SPACE */, " ")
3899
- ];
3779
+ ]);
3900
3780
  }],
3901
3781
  // this backslash is dummy and will be ignored in later stages
3902
- [String.raw`\\\S`, (_s) => new TypstToken(7 /* CONTROL */, "")],
3782
+ [String.raw`\\\S`, (_s) => ScanResult.Accepted(new TypstToken(7 /* CONTROL */, ""))],
3903
3783
  [
3904
3784
  String.raw`"([^"]|(\\"))*"`,
3905
3785
  (s) => {
3906
- const text = s.text().substring(1, s.text().length - 1);
3786
+ const text = s.text.substring(1, s.text.length - 1);
3907
3787
  text.replaceAll('\\"', '"');
3908
- return new TypstToken(4 /* TEXT */, text);
3788
+ return ScanResult.Accepted(new TypstToken(4 /* TEXT */, text));
3909
3789
  }
3910
3790
  ],
3911
3791
  [
3912
3792
  REGEX_SHORTHANDS,
3913
3793
  (s) => {
3914
- const shorthand = s.text();
3794
+ const shorthand = s.text;
3915
3795
  const symbol = reverseShorthandMap.get(shorthand);
3916
- return new TypstToken(1 /* SYMBOL */, symbol);
3796
+ return ScanResult.Accepted(new TypstToken(1 /* SYMBOL */, symbol));
3917
3797
  }
3918
3798
  ],
3919
- [String.raw`[0-9]+(\.[0-9]+)?`, (s) => new TypstToken(2 /* ELEMENT */, s.text())],
3920
- [String.raw`[+\-*/=\'<>!.,;?()\[\]|]`, (s) => new TypstToken(2 /* ELEMENT */, s.text())],
3921
- [String.raw`#h\((.+?)\)`, (s) => {
3922
- const match = s.reMatchArray();
3923
- return [
3924
- new TypstToken(1 /* SYMBOL */, "#h"),
3925
- new TypstToken(2 /* ELEMENT */, "("),
3926
- new TypstToken(3 /* LITERAL */, match[1]),
3927
- new TypstToken(2 /* ELEMENT */, ")")
3928
- ];
3929
- }],
3930
- [String.raw`#none`, (s) => new TypstToken(0 /* NONE */, s.text())],
3799
+ [String.raw`[0-9]+(\.[0-9]+)?`, (s) => ScanResult.Accepted(new TypstToken(2 /* ELEMENT */, s.text))],
3800
+ [String.raw`[+\-*/=\'<>!.,;?()\[\]|]`, (s) => ScanResult.Accepted(new TypstToken(2 /* ELEMENT */, s.text))],
3801
+ [
3802
+ String.raw`#h\((.+?)\)`,
3803
+ (s) => {
3804
+ const match = s.reMatchArray;
3805
+ return ScanResult.Accepted([
3806
+ new TypstToken(1 /* SYMBOL */, "#h"),
3807
+ new TypstToken(2 /* ELEMENT */, "("),
3808
+ new TypstToken(3 /* LITERAL */, match[1]),
3809
+ new TypstToken(2 /* ELEMENT */, ")")
3810
+ ]);
3811
+ }
3812
+ ],
3813
+ [String.raw`#none`, (s) => ScanResult.Accepted(new TypstToken(0 /* NONE */, s.text))],
3814
+ [
3815
+ String.raw`#none`,
3816
+ (s) => ScanResult.Accepted(new TypstToken(0 /* NONE */, s.text))
3817
+ ],
3931
3818
  [String.raw`#?[a-zA-Z\.]+`, (s) => {
3932
- return new TypstToken(s.text().length === 1 ? 2 /* ELEMENT */ : 1 /* SYMBOL */, s.text());
3819
+ return ScanResult.Accepted(new TypstToken(s.text.length === 1 ? 2 /* ELEMENT */ : 1 /* SYMBOL */, s.text));
3933
3820
  }],
3934
- [String.raw`.`, (s) => new TypstToken(2 /* ELEMENT */, s.text())]
3821
+ [String.raw`.`, (s) => ScanResult.Accepted(new TypstToken(2 /* ELEMENT */, s.text))]
3935
3822
  ]);
3936
- var spec2 = {
3937
- "start": rules_map2
3938
- };
3939
3823
  function tokenize_typst(input) {
3940
- const lexer = new JSLex(spec2);
3824
+ const lexer = new JSLex(rules_map2);
3941
3825
  return lexer.collect(input);
3942
3826
  }
3943
3827