tex2typst 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +451 -241
- package/dist/tex-parser.d.ts +1 -0
- package/dist/tex2typst.min.js +20 -19
- package/dist/types.d.ts +8 -4
- package/dist/typst-shorthands.d.ts +3 -0
- package/dist/typst-writer.d.ts +9 -3
- package/docs/api-reference.md +64 -0
- package/package.json +5 -5
- package/src/convert.ts +43 -6
- package/src/index.ts +12 -15
- package/src/map.ts +13 -36
- package/src/tex-parser.ts +33 -44
- package/src/tex-writer.ts +0 -1
- package/src/types.ts +10 -2
- package/src/typst-parser.ts +24 -5
- package/src/typst-shorthands.ts +51 -0
- package/src/typst-writer.ts +52 -25
- package/tools/make-shorthand-map.py +33 -0
- package/tools/make-symbol-map.py +4 -3
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const shorthandMap = new Map<string, string>([
|
|
2
|
+
['arrow.l.r.double.long', '<==>'],
|
|
3
|
+
['arrow.l.r.long', '<-->'],
|
|
4
|
+
['arrow.r.bar', '|->'],
|
|
5
|
+
['arrow.r.double.bar', '|=>'],
|
|
6
|
+
['arrow.r.double.long', '==>'],
|
|
7
|
+
['arrow.r.long', '-->'],
|
|
8
|
+
['arrow.r.long.squiggly', '~~>'],
|
|
9
|
+
['arrow.r.tail', '>->'],
|
|
10
|
+
['arrow.r.twohead', '->>'],
|
|
11
|
+
['arrow.l.double.long', '<=='],
|
|
12
|
+
['arrow.l.long', '<--'],
|
|
13
|
+
['arrow.l.long.squiggly', '<~~'],
|
|
14
|
+
['arrow.l.tail', '<-<'],
|
|
15
|
+
['arrow.l.twohead', '<<-'],
|
|
16
|
+
['arrow.l.r', '<->'],
|
|
17
|
+
['arrow.l.r.double', '<=>'],
|
|
18
|
+
['colon.double.eq', '::='],
|
|
19
|
+
['dots.h', '...'],
|
|
20
|
+
['gt.triple', '>>>'],
|
|
21
|
+
['lt.triple', '<<<'],
|
|
22
|
+
['arrow.r', '->'],
|
|
23
|
+
['arrow.r.double', '=>'],
|
|
24
|
+
['arrow.r.squiggly', '~>'],
|
|
25
|
+
['arrow.l', '<-'],
|
|
26
|
+
['arrow.l.squiggly', '<~'],
|
|
27
|
+
['bar.v.double', '||'],
|
|
28
|
+
['bracket.l.double', '[|'],
|
|
29
|
+
['bracket.r.double', '|]'],
|
|
30
|
+
['colon.eq', ':='],
|
|
31
|
+
['eq.colon', '=:'],
|
|
32
|
+
['eq.not', '!='],
|
|
33
|
+
['gt.double', '>>'],
|
|
34
|
+
['gt.eq', '>='],
|
|
35
|
+
['lt.double', '<<'],
|
|
36
|
+
['lt.eq', '<='],
|
|
37
|
+
['ast.op', '*'],
|
|
38
|
+
['minus', '-'],
|
|
39
|
+
['tilde.op', '~'],
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
const reverseShorthandMap = new Map<string, string>();
|
|
44
|
+
for (const [key, value] of shorthandMap.entries()) {
|
|
45
|
+
// filter out single character values ('-', '~', '*')
|
|
46
|
+
if(value.length > 1) {
|
|
47
|
+
reverseShorthandMap.set(value, key);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { shorthandMap, reverseShorthandMap };
|
package/src/typst-writer.ts
CHANGED
|
@@ -1,29 +1,33 @@
|
|
|
1
|
-
import { TexNode, TypstNode, TypstSupsubData, TypstToken, TypstTokenType } from "./types";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
// symbols that are supported by Typst but not by KaTeX
|
|
5
|
-
export const TYPST_INTRINSIC_SYMBOLS = [
|
|
6
|
-
'dim',
|
|
7
|
-
'id',
|
|
8
|
-
'im',
|
|
9
|
-
'mod',
|
|
10
|
-
'Pr',
|
|
11
|
-
'sech',
|
|
12
|
-
'csch',
|
|
13
|
-
// 'sgn
|
|
14
|
-
];
|
|
15
|
-
|
|
1
|
+
import { TexNode, TypstNode, TypstPrimitiveValue, TypstSupsubData, TypstToken, TypstTokenType } from "./types";
|
|
2
|
+
import { shorthandMap } from "./typst-shorthands";
|
|
16
3
|
|
|
17
4
|
function is_delimiter(c: TypstNode): boolean {
|
|
18
5
|
return c.type === 'atom' && ['(', ')', '[', ']', '{', '}', '|', '⌊', '⌋', '⌈', '⌉'].includes(c.content);
|
|
19
6
|
}
|
|
20
7
|
|
|
21
|
-
|
|
22
8
|
const TYPST_LEFT_PARENTHESIS: TypstToken = new TypstToken(TypstTokenType.ELEMENT, '(');
|
|
23
9
|
const TYPST_RIGHT_PARENTHESIS: TypstToken = new TypstToken(TypstTokenType.ELEMENT, ')');
|
|
24
10
|
const TYPST_COMMA: TypstToken = new TypstToken(TypstTokenType.ELEMENT, ',');
|
|
25
11
|
const TYPST_NEWLINE: TypstToken = new TypstToken(TypstTokenType.SYMBOL, '\n');
|
|
26
12
|
|
|
13
|
+
function typst_primitive_to_string(value: TypstPrimitiveValue) {
|
|
14
|
+
switch (typeof value) {
|
|
15
|
+
case 'string':
|
|
16
|
+
return `"${value}"`;
|
|
17
|
+
case 'number':
|
|
18
|
+
return (value as number).toString();
|
|
19
|
+
case 'boolean':
|
|
20
|
+
return (value as boolean) ? '#true' : '#false';
|
|
21
|
+
default:
|
|
22
|
+
if (value === null) {
|
|
23
|
+
return '#none';
|
|
24
|
+
} else if (value instanceof TypstToken) {
|
|
25
|
+
return value.toString();
|
|
26
|
+
}
|
|
27
|
+
throw new TypstWriterError(`Invalid primitive value: ${value}`, value);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
export class TypstWriterError extends Error {
|
|
28
32
|
node: TexNode | TypstNode | TypstToken;
|
|
29
33
|
|
|
@@ -34,20 +38,29 @@ export class TypstWriterError extends Error {
|
|
|
34
38
|
}
|
|
35
39
|
}
|
|
36
40
|
|
|
41
|
+
export interface TypstWriterOptions {
|
|
42
|
+
nonStrict: boolean;
|
|
43
|
+
preferShorthands: boolean;
|
|
44
|
+
keepSpaces: boolean;
|
|
45
|
+
inftyToOo: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
37
48
|
export class TypstWriter {
|
|
38
49
|
private nonStrict: boolean;
|
|
39
|
-
private
|
|
50
|
+
private preferShorthands: boolean;
|
|
40
51
|
private keepSpaces: boolean;
|
|
52
|
+
private inftyToOo: boolean;
|
|
41
53
|
|
|
42
54
|
protected buffer: string = "";
|
|
43
55
|
protected queue: TypstToken[] = [];
|
|
44
56
|
|
|
45
57
|
private insideFunctionDepth = 0;
|
|
46
58
|
|
|
47
|
-
constructor(
|
|
48
|
-
this.nonStrict = nonStrict;
|
|
49
|
-
this.
|
|
50
|
-
this.keepSpaces = keepSpaces;
|
|
59
|
+
constructor(opt: TypstWriterOptions) {
|
|
60
|
+
this.nonStrict = opt.nonStrict;
|
|
61
|
+
this.preferShorthands = opt.preferShorthands;
|
|
62
|
+
this.keepSpaces = opt.keepSpaces;
|
|
63
|
+
this.inftyToOo = opt.inftyToOo;
|
|
51
64
|
}
|
|
52
65
|
|
|
53
66
|
|
|
@@ -58,6 +71,8 @@ export class TypstWriter {
|
|
|
58
71
|
return;
|
|
59
72
|
}
|
|
60
73
|
|
|
74
|
+
// TODO: "C \frac{xy}{z}" should translate to "C (x y)/z" instead of "C(x y)/z"
|
|
75
|
+
|
|
61
76
|
let no_need_space = false;
|
|
62
77
|
// putting the first token in clause
|
|
63
78
|
no_need_space ||= /[\(\[\|]$/.test(this.buffer) && /^\w/.test(str);
|
|
@@ -105,9 +120,19 @@ export class TypstWriter {
|
|
|
105
120
|
}
|
|
106
121
|
break;
|
|
107
122
|
}
|
|
108
|
-
case 'symbol':
|
|
109
|
-
|
|
123
|
+
case 'symbol': {
|
|
124
|
+
let content = node.content;
|
|
125
|
+
if(this.preferShorthands) {
|
|
126
|
+
if (shorthandMap.has(content)) {
|
|
127
|
+
content = shorthandMap.get(content)!;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (this.inftyToOo && content === 'infinity') {
|
|
131
|
+
content = 'oo';
|
|
132
|
+
}
|
|
133
|
+
this.queue.push(new TypstToken(TypstTokenType.SYMBOL, content));
|
|
110
134
|
break;
|
|
135
|
+
}
|
|
111
136
|
case 'text':
|
|
112
137
|
this.queue.push(new TypstToken(TypstTokenType.TEXT, node.content));
|
|
113
138
|
break;
|
|
@@ -172,7 +197,8 @@ export class TypstWriter {
|
|
|
172
197
|
}
|
|
173
198
|
if (node.options) {
|
|
174
199
|
for (const [key, value] of Object.entries(node.options)) {
|
|
175
|
-
|
|
200
|
+
const value_str = typst_primitive_to_string(value);
|
|
201
|
+
this.queue.push(new TypstToken(TypstTokenType.SYMBOL, `, ${key}: ${value_str}`));
|
|
176
202
|
}
|
|
177
203
|
}
|
|
178
204
|
this.queue.push(TYPST_RIGHT_PARENTHESIS);
|
|
@@ -220,7 +246,8 @@ export class TypstWriter {
|
|
|
220
246
|
this.queue.push(TYPST_LEFT_PARENTHESIS);
|
|
221
247
|
if (node.options) {
|
|
222
248
|
for (const [key, value] of Object.entries(node.options)) {
|
|
223
|
-
|
|
249
|
+
const value_str = typst_primitive_to_string(value);
|
|
250
|
+
this.queue.push(new TypstToken(TypstTokenType.SYMBOL, `${key}: ${value_str}, `));
|
|
224
251
|
}
|
|
225
252
|
}
|
|
226
253
|
matrix.forEach((row, i) => {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import urllib.request
|
|
2
|
+
import html
|
|
3
|
+
from bs4 import BeautifulSoup
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
if __name__ == '__main__':
|
|
7
|
+
shorthand_map = []
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
url = "https://typst.app/docs/reference/symbols/"
|
|
11
|
+
with urllib.request.urlopen(url) as response:
|
|
12
|
+
html_text = response.read().decode('utf-8')
|
|
13
|
+
|
|
14
|
+
soup = BeautifulSoup(html_text, 'html.parser')
|
|
15
|
+
|
|
16
|
+
# <ul class="symbol-grid">
|
|
17
|
+
ul_list = soup.find_all('ul', class_='symbol-grid')
|
|
18
|
+
# ul_shorthands_markup = ul_list[0]
|
|
19
|
+
ul_shorthands_math = ul_list[1]
|
|
20
|
+
|
|
21
|
+
li_list = ul_shorthands_math.find_all('li')
|
|
22
|
+
for li in li_list:
|
|
23
|
+
# e.g. <li id="symbol-arrow.r" data-math-shorthand="->"><button>...</button></li>
|
|
24
|
+
# ==> typst = "arrow.r"
|
|
25
|
+
# ==> shorthand = "->"
|
|
26
|
+
typst = li['id'][7:]
|
|
27
|
+
shorthand = html.unescape(li['data-math-shorthand'])
|
|
28
|
+
shorthand_map.append((typst, shorthand))
|
|
29
|
+
|
|
30
|
+
# Sort by length of shorthand, order from longest to shortest
|
|
31
|
+
shorthand_map.sort(key=lambda x: len(x[1]), reverse=True)
|
|
32
|
+
for typst, shorthand in shorthand_map:
|
|
33
|
+
print(f"['{typst}', '{shorthand}'],")
|
package/tools/make-symbol-map.py
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import urllib.request
|
|
2
2
|
from bs4 import BeautifulSoup
|
|
3
3
|
|
|
4
|
-
|
|
5
4
|
if __name__ == '__main__':
|
|
6
5
|
symbol_map = {}
|
|
7
6
|
|
|
8
7
|
url = "https://typst.app/docs/reference/symbols/sym/"
|
|
9
|
-
|
|
8
|
+
with urllib.request.urlopen(url) as response:
|
|
9
|
+
html_text = response.read().decode('utf-8')
|
|
10
|
+
|
|
10
11
|
soup = BeautifulSoup(html_text, 'html.parser')
|
|
11
12
|
# <ul class="symbol-grid">
|
|
12
13
|
ul = soup.find('ul', class_='symbol-grid')
|