tex2typst 0.0.17 → 0.0.19
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 +18 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +844 -22
- package/dist/parser.d.ts +1 -1
- package/dist/tex2typst.min.js +1 -0
- package/package.json +6 -2
- package/src/index.ts +3 -0
- package/src/map.ts +5 -8
- package/src/parser.ts +1 -1
- package/src/tex2typst.ts +9 -0
- package/src/writer.ts +13 -0
- package/tool/dist/dist/ka.js +13654 -0
- package/tool/dist/ka.js +13634 -0
- package/tsconfig.json +4 -3
- package/.github/workflows/github-ci.yml +0 -35
- package/dist/map.js +0 -291
- package/dist/parser.js +0 -265
- package/dist/types.js +0 -2
- package/dist/writer.js +0 -356
package/dist/writer.js
DELETED
|
@@ -1,356 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TypstWriter = exports.TypstWriterError = void 0;
|
|
4
|
-
const map_1 = require("./map");
|
|
5
|
-
// symbols that are supported by Typst but not by KaTeX
|
|
6
|
-
const TYPST_INTRINSIC_SYMBOLS = [
|
|
7
|
-
'dim',
|
|
8
|
-
'id',
|
|
9
|
-
'im',
|
|
10
|
-
'mod',
|
|
11
|
-
'Pr',
|
|
12
|
-
'sech',
|
|
13
|
-
'csch',
|
|
14
|
-
// 'sgn
|
|
15
|
-
];
|
|
16
|
-
class TypstWriterError extends Error {
|
|
17
|
-
constructor(message, node) {
|
|
18
|
-
super(message);
|
|
19
|
-
this.name = "TypstWriterError";
|
|
20
|
-
this.node = node;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
exports.TypstWriterError = TypstWriterError;
|
|
24
|
-
class TypstWriter {
|
|
25
|
-
constructor(nonStrict, preferTypstIntrinsic) {
|
|
26
|
-
this.buffer = "";
|
|
27
|
-
this.queue = [];
|
|
28
|
-
this.needSpaceAfterSingleItemScript = false;
|
|
29
|
-
this.insideFunctionDepth = 0;
|
|
30
|
-
this.nonStrict = nonStrict;
|
|
31
|
-
this.preferTypstIntrinsic = preferTypstIntrinsic;
|
|
32
|
-
}
|
|
33
|
-
writeBuffer(str) {
|
|
34
|
-
if (this.needSpaceAfterSingleItemScript && /^[0-9a-zA-Z\(]/.test(str)) {
|
|
35
|
-
this.buffer += ' ';
|
|
36
|
-
}
|
|
37
|
-
else {
|
|
38
|
-
let no_need_space = false;
|
|
39
|
-
// starting clause
|
|
40
|
-
no_need_space || (no_need_space = /[\(\|]$/.test(this.buffer) && /^\w/.test(str));
|
|
41
|
-
// putting punctuation
|
|
42
|
-
no_need_space || (no_need_space = /^[}()_^,;!\|]$/.test(str));
|
|
43
|
-
// putting a prime
|
|
44
|
-
no_need_space || (no_need_space = str === "'");
|
|
45
|
-
// continue a number
|
|
46
|
-
no_need_space || (no_need_space = /[0-9]$/.test(this.buffer) && /^[0-9]/.test(str));
|
|
47
|
-
// leading sign
|
|
48
|
-
no_need_space || (no_need_space = /[\(\[{]\s*(-|\+)$/.test(this.buffer) || this.buffer === "-" || this.buffer === "+");
|
|
49
|
-
// buffer is empty
|
|
50
|
-
no_need_space || (no_need_space = this.buffer === "");
|
|
51
|
-
// other cases
|
|
52
|
-
no_need_space || (no_need_space = /[\s"_^{\(]$/.test(this.buffer));
|
|
53
|
-
if (!no_need_space) {
|
|
54
|
-
this.buffer += ' ';
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
if (this.needSpaceAfterSingleItemScript) {
|
|
58
|
-
this.needSpaceAfterSingleItemScript = false;
|
|
59
|
-
}
|
|
60
|
-
this.buffer += str;
|
|
61
|
-
}
|
|
62
|
-
append(node) {
|
|
63
|
-
if (node.type === 'empty') {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
else if (node.type === 'ordgroup') {
|
|
67
|
-
// const index = this.startBlock();
|
|
68
|
-
node.args.forEach((arg) => this.append(arg));
|
|
69
|
-
// this.endBlock(index);
|
|
70
|
-
}
|
|
71
|
-
else if (node.type === 'atom') {
|
|
72
|
-
let content = node.content;
|
|
73
|
-
if (node.content === ',' && this.insideFunctionDepth > 0) {
|
|
74
|
-
content = 'comma';
|
|
75
|
-
}
|
|
76
|
-
this.queue.push({ type: 'atom', content: content });
|
|
77
|
-
}
|
|
78
|
-
else if (node.type === 'symbol') {
|
|
79
|
-
this.queue.push({ type: 'symbol', content: node.content });
|
|
80
|
-
}
|
|
81
|
-
else if (node.type === 'text') {
|
|
82
|
-
this.queue.push(node);
|
|
83
|
-
}
|
|
84
|
-
else if (node.type === 'supsub') {
|
|
85
|
-
let { base, sup, sub } = node.irregularData;
|
|
86
|
-
// Special logic for overbrace
|
|
87
|
-
if (base && base.type === 'unaryFunc' && base.content === '\\overbrace' && sup) {
|
|
88
|
-
this.append({ type: 'binaryFunc', content: '\\overbrace', args: [base.args[0], sup] });
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
else if (base && base.type === 'unaryFunc' && base.content === '\\underbrace' && sub) {
|
|
92
|
-
this.append({ type: 'binaryFunc', content: '\\underbrace', args: [base.args[0], sub] });
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
if (!base) {
|
|
96
|
-
this.queue.push({ type: 'text', content: '' });
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
this.appendWithBracketsIfNeeded(base);
|
|
100
|
-
}
|
|
101
|
-
let trailing_space_needed = false;
|
|
102
|
-
const has_prime = (sup && sup.type === 'symbol' && sup.content === '\\prime');
|
|
103
|
-
if (has_prime) {
|
|
104
|
-
// Put prime symbol before '_'. Because $y_1'$ is not displayed properly in Typst (so far)
|
|
105
|
-
// e.g.
|
|
106
|
-
// y_1' -> y'_1
|
|
107
|
-
// y_{a_1}' -> y'_{a_1}
|
|
108
|
-
this.queue.push({ type: 'atom', content: '\'' });
|
|
109
|
-
trailing_space_needed = false;
|
|
110
|
-
}
|
|
111
|
-
if (sub) {
|
|
112
|
-
this.queue.push({ type: 'atom', content: '_' });
|
|
113
|
-
trailing_space_needed = this.appendWithBracketsIfNeeded(sub);
|
|
114
|
-
}
|
|
115
|
-
if (sup && !has_prime) {
|
|
116
|
-
this.queue.push({ type: 'atom', content: '^' });
|
|
117
|
-
trailing_space_needed = this.appendWithBracketsIfNeeded(sup);
|
|
118
|
-
}
|
|
119
|
-
if (trailing_space_needed) {
|
|
120
|
-
this.queue.push({ type: 'softSpace', content: '' });
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
else if (node.type === 'leftright') {
|
|
124
|
-
const [left, body, right] = node.args;
|
|
125
|
-
// These pairs will be handled by Typst compiler by default. No need to add lr()
|
|
126
|
-
if (["[]", "()", "{}", "\\lfloor\\rfloor", "\\lceil\\rceil"].includes(left.content + right.content)) {
|
|
127
|
-
this.append(left);
|
|
128
|
-
this.append(body);
|
|
129
|
-
this.append(right);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
const func_symbol = { type: 'symbol', content: 'lr' };
|
|
133
|
-
this.queue.push(func_symbol);
|
|
134
|
-
this.insideFunctionDepth++;
|
|
135
|
-
this.queue.push({ type: 'atom', content: '(' });
|
|
136
|
-
this.append(left);
|
|
137
|
-
this.append(body);
|
|
138
|
-
this.append(right);
|
|
139
|
-
this.queue.push({ type: 'atom', content: ')' });
|
|
140
|
-
this.insideFunctionDepth--;
|
|
141
|
-
}
|
|
142
|
-
else if (node.type === 'binaryFunc') {
|
|
143
|
-
const func_symbol = { type: 'symbol', content: node.content };
|
|
144
|
-
const [arg0, arg1] = node.args;
|
|
145
|
-
this.queue.push(func_symbol);
|
|
146
|
-
this.insideFunctionDepth++;
|
|
147
|
-
this.queue.push({ type: 'atom', content: '(' });
|
|
148
|
-
this.append(arg0);
|
|
149
|
-
this.queue.push({ type: 'atom', content: ',' });
|
|
150
|
-
this.append(arg1);
|
|
151
|
-
this.queue.push({ type: 'atom', content: ')' });
|
|
152
|
-
this.insideFunctionDepth--;
|
|
153
|
-
}
|
|
154
|
-
else if (node.type === 'unaryFunc') {
|
|
155
|
-
const func_symbol = { type: 'symbol', content: node.content };
|
|
156
|
-
const arg0 = node.args[0];
|
|
157
|
-
if (node.content === '\\sqrt' && node.irregularData) {
|
|
158
|
-
func_symbol.content = 'root';
|
|
159
|
-
this.queue.push(func_symbol);
|
|
160
|
-
this.insideFunctionDepth++;
|
|
161
|
-
this.queue.push({ type: 'atom', content: '(' });
|
|
162
|
-
this.append(node.irregularData); // the number of times to take the root
|
|
163
|
-
this.queue.push({ type: 'atom', content: ',' });
|
|
164
|
-
this.append(arg0);
|
|
165
|
-
this.queue.push({ type: 'atom', content: ')' });
|
|
166
|
-
this.insideFunctionDepth--;
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
else if (node.content === '\\mathbb') {
|
|
170
|
-
const body = node.args[0];
|
|
171
|
-
if (body.type === 'symbol' && /^[A-Z]$/.test(body.content)) {
|
|
172
|
-
// \mathbb{R} -> RR
|
|
173
|
-
this.queue.push({ type: 'symbol', content: body.content + body.content });
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
// Fall through
|
|
177
|
-
}
|
|
178
|
-
else if (node.content === '\\operatorname') {
|
|
179
|
-
let body = node.args;
|
|
180
|
-
if (body.length === 1 && body[0].type == 'ordgroup') {
|
|
181
|
-
body = body[0].args;
|
|
182
|
-
}
|
|
183
|
-
const text = body.reduce((buff, n) => {
|
|
184
|
-
// Hope convertToken() will not throw an error
|
|
185
|
-
// If it does, the input is bad.
|
|
186
|
-
buff += convertToken(n.content);
|
|
187
|
-
return buff;
|
|
188
|
-
}, "");
|
|
189
|
-
if (this.preferTypstIntrinsic && TYPST_INTRINSIC_SYMBOLS.includes(text)) {
|
|
190
|
-
// e.g. we prefer just sech over op("sech")
|
|
191
|
-
this.queue.push({ type: 'symbol', content: text });
|
|
192
|
-
}
|
|
193
|
-
else if (text.startsWith('SyMb01-')) {
|
|
194
|
-
// special hacks made in parseTex()
|
|
195
|
-
this.queue.push({ type: 'symbol', content: '\\' + text.substring(7) });
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
this.queue.push({ type: 'symbol', content: 'op' });
|
|
199
|
-
this.queue.push({ type: 'atom', content: '(' });
|
|
200
|
-
this.queue.push({ type: 'text', content: text });
|
|
201
|
-
this.queue.push({ type: 'atom', content: ')' });
|
|
202
|
-
}
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
this.queue.push(func_symbol);
|
|
206
|
-
this.insideFunctionDepth++;
|
|
207
|
-
this.queue.push({ type: 'atom', content: '(' });
|
|
208
|
-
this.append(arg0);
|
|
209
|
-
this.queue.push({ type: 'atom', content: ')' });
|
|
210
|
-
this.insideFunctionDepth--;
|
|
211
|
-
}
|
|
212
|
-
else if (node.type === 'align') {
|
|
213
|
-
const matrix = node.irregularData;
|
|
214
|
-
matrix.forEach((row, i) => {
|
|
215
|
-
row.forEach((cell, j) => {
|
|
216
|
-
if (j > 0) {
|
|
217
|
-
this.queue.push({ type: 'atom', content: '&' });
|
|
218
|
-
}
|
|
219
|
-
this.append(cell);
|
|
220
|
-
});
|
|
221
|
-
if (i < matrix.length - 1) {
|
|
222
|
-
this.queue.push({ type: 'symbol', content: '\\\\' });
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
else if (node.type === 'matrix') {
|
|
227
|
-
const matrix = node.irregularData;
|
|
228
|
-
this.queue.push({ type: 'symbol', content: 'mat' });
|
|
229
|
-
this.insideFunctionDepth++;
|
|
230
|
-
this.queue.push({ type: 'atom', content: '(' });
|
|
231
|
-
this.queue.push({ type: 'symbol', content: 'delim: #none, ' });
|
|
232
|
-
matrix.forEach((row, i) => {
|
|
233
|
-
row.forEach((cell, j) => {
|
|
234
|
-
// There is a leading & in row
|
|
235
|
-
if (cell.type === 'ordgroup' && cell.args.length === 0) {
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
this.append(cell);
|
|
239
|
-
// cell.args!.forEach((n) => this.append(n));
|
|
240
|
-
if (j < row.length - 1) {
|
|
241
|
-
this.queue.push({ type: 'atom', content: ',' });
|
|
242
|
-
}
|
|
243
|
-
else {
|
|
244
|
-
if (i < matrix.length - 1) {
|
|
245
|
-
this.queue.push({ type: 'atom', content: ';' });
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
this.queue.push({ type: 'atom', content: ')' });
|
|
251
|
-
this.insideFunctionDepth--;
|
|
252
|
-
}
|
|
253
|
-
else if (node.type === 'unknownMacro') {
|
|
254
|
-
if (this.nonStrict) {
|
|
255
|
-
this.queue.push({ type: 'symbol', content: node.content });
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
throw new TypstWriterError(`Unknown macro: ${node.content}`, node);
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
else {
|
|
262
|
-
throw new TypstWriterError(`Unimplemented node type to append: ${node.type}`, node);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
flushQueue() {
|
|
266
|
-
this.queue.forEach((node) => {
|
|
267
|
-
let str = "";
|
|
268
|
-
switch (node.type) {
|
|
269
|
-
case 'atom':
|
|
270
|
-
str = node.content;
|
|
271
|
-
break;
|
|
272
|
-
case 'symbol':
|
|
273
|
-
str = convertToken(node.content);
|
|
274
|
-
break;
|
|
275
|
-
case 'text':
|
|
276
|
-
str = `"${node.content}"`;
|
|
277
|
-
break;
|
|
278
|
-
case 'softSpace':
|
|
279
|
-
this.needSpaceAfterSingleItemScript = true;
|
|
280
|
-
str = '';
|
|
281
|
-
break;
|
|
282
|
-
default:
|
|
283
|
-
throw new TypstWriterError(`Unexpected node type to stringify: ${node.type}`, node);
|
|
284
|
-
}
|
|
285
|
-
if (str !== '') {
|
|
286
|
-
this.writeBuffer(str);
|
|
287
|
-
}
|
|
288
|
-
});
|
|
289
|
-
this.queue = [];
|
|
290
|
-
}
|
|
291
|
-
appendWithBracketsIfNeeded(node) {
|
|
292
|
-
const is_single_atom = (node.type === 'atom');
|
|
293
|
-
const is_single_function = (node.type === 'unaryFunc' || node.type === 'binaryFunc' || node.type === 'leftright');
|
|
294
|
-
const is_single = ['atom', 'symbol', 'unaryFunc', 'binaryFunc', 'leftright'].includes(node.type);
|
|
295
|
-
if (is_single) {
|
|
296
|
-
this.append(node);
|
|
297
|
-
}
|
|
298
|
-
else {
|
|
299
|
-
this.queue.push({
|
|
300
|
-
type: 'atom',
|
|
301
|
-
content: '('
|
|
302
|
-
});
|
|
303
|
-
this.append(node);
|
|
304
|
-
this.queue.push({
|
|
305
|
-
type: 'atom',
|
|
306
|
-
content: ')'
|
|
307
|
-
});
|
|
308
|
-
}
|
|
309
|
-
return is_single;
|
|
310
|
-
}
|
|
311
|
-
finalize() {
|
|
312
|
-
this.flushQueue();
|
|
313
|
-
const smartFloorPass = function (input) {
|
|
314
|
-
// Use regex to replace all "⌊ xxx ⌋" with "floor(xxx)"
|
|
315
|
-
let res = input.replace(/⌊\s*(.*?)\s*⌋/g, "floor($1)");
|
|
316
|
-
// Typst disallow "floor()" with empty argument, so add am empty string inside if it's empty.
|
|
317
|
-
res = res.replace(/floor\(\)/g, 'floor("")');
|
|
318
|
-
return res;
|
|
319
|
-
};
|
|
320
|
-
const smartCeilPass = function (input) {
|
|
321
|
-
// Use regex to replace all "⌈ xxx ⌉" with "ceil(xxx)"
|
|
322
|
-
let res = input.replace(/⌈\s*(.*?)\s*⌉/g, "ceil($1)");
|
|
323
|
-
// Typst disallow "ceil()" with empty argument, so add an empty string inside if it's empty.
|
|
324
|
-
res = res.replace(/ceil\(\)/g, 'ceil("")');
|
|
325
|
-
return res;
|
|
326
|
-
};
|
|
327
|
-
this.buffer = smartFloorPass(this.buffer);
|
|
328
|
-
this.buffer = smartCeilPass(this.buffer);
|
|
329
|
-
return this.buffer;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
exports.TypstWriter = TypstWriter;
|
|
333
|
-
function convertToken(token) {
|
|
334
|
-
if (/^[a-zA-Z0-9]$/.test(token)) {
|
|
335
|
-
return token;
|
|
336
|
-
}
|
|
337
|
-
else if (token === '\\\\') {
|
|
338
|
-
return '\\\n';
|
|
339
|
-
}
|
|
340
|
-
else if (['\\$', '\\#', '\\&', '\\_'].includes(token)) {
|
|
341
|
-
return token;
|
|
342
|
-
}
|
|
343
|
-
else if (token.startsWith('\\')) {
|
|
344
|
-
const symbol = token.slice(1);
|
|
345
|
-
if (map_1.symbolMap.has(symbol)) {
|
|
346
|
-
return map_1.symbolMap.get(symbol);
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
349
|
-
// Fall back to the original macro.
|
|
350
|
-
// This works for \alpha, \beta, \gamma, etc.
|
|
351
|
-
// If this.nonStrict is true, this also works for all unknown macros.
|
|
352
|
-
return symbol;
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
return token;
|
|
356
|
-
}
|