tex2typst 0.2.3 → 0.2.7

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/README.md CHANGED
@@ -16,12 +16,12 @@ npm install tex2typst
16
16
  ## Or just loading it in a web page
17
17
 
18
18
  ```html
19
- <script src="https://cdn.jsdelivr.net/npm/tex2typst@0.1.20/dist/tex2typst.min.js"></script>
19
+ <script src="https://cdn.jsdelivr.net/npm/tex2typst@0.2.7/dist/tex2typst.min.js"></script>
20
20
  <!-- or -->
21
- <script src="https://unpkg.com/tex2typst@0.1.20/dist/tex2typst.min.js"></script>
21
+ <script src="https://unpkg.com/tex2typst@0.2.7/dist/tex2typst.min.js"></script>
22
22
  ```
23
23
 
24
- Replace `0.1.20` with the latest version number in case this README is outdated.
24
+ Replace `0.2.7` with the latest version number in case this README is outdated.
25
25
 
26
26
  The size of minimized library `tex2typst.min.js` is about 23 KB.
27
27
 
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import { Tex2TypstOptions } from "./types";
2
+ import { symbolMap } from "./map";
2
3
  export declare function tex2typst(tex: string, options?: Tex2TypstOptions): string;
3
- export { Tex2TypstOptions };
4
+ export { symbolMap, Tex2TypstOptions };
package/dist/index.js CHANGED
@@ -189,17 +189,15 @@ function tokenize(latex) {
189
189
  throw new LatexParserError("Expecting command name after \\");
190
190
  }
191
191
  const firstTwoChars = latex.slice(pos, pos + 2);
192
- if (firstTwoChars === "\\\\") {
193
- token = { type: "control", value: "\\\\" };
194
- pos += 2;
192
+ if (["\\\\", "\\,"].includes(firstTwoChars)) {
193
+ token = { type: "control", value: firstTwoChars };
195
194
  } else if (["\\{", "\\}", "\\%", "\\$", "\\&", "\\#", "\\_"].includes(firstTwoChars)) {
196
195
  token = { type: "element", value: firstTwoChars };
197
- pos += 2;
198
196
  } else {
199
197
  const command = eat_command_name(latex, pos + 1);
200
198
  token = { type: "command", value: "\\" + command };
201
- pos += 1 + command.length;
202
199
  }
200
+ pos += token.value.length;
203
201
  break;
204
202
  }
