@r4ai/typst-ast-node 0.2.1 → 0.3.0

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
@@ -1,6 +1,6 @@
1
1
  # typst-ast
2
2
 
3
- A WebAssembly library that parses [Typst](https://typst.app/) source code and returns its AST as a JSON-serializable object. Built with Rust and [`typst-syntax`](https://crates.io/crates/typst-syntax), distributed as npm packages via `wasm-pack`.
3
+ A WebAssembly library that parses [Typst](https://typst.app/) source code and returns its CST/AST as a JSON-serializable object. Built with Rust and [`typst-syntax`](https://crates.io/crates/typst-syntax), distributed as npm packages via `wasm-pack`.
4
4
 
5
5
  ## Packages
6
6
 
@@ -14,39 +14,50 @@ A WebAssembly library that parses [Typst](https://typst.app/) source code and re
14
14
  ### Browser
15
15
 
16
16
  ```ts
17
- import init, { parse } from "@r4ai/typst-ast-web";
17
+ import init, { parse, parseAst } from "@r4ai/typst-ast-web";
18
18
 
19
19
  await init();
20
20
 
21
- const result = parse("= Hello\n\nThis is *typst*.", { mode: "markup" });
22
- console.log(result);
21
+ // CST (Concrete Syntax Tree)
22
+ const cst = parse("= Hello\n\nThis is *typst*.", { mode: "markup" });
23
+ console.log(cst);
23
24
  // {
24
25
  // root: { kind: "Markup", range: [0, 25], children: [...] },
25
26
  // errors: []
26
27
  // }
28
+
29
+ // AST (Abstract Syntax Tree)
30
+ const ast = parseAst("= Hello\n\nThis is *typst*.", { mode: "markup" });
31
+ console.log(ast);
32
+ // {
33
+ // root: [
34
+ // { kind: "heading", ... },
35
+ // { kind: "parbreak", ... },
36
+ // ...
37
+ // ],
38
+ // errors: []
39
+ // }
27
40
  ```
28
41
 
29
42
  ### Node.js
30
43
 
31
44
  ```ts
32
- import { parse } from "@r4ai/typst-ast-node";
45
+ import { parse, parseAst } from "@r4ai/typst-ast-node";
33
46
 
34
- const result = parse("#let x = 1 + 2", { mode: "code" });
35
- console.log(result);
47
+ const cst = parse("#let x = 1 + 2", { mode: "code" });
48
+ const ast = parseAst("#let x = 1 + 2", { mode: "code" });
36
49
  ```
37
50
 
38
51
  ### API
39
52
 
40
- #### `parse(text, options?)`
41
-
42
- Parses Typst source text and returns the AST.
43
-
44
- **Parameters**
53
+ Both functions accept the same parameters:
45
54
 
46
55
  - `text: string` — Typst source code to parse
47
56
  - `options.mode?: "markup" | "code" | "math"` — Parse mode (default: `"markup"`)
48
57
 
49
- **Return value**
58
+ #### `parse(text, options?)`
59
+
60
+ Returns the CST (Concrete Syntax Tree) — a lossless syntax tree that preserves all tokens including whitespace and punctuation.
50
61
 
51
62
  ```ts
52
63
  interface ParseResult {
@@ -60,13 +71,31 @@ interface SyntaxNode {
60
71
  text?: string;
61
72
  children: SyntaxNode[];
62
73
  }
74
+ ```
63
75
 
64
- interface ParseError {
65
- message: string;
66
- range: [number, number];
76
+ #### `parseAst(text, options?)`
77
+
78
+ Returns the AST (Abstract Syntax Tree) — a typed, semantic tree where each node is a tagged union discriminated by `kind`. Unlike the CST, the AST extracts semantic information (e.g. heading depth, function callee, binary operator) into dedicated fields.
79
+
80
+ ```ts
81
+ interface ParseAstResult {
82
+ root: AstExpr[];
83
+ errors: ParseError[];
67
84
  }
85
+
86
+ // AstExpr is a discriminated union of 59 node types.
87
+ // Examples:
88
+ type AstExpr =
89
+ | { kind: "text"; range: [number, number] | null; text: string }
90
+ | { kind: "heading"; range: [number, number] | null; depth: number; body: AstExpr[] }
91
+ | { kind: "strong"; range: [number, number] | null; body: AstExpr[] }
92
+ | { kind: "funcCall"; range: [number, number] | null; callee: AstExpr; args: AstArg[] }
93
+ | { kind: "binary"; range: [number, number] | null; op: AstBinOp; lhs: AstExpr; rhs: AstExpr }
94
+ | // ... and 54 more variants
68
95
  ```
69
96
 
97
+ See [`src/types.ts`](./src/types.ts) for the full type definitions.
98
+
70
99
  ## Development
71
100
 
72
101
  ### Prerequisites
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "collaborators": [
4
4
  "r4ai"
5
5
  ],
6
- "version": "0.2.1",
6
+ "version": "0.3.0",
7
7
  "license": "MIT",
8
8
  "repository": {
9
9
  "type": "git",
package/typst_ast.d.ts CHANGED
@@ -28,6 +28,574 @@ text: string,
28
28
  options?: ParseOptions,
29
29
  ): ParseResult;
30
30
 
31
+ // --- AST types ---
32
+
33
+ export type Range = [number, number] | null;
34
+
35
+ export type AstExpr =
36
+ | AstText
37
+ | AstSpace
38
+ | AstLinebreak
39
+ | AstParbreak
40
+ | AstEscape
41
+ | AstShorthand
42
+ | AstSmartQuote
43
+ | AstStrong
44
+ | AstEmph
45
+ | AstRaw
46
+ | AstLink
47
+ | AstLabel
48
+ | AstRef
49
+ | AstHeading
50
+ | AstListItem
51
+ | AstEnumItem
52
+ | AstTermItem
53
+ | AstEquation
54
+ | AstMath
55
+ | AstMathText
56
+ | AstMathIdent
57
+ | AstMathShorthand
58
+ | AstMathAlignPoint
59
+ | AstMathDelimited
60
+ | AstMathAttach
61
+ | AstMathPrimes
62
+ | AstMathFrac
63
+ | AstMathRoot
64
+ | AstIdent
65
+ | AstNone
66
+ | AstAuto
67
+ | AstBool
68
+ | AstInt
69
+ | AstFloat
70
+ | AstNumeric
71
+ | AstStr
72
+ | AstCodeBlock
73
+ | AstContentBlock
74
+ | AstParenthesized
75
+ | AstArray
76
+ | AstDict
77
+ | AstUnary
78
+ | AstBinary
79
+ | AstFieldAccess
80
+ | AstFuncCall
81
+ | AstClosure
82
+ | AstLetBinding
83
+ | AstDestructAssignment
84
+ | AstSetRule
85
+ | AstShowRule
86
+ | AstContextual
87
+ | AstConditional
88
+ | AstWhileLoop
89
+ | AstForLoop
90
+ | AstModuleImport
91
+ | AstModuleInclude
92
+ | AstLoopBreak
93
+ | AstLoopContinue
94
+ | AstFuncReturn;
95
+
96
+ // Markup
97
+
98
+ export interface AstText {
99
+ kind: "text";
100
+ range: Range;
101
+ text: string;
102
+ }
103
+
104
+ export interface AstSpace {
105
+ kind: "space";
106
+ range: Range;
107
+ }
108
+
109
+ export interface AstLinebreak {
110
+ kind: "linebreak";
111
+ range: Range;
112
+ }
113
+
114
+ export interface AstParbreak {
115
+ kind: "parbreak";
116
+ range: Range;
117
+ }
118
+
119
+ export interface AstEscape {
120
+ kind: "escape";
121
+ range: Range;
122
+ character: string;
123
+ }
124
+
125
+ export interface AstShorthand {
126
+ kind: "shorthand";
127
+ range: Range;
128
+ character: string;
129
+ }
130
+
131
+ export interface AstSmartQuote {
132
+ kind: "smartQuote";
133
+ range: Range;
134
+ double: boolean;
135
+ }
136
+
137
+ export interface AstStrong {
138
+ kind: "strong";
139
+ range: Range;
140
+ body: AstExpr[];
141
+ }
142
+
143
+ export interface AstEmph {
144
+ kind: "emph";
145
+ range: Range;
146
+ body: AstExpr[];
147
+ }
148
+
149
+ export interface AstRaw {
150
+ kind: "raw";
151
+ range: Range;
152
+ lines: string[];
153
+ lang: string | null;
154
+ block: boolean;
155
+ }
156
+
157
+ export interface AstLink {
158
+ kind: "link";
159
+ range: Range;
160
+ url: string;
161
+ }
162
+
163
+ export interface AstLabel {
164
+ kind: "label";
165
+ range: Range;
166
+ name: string;
167
+ }
168
+
169
+ export interface AstRef {
170
+ kind: "ref";
171
+ range: Range;
172
+ target: string;
173
+ supplement: AstExpr[] | null;
174
+ }
175
+
176
+ export interface AstHeading {
177
+ kind: "heading";
178
+ range: Range;
179
+ depth: number;
180
+ body: AstExpr[];
181
+ }
182
+
183
+ export interface AstListItem {
184
+ kind: "listItem";
185
+ range: Range;
186
+ body: AstExpr[];
187
+ }
188
+
189
+ export interface AstEnumItem {
190
+ kind: "enumItem";
191
+ range: Range;
192
+ number: number | null;
193
+ body: AstExpr[];
194
+ }
195
+
196
+ export interface AstTermItem {
197
+ kind: "termItem";
198
+ range: Range;
199
+ term: AstExpr[];
200
+ description: AstExpr[];
201
+ }
202
+
203
+ export interface AstEquation {
204
+ kind: "equation";
205
+ range: Range;
206
+ body: AstExpr[];
207
+ block: boolean;
208
+ }
209
+
210
+ // Math
211
+
212
+ export interface AstMath {
213
+ kind: "math";
214
+ range: Range;
215
+ body: AstExpr[];
216
+ }
217
+
218
+ export type AstMathTextKind =
219
+ | { kind: "character"; value: string }
220
+ | { kind: "number"; value: string };
221
+
222
+ export interface AstMathText {
223
+ kind: "mathText";
224
+ range: Range;
225
+ text: AstMathTextKind;
226
+ }
227
+
228
+ export interface AstMathIdent {
229
+ kind: "mathIdent";
230
+ range: Range;
231
+ name: string;
232
+ }
233
+
234
+ export interface AstMathShorthand {
235
+ kind: "mathShorthand";
236
+ range: Range;
237
+ character: string;
238
+ }
239
+
240
+ export interface AstMathAlignPoint {
241
+ kind: "mathAlignPoint";
242
+ range: Range;
243
+ }
244
+
245
+ export interface AstMathDelimited {
246
+ kind: "mathDelimited";
247
+ range: Range;
248
+ open: AstExpr;
249
+ body: AstExpr[];
250
+ close: AstExpr;
251
+ }
252
+
253
+ export interface AstMathAttach {
254
+ kind: "mathAttach";
255
+ range: Range;
256
+ base: AstExpr;
257
+ bottom: AstExpr | null;
258
+ top: AstExpr | null;
259
+ primes: number | null;
260
+ }
261
+
262
+ export interface AstMathPrimes {
263
+ kind: "mathPrimes";
264
+ range: Range;
265
+ count: number;
266
+ }
267
+
268
+ export interface AstMathFrac {
269
+ kind: "mathFrac";
270
+ range: Range;
271
+ num: AstExpr;
272
+ denom: AstExpr;
273
+ }
274
+
275
+ export interface AstMathRoot {
276
+ kind: "mathRoot";
277
+ range: Range;
278
+ index: number | null;
279
+ radicand: AstExpr;
280
+ }
281
+
282
+ // Literals
283
+
284
+ export interface AstIdent {
285
+ kind: "ident";
286
+ range: Range;
287
+ name: string;
288
+ }
289
+
290
+ export interface AstNone {
291
+ kind: "none";
292
+ range: Range;
293
+ }
294
+
295
+ export interface AstAuto {
296
+ kind: "auto";
297
+ range: Range;
298
+ }
299
+
300
+ export interface AstBool {
301
+ kind: "bool";
302
+ range: Range;
303
+ value: boolean;
304
+ }
305
+
306
+ export interface AstInt {
307
+ kind: "int";
308
+ range: Range;
309
+ value: number;
310
+ }
311
+
312
+ export interface AstFloat {
313
+ kind: "float";
314
+ range: Range;
315
+ value: number;
316
+ }
317
+
318
+ export type AstUnit =
319
+ | "pt"
320
+ | "mm"
321
+ | "cm"
322
+ | "in"
323
+ | "rad"
324
+ | "deg"
325
+ | "em"
326
+ | "fr"
327
+ | "percent";
328
+
329
+ export interface AstNumeric {
330
+ kind: "numeric";
331
+ range: Range;
332
+ value: number;
333
+ unit: AstUnit;
334
+ }
335
+
336
+ export interface AstStr {
337
+ kind: "str";
338
+ range: Range;
339
+ value: string;
340
+ }
341
+
342
+ // Code structures
343
+
344
+ export interface AstCodeBlock {
345
+ kind: "codeBlock";
346
+ range: Range;
347
+ body: AstExpr[];
348
+ }
349
+
350
+ export interface AstContentBlock {
351
+ kind: "contentBlock";
352
+ range: Range;
353
+ body: AstExpr[];
354
+ }
355
+
356
+ export interface AstParenthesized {
357
+ kind: "parenthesized";
358
+ range: Range;
359
+ expr: AstExpr;
360
+ }
361
+
362
+ export type AstArrayItem =
363
+ | { kind: "pos"; expr: AstExpr }
364
+ | { kind: "spread"; expr: AstExpr; sinkIdent: string | null };
365
+
366
+ export interface AstArray {
367
+ kind: "array";
368
+ range: Range;
369
+ items: AstArrayItem[];
370
+ }
371
+
372
+ export type AstDictItem =
373
+ | { kind: "named"; name: string; expr: AstExpr }
374
+ | { kind: "keyed"; key: AstExpr; expr: AstExpr }
375
+ | { kind: "spread"; expr: AstExpr; sinkIdent: string | null };
376
+
377
+ export interface AstDict {
378
+ kind: "dict";
379
+ range: Range;
380
+ items: AstDictItem[];
381
+ }
382
+
383
+ // Operations
384
+
385
+ export type AstUnOp = "pos" | "neg" | "not";
386
+
387
+ export type AstBinOp =
388
+ | "add"
389
+ | "sub"
390
+ | "mul"
391
+ | "div"
392
+ | "and"
393
+ | "or"
394
+ | "eq"
395
+ | "neq"
396
+ | "lt"
397
+ | "leq"
398
+ | "gt"
399
+ | "geq"
400
+ | "assign"
401
+ | "in"
402
+ | "notIn"
403
+ | "addAssign"
404
+ | "subAssign"
405
+ | "mulAssign"
406
+ | "divAssign";
407
+
408
+ export interface AstUnary {
409
+ kind: "unary";
410
+ range: Range;
411
+ op: AstUnOp;
412
+ expr: AstExpr;
413
+ }
414
+
415
+ export interface AstBinary {
416
+ kind: "binary";
417
+ range: Range;
418
+ op: AstBinOp;
419
+ lhs: AstExpr;
420
+ rhs: AstExpr;
421
+ }
422
+
423
+ export interface AstFieldAccess {
424
+ kind: "fieldAccess";
425
+ range: Range;
426
+ target: AstExpr;
427
+ field: string;
428
+ }
429
+
430
+ export type AstArg =
431
+ | { kind: "pos"; expr: AstExpr }
432
+ | { kind: "named"; name: string; expr: AstExpr }
433
+ | { kind: "spread"; expr: AstExpr; sinkIdent: string | null };
434
+
435
+ export interface AstFuncCall {
436
+ kind: "funcCall";
437
+ range: Range;
438
+ callee: AstExpr;
439
+ args: AstArg[];
440
+ }
441
+
442
+ export type AstParam =
443
+ | { kind: "pos"; pattern: AstPattern }
444
+ | { kind: "named"; name: string; expr: AstExpr }
445
+ | { kind: "spread"; sinkIdent: string | null; sinkExpr: AstExpr | null };
446
+
447
+ export interface AstClosure {
448
+ kind: "closure";
449
+ range: Range;
450
+ name: string | null;
451
+ params: AstParam[];
452
+ body: AstExpr;
453
+ }
454
+
455
+ // Patterns
456
+
457
+ export type AstDestructuringItem =
458
+ | { kind: "pattern"; pattern: AstPattern }
459
+ | { kind: "named"; name: string; pattern: AstPattern }
460
+ | { kind: "spread"; sinkIdent: string | null };
461
+
462
+ export type AstPattern =
463
+ | { kind: "normal"; expr: AstExpr }
464
+ | { kind: "placeholder"; range: Range }
465
+ | { kind: "parenthesized"; expr: AstExpr }
466
+ | {
467
+ kind: "destructuring";
468
+ range: Range;
469
+ items: AstDestructuringItem[];
470
+ };
471
+
472
+ // Bindings
473
+
474
+ export type AstLetBindingKind =
475
+ | { kind: "normal"; pattern: AstPattern }
476
+ | { kind: "closure"; name: string };
477
+
478
+ export interface AstLetBinding {
479
+ kind: "letBinding";
480
+ range: Range;
481
+ bindingKind: AstLetBindingKind;
482
+ init: AstExpr | null;
483
+ }
484
+
485
+ export interface AstDestructAssignment {
486
+ kind: "destructAssignment";
487
+ range: Range;
488
+ pattern: AstPattern;
489
+ value: AstExpr;
490
+ }
491
+
492
+ // Rules
493
+
494
+ export interface AstSetRule {
495
+ kind: "setRule";
496
+ range: Range;
497
+ target: AstExpr;
498
+ args: AstArg[];
499
+ condition: AstExpr | null;
500
+ }
501
+
502
+ export interface AstShowRule {
503
+ kind: "showRule";
504
+ range: Range;
505
+ selector: AstExpr | null;
506
+ transform: AstExpr;
507
+ }
508
+
509
+ export interface AstContextual {
510
+ kind: "contextual";
511
+ range: Range;
512
+ body: AstExpr;
513
+ }
514
+
515
+ // Control flow
516
+
517
+ export interface AstConditional {
518
+ kind: "conditional";
519
+ range: Range;
520
+ condition: AstExpr;
521
+ ifBody: AstExpr;
522
+ elseBody: AstExpr | null;
523
+ }
524
+
525
+ export interface AstWhileLoop {
526
+ kind: "whileLoop";
527
+ range: Range;
528
+ condition: AstExpr;
529
+ body: AstExpr;
530
+ }
531
+
532
+ export interface AstForLoop {
533
+ kind: "forLoop";
534
+ range: Range;
535
+ pattern: AstPattern;
536
+ iterable: AstExpr;
537
+ body: AstExpr;
538
+ }
539
+
540
+ // Module
541
+
542
+ export type AstImportItem =
543
+ | { kind: "simple"; path: string[]; name: string }
544
+ | {
545
+ kind: "renamed";
546
+ path: string[];
547
+ originalName: string;
548
+ newName: string;
549
+ };
550
+
551
+ export type AstImports =
552
+ | { kind: "wildcard" }
553
+ | { kind: "items"; items: AstImportItem[] };
554
+
555
+ export interface AstModuleImport {
556
+ kind: "moduleImport";
557
+ range: Range;
558
+ source: AstExpr;
559
+ newName: string | null;
560
+ imports: AstImports | null;
561
+ }
562
+
563
+ export interface AstModuleInclude {
564
+ kind: "moduleInclude";
565
+ range: Range;
566
+ source: AstExpr;
567
+ }
568
+
569
+ // Jump
570
+
571
+ export interface AstLoopBreak {
572
+ kind: "loopBreak";
573
+ range: Range;
574
+ }
575
+
576
+ export interface AstLoopContinue {
577
+ kind: "loopContinue";
578
+ range: Range;
579
+ }
580
+
581
+ export interface AstFuncReturn {
582
+ kind: "funcReturn";
583
+ range: Range;
584
+ body: AstExpr | null;
585
+ }
586
+
587
+ // Parse AST result
588
+
589
+ export interface ParseAstResult {
590
+ root: AstExpr[];
591
+ errors: ParseError[];
592
+ }
593
+
594
+ export declare function parseAst(
595
+ text: string,
596
+ options?: ParseOptions,
597
+ ): ParseAstResult;
598
+
31
599
 
32
600
 
33
601
  export function start(): void;
package/typst_ast.js CHANGED
@@ -16,6 +16,22 @@ function parse(text, options) {
16
16
  }
17
17
  exports.parse = parse;
18
18
 
19
+ /**
20
+ * @param {string} text
21
+ * @param {any} options
22
+ * @returns {any}
23
+ */
24
+ function parseAst(text, options) {
25
+ const ptr0 = passStringToWasm0(text, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
26
+ const len0 = WASM_VECTOR_LEN;
27
+ const ret = wasm.parseAst(ptr0, len0, options);
28
+ if (ret[2]) {
29
+ throw takeFromExternrefTable0(ret[1]);
30
+ }
31
+ return takeFromExternrefTable0(ret[0]);
32
+ }
33
+ exports.parseAst = parseAst;
34
+
19
35
  function start() {
20
36
  wasm.start();
21
37
  }
@@ -93,6 +109,10 @@ function __wbg_get_imports() {
93
109
  const ret = Object.entries(arg0);
94
110
  return ret;
95
111
  },
112
+ __wbg_fromCodePoint_22365db7b7d6ac39: function() { return handleError(function (arg0) {
113
+ const ret = String.fromCodePoint(arg0 >>> 0);
114
+ return ret;
115
+ }, arguments); },
96
116
  __wbg_get_9b94d73e6221f75c: function(arg0, arg1) {
97
117
  const ret = arg0[arg1 >>> 0];
98
118
  return ret;
@@ -155,12 +175,17 @@ function __wbg_get_imports() {
155
175
  const ret = arg0;
156
176
  return ret;
157
177
  },
158
- __wbindgen_cast_0000000000000002: function(arg0, arg1) {
178
+ __wbindgen_cast_0000000000000002: function(arg0) {
179
+ // Cast intrinsic for `I64 -> Externref`.
180
+ const ret = arg0;
181
+ return ret;
182
+ },
183
+ __wbindgen_cast_0000000000000003: function(arg0, arg1) {
159
184
  // Cast intrinsic for `Ref(String) -> Externref`.
160
185
  const ret = getStringFromWasm0(arg0, arg1);
161
186
  return ret;
162
187
  },
163
- __wbindgen_cast_0000000000000003: function(arg0) {
188
+ __wbindgen_cast_0000000000000004: function(arg0) {
164
189
  // Cast intrinsic for `U64 -> Externref`.
165
190
  const ret = BigInt.asUintN(64, arg0);
166
191
  return ret;
@@ -181,6 +206,12 @@ function __wbg_get_imports() {
181
206
  };
182
207
  }
183
208
 
209
+ function addToExternrefTable0(obj) {
210
+ const idx = wasm.__externref_table_alloc();
211
+ wasm.__wbindgen_externrefs.set(idx, obj);
212
+ return idx;
213
+ }
214
+
184
215
  function debugString(val) {
185
216
  // primitive types
186
217
  const type = typeof val;
@@ -272,6 +303,15 @@ function getUint8ArrayMemory0() {
272
303
  return cachedUint8ArrayMemory0;
273
304
  }
274
305
 
306
+ function handleError(f, args) {
307
+ try {
308
+ return f.apply(this, args);
309
+ } catch (e) {
310
+ const idx = addToExternrefTable0(e);
311
+ wasm.__wbindgen_exn_store(idx);
312
+ }
313
+ }
314
+
275
315
  function isLikeNone(x) {
276
316
  return x === undefined || x === null;
277
317
  }
package/typst_ast_bg.wasm CHANGED
Binary file