@nova-lang/cli 0.1.1 → 0.2.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/lib/parser.js ADDED
@@ -0,0 +1,1037 @@
1
+ const { TokenType, NodeTag, makeNode } = require("./types");
2
+
3
+ function parse(tokens) {
4
+ const pool = new StringPool();
5
+ const p = new Parser(tokens, pool);
6
+ return p.parse();
7
+ }
8
+
9
+ class ParseError extends Error {
10
+ constructor(msg, tok) {
11
+ super(`ParseError: ${msg} at line ${tok.line}, col ${tok.col}`);
12
+ this.name = "ParseError";
13
+ }
14
+ }
15
+
16
+ class StringPool {
17
+ constructor() {
18
+ this._map = new Map();
19
+ this._strings = [];
20
+ }
21
+
22
+ dup(s) {
23
+ if (this._map.has(s)) return this._map.get(s);
24
+ this._map.set(s, s);
25
+ this._strings.push(s);
26
+ return s;
27
+ }
28
+ }
29
+
30
+ class Parser {
31
+ constructor(tokens, pool) {
32
+ this.tokens = tokens;
33
+ this.pos = 0;
34
+ this.pool = pool;
35
+ }
36
+
37
+ peek(offset = 0) {
38
+ const idx = this.pos + offset;
39
+ return idx < this.tokens.length ? this.tokens[idx] : this.tokens[this.tokens.length - 1];
40
+ }
41
+
42
+ advance() {
43
+ return this.tokens[this.pos++];
44
+ }
45
+
46
+ expect(type) {
47
+ const tok = this.peek();
48
+ if (tok.type !== type) {
49
+ throw new ParseError(
50
+ `Expected ${type}, got ${tok.type} ('${tok.value}')`,
51
+ tok
52
+ );
53
+ }
54
+ return this.advance();
55
+ }
56
+
57
+ maybe(type) {
58
+ if (this.peek().type === type) return this.advance();
59
+ return null;
60
+ }
61
+
62
+ skipNewlines() {
63
+ while (this.peek().type === TokenType.newline) this.advance();
64
+ }
65
+
66
+ skipIndentDedent() {
67
+ while (this.peek().type === TokenType.indent || this.peek().type === TokenType.dedent) this.advance();
68
+ }
69
+
70
+ skipToRbrace() {
71
+ let depth = 1;
72
+ while (depth > 0 && this.peek().type !== TokenType.eof) {
73
+ const t = this.peek().type;
74
+ if (t === TokenType.lbrace) depth++;
75
+ else if (t === TokenType.rbrace) depth--;
76
+ if (depth > 0) this.advance();
77
+ }
78
+ }
79
+
80
+ parse() {
81
+ const children = [];
82
+ const meta = {};
83
+
84
+ while (this.peek().type !== TokenType.eof) {
85
+ const node = this.parseTopLevel();
86
+ if (node) {
87
+ if (node.tag === NodeTag.meta_block) {
88
+ Object.assign(meta, node.data.pairs);
89
+ } else {
90
+ children.push(node);
91
+ }
92
+ }
93
+ this.skipNewlines();
94
+ }
95
+
96
+ return makeNode(NodeTag.document, {
97
+ children,
98
+ meta,
99
+ });
100
+ }
101
+
102
+ parseTopLevel() {
103
+ this.skipNewlines();
104
+ this.skipIndentDedent();
105
+ const tok = this.peek();
106
+ switch (tok.type) {
107
+ case TokenType.at:
108
+ return this.parseBlock();
109
+ case TokenType.text: {
110
+ this.advance();
111
+ return makeNode(NodeTag.text, { value: tok.value });
112
+ }
113
+ case TokenType.dash:
114
+ case TokenType.star:
115
+ return this.parseList(false);
116
+ case TokenType.plus:
117
+ return this.parseList(true);
118
+ case TokenType.lbracket: {
119
+ this.advance();
120
+ return this.parseTableData();
121
+ }
122
+ default:
123
+ this.advance();
124
+ return null;
125
+ }
126
+ }
127
+
128
+ parseBlock() {
129
+ this.expect(TokenType.at);
130
+ const name = this.expect(TokenType.ident).value;
131
+
132
+ const handlers = {
133
+ def: () => this.parseMacroDef(),
134
+ macro: () => this.parseMacroDef(),
135
+ use: () => this.parseUse(),
136
+ meta: () => this.parseMeta(),
137
+ schema: () => this.parseSchema(),
138
+ service: () => this.parseService(),
139
+ if: () => this.parseIf(),
140
+ for: () => this.parseFor(),
141
+ table: () => this.parseTable(),
142
+ ul: () => this.parseUL(false),
143
+ ol: () => this.parseUL(true),
144
+ entry: () => this.parseEntry(),
145
+ config: () => this.parseMapping(name),
146
+ };
147
+
148
+ if (handlers[name]) return handlers[name]();
149
+
150
+ const attrs = this.parseAttrs();
151
+ const inline_content = this.parseInlineContent();
152
+ let children = [];
153
+ if (this.peek().type === TokenType.lbrace) {
154
+ children = this.parseBlockWithChildren();
155
+ }
156
+ return makeNode(NodeTag.block, {
157
+ name,
158
+ attrs,
159
+ inline_content,
160
+ children,
161
+ });
162
+ }
163
+
164
+ parseBlockWithChildren() {
165
+ this.expect(TokenType.lbrace);
166
+ const children = [];
167
+ this.skipNewlines();
168
+ this.skipIndentDedent();
169
+ while (this.peek().type !== TokenType.rbrace && this.peek().type !== TokenType.eof) {
170
+ this.skipNewlines();
171
+ this.skipIndentDedent();
172
+ if (this.peek().type === TokenType.rbrace) break;
173
+ const node = this.parseTopLevel();
174
+ if (node) children.push(node);
175
+ }
176
+ this.expect(TokenType.rbrace);
177
+ return children;
178
+ }
179
+
180
+ parseAttrs() {
181
+ const attrs = {};
182
+ if (this.peek().type !== TokenType.lparen) return attrs;
183
+ this.advance();
184
+ let idx = 0;
185
+ this.skipNewlines();
186
+ while (this.peek().type !== TokenType.rparen && this.peek().type !== TokenType.eof) {
187
+ this.skipNewlines();
188
+ this.skipIndentDedent();
189
+ if (this.peek().type === TokenType.rparen) break;
190
+ if (this.peek().type === TokenType.comma) { this.advance(); continue; }
191
+
192
+ const keyTok = this.peek();
193
+ if (keyTok.type === TokenType.ident && this.peek(1).type === TokenType.colon) {
194
+ this.advance();
195
+ this.advance();
196
+ attrs[keyTok.value] = this.parseAttrValue();
197
+ } else if (keyTok.type === TokenType.ident && this.peek(1).type === TokenType.equals) {
198
+ this.advance();
199
+ this.advance();
200
+ attrs[keyTok.value] = this.parseAttrValue();
201
+ } else if (keyTok.type === TokenType.ident &&
202
+ (this.peek(1).type === TokenType.comma ||
203
+ this.peek(1).type === TokenType.rparen ||
204
+ this.peek(1).type === TokenType.newline)) {
205
+ this.advance();
206
+ attrs[keyTok.value] = "true";
207
+ } else {
208
+ const val = this.parseAttrValue();
209
+ attrs[String(idx++)] = val;
210
+ }
211
+ this.maybe(TokenType.comma);
212
+ this.skipNewlines();
213
+ }
214
+ this.expect(TokenType.rparen);
215
+ return attrs;
216
+ }
217
+
218
+ parseAttrValue() {
219
+ const tok = this.peek();
220
+ switch (tok.type) {
221
+ case TokenType.string:
222
+ case TokenType.number:
223
+ case TokenType.bool:
224
+ case TokenType.null:
225
+ return this.advance().value;
226
+ case TokenType.ident: {
227
+ const val = this.advance().value;
228
+ return val;
229
+ }
230
+ case TokenType.text:
231
+ return this.advance().value;
232
+ case TokenType.lbracket:
233
+ return this.parseArrayLiteral();
234
+ case TokenType.at:
235
+ return this.parseInlineCommandValue();
236
+ default:
237
+ return "";
238
+ }
239
+ }
240
+
241
+ parseArrayLiteral() {
242
+ this.expect(TokenType.lbracket);
243
+ const parts = ["["];
244
+ while (this.peek().type !== TokenType.rbracket && this.peek().type !== TokenType.eof) {
245
+ this.skipNewlines();
246
+ if (this.peek().type === TokenType.rbracket) break;
247
+ if ([TokenType.comma, TokenType.indent, TokenType.dedent].includes(this.peek().type)) {
248
+ this.advance();
249
+ parts.push(",");
250
+ continue;
251
+ }
252
+ if (this.peek().type === TokenType.lbracket) {
253
+ parts.push(this.parseArrayLiteral());
254
+ } else {
255
+ parts.push(this.parseAttrValue());
256
+ }
257
+ this.maybe(TokenType.comma);
258
+ this.skipNewlines();
259
+ }
260
+ while ([TokenType.indent, TokenType.dedent].includes(this.peek().type)) this.advance();
261
+ this.expect(TokenType.rbracket);
262
+ parts.push("]");
263
+ return parts.join("");
264
+ }
265
+
266
+ parseInlineContent() {
267
+ const nodes = [];
268
+ let textBuf = "";
269
+
270
+ const flush = () => {
271
+ if (textBuf) {
272
+ nodes.push(makeNode(NodeTag.text, { value: this.pool.dup(textBuf) }));
273
+ textBuf = "";
274
+ }
275
+ };
276
+
277
+ while (true) {
278
+ const tok = this.peek();
279
+ switch (tok.type) {
280
+ case TokenType.text:
281
+ case TokenType.string:
282
+ case TokenType.number:
283
+ this.advance();
284
+ textBuf += tok.value;
285
+ break;
286
+ case TokenType.bool:
287
+ case TokenType.null:
288
+ this.advance();
289
+ textBuf += tok.value;
290
+ break;
291
+ case TokenType.ident:
292
+ if (this.peek(1).type === TokenType.colon) break;
293
+ this.advance();
294
+ if (textBuf.length > 0 && /\w/.test(textBuf[textBuf.length - 1]) && /\w/.test(tok.value[0])) {
295
+ textBuf += " ";
296
+ }
297
+ textBuf += tok.value;
298
+ break;
299
+ case TokenType.at:
300
+ flush();
301
+ const inl = this.parseInlineCommand();
302
+ if (inl) nodes.push(inl);
303
+ break;
304
+ case TokenType.math_inline:
305
+ flush();
306
+ this.advance();
307
+ nodes.push(makeNode(NodeTag.math_inline, { source: tok.value }));
308
+ break;
309
+ case TokenType.math_display:
310
+ flush();
311
+ this.advance();
312
+ nodes.push(makeNode(NodeTag.math_display, { source: tok.value }));
313
+ break;
314
+ case TokenType.interp_start:
315
+ flush();
316
+ this.advance();
317
+ nodes.push(makeNode(NodeTag.interpolation, { expression: tok.value }));
318
+ break;
319
+ case TokenType.lbrace:
320
+ if (nodes.length > 0 && nodes[nodes.length - 1].tag === NodeTag.text) {
321
+ this.advance();
322
+ textBuf += "{";
323
+ break;
324
+ } else {
325
+ flush();
326
+ return nodes;
327
+ }
328
+ case TokenType.lparen:
329
+ case TokenType.rparen:
330
+ case TokenType.lbracket:
331
+ case TokenType.rbracket:
332
+ case TokenType.equals:
333
+ case TokenType.dot:
334
+ this.advance();
335
+ break;
336
+ default:
337
+ flush();
338
+ return nodes;
339
+ }
340
+ }
341
+ }
342
+
343
+ parseInlineCommand() {
344
+ this.expect(TokenType.at);
345
+ const name = this.expect(TokenType.ident).value;
346
+ if (["ref", "cite", "label"].includes(name)) return this.parseRefLikeInline(name);
347
+ const attrs = this.parseAttrs();
348
+ let content = [];
349
+ if (this.peek().type === TokenType.lbrace) {
350
+ content = this.parseInlineBracedContent();
351
+ }
352
+ return makeNode(NodeTag.inline_block, { name, attrs, content });
353
+ }
354
+
355
+ parseInlineCommandValue() {
356
+ this.expect(TokenType.at);
357
+ const name = this.expect(TokenType.ident).value;
358
+ this.parseAttrs();
359
+ if (this.peek().type === TokenType.lparen) this.parseAttrs();
360
+ let val = "";
361
+ if (this.peek().type === TokenType.lbrace) {
362
+ val = this.parseInlineBracedString();
363
+ }
364
+ if (val) return `@${name}(${val})`;
365
+ return `@${name}`;
366
+ }
367
+
368
+ parseInlineBracedString() {
369
+ this.expect(TokenType.lbrace);
370
+ let buf = "";
371
+ while (this.peek().type !== TokenType.rbrace && this.peek().type !== TokenType.eof) {
372
+ const tok = this.peek();
373
+ if ([TokenType.text, TokenType.string, TokenType.number, TokenType.ident].includes(tok.type)) {
374
+ this.advance();
375
+ buf += tok.value;
376
+ } else break;
377
+ }
378
+ this.expect(TokenType.rbrace);
379
+ return this.pool.dup(buf);
380
+ }
381
+
382
+ parseRefLikeInline(name) {
383
+ const keyAttr = name === "label" ? "id" : "key";
384
+ const attrs = {};
385
+ if (this.peek().type === TokenType.lparen) {
386
+ this.advance();
387
+ let parts = [];
388
+ while (this.peek().type !== TokenType.rparen && this.peek().type !== TokenType.eof) {
389
+ const tok = this.peek();
390
+ if ([TokenType.ident, TokenType.colon, TokenType.string, TokenType.number, TokenType.dot].includes(tok.type)) {
391
+ this.advance();
392
+ parts.push(tok.value || ":");
393
+ } else if (tok.type === TokenType.comma) {
394
+ this.advance();
395
+ parts.push(", ");
396
+ } else if ([TokenType.newline, TokenType.indent, TokenType.dedent].includes(tok.type)) {
397
+ this.advance();
398
+ } else break;
399
+ }
400
+ const combined = parts.join("").trim();
401
+ if (combined) attrs[keyAttr] = this.pool.dup(combined);
402
+ this.expect(TokenType.rparen);
403
+ }
404
+ let content = [];
405
+ if (this.peek().type === TokenType.lbrace) {
406
+ content = this.parseInlineBracedContent();
407
+ }
408
+ return makeNode(NodeTag.inline_block, { name, attrs, content });
409
+ }
410
+
411
+ parseInlineBracedContent() {
412
+ this.expect(TokenType.lbrace);
413
+ const nodes = [];
414
+ let textBuf = "";
415
+
416
+ const flush = () => {
417
+ if (textBuf) {
418
+ nodes.push(makeNode(NodeTag.text, { value: this.pool.dup(textBuf) }));
419
+ textBuf = "";
420
+ }
421
+ };
422
+
423
+ while (this.peek().type !== TokenType.rbrace && this.peek().type !== TokenType.eof) {
424
+ const tok = this.peek();
425
+ switch (tok.type) {
426
+ case TokenType.text:
427
+ case TokenType.string:
428
+ case TokenType.number:
429
+ case TokenType.bool:
430
+ case TokenType.null:
431
+ this.advance();
432
+ if (textBuf.length > 0 && /\w/.test(textBuf[textBuf.length - 1]) && /\w/.test(tok.value[0])) textBuf += " ";
433
+ textBuf += tok.value;
434
+ break;
435
+ case TokenType.ident:
436
+ this.advance();
437
+ if (textBuf.length > 0 && /\w/.test(textBuf[textBuf.length - 1]) && /\w/.test(tok.value[0])) textBuf += " ";
438
+ textBuf += tok.value;
439
+ break;
440
+ case TokenType.at:
441
+ flush();
442
+ const inl = this.parseInlineCommand();
443
+ if (inl) nodes.push(inl);
444
+ break;
445
+ case TokenType.math_inline:
446
+ flush();
447
+ this.advance();
448
+ nodes.push(makeNode(NodeTag.math_inline, { source: tok.value }));
449
+ break;
450
+ case TokenType.interp_start:
451
+ flush();
452
+ this.advance();
453
+ nodes.push(makeNode(NodeTag.interpolation, { expression: tok.value }));
454
+ break;
455
+ case TokenType.lparen:
456
+ case TokenType.rparen:
457
+ case TokenType.lbracket:
458
+ case TokenType.rbracket:
459
+ case TokenType.equals:
460
+ case TokenType.colon:
461
+ case TokenType.dot: {
462
+ this.advance();
463
+ const c =
464
+ tok.type === TokenType.lparen ? "(" :
465
+ tok.type === TokenType.rparen ? ")" :
466
+ tok.type === TokenType.lbracket ? "[" :
467
+ tok.type === TokenType.rbracket ? "]" :
468
+ tok.type === TokenType.equals ? "=" :
469
+ tok.type === TokenType.colon ? ":" :
470
+ tok.value[0] || " ";
471
+ textBuf += c;
472
+ break;
473
+ }
474
+ case TokenType.comma:
475
+ this.advance();
476
+ flush();
477
+ nodes.push(makeNode(NodeTag.text, { value: ", " }));
478
+ break;
479
+ case TokenType.newline:
480
+ case TokenType.indent:
481
+ case TokenType.dedent:
482
+ this.advance();
483
+ break;
484
+ default:
485
+ flush();
486
+ return nodes;
487
+ }
488
+ }
489
+ flush();
490
+ if (this.peek().type === TokenType.rbrace) this.advance();
491
+ else this.skipToRbrace();
492
+ return nodes;
493
+ }
494
+
495
+ parseList(ordered) {
496
+ const items = [];
497
+ while ([TokenType.dash, TokenType.star, TokenType.plus].includes(this.peek().type)) {
498
+ this.advance();
499
+ const itemNodes = this.parseListItemContent();
500
+ const item = itemNodes.length === 1 ? itemNodes[0] :
501
+ makeNode(NodeTag.block, {
502
+ name: "li",
503
+ attrs: {},
504
+ inline_content: [],
505
+ children: itemNodes,
506
+ });
507
+ items.push(item);
508
+ this.skipNewlines();
509
+ }
510
+ return makeNode(NodeTag.list_block, { ordered, items });
511
+ }
512
+
513
+ parseUL(ordered) {
514
+ this.parseAttrs();
515
+ const items = [];
516
+ if (this.peek().type === TokenType.lbrace) {
517
+ this.advance();
518
+ this.skipNewlines();
519
+ this.skipIndentDedent();
520
+ while (this.peek().type !== TokenType.rbrace && this.peek().type !== TokenType.eof) {
521
+ this.skipNewlines();
522
+ this.skipIndentDedent();
523
+ if (this.peek().type === TokenType.rbrace) break;
524
+ const tok = this.peek();
525
+ if (tok.type === TokenType.dash || tok.type === TokenType.star || (ordered && tok.type === TokenType.plus)) {
526
+ this.advance();
527
+ const itemNodes = this.parseListItemContent();
528
+ const item = itemNodes.length === 1 ? itemNodes[0] :
529
+ makeNode(NodeTag.block, {
530
+ name: "li",
531
+ attrs: {},
532
+ inline_content: [],
533
+ children: itemNodes,
534
+ });
535
+ items.push(item);
536
+ } else break;
537
+ this.skipNewlines();
538
+ }
539
+ this.skipToRbrace();
540
+ this.expect(TokenType.rbrace);
541
+ }
542
+ return makeNode(NodeTag.list_block, { ordered, items });
543
+ }
544
+
545
+ parseListItemContent() {
546
+ const nodes = [];
547
+ this.skipNewlines();
548
+ if (this.peek().type === TokenType.at) {
549
+ const block = this.parseBlock();
550
+ if (block) nodes.push(block);
551
+ return nodes;
552
+ }
553
+ let textParts = "";
554
+ let lastWasText = false;
555
+
556
+ while (true) {
557
+ const tok = this.peek();
558
+ switch (tok.type) {
559
+ case TokenType.text:
560
+ this.advance();
561
+ textParts += tok.value;
562
+ lastWasText = true;
563
+ break;
564
+ case TokenType.string:
565
+ case TokenType.bool:
566
+ case TokenType.null:
567
+ this.advance();
568
+ textParts += tok.value;
569
+ lastWasText = false;
570
+ break;
571
+ case TokenType.number:
572
+ this.advance();
573
+ if (lastWasText) textParts += " ";
574
+ textParts += tok.value;
575
+ lastWasText = false;
576
+ break;
577
+ case TokenType.ident:
578
+ this.advance();
579
+ if (lastWasText) textParts += " ";
580
+ textParts += tok.value;
581
+ lastWasText = false;
582
+ break;
583
+ case TokenType.at:
584
+ if (textParts) {
585
+ nodes.push(makeNode(NodeTag.text, { value: this.pool.dup(textParts) }));
586
+ textParts = "";
587
+ }
588
+ const inl = this.parseInlineCommand();
589
+ if (inl) nodes.push(inl);
590
+ lastWasText = false;
591
+ break;
592
+ case TokenType.math_inline:
593
+ if (textParts) {
594
+ nodes.push(makeNode(NodeTag.text, { value: this.pool.dup(textParts) }));
595
+ textParts = "";
596
+ }
597
+ this.advance();
598
+ nodes.push(makeNode(NodeTag.math_inline, { source: tok.value }));
599
+ lastWasText = false;
600
+ break;
601
+ case TokenType.interp_start:
602
+ if (textParts) {
603
+ nodes.push(makeNode(NodeTag.text, { value: this.pool.dup(textParts) }));
604
+ textParts = "";
605
+ }
606
+ this.advance();
607
+ nodes.push(makeNode(NodeTag.interpolation, { expression: tok.value }));
608
+ lastWasText = false;
609
+ break;
610
+ default:
611
+ if (textParts) {
612
+ nodes.push(makeNode(NodeTag.text, { value: this.pool.dup(textParts) }));
613
+ }
614
+ return nodes;
615
+ }
616
+ }
617
+ }
618
+
619
+ parseMapping(name) {
620
+ const pairs = {};
621
+ if (this.peek().type === TokenType.lbrace) {
622
+ this.advance();
623
+ this.skipNewlines();
624
+ while (this.peek().type !== TokenType.rbrace && this.peek().type !== TokenType.eof) {
625
+ this.skipNewlines();
626
+ this.skipIndentDedent();
627
+ if (this.peek().type === TokenType.rbrace) break;
628
+ const keyTok = this.peek();
629
+ if (keyTok.type === TokenType.ident &&
630
+ (this.peek(1).type === TokenType.colon || this.peek(1).type === TokenType.equals)) {
631
+ this.advance();
632
+ this.advance();
633
+ pairs[keyTok.value] = this.parseAttrValue();
634
+ } else break;
635
+ this.maybe(TokenType.comma);
636
+ this.skipNewlines();
637
+ }
638
+ this.skipToRbrace();
639
+ this.expect(TokenType.rbrace);
640
+ }
641
+ return makeNode(NodeTag.block, { name, attrs: pairs, inline_content: [], children: [] });
642
+ }
643
+
644
+ parseMacroDef() {
645
+ const name = this.expect(TokenType.ident).value;
646
+ const params = [];
647
+ const kwargs = {};
648
+ let hasBlockParam = false;
649
+ let blockParamName = "content";
650
+
651
+ if (this.peek().type === TokenType.lparen) {
652
+ this.advance();
653
+ while (this.peek().type !== TokenType.rparen && this.peek().type !== TokenType.eof) {
654
+ if (this.peek().type === TokenType.comma) { this.advance(); continue; }
655
+ if (this.peek().type === TokenType.at) {
656
+ this.advance();
657
+ hasBlockParam = true;
658
+ blockParamName = this.expect(TokenType.ident).value;
659
+ } else if (this.peek().type === TokenType.ident && this.peek(1).type === TokenType.colon) {
660
+ const key = this.advance().value;
661
+ this.advance();
662
+ this.advance(); // type name
663
+ let defaultVal = "";
664
+ if (this.peek().type === TokenType.equals) {
665
+ this.advance();
666
+ defaultVal = this.parseAttrValue();
667
+ }
668
+ kwargs[key] = defaultVal;
669
+ } else {
670
+ params.push(this.expect(TokenType.ident).value);
671
+ }
672
+ this.maybe(TokenType.comma);
673
+ }
674
+ this.expect(TokenType.rparen);
675
+ }
676
+
677
+ let body = [];
678
+ if (this.peek().type === TokenType.lbrace) {
679
+ body = this.parseBlockWithChildren();
680
+ }
681
+ return makeNode(NodeTag.macro_def, {
682
+ name,
683
+ params,
684
+ kwargs,
685
+ hasBlockParam,
686
+ blockParamName,
687
+ body,
688
+ });
689
+ }
690
+
691
+ parseUse() {
692
+ const path = this.expect(TokenType.string).value;
693
+ return makeNode(NodeTag.use_block, { path });
694
+ }
695
+
696
+ parseMeta() {
697
+ const pairs = {};
698
+ if (this.peek().type === TokenType.lbrace) {
699
+ this.advance();
700
+ this.skipNewlines();
701
+ this.skipIndentDedent();
702
+ while (this.peek().type !== TokenType.rbrace && this.peek().type !== TokenType.eof) {
703
+ this.skipNewlines();
704
+ this.skipIndentDedent();
705
+ if (this.peek().type === TokenType.rbrace) break;
706
+ const key = this.expect(TokenType.ident).value;
707
+ this.expect(TokenType.colon);
708
+ pairs[key] = this.parseMetaValue();
709
+ this.maybe(TokenType.comma);
710
+ this.skipNewlines();
711
+ }
712
+ this.expect(TokenType.rbrace);
713
+ }
714
+ return makeNode(NodeTag.meta_block, { pairs });
715
+ }
716
+
717
+ parseMetaValue() {
718
+ const tok = this.peek();
719
+ switch (tok.type) {
720
+ case TokenType.string:
721
+ case TokenType.bool:
722
+ case TokenType.null:
723
+ return this.advance().value;
724
+ case TokenType.lbracket:
725
+ return this.parseArrayLiteral();
726
+ default: {
727
+ let buf = "";
728
+ while (true) {
729
+ const t = this.peek();
730
+ if ([TokenType.newline, TokenType.rbrace, TokenType.comma, TokenType.eof, TokenType.dedent].includes(t.type)) break;
731
+ if ([TokenType.text, TokenType.number, TokenType.ident, TokenType.dash, TokenType.dot, TokenType.colon, TokenType.equals].includes(t.type)) {
732
+ this.advance();
733
+ buf += t.value || "";
734
+ } else break;
735
+ }
736
+ return this.pool.dup(buf.trim());
737
+ }
738
+ }
739
+ }
740
+
741
+ parseEntry() {
742
+ let attrs = {};
743
+ if (this.peek().type === TokenType.lparen) {
744
+ attrs = this.parseAttrs();
745
+ }
746
+ const key = attrs.key || "";
747
+ return makeNode(NodeTag.biblio_entry, { key, attrs });
748
+ }
749
+
750
+ parseSchema() {
751
+ let name;
752
+ if (this.peek().type === TokenType.lparen) {
753
+ this.advance();
754
+ name = this.expect(TokenType.ident).value;
755
+ this.expect(TokenType.rparen);
756
+ } else {
757
+ name = this.expect(TokenType.ident).value;
758
+ }
759
+ const fields = [];
760
+ if (this.peek().type === TokenType.lbrace) {
761
+ this.advance();
762
+ this.skipNewlines();
763
+ while (this.peek().type !== TokenType.rbrace && this.peek().type !== TokenType.eof) {
764
+ this.skipNewlines();
765
+ this.skipIndentDedent();
766
+ if (this.peek().type === TokenType.rbrace) break;
767
+ const fieldName = this.expect(TokenType.ident).value;
768
+ this.expect(TokenType.colon);
769
+ const typeName = this.parseTypeExpr();
770
+ let optional = false;
771
+ if (this.peek().type === TokenType.question) {
772
+ this.advance();
773
+ optional = true;
774
+ }
775
+ let tag = 0;
776
+ if (this.peek().type === TokenType.at) {
777
+ this.advance();
778
+ tag = parseInt(this.expect(TokenType.number).value, 10);
779
+ }
780
+ let defaultVal = null;
781
+ if (this.peek().type === TokenType.equals) {
782
+ this.advance();
783
+ defaultVal = this.parseAttrValue();
784
+ }
785
+ fields.push({ name: fieldName, type_name: typeName, tag, optional, default: defaultVal });
786
+ this.maybe(TokenType.comma);
787
+ this.skipNewlines();
788
+ }
789
+ this.expect(TokenType.rbrace);
790
+ }
791
+ return makeNode(NodeTag.schema_def, { name, fields });
792
+ }
793
+
794
+ parseTypeExpr() {
795
+ let buf = this.expect(TokenType.ident).value;
796
+ if (this.peek().type === TokenType.lt) {
797
+ this.advance();
798
+ buf += "<";
799
+ buf += this.parseTypeExpr();
800
+ while (this.peek().type === TokenType.comma) {
801
+ this.advance();
802
+ buf += ", ";
803
+ buf += this.parseTypeExpr();
804
+ }
805
+ this.expect(TokenType.gt);
806
+ buf += ">";
807
+ }
808
+ return this.pool.dup(buf);
809
+ }
810
+
811
+ parseService() {
812
+ let name;
813
+ if (this.peek().type === TokenType.lparen) {
814
+ this.advance();
815
+ name = this.expect(TokenType.ident).value;
816
+ this.expect(TokenType.rparen);
817
+ } else {
818
+ name = this.expect(TokenType.ident).value;
819
+ }
820
+ const methods = [];
821
+ if (this.peek().type === TokenType.lbrace) {
822
+ this.advance();
823
+ this.skipNewlines();
824
+ while (this.peek().type !== TokenType.rbrace && this.peek().type !== TokenType.eof) {
825
+ this.skipNewlines();
826
+ this.skipIndentDedent();
827
+ if (this.peek().type === TokenType.rbrace) break;
828
+ const methodName = this.expect(TokenType.ident).value;
829
+ const params = [];
830
+ if (this.peek().type === TokenType.lparen) {
831
+ this.advance();
832
+ while (this.peek().type !== TokenType.rparen && this.peek().type !== TokenType.eof) {
833
+ if (this.peek().type === TokenType.comma) { this.advance(); continue; }
834
+ const pname = this.expect(TokenType.ident).value;
835
+ this.expect(TokenType.colon);
836
+ const ptype = this.parseTypeExpr();
837
+ params.push({ name: pname, type_name: ptype });
838
+ this.maybe(TokenType.comma);
839
+ }
840
+ this.expect(TokenType.rparen);
841
+ }
842
+ let returns = null;
843
+ if (this.peek().type === TokenType.dash && this.peek(1).type === TokenType.gt) {
844
+ this.advance();
845
+ this.advance();
846
+ returns = this.parseTypeExpr();
847
+ }
848
+ methods.push({ name: methodName, params, returns });
849
+ this.maybe(TokenType.comma);
850
+ this.skipNewlines();
851
+ }
852
+ this.expect(TokenType.rbrace);
853
+ }
854
+ return makeNode(NodeTag.service_def, { name, methods });
855
+ }
856
+
857
+ parseIf() {
858
+ let condition = "";
859
+ if (this.peek().type === TokenType.lparen) {
860
+ this.advance();
861
+ const parts = [];
862
+ let depth = 1;
863
+ while (depth > 0 && this.peek().type !== TokenType.eof) {
864
+ const tok = this.peek();
865
+ if (tok.type === TokenType.lparen) depth++;
866
+ else if (tok.type === TokenType.rparen) depth--;
867
+ if (depth === 0) break;
868
+ this.advance();
869
+ parts.push(tok.value);
870
+ }
871
+ condition = this.pool.dup(parts.join("").trim());
872
+ this.expect(TokenType.rparen);
873
+ }
874
+ let thenBody = [];
875
+ if (this.peek().type === TokenType.lbrace) thenBody = this.parseBlockWithChildren();
876
+ let elseBody = [];
877
+ this.skipNewlines();
878
+ if (this.peek().type === TokenType.at && this.peek(1).type === TokenType.ident &&
879
+ this.peek(1).value === "else") {
880
+ this.advance();
881
+ this.advance();
882
+ if (this.peek().type === TokenType.lbrace) elseBody = this.parseBlockWithChildren();
883
+ }
884
+ return makeNode(NodeTag.if_block, { condition, then_body: thenBody, else_body: elseBody });
885
+ }
886
+
887
+ parseFor() {
888
+ let varName = "";
889
+ let iterable = "";
890
+ if (this.peek().type === TokenType.lparen) {
891
+ this.advance();
892
+ varName = this.expect(TokenType.ident).value;
893
+ this.expect(TokenType.comma);
894
+ iterable = this.expect(TokenType.ident).value;
895
+ this.expect(TokenType.rparen);
896
+ } else {
897
+ varName = this.expect(TokenType.ident).value;
898
+ if (this.peek().type === TokenType.ident && this.peek().value === "in") this.advance();
899
+ iterable = this.expect(TokenType.ident).value;
900
+ }
901
+ let body = [];
902
+ if (this.peek().type === TokenType.lbrace) body = this.parseBlockWithChildren();
903
+ return makeNode(NodeTag.for_block, { var_name: varName, iterable, body });
904
+ }
905
+
906
+ parseTable() {
907
+ let attrs = this.parseAttrs();
908
+ const src = attrs.src || null;
909
+ const caption = attrs.caption || attrs.cap || null;
910
+ let headers = null;
911
+ const rows = [];
912
+ let data = null;
913
+
914
+ if (this.peek().type === TokenType.lbrace) {
915
+ this.advance();
916
+ this.skipNewlines();
917
+ this.skipIndentDedent();
918
+ while (this.peek().type !== TokenType.rbrace && this.peek().type !== TokenType.eof) {
919
+ this.skipNewlines();
920
+ this.skipIndentDedent();
921
+ if (this.peek().type === TokenType.rbrace) break;
922
+ if (this.peek().type === TokenType.at) {
923
+ this.advance();
924
+ const subName = this.expect(TokenType.ident).value;
925
+ const cells = this.parseTableRowContent();
926
+ if (subName === "header") headers = cells;
927
+ else if (subName === "row") rows.push(cells);
928
+ } else if (this.peek().type === TokenType.lbracket) {
929
+ data = this.parseTableArrayData();
930
+ } else break;
931
+ this.skipNewlines();
932
+ this.skipIndentDedent();
933
+ }
934
+ this.skipToRbrace();
935
+ this.expect(TokenType.rbrace);
936
+ }
937
+ return makeNode(NodeTag.table_block, { headers, rows, src, caption, data });
938
+ }
939
+
940
+ parseTableRowContent() {
941
+ const cells = [];
942
+ if (this.peek().type === TokenType.lbrace) {
943
+ this.advance();
944
+ this.skipNewlines();
945
+ while (this.peek().type !== TokenType.rbrace && this.peek().type !== TokenType.eof) {
946
+ if ([TokenType.comma, TokenType.newline, TokenType.indent, TokenType.dedent].includes(this.peek().type)) {
947
+ this.advance();
948
+ continue;
949
+ }
950
+ const val = this.parseAttrValue();
951
+ cells.push(val || "");
952
+ this.maybe(TokenType.comma);
953
+ }
954
+ this.skipToRbrace();
955
+ this.expect(TokenType.rbrace);
956
+ } else if (this.peek().type === TokenType.lbracket) {
957
+ this.advance();
958
+ this.skipNewlines();
959
+ while (this.peek().type !== TokenType.rbracket && this.peek().type !== TokenType.eof) {
960
+ if ([TokenType.comma, TokenType.newline, TokenType.indent, TokenType.dedent].includes(this.peek().type)) {
961
+ this.advance();
962
+ continue;
963
+ }
964
+ const val = this.parseAttrValue();
965
+ cells.push(val || "");
966
+ this.maybe(TokenType.comma);
967
+ this.skipNewlines();
968
+ }
969
+ this.expect(TokenType.rbracket);
970
+ } else {
971
+ let cellText = "";
972
+ while (true) {
973
+ const tok = this.peek();
974
+ if ([TokenType.newline, TokenType.rbrace, TokenType.dedent, TokenType.indent, TokenType.eof].includes(tok.type)) break;
975
+ if (tok.type === TokenType.comma) {
976
+ this.advance();
977
+ cells.push(this.pool.dup(cellText.trim()));
978
+ cellText = "";
979
+ } else if ([TokenType.text, TokenType.string, TokenType.number, TokenType.ident, TokenType.bool, TokenType.null].includes(tok.type)) {
980
+ this.advance();
981
+ cellText += tok.value;
982
+ } else break;
983
+ }
984
+ if (cellText) cells.push(this.pool.dup(cellText.trim()));
985
+ }
986
+ return cells;
987
+ }
988
+
989
+ parseTableData() {
990
+ this.advance();
991
+ const data = this.parseTableArrayData2();
992
+ return makeNode(NodeTag.table_block, {
993
+ headers: null,
994
+ rows: [],
995
+ src: null,
996
+ caption: null,
997
+ data,
998
+ });
999
+ }
1000
+
1001
+ parseTableArrayData() {
1002
+ const data = [];
1003
+ this.expect(TokenType.lbracket);
1004
+ this.skipNewlines();
1005
+ this.skipIndentDedent();
1006
+ while (this.peek().type === TokenType.lbracket) {
1007
+ data.push(this.parseTableRowContent());
1008
+ this.maybe(TokenType.comma);
1009
+ this.skipNewlines();
1010
+ this.skipIndentDedent();
1011
+ }
1012
+ this.expect(TokenType.rbracket);
1013
+ return data;
1014
+ }
1015
+
1016
+ parseTableArrayData2() {
1017
+ const data = [];
1018
+ this.expect(TokenType.lbracket);
1019
+ this.skipNewlines();
1020
+ this.skipIndentDedent();
1021
+ while (this.peek().type !== TokenType.rbracket && this.peek().type !== TokenType.eof) {
1022
+ this.skipNewlines();
1023
+ this.skipIndentDedent();
1024
+ if (this.peek().type === TokenType.rbracket) break;
1025
+ if (this.peek().type === TokenType.lbracket) {
1026
+ data.push(this.parseTableRowContent());
1027
+ } else break;
1028
+ this.maybe(TokenType.comma);
1029
+ this.skipNewlines();
1030
+ this.skipIndentDedent();
1031
+ }
1032
+ this.expect(TokenType.rbracket);
1033
+ return data;
1034
+ }
1035
+ }
1036
+
1037
+ module.exports = { parse, Parser };