205
203
  default: {
@@ -242,53 +240,61 @@ function tokenize(latex) {
242
240
  function token_eq(token1, token2) {
243
241
  return token1.type == token2.type && token1.value == token2.value;
244
242
  }
245
- function parseTex(tex, customTexMacros) {
246
- const parser = new LatexParser;
247
- const original_tokens = tokenize(tex);
248
- let processed_tokens = [];
249
- for (const token of original_tokens) {
243
+ function passIgnoreWhitespaceBeforeScriptMark(tokens) {
244
+ const is_script_mark = (token) => token_eq(token, SUB_SYMBOL) || token_eq(token, SUP_SYMBOL);
245
+ let out_tokens = [];
246
+ for (let i = 0;i < tokens.length; i++) {
247
+ if (tokens[i].type === "whitespace" && i + 1 < tokens.length && is_script_mark(tokens[i + 1])) {
248
+ continue;
249
+ }
250
+ if (tokens[i].type === "whitespace" && i - 1 >= 0 && is_script_mark(tokens[i - 1])) {
251
+ continue;
252
+ }
253
+ out_tokens.push(tokens[i]);
254
+ }
255
+ return out_tokens;
256
+ }
257
+ function passExpandCustomTexMacros(tokens, customTexMacros) {
258
+ let out_tokens = [];
259
+ for (const token of tokens) {
250
260
  if (token.type === "command" && customTexMacros[token.value]) {
251
261
  const expanded_tokens = tokenize(customTexMacros[token.value]);
252
- processed_tokens = processed_tokens.concat(expanded_tokens);
262
+ out_tokens = out_tokens.concat(expanded_tokens);
253
263
  } else {
254
- processed_tokens.push(token);
264
+ out_tokens.push(token);
255
265
  }
256
266
  }
257
- return parser.parse(processed_tokens);
267
+ return out_tokens;
268
+ }
269
+ function parseTex(tex, customTexMacros) {
270
+ const parser = new LatexParser;
271
+ let tokens = tokenize(tex);
272
+ tokens = passIgnoreWhitespaceBeforeScriptMark(tokens);
273
+ tokens = passExpandCustomTexMacros(tokens, customTexMacros);
274
+ return parser.parse(tokens);
258
275
  }
259
276
  var UNARY_COMMANDS = [
260
277
  "sqrt",
261
278
  "text",
262
- "arccos",
263
- "arcsin",
264
- "arctan",
265
- "arg",
266
279
  "bar",
267
280
  "bold",
268
281
  "boldsymbol",
269
282
  "ddot",
270
- "det",
271
- "dim",
272
283
  "dot",
273
- "exp",
274
- "gcd",
275
284
  "hat",
276
- "ker",
277
285
  "mathbb",
278
286
  "mathbf",
279
287
  "mathcal",
288
+ "mathfrak",
289
+ "mathit",
290
+ "mathrm",
280
291
  "mathscr",
281
292
  "mathsf",
282
293
  "mathtt",
283
- "mathrm",
284
- "max",
285
- "min",
286
- "mod",
287
294
  "operatorname",
288
295
  "overbrace",
289
296
  "overline",
290
297
  "pmb",
291
- "sup",
292
298
  "rm",
293
299
  "tilde",
294
300
  "underbrace",
@@ -449,23 +455,13 @@ class LatexParser {
449
455
  throw new LatexParserError("Unmatched '}'");
450
456
  case "\\\\":
451
457
  return [{ type: "control", content: "\\\\" }, start + 1];
458
+ case "\\,":
459
+ return [{ type: "control", content: "\\," }, start + 1];
452
460
  case "_": {
453
- let [sub, pos] = this.parseNextExpr(tokens, start + 1);
454
- let sup = undefined;
455
- if (pos < tokens.length && token_eq(tokens[pos], SUP_SYMBOL)) {
456
- [sup, pos] = this.parseNextExpr(tokens, pos + 1);
457
- }
458
- const subData = { base: EMPTY_NODE, sub, sup };
459
- return [{ type: "supsub", content: "", data: subData }, pos];
461
+ return [EMPTY_NODE, start];
460
462
  }
461
463
  case "^": {
462
- let [sup, pos] = this.parseNextExpr(tokens, start + 1);
463
- let sub = undefined;
464
- if (pos < tokens.length && token_eq(tokens[pos], SUB_SYMBOL)) {
465
- [sub, pos] = this.parseNextExpr(tokens, pos + 1);
466
- }
467
- const supData = { base: EMPTY_NODE, sub, sup };
468
- return [{ type: "supsub", content: "", data: supData }, pos];
464
+ return [EMPTY_NODE, start];
469
465
  }
470
466
  case "&":
471
467
  return [{ type: "control", content: "&" }, start + 1];
@@ -615,7 +611,6 @@ class LatexParser {
615
611
 
616
612
  // src/map.ts
617
613
  var symbolMap = new Map([
618
- ["gets", "arrow.l"],
619
614
  ["nonumber", ""],
620
615
  ["vec", "arrow"],
621
616
  ["neq", "eq.not"],
@@ -640,13 +635,14 @@ var symbolMap = new Map([
640
635
  ["dfrac", "frac"],
641
636
  ["tfrac", "frac"],
642
637
  ["boldsymbol", "bold"],
643
- ["mathbf", "bold"],
644
638
  ["mathbb", "bb"],
639
+ ["mathbf", "bold"],
645
640
  ["mathcal", "cal"],
641
+ ["mathit", "italic"],
646
642
  ["mathfrak", "frak"],
643
+ ["mathrm", "upright"],
647
644
  ["mathsf", "sans"],
648
645
  ["mathtt", "mono"],
649
- ["mathrm", "upright"],
650
646
  ["rm", "upright"],
651
647
  ["pmb", "bold"],
652
648
  ["pm", "plus.minus"],
@@ -662,29 +658,56 @@ var symbolMap = new Map([
662
658
  ["asymp", "\u224D"],
663
659
  ["equiv", "equiv"],
664
660
  ["propto", "prop"],
665
- ["implies", "arrow.r.double.long"],
666
- ["Longrightarrow", "arrow.r.double.long"],
667
- ["iff", "arrow.l.r.double.long"],
668
- ["Longleftrightarrow", "arrow.l.r.double.long"],
669
- ["leftrightarrow", "arrow.l.r"],
670
- ["longleftrightarrow", "arrow.l.r.long"],
671
- ["rightrightarrows", "arrows.rr"],
672
661
  ["lfloor", "\u230A"],
673
662
  ["rfloor", "\u230B"],
674
663
  ["lceil", "\u2308"],
675
664
  ["rceil", "\u2309"],
665
+ ["gets", "arrow.l"],
666
+ ["hookleftarrow", "arrow.l.hook"],
667
+ ["leftharpoonup", "harpoon.lt"],
668
+ ["leftharpoondown", "harpoon.lb"],
669
+ ["rightleftharpoons", "harpoons.rtlb"],
670
+ ["longleftarrow", "arrow.l.long"],
671
+ ["longrightarrow", "arrow.r.long"],
672
+ ["longleftrightarrow", "arrow.l.r.long"],
673
+ ["Longleftarrow", "arrow.l.double.long"],
674
+ ["Longrightarrow", "arrow.r.double.long"],
675
+ ["Longleftrightarrow", "arrow.l.r.double.long"],
676
+ ["longmapsto", "arrow.r.bar"],
677
+ ["hookrightarrow", "arrow.r.hook"],
678
+ ["rightharpoonup", "harpoon.rt"],
679
+ ["rightharpoondown", "harpoon.rb"],
680
+ ["iff", "arrow.l.r.double.long"],
681
+ ["implies", "arrow.r.double.long"],
682
+ ["uparrow", "arrow.t"],
683
+ ["downarrow", "arrow.b"],
684
+ ["updownarrow", "arrow.t.b"],
685
+ ["Uparrow", "arrow.t.double"],
686
+ ["Downarrow", "arrow.b.double"],
687
+ ["Updownarrow", "arrow.t.b.double"],
688
+ ["nearrow", "arrow.tr"],
689
+ ["searrow", "arrow.br"],
690
+ ["swarrow", "arrow.bl"],
691
+ ["nwarrow", "arrow.tl"],
692
+ ["leadsto", "arrow.squiggly"],
693
+ ["leftleftarrows", "arrows.ll"],
694
+ ["rightrightarrows", "arrows.rr"],
676
695
  ["Cap", "sect.double"],
677
696
  ["Cup", "union.double"],
678
697
  ["Delta", "Delta"],
679
698
  ["Gamma", "Gamma"],
680
699
  ["Join", "join"],
681
700
  ["Lambda", "Lambda"],
701
+ ["Leftarrow", "arrow.l.double"],
702
+ ["Leftrightarrow", "arrow.l.r.double"],
682
703
  ["Longrightarrow", "arrow.r.double.long"],
683
704
  ["Omega", "Omega"],
705
+ ["P", "pilcrow"],
684
706
  ["Phi", "Phi"],
685
707
  ["Pi", "Pi"],
686
708
  ["Psi", "Psi"],
687
- ["Rightarrow", "arrow.double"],
709
+ ["Rightarrow", "arrow.r.double"],
710
+ ["S", "section"],
688
711
  ["Sigma", "Sigma"],
689
712
  ["Theta", "Theta"],
690
713
  ["aleph", "alef"],
@@ -716,6 +739,7 @@ var symbolMap = new Map([
716
739
  ["colon", "colon"],
717
740
  ["cong", "tilde.equiv"],
718
741
  ["coprod", "product.co"],
742
+ ["copyright", "copyright"],
719
743
  ["cup", "union"],
720
744
  ["curlyvee", "or.curly"],
721
745
  ["curlywedge", "and.curly"],
@@ -768,7 +792,7 @@ var symbolMap = new Map([
768
792
  ["leqslant", "lt.eq.slant"],
769
793
  ["lhd", "triangle.l"],
770
794
  ["ll", "lt.double"],
771
- ["longmapsto", "arrow.long.bar"],
795
+ ["longmapsto", "arrow.bar.long"],
772
796
  ["longrightarrow", "arrow.long"],
773
797
  ["lor", "or"],
774
798
  ["ltimes", "times.l"],
@@ -1141,6 +1165,8 @@ class TypstWriter {
1141
1165
  } else if (node.type === "control") {
1142
1166
  if (node.content === "\\\\") {
1143
1167
  this.queue.push({ type: "symbol", content: node.content });
1168
+ } else if (node.content === "\\,") {
1169
+ this.queue.push({ type: "symbol", content: "thin" });
1144
1170
  } else {
1145
1171
  throw new TypstWriterError(`Unknown control sequence: ${node.content}`, node);
1146
1172
  }
@@ -1241,5 +1267,6 @@ function tex2typst(tex, options) {
1241
1267
  return writer2.finalize();
1242
1268
  }
1243
1269
  export {
1244
- tex2typst
1270
+ tex2typst,
1271
+ symbolMap
1245
1272
  };
package/dist/parser.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import { TexNode } from "./types";
2
- interface Token {
2
+ export interface Token {
3
3
  type: 'element' | 'command' | 'text' | 'comment' | 'whitespace' | 'newline' | 'control' | 'unknown';
4
4
  value: string;
5
5
  }
6
+ export declare function tokenize(latex: string): Token[];
6
7
  export declare class LatexParserError extends Error {
7
8
  constructor(message: string);
8
9
  }
@@ -1 +1 @@
1
- function G(J,Z=""){if(!J)throw new v(Z)}function c(J){if(E.includes(J))return 1;else if(m.includes(J))return 2;else return 0}function x(J,Z){G(H(J[Z],D));let V=1,$=Z+1;while(V>0){if($>=J.length)throw new v("Unmatched curly brackets");if(H(J[$],D))V+=1;else if(H(J[$],S))V-=1;$+=1}return $-1}function l(J,Z){G(H(J[Z],I));let V=1,$=Z+1;while(V>0){if($>=J.length)throw new v("Unmatched square brackets");if(H(J[$],I))V+=1;else if(H(J[$],i))V-=1;$+=1}return $-1}function C(J){return"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".includes(J)}function w(J){return"0123456789".includes(J)}function N(J,Z){let V=Z;while(V<J.length&&["whitespace","newline"].includes(J[V].type))V++;return J.slice(Z,V)}function R(J,Z){const V=J[Z];if(V.type==="element"&&["(",")","[","]","|","\\{","\\}"].includes(V.value))return V;else if(V.type==="command"&&["lfloor","rfloor","lceil","rceil","langle","rangle"].includes(V.value.slice(1)))return V;else return null}function K(J,Z){let V=Z;while(V<J.length&&H(J[V],{type:"element",value:"'"}))V+=1;return V-Z}function _(J,Z){let V=Z;while(V<J.length&&C(J[V]))V+=1;return J.substring(Z,V)}function p(J,Z){let V=1,$=Z;while(V>0){if($>=J.length)return-1;if(H(J[$],q))V+=1;else if(H(J[$],d))V-=1;$+=1}return $-1}function a(J,Z){let V=1,$=Z;while(V>0){if($>=J.length)return-1;if(H(J[$],M))V+=1;else if(H(J[$],k))V-=1;$+=1}return $-1}function r(J,Z){G(J[Z]==="{");let V=1,$=Z+1;while(V>0){if($>=J.length)throw new v("Unmatched curly brackets");if($+1<J.length&&["\\{","\\}"].includes(J.substring($,$+2))){$+=2;continue}if(J[$]==="{")V+=1;else if(J[$]==="}")V-=1;$+=1}return $-1}function g(J){const Z=[];let V=0;while(V<J.length){const $=J[V];let X;switch($){case"%":{let z=V+1;while(z<J.length&&J[z]!=="\n")z+=1;X={type:"comment",value:J.slice(V+1,z)},V=z;break}case"{":case"}":case"_":case"^":case"&":X={type:"control",value:$},V++;break;case"\n":X={type:"newline",value:$},V++;break;case"\r":{if(V+1<J.length&&J[V+1]==="\n")X={type:"newline",value:"\n"},V+=2;else X={type:"newline",value:"\n"},V++;break}case" ":{let z=V;while(z<J.length&&J[z]===" ")z+=1;X={type:"whitespace",value:J.slice(V,z)},V=z;break}case"\\":{if(V+1>=J.length)throw new v("Expecting command name after \\");const z=J.slice(V,V+2);if(z==="\\\\")X={type:"control",value:"\\\\"},V+=2;else if(["\\{","\\}","\\%","\\$","\\&","\\#","\\_"].includes(z))X={type:"element",value:z},V+=2;else{const j=_(J,V+1);X={type:"command",value:"\\"+j},V+=1+j.length}break}default:{if(w($)){let z=V;while(z<J.length&&w(J[z]))z+=1;X={type:"element",value:J.slice(V,z)}}else if(C($))X={type:"element",value:$};else if("+-*/=\'<>!.,;?()[]|".includes($))X={type:"element",value:$};else X={type:"unknown",value:$};V+=X.value.length}}if(Z.push(X),X.type==="command"&&["\\text","\\begin","\\end"].includes(X.value)){if(V>=J.length||J[V]!=="{")throw new v(`No content for ${X.value} command`);Z.push({type:"control",value:"{"});const z=r(J,V);V++;let j=J.slice(V,z);const Q=["{","}","\\","$","&","#","_","%"];for(let W of Q)j=j.replaceAll("\\"+W,W);Z.push({type:"text",value:j}),Z.push({type:"control",value:"}"}),V=z+1}}return Z}function H(J,Z){return J.type==Z.type&&J.value==Z.value}function b(J,Z){const V=new T,$=g(J);let X=[];for(let z of $)if(z.type==="command"&&Z[z.value]){const j=g(Z[z.value]);X=X.concat(j)}else X.push(z);return V.parse(X)}var E=["sqrt","text","arccos","arcsin","arctan","arg","bar","bold","boldsymbol","ddot","det","dim","dot","exp","gcd","hat","ker","mathbb","mathbf","mathcal","mathscr","mathsf","mathtt","mathrm","max","min","mod","operatorname","overbrace","overline","pmb","sup","rm","tilde","underbrace","underline","vec","widehat","widetilde"],m=["frac","tfrac","binom","dbinom","dfrac","tbinom"],Y={type:"empty",content:""},D={type:"control",value:"{"},S={type:"control",value:"}"},I={type:"element",value:"["},i={type:"element",value:"]"},q={type:"command",value:"\\left"},d={type:"command",value:"\\right"},M={type:"command",value:"\\begin"},k={type:"command",value:"\\end"};class v extends Error{constructor(J){super(J);this.name="LatexParserError"}}var y={type:"control",value:"_"},A={type:"control",value:"^"};class T{space_sensitive;newline_sensitive;constructor(J=!1,Z=!0){this.space_sensitive=J,this.newline_sensitive=Z}parse(J){const Z=[];let V=0;while(V<J.length){const $=[];let X=0;while(X<J.length){const[z,j]=this.parseNextExpr(J,X);if(X=j,!this.space_sensitive&&z.type==="whitespace")continue;if(!this.newline_sensitive&&z.type==="newline")continue;if(z.type==="control"&&z.content==="&")throw new v("Unexpected & outside of an alignment");$.push(z)}if($.length===0)return Y;else if($.length===1)return $[0];else return{type:"ordgroup",content:"",args:$}}if(Z.length===0)return Y;else if(Z.length===1)return Z[0];else return{type:"ordgroup",content:"",args:Z}}parseNextExpr(J,Z){let[V,$]=this.parseNextExprWithoutSupSub(J,Z),X=null,z=null,j=0;if(j+=K(J,$),$+=j,$<J.length&&H(J[$],y)){if([X,$]=this.parseNextExprWithoutSupSub(J,$+1),j+=K(J,$),$+=j,$<J.length&&H(J[$],A)){if([z,$]=this.parseNextExprWithoutSupSub(J,$+1),K(J,$)>0)throw new v("Double superscript")}}else if($<J.length&&H(J[$],A)){if([z,$]=this.parseNextExprWithoutSupSub(J,$+1),K(J,$)>0)throw new v("Double superscript");if($<J.length&&H(J[$],y)){if([X,$]=this.parseNextExprWithoutSupSub(J,$+1),K(J,$)>0)throw new v("Double superscript")}}if(X!==null||z!==null||j>0){const Q={base:V};if(X)Q.sub=X;if(j>0){Q.sup={type:"ordgroup",content:"",args:[]};for(let W=0;W<j;W++)Q.sup.args.push({type:"symbol",content:"\\prime"});if(z)Q.sup.args.push(z);if(Q.sup.args.length===1)Q.sup=Q.sup.args[0]}else if(z)Q.sup=z;return[{type:"supsub",content:"",data:Q},$]}else return[V,$]}parseNextExprWithoutSupSub(J,Z){const V=J[Z],$=V.type;switch($){case"element":case"text":case"comment":case"whitespace":case"newline":return[{type:$,content:V.value},Z+1];case"command":if(H(V,M))return this.parseBeginEndExpr(J,Z);else if(H(V,q))return this.parseLeftRightExpr(J,Z);else return this.parseCommandExpr(J,Z);case"control":switch(V.value){case"{":const z=x(J,Z),j=J.slice(Z+1,z);return[this.parse(j),z+1];case"}":throw new v("Unmatched '}'");case"\\\\":return[{type:"control",content:"\\\\"},Z+1];case"_":{let[Q,W]=this.parseNextExpr(J,Z+1),F=void 0;if(W<J.length&&H(J[W],A))[F,W]=this.parseNextExpr(J,W+1);return[{type:"supsub",content:"",data:{base:Y,sub:Q,sup:F}},W]}case"^":{let[Q,W]=this.parseNextExpr(J,Z+1),F=void 0;if(W<J.length&&H(J[W],y))[F,W]=this.parseNextExpr(J,W+1);return[{type:"supsub",content:"",data:{base:Y,sub:F,sup:Q}},W]}case"&":return[{type:"control",content:"&"},Z+1];default:throw new v("Unknown control sequence")}default:throw new v("Unknown token type")}}parseCommandExpr(J,Z){G(J[Z].type==="command");const V=J[Z].value;let $=Z+1;if(["left","right","begin","end"].includes(V.slice(1)))throw new v("Unexpected command: "+V);const X=c(V.slice(1));if(X===0)return[{type:"symbol",content:V},$];else if(X===1){if(V==="\\sqrt"&&$<J.length&&H(J[$],I)){const Q=$,W=l(J,$),F=J.slice(Q+1,W),U=this.parse(F),[f,u]=this.parseNextExprWithoutSupSub(J,W+1);return[{type:"unaryFunc",content:V,args:[f],data:U},u]}else if(V==="\\text"){if($+2>=J.length)throw new v("Expecting content for \\text command");return G(H(J[$],D)),G(J[$+1].type==="text"),G(H(J[$+2],S)),[{type:"text",content:J[$+1].value},$+3]}let[z,j]=this.parseNextExprWithoutSupSub(J,$);return[{type:"unaryFunc",content:V,args:[z]},j]}else if(X===2){const[z,j]=this.parseNextExprWithoutSupSub(J,$),[Q,W]=this.parseNextExprWithoutSupSub(J,j);return[{type:"binaryFunc",content:V,args:[z,Q]},W]}else throw new Error("Invalid number of parameters")}parseLeftRightExpr(J,Z){G(H(J[Z],q));let V=Z+1;if(V+=N(J,V).length,V>=J.length)throw new v("Expecting delimiter after \\left");const $=R(J,V);if($===null)throw new v("Invalid delimiter after \\left");V++;const X=V,z=p(J,V);if(z===-1)throw new v("No matching \\right");const j=z;if(V=z+1,V+=N(J,V).length,V>=J.length)throw new v("Expecting \\right after \\left");const Q=R(J,V);if(Q===null)throw new v("Invalid delimiter after \\right");V++;const W=J.slice(X,j),F=this.parse(W);return[{type:"leftright",content:"",args:[{type:"element",content:$.value},F,{type:"element",content:Q.value}]},V]}parseBeginEndExpr(J,Z){G(H(J[Z],M));let V=Z+1;G(H(J[V],D)),G(J[V+1].type==="text"),G(H(J[V+2],S));const $=J[V+1].value;V+=3,V+=N(J,V).length;const X=V,z=a(J,V);if(z===-1)throw new v("No matching \\end");const j=z;if(V=z+1,G(H(J[V],D)),G(J[V+1].type==="text"),G(H(J[V+2],S)),J[V+1].value!==$)throw new v("Mismatched \\begin and \\end environments");V+=3;const Q=J.slice(X,j);while(Q.length>0&&["whitespace","newline"].includes(Q[Q.length-1].type))Q.pop();const W=this.parseAligned(Q);return[{type:"beginend",content:$,data:W},V]}parseAligned(J){let Z=0;const V=[];let $=[];V.push($);let X={type:"ordgroup",content:"",args:[]};$.push(X);while(Z<J.length){const[z,j]=this.parseNextExpr(J,Z);if(Z=j,z.type==="whitespace")continue;else if(z.type==="newline"&&!this.newline_sensitive)continue;else if(z.type==="control"&&z.content==="\\\\")$=[],X={type:"ordgroup",content:"",args:[]},$.push(X),V.push($);else if(z.type==="control"&&z.content==="&")X={type:"ordgroup",content:"",args:[]},$.push(X);else X.args.push(z)}return V}}var B=new Map([["gets","arrow.l"],["nonumber",""],["vec","arrow"],["neq","eq.not"],["dot","dot"],["ddot","dot.double"],["doteq","dot(eq)"],["dots","dots.h"],["ldots","dots.h"],["vdots","dots.v"],["ddots","dots.down"],["widehat","hat"],["widetilde","tilde"],["quad","quad"],["qquad","wide"],["overbrace","overbrace"],["underbrace","underbrace"],["overline","overline"],["underline","underline"],["bar","macron"],["dbinom","binom"],["tbinom","binom"],["dfrac","frac"],["tfrac","frac"],["boldsymbol","bold"],["mathbf","bold"],["mathbb","bb"],["mathcal","cal"],["mathfrak","frak"],["mathsf","sans"],["mathtt","mono"],["mathrm","upright"],["rm","upright"],["pmb","bold"],["pm","plus.minus"],["mp","minus.plus"],["oplus","xor"],["boxplus","plus.square"],["otimes","times.circle"],["boxtimes","times.square"],["sim","tilde"],["approx","approx"],["cong","tilde.equiv"],["simeq","tilde.eq"],["asymp","\u224D"],["equiv","equiv"],["propto","prop"],["implies","arrow.r.double.long"],["Longrightarrow","arrow.r.double.long"],["iff","arrow.l.r.double.long"],["Longleftrightarrow","arrow.l.r.double.long"],["leftrightarrow","arrow.l.r"],["longleftrightarrow","arrow.l.r.long"],["rightrightarrows","arrows.rr"],["lfloor","\u230A"],["rfloor","\u230B"],["lceil","\u2308"],["rceil","\u2309"],["Cap","sect.double"],["Cup","union.double"],["Delta","Delta"],["Gamma","Gamma"],["Join","join"],["Lambda","Lambda"],["Longrightarrow","arrow.r.double.long"],["Omega","Omega"],["Phi","Phi"],["Pi","Pi"],["Psi","Psi"],["Rightarrow","arrow.double"],["Sigma","Sigma"],["Theta","Theta"],["aleph","alef"],["alpha","alpha"],["angle","angle"],["approx","approx"],["approxeq","approx.eq"],["ast","ast"],["beta","beta"],["bigcap","sect.big"],["bigcirc","circle.big"],["bigcup","union.big"],["bigodot","dot.circle.big"],["bigoplus","xor.big"],["bigotimes","times.circle.big"],["bigsqcup","union.sq.big"],["bigtriangledown","triangle.b"],["bigtriangleup","triangle.t"],["biguplus","union.plus.big"],["bigvee","or.big"],["bigwedge","and.big"],["bullet","bullet"],["cap","sect"],["cdot","dot.op"],["cdots","dots.c"],["checkmark","checkmark"],["chi","chi"],["circ","circle.small"],["colon","colon"],["cong","tilde.equiv"],["coprod","product.co"],["cup","union"],["curlyvee","or.curly"],["curlywedge","and.curly"],["dagger","dagger"],["dashv","tack.l"],["ddagger","dagger.double"],["delta","delta"],["ddots","dots.down"],["diamond","diamond"],["div","div"],["divideontimes","times.div"],["dotplus","plus.dot"],["downarrow","arrow.b"],["ell","ell"],["emptyset","nothing"],["epsilon","epsilon.alt"],["equiv","equiv"],["eta","eta"],["exists","exists"],["forall","forall"],["gamma","gamma"],["ge","gt.eq"],["geq","gt.eq"],["geqslant","gt.eq.slant"],["gg","gt.double"],["hbar","planck.reduce"],["imath","dotless.i"],["iiiint","intgral.quad"],["iiint","integral.triple"],["iint","integral.double"],["in","in"],["infty","infinity"],["int","integral"],["intercal","top"],["iota","iota"],["jmath","dotless.j"],["kappa","kappa"],["lambda","lambda"],["land","and"],["langle","angle.l"],["lbrace","brace.l"],["lbrack","bracket.l"],["ldots","dots.l"],["le","lt.eq"],["leadsto","arrow.squiggly"],["leftarrow","arrow.l"],["leftthreetimes","times.three.l"],["leftrightarrow","arrow.l.r"],["leq","lt.eq"],["leqslant","lt.eq.slant"],["lhd","triangle.l"],["ll","lt.double"],["longmapsto","arrow.long.bar"],["longrightarrow","arrow.long"],["lor","or"],["ltimes","times.l"],["mapsto","arrow.bar"],["measuredangle","angle.arc"],["mid","divides"],["models","models"],["mp","minus.plus"],["mu","mu"],["nRightarrow","arrow.double.not"],["nabla","nabla"],["ncong","tilde.nequiv"],["ne","eq.not"],["neg","not"],["neq","eq.not"],["nexists","exists.not"],["ni","in.rev"],["nleftarrow","arrow.l.not"],["nleq","lt.eq.not"],["nparallel","parallel.not"],["ngeq","gt.eq.not"],["nmid","divides.not"],["notin","in.not"],["nrightarrow","arrow.not"],["nsim","tilde.not"],["nsubseteq","subset.eq.not"],["nu","nu"],["ntriangleleft","lt.tri.not"],["ntriangleright","gt.tri.not"],["nwarrow","arrow.tl"],["odot","dot.circle"],["oint","integral.cont"],["oiint","integral.surf"],["oiiint","integral.vol"],["omega","omega"],["ominus","minus.circle"],["oplus","xor"],["otimes","times.circle"],["parallel","parallel"],["partial","diff"],["perp","perp"],["phi","phi.alt"],["pi","pi"],["pm","plus.minus"],["pounds","pound"],["prec","prec"],["preceq","prec.eq"],["prime","prime"],["prod","product"],["propto","prop"],["psi","psi"],["rangle","angle.r"],["rbrace","brace.r"],["rbrack","bracket.r"],["rhd","triangle"],["rho","rho"],["rightarrow","arrow.r"],["rightthreetimes","times.three.r"],["rtimes","times.r"],["setminus","without"],["sigma","sigma"],["sim","tilde"],["simeq","tilde.eq"],["slash","slash"],["smallsetminus","without"],["spadesuit","suit.spade"],["sqcap","sect.sq"],["sqcup","union.sq"],["sqsubseteq","subset.eq.sq"],["sqsupseteq","supset.eq.sq"],["star","star"],["subset","subset"],["subseteq","subset.eq"],["subsetneq","subset.neq"],["succ","succ"],["succeq","succ.eq"],["sum","sum"],["supset","supset"],["supseteq","supset.eq"],["supsetneq","supset.neq"],["swarrow","arrow.bl"],["tau","tau"],["theta","theta"],["times","times"],["to","arrow.r"],["top","top"],["triangle","triangle.t"],["triangledown","triangle.b.small"],["triangleleft","triangle.l.small"],["triangleright","triangle.r.small"],["twoheadrightarrow","arrow.r.twohead"],["uparrow","arrow.t"],["updownarrow","arrow.t.b"],["upharpoonright","harpoon.tr"],["uplus","union.plus"],["upsilon","upsilon"],["varepsilon","epsilon"],["varnothing","diameter"],["varphi","phi"],["varpi","pi.alt"],["varrho","rho.alt"],["varsigma","sigma.alt"],["vartheta","theta.alt"],["vdash","tack.r"],["vdots","dots.v"],["vee","or"],["wedge","and"],["wr","wreath"],["xi","xi"],["yen","yen"],["zeta","zeta"],["mathscr","scr"],["LaTeX","#LaTeX"],["TeX","#TeX"]]);function L(J){if(/^[a-zA-Z0-9]$/.test(J))return J;else if(J==="\\\\")return"\\";else if(J=="/")return"\\/";else if(["\\$","\\#","\\&","\\_"].includes(J))return J;else if(J.startsWith("\\")){const Z=J.slice(1);if(B.has(Z))return B.get(Z);else return Z}return J}var n=["dim","id","im","mod","Pr","sech","csch"];class O extends Error{node;constructor(J,Z){super(J);this.name="TypstWriterError",this.node=Z}}class h{nonStrict;preferTypstIntrinsic;buffer="";queue=[];needSpaceAfterSingleItemScript=!1;insideFunctionDepth=0;constructor(J,Z){this.nonStrict=J,this.preferTypstIntrinsic=Z}writeBuffer(J){if(this.needSpaceAfterSingleItemScript&&/^[0-9a-zA-Z\(]/.test(J))this.buffer+=" ";else{let Z=!1;if(Z||=/[\(\|]$/.test(this.buffer)&&/^\w/.test(J),Z||=/^[}()_^,;!\|]$/.test(J),Z||=J==="'",Z||=/[0-9]$/.test(this.buffer)&&/^[0-9]/.test(J),Z||=/[\(\[{]\s*(-|\+)$/.test(this.buffer)||this.buffer==="-"||this.buffer==="+",Z||=J.startsWith("\n"),Z||=this.buffer==="",Z||=/[\s"_^{\(]$/.test(this.buffer),!Z)this.buffer+=" "}if(this.needSpaceAfterSingleItemScript)this.needSpaceAfterSingleItemScript=!1;this.buffer+=J}append(J){if(J.type==="empty"||J.type==="whitespace")return;else if(J.type==="ordgroup")J.args.forEach((Z)=>this.append(Z));else if(J.type==="element"){let Z=J.content;if(J.content===","&&this.insideFunctionDepth>0)Z="comma";this.queue.push({type:"symbol",content:Z})}else if(J.type==="symbol")this.queue.push({type:"symbol",content:J.content});else if(J.type==="text")this.queue.push(J);else if(J.type==="supsub"){let{base:Z,sup:V,sub:$}=J.data;if(Z&&Z.type==="unaryFunc"&&Z.content==="\\overbrace"&&V){this.append({type:"binaryFunc",content:"\\overbrace",args:[Z.args[0],V]});return}else if(Z&&Z.type==="unaryFunc"&&Z.content==="\\underbrace"&&$){this.append({type:"binaryFunc",content:"\\underbrace",args:[Z.args[0],$]});return}if(Z.type==="empty")this.queue.push({type:"text",content:""});else this.appendWithBracketsIfNeeded(Z);let X=!1;const z=V&&V.type==="symbol"&&V.content==="\\prime";if(z)this.queue.push({type:"atom",content:"\'"}),X=!1;if($)this.queue.push({type:"atom",content:"_"}),X=this.appendWithBracketsIfNeeded($);if(V&&!z)this.queue.push({type:"atom",content:"^"}),X=this.appendWithBracketsIfNeeded(V);if(X)this.queue.push({type:"softSpace",content:""})}else if(J.type==="leftright"){const[Z,V,$]=J.args;if(["[]","()","\\{\\}","\\lfloor\\rfloor","\\lceil\\rceil"].includes(Z.content+$.content)){this.append(Z),this.append(V),this.append($);return}const X={type:"symbol",content:"lr"};this.queue.push(X),this.insideFunctionDepth++,this.queue.push({type:"atom",content:"("}),this.append(Z),this.append(V),this.append($),this.queue.push({type:"atom",content:")"}),this.insideFunctionDepth--}else if(J.type==="binaryFunc"){const Z={type:"symbol",content:J.content},[V,$]=J.args;this.queue.push(Z),this.insideFunctionDepth++,this.queue.push({type:"atom",content:"("}),this.append(V),this.queue.push({type:"atom",content:","}),this.append($),this.queue.push({type:"atom",content:")"}),this.insideFunctionDepth--}else if(J.type==="unaryFunc"){const Z={type:"symbol",content:J.content},V=J.args[0];if(J.content==="\\sqrt"&&J.data){Z.content="root",this.queue.push(Z),this.insideFunctionDepth++,this.queue.push({type:"atom",content:"("}),this.append(J.data),this.queue.push({type:"atom",content:","}),this.append(V),this.queue.push({type:"atom",content:")"}),this.insideFunctionDepth--;return}else if(J.content==="\\mathbf"){this.append({type:"symbol",content:"upright"}),this.insideFunctionDepth++,this.queue.push({type:"atom",content:"("}),this.queue.push(Z),this.insideFunctionDepth++,this.queue.push({type:"atom",content:"("}),this.append(V),this.queue.push({type:"atom",content:")"}),this.insideFunctionDepth--,this.queue.push({type:"atom",content:")"}),this.insideFunctionDepth--;return}else if(J.content==="\\mathbb"){const $=J.args[0];if($.type==="element"&&/^[A-Z]$/.test($.content)){this.queue.push({type:"symbol",content:$.content+$.content});return}}else if(J.content==="\\operatorname"){let $=J.args;if($.length===1&&$[0].type=="ordgroup")$=$[0].args;const X=$.reduce((z,j)=>{return z+=L(j.content),z},"");if(this.preferTypstIntrinsic&&n.includes(X))this.queue.push({type:"symbol",content:X});else this.queue.push({type:"symbol",content:"op"}),this.queue.push({type:"atom",content:"("}),this.queue.push({type:"text",content:X}),this.queue.push({type:"atom",content:")"});return}this.queue.push(Z),this.insideFunctionDepth++,this.queue.push({type:"atom",content:"("}),this.append(V),this.queue.push({type:"atom",content:")"}),this.insideFunctionDepth--}else if(J.type==="newline"){this.queue.push({type:"newline",content:"\n"});return}else if(J.type==="beginend")if(J.content.startsWith("align")){const Z=J.data;Z.forEach((V,$)=>{if(V.forEach((X,z)=>{if(z>0)this.queue.push({type:"atom",content:"&"});this.append(X)}),$<Z.length-1)this.queue.push({type:"symbol",content:"\\\\"})})}else{const Z=J.data;this.queue.push({type:"symbol",content:"mat"}),this.insideFunctionDepth++,this.queue.push({type:"atom",content:"("}),this.queue.push({type:"symbol",content:"delim: #none, "}),Z.forEach((V,$)=>{V.forEach((X,z)=>{if(X.type==="ordgroup"&&X.args.length===0){this.queue.push({type:"atom",content:","});return}if(this.append(X),z<V.length-1)this.queue.push({type:"atom",content:","});else if($<Z.length-1)this.queue.push({type:"atom",content:";"})})}),this.queue.push({type:"atom",content:")"}),this.insideFunctionDepth--}else if(J.type==="matrix");else if(J.type==="unknownMacro")if(this.nonStrict)this.queue.push({type:"symbol",content:J.content});else throw new O(`Unknown macro: ${J.content}`,J);else if(J.type==="control")if(J.content==="\\\\")this.queue.push({type:"symbol",content:J.content});else throw new O(`Unknown control sequence: ${J.content}`,J);else if(J.type==="comment")this.queue.push({type:"comment",content:J.content});else throw new O(`Unimplemented node type to append: ${J.type}`,J)}flushQueue(){this.queue.forEach((J)=>{let Z="";switch(J.type){case"atom":Z=J.content;break;case"symbol":Z=L(J.content);break;case"text":Z=`"${J.content}"`;break;case"softSpace":this.needSpaceAfterSingleItemScript=!0,Z="";break;case"comment":Z=`//${J.content}`;break;case"newline":Z="\n";break;default:throw new O(`Unexpected node type to stringify: ${J.type}`,J)}if(Z!=="")this.writeBuffer(Z)}),this.queue=[]}appendWithBracketsIfNeeded(J){const Z=["symbol","element","unaryFunc","binaryFunc","leftright"].includes(J.type);if(Z)this.append(J);else this.queue.push({type:"atom",content:"("}),this.append(J),this.queue.push({type:"atom",content:")"});return Z}finalize(){this.flushQueue();const J=function(V){let $=V.replace(/⌊\s*(.*?)\s*⌋/g,"floor($1)");return $=$.replace(/floor\(\)/g,'floor("")'),$},Z=function(V){let $=V.replace(/⌈\s*(.*?)\s*⌉/g,"ceil($1)");return $=$.replace(/ceil\(\)/g,'ceil("")'),$};return this.buffer=J(this.buffer),this.buffer=Z(this.buffer),this.buffer}}function P(J,Z){const V={nonStrict:!1,preferTypstIntrinsic:!0,customTexMacros:{}};if(Z){if(Z.nonStrict)V.nonStrict=Z.nonStrict;if(Z.preferTypstIntrinsic)V.preferTypstIntrinsic=Z.preferTypstIntrinsic;if(Z.customTexMacros)V.customTexMacros=Z.customTexMacros}const $=b(J,V.customTexMacros),X=new h(V.nonStrict,V.preferTypstIntrinsic);return X.append($),X.finalize()}if(typeof window!=="undefined")window.tex2typst=P;
1
+ function v(z,Z=""){if(!z)throw new H(Z)}function c(z){if(u.includes(z))return 1;else if(m.includes(z))return 2;else return 0}function x(z,Z){v(q(z[Z],F));let J=1,V=Z+1;while(J>0){if(V>=z.length)throw new H("Unmatched curly brackets");if(q(z[V],F))J+=1;else if(q(z[V],D))J-=1;V+=1}return V-1}function l(z,Z){v(q(z[Z],y));let J=1,V=Z+1;while(J>0){if(V>=z.length)throw new H("Unmatched square brackets");if(q(z[V],y))J+=1;else if(q(z[V],_))J-=1;V+=1}return V-1}function g(z){return"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".includes(z)}function R(z){return"0123456789".includes(z)}function S(z,Z){let J=Z;while(J<z.length&&["whitespace","newline"].includes(z[J].type))J++;return z.slice(Z,J)}function w(z,Z){const J=z[Z];if(J.type==="element"&&["(",")","[","]","|","\\{","\\}"].includes(J.value))return J;else if(J.type==="command"&&["lfloor","rfloor","lceil","rceil","langle","rangle"].includes(J.value.slice(1)))return J;else return null}function G(z,Z){let J=Z;while(J<z.length&&q(z[J],{type:"element",value:"'"}))J+=1;return J-Z}function i(z,Z){let J=Z;while(J<z.length&&g(z[J]))J+=1;return z.substring(Z,J)}function p(z,Z){let J=1,V=Z;while(J>0){if(V>=z.length)return-1;if(q(z[V],N))J+=1;else if(q(z[V],d))J-=1;V+=1}return V-1}function a(z,Z){let J=1,V=Z;while(J>0){if(V>=z.length)return-1;if(q(z[V],A))J+=1;else if(q(z[V],k))J-=1;V+=1}return V-1}function n(z,Z){v(z[Z]==="{");let J=1,V=Z+1;while(J>0){if(V>=z.length)throw new H("Unmatched curly brackets");if(V+1<z.length&&["\\{","\\}"].includes(z.substring(V,V+2))){V+=2;continue}if(z[V]==="{")J+=1;else if(z[V]==="}")J-=1;V+=1}return V-1}function C(z){const Z=[];let J=0;while(J<z.length){const V=z[J];let X;switch(V){case"%":{let $=J+1;while($<z.length&&z[$]!=="\n")$+=1;X={type:"comment",value:z.slice(J+1,$)},J=$;break}case"{":case"}":case"_":case"^":case"&":X={type:"control",value:V},J++;break;case"\n":X={type:"newline",value:V},J++;break;case"\r":{if(J+1<z.length&&z[J+1]==="\n")X={type:"newline",value:"\n"},J+=2;else X={type:"newline",value:"\n"},J++;break}case" ":{let $=J;while($<z.length&&z[$]===" ")$+=1;X={type:"whitespace",value:z.slice(J,$)},J=$;break}case"\\":{if(J+1>=z.length)throw new H("Expecting command name after \\");const $=z.slice(J,J+2);if(["\\\\","\\,"].includes($))X={type:"control",value:$};else if(["\\{","\\}","\\%","\\$","\\&","\\#","\\_"].includes($))X={type:"element",value:$};else X={type:"command",value:"\\"+i(z,J+1)};J+=X.value.length;break}default:{if(R(V)){let $=J;while($<z.length&&R(z[$]))$+=1;X={type:"element",value:z.slice(J,$)}}else if(g(V))X={type:"element",value:V};else if("+-*/=\'<>!.,;?()[]|".includes(V))X={type:"element",value:V};else X={type:"unknown",value:V};J+=X.value.length}}if(Z.push(X),X.type==="command"&&["\\text","\\begin","\\end"].includes(X.value)){if(J>=z.length||z[J]!=="{")throw new H(`No content for ${X.value} command`);Z.push({type:"control",value:"{"});const $=n(z,J);J++;let j=z.slice(J,$);const Q=["{","}","\\","$","&","#","_","%"];for(let W of Q)j=j.replaceAll("\\"+W,W);Z.push({type:"text",value:j}),Z.push({type:"control",value:"}"}),J=$+1}}return Z}function q(z,Z){return z.type==Z.type&&z.value==Z.value}function r(z){const Z=(V)=>q(V,I)||q(V,B);let J=[];for(let V=0;V<z.length;V++){if(z[V].type==="whitespace"&&V+1<z.length&&Z(z[V+1]))continue;if(z[V].type==="whitespace"&&V-1>=0&&Z(z[V-1]))continue;J.push(z[V])}return J}function t(z,Z){let J=[];for(let V of z)if(V.type==="command"&&Z[V.value]){const X=C(Z[V.value]);J=J.concat(X)}else J.push(V);return J}function T(z,Z){const J=new b;let V=C(z);return V=r(V),V=t(V,Z),J.parse(V)}var u=["sqrt","text","bar","bold","boldsymbol","ddot","dot","hat","mathbb","mathbf","mathcal","mathfrak","mathit","mathrm","mathscr","mathsf","mathtt","operatorname","overbrace","overline","pmb","rm","tilde","underbrace","underline","vec","widehat","widetilde"],m=["frac","tfrac","binom","dbinom","dfrac","tbinom"],U={type:"empty",content:""},F={type:"control",value:"{"},D={type:"control",value:"}"},y={type:"element",value:"["},_={type:"element",value:"]"},N={type:"command",value:"\\left"},d={type:"command",value:"\\right"},A={type:"command",value:"\\begin"},k={type:"command",value:"\\end"};class H extends Error{constructor(z){super(z);this.name="LatexParserError"}}var I={type:"control",value:"_"},B={type:"control",value:"^"};class b{space_sensitive;newline_sensitive;constructor(z=!1,Z=!0){this.space_sensitive=z,this.newline_sensitive=Z}parse(z){const Z=[];let J=0;while(J<z.length){const V=[];let X=0;while(X<z.length){const[$,j]=this.parseNextExpr(z,X);if(X=j,!this.space_sensitive&&$.type==="whitespace")continue;if(!this.newline_sensitive&&$.type==="newline")continue;if($.type==="control"&&$.content==="&")throw new H("Unexpected & outside of an alignment");V.push($)}if(V.length===0)return U;else if(V.length===1)return V[0];else return{type:"ordgroup",content:"",args:V}}if(Z.length===0)return U;else if(Z.length===1)return Z[0];else return{type:"ordgroup",content:"",args:Z}}parseNextExpr(z,Z){let[J,V]=this.parseNextExprWithoutSupSub(z,Z),X=null,$=null,j=0;if(j+=G(z,V),V+=j,V<z.length&&q(z[V],I)){if([X,V]=this.parseNextExprWithoutSupSub(z,V+1),j+=G(z,V),V+=j,V<z.length&&q(z[V],B)){if([$,V]=this.parseNextExprWithoutSupSub(z,V+1),G(z,V)>0)throw new H("Double superscript")}}else if(V<z.length&&q(z[V],B)){if([$,V]=this.parseNextExprWithoutSupSub(z,V+1),G(z,V)>0)throw new H("Double superscript");if(V<z.length&&q(z[V],I)){if([X,V]=this.parseNextExprWithoutSupSub(z,V+1),G(z,V)>0)throw new H("Double superscript")}}if(X!==null||$!==null||j>0){const Q={base:J};if(X)Q.sub=X;if(j>0){Q.sup={type:"ordgroup",content:"",args:[]};for(let W=0;W<j;W++)Q.sup.args.push({type:"symbol",content:"\\prime"});if($)Q.sup.args.push($);if(Q.sup.args.length===1)Q.sup=Q.sup.args[0]}else if($)Q.sup=$;return[{type:"supsub",content:"",data:Q},V]}else return[J,V]}parseNextExprWithoutSupSub(z,Z){const J=z[Z],V=J.type;switch(V){case"element":case"text":case"comment":case"whitespace":case"newline":return[{type:V,content:J.value},Z+1];case"command":if(q(J,A))return this.parseBeginEndExpr(z,Z);else if(q(J,N))return this.parseLeftRightExpr(z,Z);else return this.parseCommandExpr(z,Z);case"control":switch(J.value){case"{":const $=x(z,Z),j=z.slice(Z+1,$);return[this.parse(j),$+1];case"}":throw new H("Unmatched '}'");case"\\\\":return[{type:"control",content:"\\\\"},Z+1];case"\\,":return[{type:"control",content:"\\,"},Z+1];case"_":return[U,Z];case"^":return[U,Z];case"&":return[{type:"control",content:"&"},Z+1];default:throw new H("Unknown control sequence")}default:throw new H("Unknown token type")}}parseCommandExpr(z,Z){v(z[Z].type==="command");const J=z[Z].value;let V=Z+1;if(["left","right","begin","end"].includes(J.slice(1)))throw new H("Unexpected command: "+J);const X=c(J.slice(1));if(X===0)return[{type:"symbol",content:J},V];else if(X===1){if(J==="\\sqrt"&&V<z.length&&q(z[V],y)){const Q=V,W=l(z,V),O=z.slice(Q+1,W),h=this.parse(O),[f,E]=this.parseNextExprWithoutSupSub(z,W+1);return[{type:"unaryFunc",content:J,args:[f],data:h},E]}else if(J==="\\text"){if(V+2>=z.length)throw new H("Expecting content for \\text command");return v(q(z[V],F)),v(z[V+1].type==="text"),v(q(z[V+2],D)),[{type:"text",content:z[V+1].value},V+3]}let[$,j]=this.parseNextExprWithoutSupSub(z,V);return[{type:"unaryFunc",content:J,args:[$]},j]}else if(X===2){const[$,j]=this.parseNextExprWithoutSupSub(z,V),[Q,W]=this.parseNextExprWithoutSupSub(z,j);return[{type:"binaryFunc",content:J,args:[$,Q]},W]}else throw new Error("Invalid number of parameters")}parseLeftRightExpr(z,Z){v(q(z[Z],N));let J=Z+1;if(J+=S(z,J).length,J>=z.length)throw new H("Expecting delimiter after \\left");const V=w(z,J);if(V===null)throw new H("Invalid delimiter after \\left");J++;const X=J,$=p(z,J);if($===-1)throw new H("No matching \\right");const j=$;if(J=$+1,J+=S(z,J).length,J>=z.length)throw new H("Expecting \\right after \\left");const Q=w(z,J);if(Q===null)throw new H("Invalid delimiter after \\right");J++;const W=z.slice(X,j),O=this.parse(W);return[{type:"leftright",content:"",args:[{type:"element",content:V.value},O,{type:"element",content:Q.value}]},J]}parseBeginEndExpr(z,Z){v(q(z[Z],A));let J=Z+1;v(q(z[J],F)),v(z[J+1].type==="text"),v(q(z[J+2],D));const V=z[J+1].value;J+=3,J+=S(z,J).length;const X=J,$=a(z,J);if($===-1)throw new H("No matching \\end");const j=$;if(J=$+1,v(q(z[J],F)),v(z[J+1].type==="text"),v(q(z[J+2],D)),z[J+1].value!==V)throw new H("Mismatched \\begin and \\end environments");J+=3;const Q=z.slice(X,j);while(Q.length>0&&["whitespace","newline"].includes(Q[Q.length-1].type))Q.pop();const W=this.parseAligned(Q);return[{type:"beginend",content:V,data:W},J]}parseAligned(z){let Z=0;const J=[];let V=[];J.push(V);let X={type:"ordgroup",content:"",args:[]};V.push(X);while(Z<z.length){const[$,j]=this.parseNextExpr(z,Z);if(Z=j,$.type==="whitespace")continue;else if($.type==="newline"&&!this.newline_sensitive)continue;else if($.type==="control"&&$.content==="\\\\")V=[],X={type:"ordgroup",content:"",args:[]},V.push(X),J.push(V);else if($.type==="control"&&$.content==="&")X={type:"ordgroup",content:"",args:[]},V.push(X);else X.args.push($)}return J}}var Y=new Map([["nonumber",""],["vec","arrow"],["neq","eq.not"],["dot","dot"],["ddot","dot.double"],["doteq","dot(eq)"],["dots","dots.h"],["ldots","dots.h"],["vdots","dots.v"],["ddots","dots.down"],["widehat","hat"],["widetilde","tilde"],["quad","quad"],["qquad","wide"],["overbrace","overbrace"],["underbrace","underbrace"],["overline","overline"],["underline","underline"],["bar","macron"],["dbinom","binom"],["tbinom","binom"],["dfrac","frac"],["tfrac","frac"],["boldsymbol","bold"],["mathbb","bb"],["mathbf","bold"],["mathcal","cal"],["mathit","italic"],["mathfrak","frak"],["mathrm","upright"],["mathsf","sans"],["mathtt","mono"],["rm","upright"],["pmb","bold"],["pm","plus.minus"],["mp","minus.plus"],["oplus","xor"],["boxplus","plus.square"],["otimes","times.circle"],["boxtimes","times.square"],["sim","tilde"],["approx","approx"],["cong","tilde.equiv"],["simeq","tilde.eq"],["asymp","\u224D"],["equiv","equiv"],["propto","prop"],["lfloor","\u230A"],["rfloor","\u230B"],["lceil","\u2308"],["rceil","\u2309"],["gets","arrow.l"],["hookleftarrow","arrow.l.hook"],["leftharpoonup","harpoon.lt"],["leftharpoondown","harpoon.lb"],["rightleftharpoons","harpoons.rtlb"],["longleftarrow","arrow.l.long"],["longrightarrow","arrow.r.long"],["longleftrightarrow","arrow.l.r.long"],["Longleftarrow","arrow.l.double.long"],["Longrightarrow","arrow.r.double.long"],["Longleftrightarrow","arrow.l.r.double.long"],["longmapsto","arrow.r.bar"],["hookrightarrow","arrow.r.hook"],["rightharpoonup","harpoon.rt"],["rightharpoondown","harpoon.rb"],["iff","arrow.l.r.double.long"],["implies","arrow.r.double.long"],["uparrow","arrow.t"],["downarrow","arrow.b"],["updownarrow","arrow.t.b"],["Uparrow","arrow.t.double"],["Downarrow","arrow.b.double"],["Updownarrow","arrow.t.b.double"],["nearrow","arrow.tr"],["searrow","arrow.br"],["swarrow","arrow.bl"],["nwarrow","arrow.tl"],["leadsto","arrow.squiggly"],["leftleftarrows","arrows.ll"],["rightrightarrows","arrows.rr"],["Cap","sect.double"],["Cup","union.double"],["Delta","Delta"],["Gamma","Gamma"],["Join","join"],["Lambda","Lambda"],["Leftarrow","arrow.l.double"],["Leftrightarrow","arrow.l.r.double"],["Longrightarrow","arrow.r.double.long"],["Omega","Omega"],["P","pilcrow"],["Phi","Phi"],["Pi","Pi"],["Psi","Psi"],["Rightarrow","arrow.r.double"],["S","section"],["Sigma","Sigma"],["Theta","Theta"],["aleph","alef"],["alpha","alpha"],["angle","angle"],["approx","approx"],["approxeq","approx.eq"],["ast","ast"],["beta","beta"],["bigcap","sect.big"],["bigcirc","circle.big"],["bigcup","union.big"],["bigodot","dot.circle.big"],["bigoplus","xor.big"],["bigotimes","times.circle.big"],["bigsqcup","union.sq.big"],["bigtriangledown","triangle.b"],["bigtriangleup","triangle.t"],["biguplus","union.plus.big"],["bigvee","or.big"],["bigwedge","and.big"],["bullet","bullet"],["cap","sect"],["cdot","dot.op"],["cdots","dots.c"],["checkmark","checkmark"],["chi","chi"],["circ","circle.small"],["colon","colon"],["cong","tilde.equiv"],["coprod","product.co"],["copyright","copyright"],["cup","union"],["curlyvee","or.curly"],["curlywedge","and.curly"],["dagger","dagger"],["dashv","tack.l"],["ddagger","dagger.double"],["delta","delta"],["ddots","dots.down"],["diamond","diamond"],["div","div"],["divideontimes","times.div"],["dotplus","plus.dot"],["downarrow","arrow.b"],["ell","ell"],["emptyset","nothing"],["epsilon","epsilon.alt"],["equiv","equiv"],["eta","eta"],["exists","exists"],["forall","forall"],["gamma","gamma"],["ge","gt.eq"],["geq","gt.eq"],["geqslant","gt.eq.slant"],["gg","gt.double"],["hbar","planck.reduce"],["imath","dotless.i"],["iiiint","intgral.quad"],["iiint","integral.triple"],["iint","integral.double"],["in","in"],["infty","infinity"],["int","integral"],["intercal","top"],["iota","iota"],["jmath","dotless.j"],["kappa","kappa"],["lambda","lambda"],["land","and"],["langle","angle.l"],["lbrace","brace.l"],["lbrack","bracket.l"],["ldots","dots.l"],["le","lt.eq"],["leadsto","arrow.squiggly"],["leftarrow","arrow.l"],["leftthreetimes","times.three.l"],["leftrightarrow","arrow.l.r"],["leq","lt.eq"],["leqslant","lt.eq.slant"],["lhd","triangle.l"],["ll","lt.double"],["longmapsto","arrow.bar.long"],["longrightarrow","arrow.long"],["lor","or"],["ltimes","times.l"],["mapsto","arrow.bar"],["measuredangle","angle.arc"],["mid","divides"],["models","models"],["mp","minus.plus"],["mu","mu"],["nRightarrow","arrow.double.not"],["nabla","nabla"],["ncong","tilde.nequiv"],["ne","eq.not"],["neg","not"],["neq","eq.not"],["nexists","exists.not"],["ni","in.rev"],["nleftarrow","arrow.l.not"],["nleq","lt.eq.not"],["nparallel","parallel.not"],["ngeq","gt.eq.not"],["nmid","divides.not"],["notin","in.not"],["nrightarrow","arrow.not"],["nsim","tilde.not"],["nsubseteq","subset.eq.not"],["nu","nu"],["ntriangleleft","lt.tri.not"],["ntriangleright","gt.tri.not"],["nwarrow","arrow.tl"],["odot","dot.circle"],["oint","integral.cont"],["oiint","integral.surf"],["oiiint","integral.vol"],["omega","omega"],["ominus","minus.circle"],["oplus","xor"],["otimes","times.circle"],["parallel","parallel"],["partial","diff"],["perp","perp"],["phi","phi.alt"],["pi","pi"],["pm","plus.minus"],["pounds","pound"],["prec","prec"],["preceq","prec.eq"],["prime","prime"],["prod","product"],["propto","prop"],["psi","psi"],["rangle","angle.r"],["rbrace","brace.r"],["rbrack","bracket.r"],["rhd","triangle"],["rho","rho"],["rightarrow","arrow.r"],["rightthreetimes","times.three.r"],["rtimes","times.r"],["setminus","without"],["sigma","sigma"],["sim","tilde"],["simeq","tilde.eq"],["slash","slash"],["smallsetminus","without"],["spadesuit","suit.spade"],["sqcap","sect.sq"],["sqcup","union.sq"],["sqsubseteq","subset.eq.sq"],["sqsupseteq","supset.eq.sq"],["star","star"],["subset","subset"],["subseteq","subset.eq"],["subsetneq","subset.neq"],["succ","succ"],["succeq","succ.eq"],["sum","sum"],["supset","supset"],["supseteq","supset.eq"],["supsetneq","supset.neq"],["swarrow","arrow.bl"],["tau","tau"],["theta","theta"],["times","times"],["to","arrow.r"],["top","top"],["triangle","triangle.t"],["triangledown","triangle.b.small"],["triangleleft","triangle.l.small"],["triangleright","triangle.r.small"],["twoheadrightarrow","arrow.r.twohead"],["uparrow","arrow.t"],["updownarrow","arrow.t.b"],["upharpoonright","harpoon.tr"],["uplus","union.plus"],["upsilon","upsilon"],["varepsilon","epsilon"],["varnothing","diameter"],["varphi","phi"],["varpi","pi.alt"],["varrho","rho.alt"],["varsigma","sigma.alt"],["vartheta","theta.alt"],["vdash","tack.r"],["vdots","dots.v"],["vee","or"],["wedge","and"],["wr","wreath"],["xi","xi"],["yen","yen"],["zeta","zeta"],["mathscr","scr"],["LaTeX","#LaTeX"],["TeX","#TeX"]]);function L(z){if(/^[a-zA-Z0-9]$/.test(z))return z;else if(z==="\\\\")return"\\";else if(z=="/")return"\\/";else if(["\\$","\\#","\\&","\\_"].includes(z))return z;else if(z.startsWith("\\")){const Z=z.slice(1);if(Y.has(Z))return Y.get(Z);else return Z}return z}var o=["dim","id","im","mod","Pr","sech","csch"];class K extends Error{node;constructor(z,Z){super(z);this.name="TypstWriterError",this.node=Z}}class M{nonStrict;preferTypstIntrinsic;buffer="";queue=[];needSpaceAfterSingleItemScript=!1;insideFunctionDepth=0;constructor(z,Z){this.nonStrict=z,this.preferTypstIntrinsic=Z}writeBuffer(z){if(this.needSpaceAfterSingleItemScript&&/^[0-9a-zA-Z\(]/.test(z))this.buffer+=" ";else{let Z=!1;if(Z||=/[\(\|]$/.test(this.buffer)&&/^\w/.test(z),Z||=/^[}()_^,;!\|]$/.test(z),Z||=z==="'",Z||=/[0-9]$/.test(this.buffer)&&/^[0-9]/.test(z),Z||=/[\(\[{]\s*(-|\+)$/.test(this.buffer)||this.buffer==="-"||this.buffer==="+",Z||=z.startsWith("\n"),Z||=this.buffer==="",Z||=/[\s"_^{\(]$/.test(this.buffer),!Z)this.buffer+=" "}if(this.needSpaceAfterSingleItemScript)this.needSpaceAfterSingleItemScript=!1;this.buffer+=z}append(z){if(z.type==="empty"||z.type==="whitespace")return;else if(z.type==="ordgroup")z.args.forEach((Z)=>this.append(Z));else if(z.type==="element"){let Z=z.content;if(z.content===","&&this.insideFunctionDepth>0)Z="comma";this.queue.push({type:"symbol",content:Z})}else if(z.type==="symbol")this.queue.push({type:"symbol",content:z.content});else if(z.type==="text")this.queue.push(z);else if(z.type==="supsub"){let{base:Z,sup:J,sub:V}=z.data;if(Z&&Z.type==="unaryFunc"&&Z.content==="\\overbrace"&&J){this.append({type:"binaryFunc",content:"\\overbrace",args:[Z.args[0],J]});return}else if(Z&&Z.type==="unaryFunc"&&Z.content==="\\underbrace"&&V){this.append({type:"binaryFunc",content:"\\underbrace",args:[Z.args[0],V]});return}if(Z.type==="empty")this.queue.push({type:"text",content:""});else this.appendWithBracketsIfNeeded(Z);let X=!1;const $=J&&J.type==="symbol"&&J.content==="\\prime";if($)this.queue.push({type:"atom",content:"\'"}),X=!1;if(V)this.queue.push({type:"atom",content:"_"}),X=this.appendWithBracketsIfNeeded(V);if(J&&!$)this.queue.push({type:"atom",content:"^"}),X=this.appendWithBracketsIfNeeded(J);if(X)this.queue.push({type:"softSpace",content:""})}else if(z.type==="leftright"){const[Z,J,V]=z.args;if(["[]","()","\\{\\}","\\lfloor\\rfloor","\\lceil\\rceil"].includes(Z.content+V.content)){this.append(Z),this.append(J),this.append(V);return}const X={type:"symbol",content:"lr"};this.queue.push(X),this.insideFunctionDepth++,this.queue.push({type:"atom",content:"("}),this.append(Z),this.append(J),this.append(V),this.queue.push({type:"atom",content:")"}),this.insideFunctionDepth--}else if(z.type==="binaryFunc"){const Z={type:"symbol",content:z.content},[J,V]=z.args;this.queue.push(Z),this.insideFunctionDepth++,this.queue.push({type:"atom",content:"("}),this.append(J),this.queue.push({type:"atom",content:","}),this.append(V),this.queue.push({type:"atom",content:")"}),this.insideFunctionDepth--}else if(z.type==="unaryFunc"){const Z={type:"symbol",content:z.content},J=z.args[0];if(z.content==="\\sqrt"&&z.data){Z.content="root",this.queue.push(Z),this.insideFunctionDepth++,this.queue.push({type:"atom",content:"("}),this.append(z.data),this.queue.push({type:"atom",content:","}),this.append(J),this.queue.push({type:"atom",content:")"}),this.insideFunctionDepth--;return}else if(z.content==="\\mathbf"){this.append({type:"symbol",content:"upright"}),this.insideFunctionDepth++,this.queue.push({type:"atom",content:"("}),this.queue.push(Z),this.insideFunctionDepth++,this.queue.push({type:"atom",content:"("}),this.append(J),this.queue.push({type:"atom",content:")"}),this.insideFunctionDepth--,this.queue.push({type:"atom",content:")"}),this.insideFunctionDepth--;return}else if(z.content==="\\mathbb"){const V=z.args[0];if(V.type==="element"&&/^[A-Z]$/.test(V.content)){this.queue.push({type:"symbol",content:V.content+V.content});return}}else if(z.content==="\\operatorname"){let V=z.args;if(V.length===1&&V[0].type=="ordgroup")V=V[0].args;const X=V.reduce(($,j)=>{return $+=L(j.content),$},"");if(this.preferTypstIntrinsic&&o.includes(X))this.queue.push({type:"symbol",content:X});else this.queue.push({type:"symbol",content:"op"}),this.queue.push({type:"atom",content:"("}),this.queue.push({type:"text",content:X}),this.queue.push({type:"atom",content:")"});return}this.queue.push(Z),this.insideFunctionDepth++,this.queue.push({type:"atom",content:"("}),this.append(J),this.queue.push({type:"atom",content:")"}),this.insideFunctionDepth--}else if(z.type==="newline"){this.queue.push({type:"newline",content:"\n"});return}else if(z.type==="beginend")if(z.content.startsWith("align")){const Z=z.data;Z.forEach((J,V)=>{if(J.forEach((X,$)=>{if($>0)this.queue.push({type:"atom",content:"&"});this.append(X)}),V<Z.length-1)this.queue.push({type:"symbol",content:"\\\\"})})}else{const Z=z.data;this.queue.push({type:"symbol",content:"mat"}),this.insideFunctionDepth++,this.queue.push({type:"atom",content:"("}),this.queue.push({type:"symbol",content:"delim: #none, "}),Z.forEach((J,V)=>{J.forEach((X,$)=>{if(X.type==="ordgroup"&&X.args.length===0){this.queue.push({type:"atom",content:","});return}if(this.append(X),$<J.length-1)this.queue.push({type:"atom",content:","});else if(V<Z.length-1)this.queue.push({type:"atom",content:";"})})}),this.queue.push({type:"atom",content:")"}),this.insideFunctionDepth--}else if(z.type==="matrix");else if(z.type==="unknownMacro")if(this.nonStrict)this.queue.push({type:"symbol",content:z.content});else throw new K(`Unknown macro: ${z.content}`,z);else if(z.type==="control")if(z.content==="\\\\")this.queue.push({type:"symbol",content:z.content});else if(z.content==="\\,")this.queue.push({type:"symbol",content:"thin"});else throw new K(`Unknown control sequence: ${z.content}`,z);else if(z.type==="comment")this.queue.push({type:"comment",content:z.content});else throw new K(`Unimplemented node type to append: ${z.type}`,z)}flushQueue(){this.queue.forEach((z)=>{let Z="";switch(z.type){case"atom":Z=z.content;break;case"symbol":Z=L(z.content);break;case"text":Z=`"${z.content}"`;break;case"softSpace":this.needSpaceAfterSingleItemScript=!0,Z="";break;case"comment":Z=`//${z.content}`;break;case"newline":Z="\n";break;default:throw new K(`Unexpected node type to stringify: ${z.type}`,z)}if(Z!=="")this.writeBuffer(Z)}),this.queue=[]}appendWithBracketsIfNeeded(z){const Z=["symbol","element","unaryFunc","binaryFunc","leftright"].includes(z.type);if(Z)this.append(z);else this.queue.push({type:"atom",content:"("}),this.append(z),this.queue.push({type:"atom",content:")"});return Z}finalize(){this.flushQueue();const z=function(J){let V=J.replace(/⌊\s*(.*?)\s*⌋/g,"floor($1)");return V=V.replace(/floor\(\)/g,'floor("")'),V},Z=function(J){let V=J.replace(/⌈\s*(.*?)\s*⌉/g,"ceil($1)");return V=V.replace(/ceil\(\)/g,'ceil("")'),V};return this.buffer=z(this.buffer),this.buffer=Z(this.buffer),this.buffer}}function P(z,Z){const J={nonStrict:!1,preferTypstIntrinsic:!0,customTexMacros:{}};if(Z){if(Z.nonStrict)J.nonStrict=Z.nonStrict;if(Z.preferTypstIntrinsic)J.preferTypstIntrinsic=Z.preferTypstIntrinsic;if(Z.customTexMacros)J.customTexMacros=Z.customTexMacros}const V=T(z,J.customTexMacros),X=new M(J.nonStrict,J.preferTypstIntrinsic);return X.append(V),X.finalize()}if(typeof window!=="undefined")window.tex2typst=P;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tex2typst",
3
- "version": "0.2.3",
3
+ "version": "0.2.7",
4
4
  "description": "JavaScript library for converting TeX code to Typst",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/parser.ts CHANGED
@@ -5,36 +5,25 @@ const UNARY_COMMANDS = [
5
5
  'sqrt',
6
6
  'text',
7
7
 
8
- 'arccos',
9
- 'arcsin',
10
- 'arctan',
11
- 'arg',
12
8
  'bar',
13
9
  'bold',
14
10
  'boldsymbol',
15
11
  'ddot',
16
- 'det',
17
- 'dim',
18
12
  'dot',
19
- 'exp',
20
- 'gcd',
21
13
  'hat',
22
- 'ker',
23
14
  'mathbb',
24
15
  'mathbf',
25
16
  'mathcal',
17
+ 'mathfrak',
18
+ 'mathit',
19
+ 'mathrm',
26
20
  'mathscr',
27
21
  'mathsf',
28
22
  'mathtt',
29
- 'mathrm',
30
- 'max',
31
- 'min',
32
- 'mod',
33
23
  'operatorname',
34
24
  'overbrace',
35
25
  'overline',
36
26
  'pmb',
37
- 'sup',
38
27
  'rm',
39
28
  'tilde',
40
29
  'underbrace',
@@ -237,12 +226,12 @@ function find_closing_curly_bracket_char(latex: string, start: number): number {
237
226
  }
238
227
 
239
228
 
240
- interface Token {
229
+ export interface Token {
241
230
  type: 'element' | 'command' | 'text' | 'comment' | 'whitespace' | 'newline' | 'control' | 'unknown';
242
231
  value: string;
243
232
  }
244
233
 
245
- function tokenize(latex: string): Token[] {
234
+ export function tokenize(latex: string): Token[] {
246
235
  const tokens: Token[] = [];
247
236
  let pos = 0;
248
237
 
@@ -295,17 +284,15 @@ function tokenize(latex: string): Token[] {
295
284
  throw new LatexParserError('Expecting command name after \\');
296
285
  }
297
286
  const firstTwoChars = latex.slice(pos, pos + 2);
298
- if (firstTwoChars === '\\\\') {
299
- token = { type: 'control', value: '\\\\' };
300
- pos += 2;
287
+ if (['\\\\', '\\,'].includes(firstTwoChars)) {
288
+ token = { type: 'control', value: firstTwoChars };
301
289
  } else if (['\\{','\\}', '\\%', '\\$', '\\&', '\\#', '\\_'].includes(firstTwoChars)) {
302
290
  token = { type: 'element', value: firstTwoChars };
303
- pos += 2;
304
291
  } else {
305
292
  const command = eat_command_name(latex, pos + 1);
306
293
  token = { type: 'command', value: '\\' + command};
307
- pos += 1 + command.length;
308
294
  }
295
+ pos += token.value.length;
309
296
  break;
310
297
  }
311
298
  default: {
@@ -502,23 +489,13 @@ export class LatexParser {
502
489
  throw new LatexParserError("Unmatched '}'");
503
490
  case '\\\\':
504
491
  return [{ type: 'control', content: '\\\\' }, start + 1];
492
+ case '\\,':
493
+ return [{ type: 'control', content: '\\,' }, start + 1];
505
494
  case '_': {
506
- let [sub, pos] = this.parseNextExpr(tokens, start + 1);
507
- let sup: TexNode | undefined = undefined;
508
- if (pos < tokens.length && token_eq(tokens[pos], SUP_SYMBOL)) {
509
- [sup, pos] = this.parseNextExpr(tokens, pos + 1);
510
- }
511
- const subData = { base: EMPTY_NODE, sub, sup };
512
- return [{ type: 'supsub', content: '', data: subData }, pos];
495
+ return [ EMPTY_NODE, start];
513
496
  }
514
497
  case '^': {
515
- let [sup, pos] = this.parseNextExpr(tokens, start + 1);
516
- let sub: TexNode | undefined = undefined;
517
- if (pos < tokens.length && token_eq(tokens[pos], SUB_SYMBOL)) {
518
- [sub, pos] = this.parseNextExpr(tokens, pos + 1);
519
- }
520
- const supData = { base: EMPTY_NODE, sub, sup };
521
- return [{ type: 'supsub', content: '', data: supData }, pos];
498
+ return [ EMPTY_NODE, start];
522
499
  }
523
500
  case '&':
524
501
  return [{ type: 'control', content: '&' }, start + 1];
@@ -688,17 +665,40 @@ export class LatexParser {
688
665
  }
689
666
  }
690
667
 
691
- export function parseTex(tex: string, customTexMacros: {[key: string]: string}): TexNode {
692
- const parser = new LatexParser();
693
- const original_tokens = tokenize(tex);
694
- let processed_tokens: Token[] = [];
695
- for (const token of original_tokens) {
668
+ // Remove all whitespace before and after _ or ^
669
+ function passIgnoreWhitespaceBeforeScriptMark(tokens: Token[]): Token[] {
670
+ const is_script_mark = (token: Token) => token_eq(token, SUB_SYMBOL) || token_eq(token, SUP_SYMBOL);
671
+ let out_tokens: Token[] = [];
672
+ for (let i = 0; i < tokens.length; i++) {
673
+ if (tokens[i].type === 'whitespace' && i + 1 < tokens.length && is_script_mark(tokens[i + 1])) {
674
+ continue;
675
+ }
676
+ if (tokens[i].type === 'whitespace' && i - 1 >= 0 && is_script_mark(tokens[i - 1])) {
677
+ continue;
678
+ }
679
+ out_tokens.push(tokens[i]);
680
+ }
681
+ return out_tokens;
682
+ }
683
+
684
+ // expand custom tex macros
685
+ function passExpandCustomTexMacros(tokens: Token[], customTexMacros: {[key: string]: string}): Token[] {
686
+ let out_tokens: Token[] = [];
687
+ for (const token of tokens) {
696
688
  if (token.type === 'command' && customTexMacros[token.value]) {
697
689
  const expanded_tokens = tokenize(customTexMacros[token.value]);
698
- processed_tokens = processed_tokens.concat(expanded_tokens);
690
+ out_tokens = out_tokens.concat(expanded_tokens);
699
691
  } else {
700
- processed_tokens.push(token);
692
+ out_tokens.push(token);
701
693
  }
702
694
  }
703
- return parser.parse(processed_tokens);
695
+ return out_tokens;
696
+ }
697
+
698
+ export function parseTex(tex: string, customTexMacros: {[key: string]: string}): TexNode {
699
+ const parser = new LatexParser();
700
+ let tokens = tokenize(tex);
701
+ tokens = passIgnoreWhitespaceBeforeScriptMark(tokens);
702
+ tokens = passExpandCustomTexMacros(tokens, customTexMacros);
703
+ return parser.parse(tokens);
704
704
  }
package/src/writer.ts CHANGED
@@ -283,6 +283,8 @@ export class TypstWriter {
283
283
  } else if (node.type === 'control') {
284
284
  if (node.content === '\\\\') {
285
285
  this.queue.push({ type: 'symbol', content: node.content });
286
+ } else if (node.content === '\\,') {
287
+ this.queue.push({ type: 'symbol', content: 'thin' });
286
288
  } else {
287
289
  throw new TypstWriterError(`Unknown control sequence: ${node.content}`, node);
288
290
  }