tex2typst 0.2.15 → 0.3.0-alpha
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/generic.d.ts +7 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +964 -196
- package/dist/map.d.ts +2 -1
- package/dist/tex-parser.d.ts +1 -7
- package/dist/tex-writer.d.ts +11 -0
- package/dist/tex2typst.min.js +1 -1
- package/dist/types.d.ts +15 -5
- package/dist/typst-parser.d.ts +21 -0
- package/dist/util.d.ts +3 -0
- package/package.json +1 -1
- package/src/generic.ts +37 -0
- package/src/index.ts +10 -0
- package/src/map.ts +29 -3
- package/src/tex-parser.ts +23 -100
- package/src/tex-writer.ts +249 -0
- package/src/tex2typst.ts +2 -1
- package/src/types.ts +206 -14
- package/src/typst-parser.ts +507 -0
- package/src/util.ts +14 -0
- package/src/writer.ts +42 -47
package/src/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { array_includes } from "./generic";
|
|
2
|
+
|
|
1
3
|
export enum TexTokenType {
|
|
2
4
|
ELEMENT,
|
|
3
5
|
COMMAND,
|
|
@@ -9,6 +11,30 @@ export enum TexTokenType {
|
|
|
9
11
|
UNKNOWN,
|
|
10
12
|
}
|
|
11
13
|
|
|
14
|
+
export class TexToken {
|
|
15
|
+
type: TexTokenType;
|
|
16
|
+
value: string;
|
|
17
|
+
|
|
18
|
+
constructor(type: TexTokenType, value: string) {
|
|
19
|
+
this.type = type;
|
|
20
|
+
this.value = value;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public eq(token: TexToken): boolean {
|
|
24
|
+
return this.type === token.type && this.value === token.value;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public toString(): string {
|
|
28
|
+
switch (this.type) {
|
|
29
|
+
case TexTokenType.TEXT:
|
|
30
|
+
return `\\text{${this.value}}`;
|
|
31
|
+
case TexTokenType.COMMENT:
|
|
32
|
+
return `%${this.value}`;
|
|
33
|
+
default:
|
|
34
|
+
return this.value;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
12
38
|
|
|
13
39
|
|
|
14
40
|
export interface TexSupsubData {
|
|
@@ -33,6 +59,14 @@ export type TexArrayData = TexNode[][];
|
|
|
33
59
|
type TexNodeType = 'element' | 'text' | 'comment' | 'whitespace' | 'control' | 'ordgroup' | 'supsub'
|
|
34
60
|
| 'unaryFunc' | 'binaryFunc' | 'leftright' | 'beginend' | 'symbol' | 'empty' | 'unknownMacro';
|
|
35
61
|
|
|
62
|
+
|
|
63
|
+
function apply_escape_if_needed(c: string) {
|
|
64
|
+
if (['{', '}', '%'].includes(c)) {
|
|
65
|
+
return '\\' + c;
|
|
66
|
+
}
|
|
67
|
+
return c;
|
|
68
|
+
}
|
|
69
|
+
|
|
36
70
|
export class TexNode {
|
|
37
71
|
type: TexNodeType;
|
|
38
72
|
content: string;
|
|
@@ -50,7 +84,8 @@ export class TexNode {
|
|
|
50
84
|
this.data = data;
|
|
51
85
|
}
|
|
52
86
|
|
|
53
|
-
|
|
87
|
+
// Note that this is only shallow equality.
|
|
88
|
+
public eq(other: TexNode): boolean {
|
|
54
89
|
return this.type === other.type && this.content === other.content;
|
|
55
90
|
}
|
|
56
91
|
|
|
@@ -62,11 +97,130 @@ export class TexNode {
|
|
|
62
97
|
throw new Error(`toString() is not implemented for type ${this.type}`);
|
|
63
98
|
}
|
|
64
99
|
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
public serialize(): TexToken[] {
|
|
103
|
+
switch (this.type) {
|
|
104
|
+
case 'empty':
|
|
105
|
+
return [];
|
|
106
|
+
case 'element': {
|
|
107
|
+
let c = this.content;
|
|
108
|
+
c = apply_escape_if_needed(c);
|
|
109
|
+
return [new TexToken(TexTokenType.ELEMENT, c)];
|
|
110
|
+
}
|
|
111
|
+
case 'symbol':
|
|
112
|
+
return [new TexToken(TexTokenType.COMMAND, this.content)];
|
|
113
|
+
case 'text':
|
|
114
|
+
return [new TexToken(TexTokenType.TEXT, this.content)];
|
|
115
|
+
case 'comment':
|
|
116
|
+
return [new TexToken(TexTokenType.COMMENT, this.content)];
|
|
117
|
+
case 'whitespace': {
|
|
118
|
+
const tokens: TexToken[] = [];
|
|
119
|
+
for (const c of this.content) {
|
|
120
|
+
const token_type = c === ' ' ? TexTokenType.SPACE : TexTokenType.NEWLINE;
|
|
121
|
+
tokens.push(new TexToken(token_type, c));
|
|
122
|
+
}
|
|
123
|
+
return tokens;
|
|
124
|
+
}
|
|
125
|
+
case 'ordgroup':
|
|
126
|
+
return this.args!.map((n) => n.serialize()).flat();
|
|
127
|
+
case 'unaryFunc': {
|
|
128
|
+
let tokens: TexToken[] = [];
|
|
129
|
+
tokens.push(new TexToken(TexTokenType.COMMAND, this.content));
|
|
130
|
+
|
|
131
|
+
// special hook for \sqrt
|
|
132
|
+
if (this.content === '\\sqrt' && this.data) {
|
|
133
|
+
tokens.push(new TexToken(TexTokenType.ELEMENT, '['));
|
|
134
|
+
tokens = tokens.concat((this.data! as TexSqrtData).serialize());
|
|
135
|
+
tokens.push(new TexToken(TexTokenType.ELEMENT, ']'));
|
|
136
|
+
}
|
|
137
|
+
// special hook for \operatorname
|
|
138
|
+
if (this.content === '\\operatorname' && this.args!.length === 1 && this.args![0].type === 'text') {
|
|
139
|
+
const text = this.args![0].content;
|
|
140
|
+
tokens.push(new TexToken(TexTokenType.ELEMENT, '{'));
|
|
141
|
+
// this.serialize(new TexNode('symbol', text));
|
|
142
|
+
tokens.push(new TexToken(TexTokenType.COMMAND, text));
|
|
143
|
+
tokens.push(new TexToken(TexTokenType.ELEMENT, '}'));
|
|
144
|
+
return tokens;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
tokens.push(new TexToken(TexTokenType.ELEMENT, '{'));
|
|
148
|
+
tokens = tokens.concat(this.args![0].serialize());
|
|
149
|
+
tokens.push(new TexToken(TexTokenType.ELEMENT, '}'));
|
|
150
|
+
|
|
151
|
+
return tokens;
|
|
152
|
+
}
|
|
153
|
+
case 'binaryFunc': {
|
|
154
|
+
let tokens: TexToken[] = [];
|
|
155
|
+
tokens.push(new TexToken(TexTokenType.COMMAND, this.content));
|
|
156
|
+
tokens.push(new TexToken(TexTokenType.ELEMENT, '{'));
|
|
157
|
+
tokens = tokens.concat(this.args![0].serialize());
|
|
158
|
+
tokens.push(new TexToken(TexTokenType.ELEMENT, '}'));
|
|
159
|
+
tokens.push(new TexToken(TexTokenType.ELEMENT, '{'));
|
|
160
|
+
tokens = tokens.concat(this.args![1].serialize());
|
|
161
|
+
tokens.push(new TexToken(TexTokenType.ELEMENT, '}'));
|
|
162
|
+
return tokens;
|
|
163
|
+
}
|
|
164
|
+
case 'supsub': {
|
|
165
|
+
let tokens: TexToken[] = [];
|
|
166
|
+
const { base, sup, sub } = this.data! as TexSupsubData;
|
|
167
|
+
tokens = tokens.concat(base.serialize());
|
|
168
|
+
if (sub) {
|
|
169
|
+
tokens.push(new TexToken(TexTokenType.CONTROL, '_'));
|
|
170
|
+
if (sub.type === 'ordgroup' || sub.type === 'supsub' || sub.type === 'empty') {
|
|
171
|
+
tokens.push(new TexToken(TexTokenType.ELEMENT, '{'));
|
|
172
|
+
tokens = tokens.concat(sub.serialize());
|
|
173
|
+
tokens.push(new TexToken(TexTokenType.ELEMENT, '}'));
|
|
174
|
+
} else {
|
|
175
|
+
tokens = tokens.concat(sub.serialize());
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (sup) {
|
|
179
|
+
tokens.push(new TexToken(TexTokenType.CONTROL, '^'));
|
|
180
|
+
if (sup.type === 'ordgroup' || sup.type === 'supsub' || sup.type === 'empty') {
|
|
181
|
+
tokens.push(new TexToken(TexTokenType.ELEMENT, '{'));
|
|
182
|
+
tokens = tokens.concat(sup.serialize());
|
|
183
|
+
tokens.push(new TexToken(TexTokenType.ELEMENT, '}'));
|
|
184
|
+
} else {
|
|
185
|
+
tokens = tokens.concat(sup.serialize());
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return tokens;
|
|
189
|
+
}
|
|
190
|
+
case 'control': {
|
|
191
|
+
return [new TexToken(TexTokenType.CONTROL, this.content)];
|
|
192
|
+
}
|
|
193
|
+
case 'beginend': {
|
|
194
|
+
let tokens: TexToken[] = [];
|
|
195
|
+
const matrix = this.data as TexArrayData;
|
|
196
|
+
tokens.push(new TexToken(TexTokenType.COMMAND, `\\begin{${this.content}}`));
|
|
197
|
+
tokens.push(new TexToken(TexTokenType.NEWLINE, '\n'));
|
|
198
|
+
for (let i = 0; i < matrix.length; i++) {
|
|
199
|
+
const row = matrix[i];
|
|
200
|
+
for (let j = 0; j < row.length; j++) {
|
|
201
|
+
const cell = row[j];
|
|
202
|
+
tokens = tokens.concat(cell.serialize());
|
|
203
|
+
if (j !== row.length - 1) {
|
|
204
|
+
tokens.push(new TexToken(TexTokenType.CONTROL, '&'));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (i !== matrix.length - 1) {
|
|
208
|
+
tokens.push(new TexToken(TexTokenType.CONTROL, '\\\\'));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
tokens.push(new TexToken(TexTokenType.NEWLINE, '\n'));
|
|
212
|
+
tokens.push(new TexToken(TexTokenType.COMMAND, `\\end{${this.content}}`));
|
|
213
|
+
return tokens;
|
|
214
|
+
}
|
|
215
|
+
default:
|
|
216
|
+
throw new Error('[TexNode.serialize] Unimplemented type: ' + this.type);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
65
219
|
}
|
|
66
220
|
|
|
67
221
|
export enum TypstTokenType {
|
|
68
222
|
SYMBOL,
|
|
69
|
-
|
|
223
|
+
ELEMENT,
|
|
70
224
|
TEXT,
|
|
71
225
|
COMMENT,
|
|
72
226
|
SPACE,
|
|
@@ -77,26 +231,63 @@ export enum TypstTokenType {
|
|
|
77
231
|
|
|
78
232
|
export class TypstToken {
|
|
79
233
|
type: TypstTokenType;
|
|
80
|
-
|
|
234
|
+
value: string;
|
|
81
235
|
|
|
82
236
|
constructor(type: TypstTokenType, content: string) {
|
|
83
237
|
this.type = type;
|
|
84
|
-
this.
|
|
238
|
+
this.value = content;
|
|
85
239
|
}
|
|
86
240
|
|
|
87
241
|
eq(other: TypstToken): boolean {
|
|
88
|
-
return this.type === other.type && this.
|
|
242
|
+
return this.type === other.type && this.value === other.value;
|
|
89
243
|
}
|
|
90
244
|
|
|
91
245
|
isOneOf(tokens: TypstToken[]): boolean {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
246
|
+
return array_includes(tokens, this);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
public toNode(): TypstNode {
|
|
250
|
+
switch(this.type) {
|
|
251
|
+
case TypstTokenType.TEXT:
|
|
252
|
+
return new TypstNode('text', this.value);
|
|
253
|
+
case TypstTokenType.COMMENT:
|
|
254
|
+
return new TypstNode('comment', this.value);
|
|
255
|
+
case TypstTokenType.SPACE:
|
|
256
|
+
case TypstTokenType.NEWLINE:
|
|
257
|
+
return new TypstNode('whitespace', this.value);
|
|
258
|
+
case TypstTokenType.ELEMENT:
|
|
259
|
+
return new TypstNode('atom', this.value);
|
|
260
|
+
case TypstTokenType.SYMBOL:
|
|
261
|
+
return new TypstNode('symbol', this.value);
|
|
262
|
+
case TypstTokenType.CONTROL: {
|
|
263
|
+
const controlChar = this.value;
|
|
264
|
+
switch (controlChar) {
|
|
265
|
+
case '':
|
|
266
|
+
case '_':
|
|
267
|
+
case '^':
|
|
268
|
+
return new TypstNode('empty', '');
|
|
269
|
+
case '&':
|
|
270
|
+
return new TypstNode('control', '&');
|
|
271
|
+
case '\\':
|
|
272
|
+
return new TypstNode('control', '\\');
|
|
273
|
+
default:
|
|
274
|
+
throw new Error(`Unexpected control character ${controlChar}`);
|
|
275
|
+
}
|
|
97
276
|
}
|
|
277
|
+
default:
|
|
278
|
+
throw new Error(`Unexpected token type ${this.type}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
public toString(): string {
|
|
283
|
+
switch (this.type) {
|
|
284
|
+
case TypstTokenType.TEXT:
|
|
285
|
+
return `"${this.value}"`;
|
|
286
|
+
case TypstTokenType.COMMENT:
|
|
287
|
+
return `//${this.value}`;
|
|
288
|
+
default:
|
|
289
|
+
return this.value;
|
|
98
290
|
}
|
|
99
|
-
return found;
|
|
100
291
|
}
|
|
101
292
|
}
|
|
102
293
|
|
|
@@ -108,8 +299,8 @@ export interface TypstSupsubData {
|
|
|
108
299
|
|
|
109
300
|
export type TypstArrayData = TypstNode[][];
|
|
110
301
|
|
|
111
|
-
type TypstNodeType = 'atom' | 'symbol' | 'text' | '
|
|
112
|
-
| 'empty' | 'group' | 'supsub' | '
|
|
302
|
+
type TypstNodeType = 'atom' | 'symbol' | 'text' | 'control' | 'comment' | 'whitespace'
|
|
303
|
+
| 'empty' | 'group' | 'supsub' | 'funcCall' | 'fraction' | 'align' | 'matrix' | 'unknown';
|
|
113
304
|
|
|
114
305
|
|
|
115
306
|
export class TypstNode {
|
|
@@ -132,7 +323,8 @@ export class TypstNode {
|
|
|
132
323
|
this.options = options;
|
|
133
324
|
}
|
|
134
325
|
|
|
135
|
-
|
|
326
|
+
// Note that this is only shallow equality.
|
|
327
|
+
public eq(other: TypstNode): boolean {
|
|
136
328
|
return this.type === other.type && this.content === other.content;
|
|
137
329
|
}
|
|
138
330
|
}
|