tex2typst 0.3.27 → 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/dist/index.d.ts +0 -1
- package/dist/index.js +173 -105
- package/dist/tex2typst.min.js +13 -13
- package/package.json +1 -1
- package/src/convert.ts +59 -6
- package/src/exposed-types.ts +0 -1
- package/src/generic.ts +16 -0
- package/src/index.ts +0 -1
- package/src/map.ts +1 -1
- package/src/tex-parser.ts +128 -107
- package/src/tex-tokenizer.ts +5 -0
- package/tests/struct-bidirection.yaml +9 -0
- package/tests/struct-tex2typst.yaml +21 -1
- package/tests/symbol.yml +5 -2
- package/tests/tex-parser.test.ts +47 -7
- package/tests/tex-to-typst.test.ts +1 -8
package/dist/index.d.ts
CHANGED
|
@@ -12,7 +12,6 @@ export interface Tex2TypstOptions {
|
|
|
12
12
|
fracToSlash?: boolean; /** default is true */
|
|
13
13
|
inftyToOo?: boolean; /** default is false */
|
|
14
14
|
optimize?: boolean; /** default is true */
|
|
15
|
-
nonAsciiWrapper?: string; /** default is "" */
|
|
16
15
|
customTexMacros?: { [key: string]: string; };
|
|
17
16
|
}
|
|
18
17
|
|
package/dist/index.js
CHANGED
|
@@ -278,6 +278,9 @@ function array_split(array, sep) {
|
|
|
278
278
|
res.push(current_slice);
|
|
279
279
|
return res;
|
|
280
280
|
}
|
|
281
|
+
function array_join(arrays, sep) {
|
|
282
|
+
return arrays.flatMap((arr, i) => i !== arrays.length - 1 ? [...arr, sep] : arr);
|
|
283
|
+
}
|
|
281
284
|
function array_intersperse(array, sep) {
|
|
282
285
|
return array.flatMap((x, i) => i !== array.length - 1 ? [x, sep] : [x]);
|
|
283
286
|
}
|
|
@@ -564,7 +567,12 @@ var TEX_UNARY_COMMANDS = [
|
|
|
564
567
|
"overrightarrow",
|
|
565
568
|
"hspace",
|
|
566
569
|
"substack",
|
|
567
|
-
"set"
|
|
570
|
+
"set",
|
|
571
|
+
"displaylines",
|
|
572
|
+
"mathinner",
|
|
573
|
+
"mathrel",
|
|
574
|
+
"mathbin",
|
|
575
|
+
"mathop"
|
|
568
576
|
];
|
|
569
577
|
var TEX_BINARY_COMMANDS = [
|
|
570
578
|
"frac",
|
|
@@ -708,7 +716,7 @@ function eat_parenthesis(tokens, start) {
|
|
|
708
716
|
const firstToken = tokens[start];
|
|
709
717
|
if (firstToken.type === 1 /* ELEMENT */ && ["(", ")", "[", "]", "|", "\\{", "\\}", ".", "\\|"].includes(firstToken.value)) {
|
|
710
718
|
return firstToken;
|
|
711
|
-
} else if (firstToken.type === 2 /* COMMAND */ && ["lfloor", "rfloor", "lceil", "rceil", "langle", "rangle"].includes(firstToken.value.slice(1))) {
|
|
719
|
+
} else if (firstToken.type === 2 /* COMMAND */ && ["lfloor", "rfloor", "lceil", "rceil", "langle", "rangle", "lparen", "rparen", "lbrace", "rbrace"].includes(firstToken.value.slice(1))) {
|
|
712
720
|
return firstToken;
|
|
713
721
|
} else {
|
|
714
722
|
return null;
|
|
@@ -721,44 +729,32 @@ function eat_primes(tokens, start) {
|
|
|
721
729
|
}
|
|
722
730
|
return pos - start;
|
|
723
731
|
}
|
|
724
|
-
function find_closing_match(tokens, start, leftToken, rightToken) {
|
|
725
|
-
assert(tokens[start].eq(leftToken));
|
|
726
|
-
let count = 1;
|
|
727
|
-
let pos = start + 1;
|
|
728
|
-
while (count > 0) {
|
|
729
|
-
if (pos >= tokens.length) {
|
|
730
|
-
return -1;
|
|
731
|
-
}
|
|
732
|
-
if (tokens[pos].eq(leftToken)) {
|
|
733
|
-
count += 1;
|
|
734
|
-
} else if (tokens[pos].eq(rightToken)) {
|
|
735
|
-
count -= 1;
|
|
736
|
-
}
|
|
737
|
-
pos += 1;
|
|
738
|
-
}
|
|
739
|
-
return pos - 1;
|
|
740
|
-
}
|
|
741
732
|
var LEFT_COMMAND = new TexToken(2 /* COMMAND */, "\\left");
|
|
742
733
|
var RIGHT_COMMAND = new TexToken(2 /* COMMAND */, "\\right");
|
|
743
|
-
function find_closing_right_command(tokens, start) {
|
|
744
|
-
return find_closing_match(tokens, start, LEFT_COMMAND, RIGHT_COMMAND);
|
|
745
|
-
}
|
|
746
734
|
var BEGIN_COMMAND = new TexToken(2 /* COMMAND */, "\\begin");
|
|
747
735
|
var END_COMMAND = new TexToken(2 /* COMMAND */, "\\end");
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
}
|
|
751
|
-
var LatexParserError = class extends Error {
|
|
736
|
+
var CONTROL_LINEBREAK = new TexToken(7 /* CONTROL */, "\\\\");
|
|
737
|
+
var LatexParserError = class _LatexParserError extends Error {
|
|
752
738
|
constructor(message) {
|
|
753
739
|
super(message);
|
|
754
740
|
this.name = "LatexParserError";
|
|
755
741
|
}
|
|
742
|
+
static UNMATCHED_LEFT_BRACE = new _LatexParserError("Unmatched '\\{'");
|
|
743
|
+
static UNMATCHED_RIGHT_BRACE = new _LatexParserError("Unmatched '\\}'");
|
|
744
|
+
static UNMATCHED_LEFT_BRACKET = new _LatexParserError("Unmatched '\\['");
|
|
745
|
+
static UNMATCHED_RIGHT_BRACKET = new _LatexParserError("Unmatched '\\]'");
|
|
746
|
+
static UNMATCHED_COMMAND_BEGIN = new _LatexParserError("Unmatched '\\begin'");
|
|
747
|
+
static UNMATCHED_COMMAND_END = new _LatexParserError("Unmatched '\\end'");
|
|
748
|
+
static UNMATCHED_COMMAND_LEFT = new _LatexParserError("Unmatched '\\left'");
|
|
749
|
+
static UNMATCHED_COMMAND_RIGHT = new _LatexParserError("Unmatched '\\right'");
|
|
756
750
|
};
|
|
757
751
|
var SUB_SYMBOL = new TexToken(7 /* CONTROL */, "_");
|
|
758
752
|
var SUP_SYMBOL = new TexToken(7 /* CONTROL */, "^");
|
|
759
753
|
var LatexParser = class {
|
|
760
754
|
space_sensitive;
|
|
761
755
|
newline_sensitive;
|
|
756
|
+
// how many levels of \begin{...} \end{...} are we currently in
|
|
757
|
+
alignmentDepth = 0;
|
|
762
758
|
constructor(space_sensitive = false, newline_sensitive = true) {
|
|
763
759
|
this.space_sensitive = space_sensitive;
|
|
764
760
|
this.newline_sensitive = newline_sensitive;
|
|
@@ -767,22 +763,30 @@ var LatexParser = class {
|
|
|
767
763
|
const token_displaystyle = new TexToken(2 /* COMMAND */, "\\displaystyle");
|
|
768
764
|
const idx = array_find(tokens, token_displaystyle);
|
|
769
765
|
if (idx === -1) {
|
|
770
|
-
|
|
771
|
-
return tree;
|
|
766
|
+
return this.parseGroup(tokens.slice(0));
|
|
772
767
|
} else if (idx === 0) {
|
|
773
|
-
const
|
|
768
|
+
const tree = this.parseGroup(tokens.slice(1));
|
|
774
769
|
return new TexFuncCall(token_displaystyle, [tree]);
|
|
775
770
|
} else {
|
|
776
|
-
const
|
|
777
|
-
const
|
|
771
|
+
const tree1 = this.parseGroup(tokens.slice(0, idx));
|
|
772
|
+
const tree2 = this.parseGroup(tokens.slice(idx + 1, tokens.length));
|
|
778
773
|
const display = new TexFuncCall(token_displaystyle, [tree2]);
|
|
779
774
|
return new TexGroup([tree1, display]);
|
|
780
775
|
}
|
|
781
776
|
}
|
|
782
|
-
parseGroup(tokens
|
|
777
|
+
parseGroup(tokens) {
|
|
778
|
+
const [tree, _] = this.parseClosure(tokens, 0, null);
|
|
779
|
+
return tree;
|
|
780
|
+
}
|
|
781
|
+
// return pos: (position of closingToken) + 1
|
|
782
|
+
// pos will be -1 if closingToken is not found
|
|
783
|
+
parseClosure(tokens, start, closingToken) {
|
|
783
784
|
const results = [];
|
|
784
785
|
let pos = start;
|
|
785
|
-
while (pos <
|
|
786
|
+
while (pos < tokens.length) {
|
|
787
|
+
if (closingToken !== null && tokens[pos].eq(closingToken)) {
|
|
788
|
+
break;
|
|
789
|
+
}
|
|
786
790
|
const [res, newPos] = this.parseNextExpr(tokens, pos);
|
|
787
791
|
pos = newPos;
|
|
788
792
|
if (res.head.type === 5 /* SPACE */ || res.head.type === 6 /* NEWLINE */) {
|
|
@@ -793,18 +797,18 @@ var LatexParser = class {
|
|
|
793
797
|
continue;
|
|
794
798
|
}
|
|
795
799
|
}
|
|
796
|
-
if (res.head.eq(new TexToken(7 /* CONTROL */, "&"))) {
|
|
797
|
-
throw new LatexParserError("Unexpected & outside of an alignment");
|
|
798
|
-
}
|
|
799
800
|
results.push(res);
|
|
800
801
|
}
|
|
802
|
+
if (pos >= tokens.length && closingToken !== null) {
|
|
803
|
+
return [EMPTY_NODE, -1];
|
|
804
|
+
}
|
|
801
805
|
let node;
|
|
802
806
|
if (results.length === 1) {
|
|
803
807
|
node = results[0];
|
|
804
808
|
} else {
|
|
805
809
|
node = new TexGroup(results);
|
|
806
810
|
}
|
|
807
|
-
return [node,
|
|
811
|
+
return [node, pos + 1];
|
|
808
812
|
}
|
|
809
813
|
parseNextExpr(tokens, start) {
|
|
810
814
|
let [base, pos] = this.parseNextExprWithoutSupSub(tokens, start);
|
|
@@ -813,25 +817,28 @@ var LatexParser = class {
|
|
|
813
817
|
let num_prime = 0;
|
|
814
818
|
num_prime += eat_primes(tokens, pos);
|
|
815
819
|
pos += num_prime;
|
|
816
|
-
if (pos < tokens.length
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
820
|
+
if (pos < tokens.length) {
|
|
821
|
+
const next_token = tokens[pos];
|
|
822
|
+
if (next_token.eq(SUB_SYMBOL)) {
|
|
823
|
+
[sub, pos] = this.parseNextExprWithoutSupSub(tokens, pos + 1);
|
|
824
|
+
num_prime += eat_primes(tokens, pos);
|
|
825
|
+
pos += num_prime;
|
|
826
|
+
if (pos < tokens.length && tokens[pos].eq(SUP_SYMBOL)) {
|
|
827
|
+
[sup, pos] = this.parseNextExprWithoutSupSub(tokens, pos + 1);
|
|
828
|
+
if (eat_primes(tokens, pos) > 0) {
|
|
829
|
+
throw new LatexParserError("Double superscript");
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
} else if (next_token.eq(SUP_SYMBOL)) {
|
|
821
833
|
[sup, pos] = this.parseNextExprWithoutSupSub(tokens, pos + 1);
|
|
822
834
|
if (eat_primes(tokens, pos) > 0) {
|
|
823
835
|
throw new LatexParserError("Double superscript");
|
|
824
836
|
}
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
}
|
|
831
|
-
if (pos < tokens.length && tokens[pos].eq(SUB_SYMBOL)) {
|
|
832
|
-
[sub, pos] = this.parseNextExprWithoutSupSub(tokens, pos + 1);
|
|
833
|
-
if (eat_primes(tokens, pos) > 0) {
|
|
834
|
-
throw new LatexParserError("Double superscript");
|
|
837
|
+
if (pos < tokens.length && tokens[pos].eq(SUB_SYMBOL)) {
|
|
838
|
+
[sub, pos] = this.parseNextExprWithoutSupSub(tokens, pos + 1);
|
|
839
|
+
if (eat_primes(tokens, pos) > 0) {
|
|
840
|
+
throw new LatexParserError("Double superscript");
|
|
841
|
+
}
|
|
835
842
|
}
|
|
836
843
|
}
|
|
837
844
|
}
|
|
@@ -859,7 +866,7 @@ var LatexParser = class {
|
|
|
859
866
|
}
|
|
860
867
|
parseNextExprWithoutSupSub(tokens, start) {
|
|
861
868
|
if (start >= tokens.length) {
|
|
862
|
-
|
|
869
|
+
throw new LatexParserError("Unexpected end of input");
|
|
863
870
|
}
|
|
864
871
|
const firstToken = tokens[start];
|
|
865
872
|
switch (firstToken.type) {
|
|
@@ -876,8 +883,12 @@ var LatexParser = class {
|
|
|
876
883
|
}
|
|
877
884
|
if (firstToken.eq(BEGIN_COMMAND)) {
|
|
878
885
|
return this.parseBeginEndExpr(tokens, start);
|
|
886
|
+
} else if (firstToken.eq(END_COMMAND)) {
|
|
887
|
+
throw LatexParserError.UNMATCHED_COMMAND_END;
|
|
879
888
|
} else if (firstToken.eq(LEFT_COMMAND)) {
|
|
880
889
|
return this.parseLeftRightExpr(tokens, start);
|
|
890
|
+
} else if (firstToken.eq(RIGHT_COMMAND)) {
|
|
891
|
+
throw LatexParserError.UNMATCHED_COMMAND_RIGHT;
|
|
881
892
|
} else {
|
|
882
893
|
return this.parseCommandExpr(tokens, start);
|
|
883
894
|
}
|
|
@@ -885,13 +896,13 @@ var LatexParser = class {
|
|
|
885
896
|
const controlChar = firstToken.value;
|
|
886
897
|
switch (controlChar) {
|
|
887
898
|
case "{":
|
|
888
|
-
const
|
|
889
|
-
if (
|
|
890
|
-
throw
|
|
899
|
+
const [group, newPos] = this.parseClosure(tokens, start + 1, RIGHT_CURLY_BRACKET);
|
|
900
|
+
if (newPos === -1) {
|
|
901
|
+
throw LatexParserError.UNMATCHED_LEFT_BRACE;
|
|
891
902
|
}
|
|
892
|
-
return
|
|
903
|
+
return [group, newPos];
|
|
893
904
|
case "}":
|
|
894
|
-
throw
|
|
905
|
+
throw LatexParserError.UNMATCHED_RIGHT_BRACE;
|
|
895
906
|
case "\\\\":
|
|
896
907
|
case "\\!":
|
|
897
908
|
case "\\,":
|
|
@@ -904,6 +915,9 @@ var LatexParser = class {
|
|
|
904
915
|
case "^":
|
|
905
916
|
return [EMPTY_NODE, start];
|
|
906
917
|
case "&":
|
|
918
|
+
if (this.alignmentDepth <= 0) {
|
|
919
|
+
throw new LatexParserError("Unexpected & outside of an alignment");
|
|
920
|
+
}
|
|
907
921
|
return [firstToken.toNode(), start + 1];
|
|
908
922
|
default:
|
|
909
923
|
throw new LatexParserError("Unknown control sequence");
|
|
@@ -917,9 +931,6 @@ var LatexParser = class {
|
|
|
917
931
|
const command_token = tokens[start];
|
|
918
932
|
const command = command_token.value;
|
|
919
933
|
let pos = start + 1;
|
|
920
|
-
if (["left", "right", "begin", "end"].includes(command.slice(1))) {
|
|
921
|
-
throw new LatexParserError("Unexpected command: " + command);
|
|
922
|
-
}
|
|
923
934
|
const paramNum = get_command_param_num(command.slice(1));
|
|
924
935
|
switch (paramNum) {
|
|
925
936
|
case 0:
|
|
@@ -929,13 +940,11 @@ var LatexParser = class {
|
|
|
929
940
|
throw new LatexParserError("Expecting argument for " + command);
|
|
930
941
|
}
|
|
931
942
|
if (command === "\\sqrt" && pos < tokens.length && tokens[pos].eq(LEFT_SQUARE_BRACKET)) {
|
|
932
|
-
const
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
throw new LatexParserError("No matching right square bracket for [");
|
|
943
|
+
const [exponent, newPos1] = this.parseClosure(tokens, pos + 1, RIGHT_SQUARE_BRACKET);
|
|
944
|
+
if (newPos1 === -1) {
|
|
945
|
+
throw LatexParserError.UNMATCHED_LEFT_BRACKET;
|
|
936
946
|
}
|
|
937
|
-
const [
|
|
938
|
-
const [arg12, newPos2] = this.parseNextArg(tokens, posRightSquareBracket + 1);
|
|
947
|
+
const [arg12, newPos2] = this.parseNextArg(tokens, newPos1);
|
|
939
948
|
return [new TexFuncCall(command_token, [arg12], exponent), newPos2];
|
|
940
949
|
} else if (command === "\\text") {
|
|
941
950
|
if (pos + 2 >= tokens.length) {
|
|
@@ -946,6 +955,14 @@ var LatexParser = class {
|
|
|
946
955
|
assert(tokens[pos + 2].eq(RIGHT_CURLY_BRACKET));
|
|
947
956
|
const literal = tokens[pos + 1];
|
|
948
957
|
return [new TexText(literal), pos + 3];
|
|
958
|
+
} else if (command === "\\displaylines") {
|
|
959
|
+
assert(tokens[pos].eq(LEFT_CURLY_BRACKET));
|
|
960
|
+
const [matrix, newPos2] = this.parseAligned(tokens, pos + 1, RIGHT_CURLY_BRACKET);
|
|
961
|
+
if (newPos2 === -1) {
|
|
962
|
+
throw LatexParserError.UNMATCHED_LEFT_BRACE;
|
|
963
|
+
}
|
|
964
|
+
const group = new TexGroup(array_join(matrix, CONTROL_LINEBREAK.toNode()));
|
|
965
|
+
return [new TexFuncCall(command_token, [group]), newPos2];
|
|
949
966
|
}
|
|
950
967
|
let [arg1, newPos] = this.parseNextArg(tokens, pos);
|
|
951
968
|
return [new TexFuncCall(command_token, [arg1]), newPos];
|
|
@@ -987,30 +1004,27 @@ var LatexParser = class {
|
|
|
987
1004
|
let pos = start + 1;
|
|
988
1005
|
pos += eat_whitespaces(tokens, pos).length;
|
|
989
1006
|
if (pos >= tokens.length) {
|
|
990
|
-
throw new LatexParserError("Expecting delimiter after \\left");
|
|
1007
|
+
throw new LatexParserError("Expecting a delimiter after \\left");
|
|
991
1008
|
}
|
|
992
1009
|
const leftDelimiter = eat_parenthesis(tokens, pos);
|
|
993
1010
|
if (leftDelimiter === null) {
|
|
994
1011
|
throw new LatexParserError("Invalid delimiter after \\left");
|
|
995
1012
|
}
|
|
996
1013
|
pos++;
|
|
997
|
-
const
|
|
998
|
-
const idx = find_closing_right_command(tokens, start);
|
|
1014
|
+
const [body, idx] = this.parseClosure(tokens, pos, RIGHT_COMMAND);
|
|
999
1015
|
if (idx === -1) {
|
|
1000
|
-
throw
|
|
1016
|
+
throw LatexParserError.UNMATCHED_COMMAND_LEFT;
|
|
1001
1017
|
}
|
|
1002
|
-
|
|
1003
|
-
pos = idx + 1;
|
|
1018
|
+
pos = idx;
|
|
1004
1019
|
pos += eat_whitespaces(tokens, pos).length;
|
|
1005
1020
|
if (pos >= tokens.length) {
|
|
1006
|
-
throw new LatexParserError("Expecting
|
|
1021
|
+
throw new LatexParserError("Expecting a delimiter after \\right");
|
|
1007
1022
|
}
|
|
1008
1023
|
const rightDelimiter = eat_parenthesis(tokens, pos);
|
|
1009
1024
|
if (rightDelimiter === null) {
|
|
1010
1025
|
throw new LatexParserError("Invalid delimiter after \\right");
|
|
1011
1026
|
}
|
|
1012
1027
|
pos++;
|
|
1013
|
-
const [body, _] = this.parseGroup(tokens, exprInsideStart, exprInsideEnd);
|
|
1014
1028
|
const left = leftDelimiter.value === "." ? null : leftDelimiter;
|
|
1015
1029
|
const right = rightDelimiter.value === "." ? null : rightDelimiter;
|
|
1016
1030
|
const res = new TexLeftRight({ body, left, right });
|
|
@@ -1029,37 +1043,36 @@ var LatexParser = class {
|
|
|
1029
1043
|
pos += eat_whitespaces(tokens, pos).length;
|
|
1030
1044
|
[data, pos] = this.parseNextArg(tokens, pos);
|
|
1031
1045
|
}
|
|
1032
|
-
|
|
1033
|
-
const exprInsideStart = pos;
|
|
1034
|
-
const endIdx = find_closing_end_command(tokens, start);
|
|
1046
|
+
const [body, endIdx] = this.parseAligned(tokens, pos, END_COMMAND);
|
|
1035
1047
|
if (endIdx === -1) {
|
|
1036
|
-
throw
|
|
1048
|
+
throw LatexParserError.UNMATCHED_COMMAND_BEGIN;
|
|
1037
1049
|
}
|
|
1038
|
-
|
|
1039
|
-
pos = endIdx + 1;
|
|
1050
|
+
pos = endIdx;
|
|
1040
1051
|
assert(tokens[pos].eq(LEFT_CURLY_BRACKET));
|
|
1041
1052
|
assert(tokens[pos + 1].type === 3 /* LITERAL */);
|
|
1042
1053
|
assert(tokens[pos + 2].eq(RIGHT_CURLY_BRACKET));
|
|
1043
1054
|
if (tokens[pos + 1].value !== envName) {
|
|
1044
|
-
throw new LatexParserError("
|
|
1055
|
+
throw new LatexParserError("\\begin and \\end environments mismatch");
|
|
1045
1056
|
}
|
|
1046
1057
|
pos += 3;
|
|
1047
|
-
const exprInside = tokens.slice(exprInsideStart, exprInsideEnd);
|
|
1048
|
-
while (exprInside.length > 0 && [5 /* SPACE */, 6 /* NEWLINE */].includes(exprInside[exprInside.length - 1].type)) {
|
|
1049
|
-
exprInside.pop();
|
|
1050
|
-
}
|
|
1051
|
-
const body = this.parseAligned(exprInside);
|
|
1052
1058
|
const res = new TexBeginEnd(new TexToken(3 /* LITERAL */, envName), body, data);
|
|
1053
1059
|
return [res, pos];
|
|
1054
1060
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1061
|
+
// return pos: (position of closingToken) + 1
|
|
1062
|
+
// pos will be -1 if closingToken is not found
|
|
1063
|
+
parseAligned(tokens, start, closingToken) {
|
|
1064
|
+
this.alignmentDepth++;
|
|
1065
|
+
let pos = start;
|
|
1066
|
+
pos += eat_whitespaces(tokens, pos).length;
|
|
1057
1067
|
const allRows = [];
|
|
1058
1068
|
let row = [];
|
|
1059
1069
|
allRows.push(row);
|
|
1060
1070
|
let group = new TexGroup([]);
|
|
1061
1071
|
row.push(group);
|
|
1062
1072
|
while (pos < tokens.length) {
|
|
1073
|
+
if (tokens[pos].eq(closingToken)) {
|
|
1074
|
+
break;
|
|
1075
|
+
}
|
|
1063
1076
|
const [res, newPos] = this.parseNextExpr(tokens, pos);
|
|
1064
1077
|
pos = newPos;
|
|
1065
1078
|
if (res.head.type === 5 /* SPACE */ || res.head.type === 6 /* NEWLINE */) {
|
|
@@ -1082,7 +1095,20 @@ var LatexParser = class {
|
|
|
1082
1095
|
group.items.push(res);
|
|
1083
1096
|
}
|
|
1084
1097
|
}
|
|
1085
|
-
|
|
1098
|
+
if (pos >= tokens.length) {
|
|
1099
|
+
return [[], -1];
|
|
1100
|
+
}
|
|
1101
|
+
if (allRows.length > 0 && allRows[allRows.length - 1].length > 0) {
|
|
1102
|
+
const last_cell = allRows[allRows.length - 1][allRows[allRows.length - 1].length - 1];
|
|
1103
|
+
if (last_cell.type === "ordgroup") {
|
|
1104
|
+
const last_cell_items = last_cell.items;
|
|
1105
|
+
while (last_cell_items.length > 0 && [5 /* SPACE */, 6 /* NEWLINE */].includes(last_cell_items[last_cell_items.length - 1].head.type)) {
|
|
1106
|
+
last_cell_items.pop();
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
this.alignmentDepth--;
|
|
1111
|
+
return [allRows, pos + 1];
|
|
1086
1112
|
}
|
|
1087
1113
|
};
|
|
1088
1114
|
function passIgnoreWhitespaceBeforeScriptMark(tokens) {
|
|
@@ -1111,7 +1137,7 @@ function passExpandCustomTexMacros(tokens, customTexMacros) {
|
|
|
1111
1137
|
}
|
|
1112
1138
|
return out_tokens;
|
|
1113
1139
|
}
|
|
1114
|
-
function parseTex(tex, customTexMacros) {
|
|
1140
|
+
function parseTex(tex, customTexMacros = {}) {
|
|
1115
1141
|
const parser = new LatexParser();
|
|
1116
1142
|
let tokens = tokenize_tex(tex);
|
|
1117
1143
|
tokens = passIgnoreWhitespaceBeforeScriptMark(tokens);
|
|
@@ -1686,6 +1712,7 @@ var symbolMap = /* @__PURE__ */ new Map([
|
|
|
1686
1712
|
["mathbb", "bb"],
|
|
1687
1713
|
["mathbf", "bold"],
|
|
1688
1714
|
["mathcal", "cal"],
|
|
1715
|
+
["mathscr", "scr"],
|
|
1689
1716
|
["mathit", "italic"],
|
|
1690
1717
|
["mathfrak", "frak"],
|
|
1691
1718
|
["mathrm", "upright"],
|
|
@@ -1886,7 +1913,6 @@ var symbolMap = /* @__PURE__ */ new Map([
|
|
|
1886
1913
|
["zeta", "zeta"],
|
|
1887
1914
|
["intop", "limits(integral)"],
|
|
1888
1915
|
// extended
|
|
1889
|
-
["mathscr", "scr"],
|
|
1890
1916
|
["LaTeX", "#LaTeX"],
|
|
1891
1917
|
["TeX", "#TeX"]
|
|
1892
1918
|
]);
|
|
@@ -2891,12 +2917,6 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
|
|
|
2891
2917
|
}
|
|
2892
2918
|
case "text": {
|
|
2893
2919
|
const node2 = abstractNode;
|
|
2894
|
-
if (/[^\x00-\x7F]+/.test(node2.head.value) && options.nonAsciiWrapper !== "") {
|
|
2895
|
-
return new TypstFuncCall(
|
|
2896
|
-
new TypstToken(1 /* SYMBOL */, options.nonAsciiWrapper),
|
|
2897
|
-
[new TypstToken(4 /* TEXT */, node2.head.value).toNode()]
|
|
2898
|
-
);
|
|
2899
|
-
}
|
|
2900
2920
|
return new TypstToken(4 /* TEXT */, node2.head.value).toNode();
|
|
2901
2921
|
}
|
|
2902
2922
|
case "ordgroup":
|
|
@@ -3026,6 +3046,30 @@ function convert_tex_node_to_typst(abstractNode, options = {}) {
|
|
|
3026
3046
|
if (node2.head.value === "\\substack") {
|
|
3027
3047
|
return arg0;
|
|
3028
3048
|
}
|
|
3049
|
+
if (node2.head.value === "\\displaylines") {
|
|
3050
|
+
return arg0;
|
|
3051
|
+
}
|
|
3052
|
+
if (node2.head.value === "\\mathinner") {
|
|
3053
|
+
return arg0;
|
|
3054
|
+
}
|
|
3055
|
+
if (node2.head.value === "\\mathrel") {
|
|
3056
|
+
return new TypstFuncCall(
|
|
3057
|
+
new TypstToken(1 /* SYMBOL */, "class"),
|
|
3058
|
+
[new TypstToken(4 /* TEXT */, "relation").toNode(), arg0]
|
|
3059
|
+
);
|
|
3060
|
+
}
|
|
3061
|
+
if (node2.head.value === "\\mathbin") {
|
|
3062
|
+
return new TypstFuncCall(
|
|
3063
|
+
new TypstToken(1 /* SYMBOL */, "class"),
|
|
3064
|
+
[new TypstToken(4 /* TEXT */, "binary").toNode(), arg0]
|
|
3065
|
+
);
|
|
3066
|
+
}
|
|
3067
|
+
if (node2.head.value === "\\mathop") {
|
|
3068
|
+
return new TypstFuncCall(
|
|
3069
|
+
new TypstToken(1 /* SYMBOL */, "class"),
|
|
3070
|
+
[new TypstToken(4 /* TEXT */, "large").toNode(), arg0]
|
|
3071
|
+
);
|
|
3072
|
+
}
|
|
3029
3073
|
if (node2.head.value === "\\set") {
|
|
3030
3074
|
return new TypstLeftright(
|
|
3031
3075
|
null,
|
|
@@ -3304,6 +3348,31 @@ function convert_typst_node_to_tex(abstractNode) {
|
|
|
3304
3348
|
assert(arg0.head.type === 4 /* TEXT */);
|
|
3305
3349
|
return new TexFuncCall(typst_token_to_tex(node.head), [new TexToken(3 /* LITERAL */, arg0.head.value).toNode()]);
|
|
3306
3350
|
}
|
|
3351
|
+
case "class": {
|
|
3352
|
+
const arg0 = node.args[0];
|
|
3353
|
+
assert(arg0.head.type === 4 /* TEXT */);
|
|
3354
|
+
let command;
|
|
3355
|
+
switch (arg0.head.value) {
|
|
3356
|
+
// \mathrel{X} <- class("relation", X)
|
|
3357
|
+
case "relation":
|
|
3358
|
+
command = "\\mathrel";
|
|
3359
|
+
break;
|
|
3360
|
+
// \mathbin{X} <- class("binary", X)
|
|
3361
|
+
case "binary":
|
|
3362
|
+
command = "\\mathbin";
|
|
3363
|
+
break;
|
|
3364
|
+
// \mathop{X} <- class("large", X)
|
|
3365
|
+
case "large":
|
|
3366
|
+
command = "\\mathop";
|
|
3367
|
+
break;
|
|
3368
|
+
default:
|
|
3369
|
+
throw new Error(`Unimplemented class: ${arg0.head.value}`);
|
|
3370
|
+
}
|
|
3371
|
+
return new TexFuncCall(
|
|
3372
|
+
new TexToken(2 /* COMMAND */, command),
|
|
3373
|
+
[convert_typst_node_to_tex(node.args[1])]
|
|
3374
|
+
);
|
|
3375
|
+
}
|
|
3307
3376
|
// general case
|
|
3308
3377
|
default: {
|
|
3309
3378
|
const func_name_tex = typst_token_to_tex(node.head);
|
|
@@ -3521,7 +3590,7 @@ function _find_closing_match(tokens, start, leftBrackets, rightBrackets) {
|
|
|
3521
3590
|
}
|
|
3522
3591
|
return pos - 1;
|
|
3523
3592
|
}
|
|
3524
|
-
function
|
|
3593
|
+
function find_closing_match(tokens, start) {
|
|
3525
3594
|
return _find_closing_match(
|
|
3526
3595
|
tokens,
|
|
3527
3596
|
start,
|
|
@@ -3741,7 +3810,7 @@ var TypstParser = class {
|
|
|
3741
3810
|
let node;
|
|
3742
3811
|
let end;
|
|
3743
3812
|
if (tokens[start].eq(LEFT_PARENTHESES)) {
|
|
3744
|
-
const pos_closing =
|
|
3813
|
+
const pos_closing = find_closing_match(tokens, start);
|
|
3745
3814
|
[node, end] = this.parseGroup(tokens, start + 1, pos_closing);
|
|
3746
3815
|
} else {
|
|
3747
3816
|
[node, end] = this.parseNextExprWithoutSupSub(tokens, start);
|
|
@@ -3757,7 +3826,7 @@ var TypstParser = class {
|
|
|
3757
3826
|
const firstToken = tokens[start];
|
|
3758
3827
|
const node = firstToken.toNode();
|
|
3759
3828
|
if (firstToken.eq(LEFT_PARENTHESES)) {
|
|
3760
|
-
const pos_closing =
|
|
3829
|
+
const pos_closing = find_closing_match(tokens, start);
|
|
3761
3830
|
return this.parseGroup(tokens, start + 1, pos_closing, true);
|
|
3762
3831
|
}
|
|
3763
3832
|
if (firstToken.type === 2 /* ELEMENT */ && !isalpha(firstToken.value[0])) {
|
|
@@ -3801,13 +3870,13 @@ var TypstParser = class {
|
|
|
3801
3870
|
}
|
|
3802
3871
|
// start: the position of the left parentheses
|
|
3803
3872
|
parseArguments(tokens, start) {
|
|
3804
|
-
const end =
|
|
3873
|
+
const end = find_closing_match(tokens, start);
|
|
3805
3874
|
return [this.parseArgumentsWithSeparator(tokens, start + 1, end, COMMA), end + 1];
|
|
3806
3875
|
}
|
|
3807
3876
|
// start: the position of the left parentheses
|
|
3808
3877
|
parseLrArguments(tokens, start) {
|
|
3809
3878
|
const lr_token = tokens[start];
|
|
3810
|
-
const end =
|
|
3879
|
+
const end = find_closing_match(tokens, start);
|
|
3811
3880
|
if (tokens[start + 1].isOneOf(TypstToken.LEFT_DELIMITERS)) {
|
|
3812
3881
|
const inner_start = start + 1;
|
|
3813
3882
|
const inner_end = find_closing_delim(tokens, inner_start);
|
|
@@ -3826,7 +3895,7 @@ var TypstParser = class {
|
|
|
3826
3895
|
}
|
|
3827
3896
|
// start: the position of the left parentheses
|
|
3828
3897
|
parseMatrix(tokens, start, rowSepToken, cellSepToken) {
|
|
3829
|
-
const end =
|
|
3898
|
+
const end = find_closing_match(tokens, start);
|
|
3830
3899
|
tokens = tokens.slice(0, end);
|
|
3831
3900
|
const matrix = [];
|
|
3832
3901
|
let named_params = {};
|
|
@@ -3940,7 +4009,6 @@ function tex2typst(tex, options) {
|
|
|
3940
4009
|
fracToSlash: true,
|
|
3941
4010
|
inftyToOo: false,
|
|
3942
4011
|
optimize: true,
|
|
3943
|
-
nonAsciiWrapper: "",
|
|
3944
4012
|
customTexMacros: {}
|
|
3945
4013
|
};
|
|
3946
4014
|
if (options !== void 0) {
|