@aeriajs/compiler 0.0.4 → 0.0.7

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.
Files changed (52) hide show
  1. package/dist/ast.d.ts +79 -0
  2. package/dist/ast.js +23 -0
  3. package/dist/ast.mjs +21 -0
  4. package/dist/codegen/generateContracts.d.ts +5 -0
  5. package/dist/codegen/generateContracts.js +84 -0
  6. package/dist/codegen/generateContracts.mjs +77 -0
  7. package/dist/codegen/generateExports.d.ts +15 -0
  8. package/dist/codegen/generateExports.js +41 -0
  9. package/dist/codegen/generateExports.mjs +32 -0
  10. package/dist/codegen/generateJSCollections.d.ts +2 -0
  11. package/dist/codegen/generateJSCollections.js +91 -0
  12. package/dist/codegen/generateJSCollections.mjs +82 -0
  13. package/dist/codegen/generateTSCollections.d.ts +2 -0
  14. package/dist/codegen/generateTSCollections.js +107 -0
  15. package/dist/codegen/generateTSCollections.mjs +99 -0
  16. package/dist/codegen/index.d.ts +4 -0
  17. package/dist/codegen/index.js +20 -0
  18. package/dist/codegen/index.mjs +5 -0
  19. package/dist/codegen/utils.d.ts +29 -0
  20. package/dist/codegen/utils.js +143 -0
  21. package/dist/codegen/utils.mjs +122 -0
  22. package/dist/codegen.d.ts +3 -0
  23. package/dist/codegen.js +102 -0
  24. package/dist/codegen.mjs +52 -0
  25. package/dist/compile.d.ts +21 -0
  26. package/dist/compile.js +96 -0
  27. package/dist/compile.mjs +57 -0
  28. package/dist/diagnostic.d.ts +8 -0
  29. package/dist/diagnostic.js +22 -0
  30. package/dist/diagnostic.mjs +16 -0
  31. package/dist/guards.d.ts +3 -0
  32. package/dist/guards.js +45 -0
  33. package/dist/guards.mjs +8 -0
  34. package/dist/index.d.ts +7 -0
  35. package/dist/index.js +23 -0
  36. package/dist/index.mjs +8 -0
  37. package/dist/lexer.d.ts +14 -0
  38. package/dist/lexer.js +272 -0
  39. package/dist/lexer.mjs +270 -0
  40. package/dist/parser.d.ts +8 -0
  41. package/dist/parser.js +907 -0
  42. package/dist/parser.mjs +851 -0
  43. package/dist/semantic.d.ts +5 -0
  44. package/dist/semantic.js +149 -0
  45. package/dist/semantic.mjs +111 -0
  46. package/dist/token.d.ts +38 -0
  47. package/dist/token.js +24 -0
  48. package/dist/token.mjs +22 -0
  49. package/dist/utils.d.ts +3 -0
  50. package/dist/utils.js +2 -0
  51. package/dist/utils.mjs +1 -0
  52. package/package.json +2 -3
@@ -0,0 +1,851 @@
1
+ "use strict";
2
+ import { TokenType } from "./token.mjs";
3
+ import { DESCRIPTION_PRESETS, PROPERTY_ARRAY_ELEMENTS, PROPERTY_FORMATS, PROPERTY_INPUT_ELEMENTS, PROPERTY_INPUT_TYPES } from "@aeriajs/types";
4
+ import { icons } from "@phosphor-icons/core";
5
+ import { Diagnostic } from "./diagnostic.mjs";
6
+ import * as AST from "./ast.mjs";
7
+ import * as guards from "./guards.mjs";
8
+ import * as lexer from "./lexer.mjs";
9
+ const MAX_ERROR_MESSAGE_ITEMS = 20;
10
+ const ICON_NAMES = icons.map((icon) => icon.name);
11
+ export const locationMap = /* @__PURE__ */ new WeakMap();
12
+ const isFileProperty = (property) => {
13
+ return property.$ref === "File";
14
+ };
15
+ export const parse = (tokens) => {
16
+ let index = 0;
17
+ const ast = {
18
+ kind: "program",
19
+ collections: [],
20
+ contracts: [],
21
+ functionsets: []
22
+ };
23
+ const errors = [];
24
+ const advance = () => index++;
25
+ const next = () => {
26
+ const token = tokens[index + 1];
27
+ if (!token) {
28
+ throw new Diagnostic("unexpected EOF", current().location);
29
+ }
30
+ return token;
31
+ };
32
+ const previous = () => {
33
+ const token = tokens[index - 1];
34
+ if (!token) {
35
+ throw new Diagnostic("invalid position");
36
+ }
37
+ return token;
38
+ };
39
+ const current = () => {
40
+ const token = tokens[index];
41
+ if (!token) {
42
+ throw new Diagnostic("unexpected EOF", previous().location);
43
+ }
44
+ return token;
45
+ };
46
+ const foldBrackets = () => {
47
+ if (match(TokenType.LeftBracket)) {
48
+ advance();
49
+ while (!match(TokenType.RightBracket)) {
50
+ foldBrackets();
51
+ advance();
52
+ }
53
+ }
54
+ };
55
+ const match = (expected, value) => {
56
+ const token = current();
57
+ if (token.type === expected) {
58
+ if (value !== void 0) {
59
+ return Array.isArray(value) ? value.includes(token.value) : token.value === value;
60
+ }
61
+ return true;
62
+ }
63
+ return false;
64
+ };
65
+ const consume = (expected, value) => {
66
+ const token = current();
67
+ if (match(expected, value)) {
68
+ advance();
69
+ return token;
70
+ }
71
+ let expectedValue;
72
+ if (value) {
73
+ expectedValue = Array.isArray(value) ? value.slice(0, MAX_ERROR_MESSAGE_ITEMS).map((elem) => `"${elem}"`).join(" | ") : `"${value}"`;
74
+ }
75
+ if (Array.isArray(value) && value.length > MAX_ERROR_MESSAGE_ITEMS) {
76
+ expectedValue += " | ...";
77
+ }
78
+ throw new Diagnostic(
79
+ expectedValue ? `expected ${expected} with value ${expectedValue} but found ${token.type} with value "${token.value}" instead` : `expected ${expected} but found ${token.type} instead`,
80
+ token.location
81
+ );
82
+ };
83
+ const recover = (keywords) => {
84
+ let token;
85
+ while (token = tokens[++index]) {
86
+ if (token.type === TokenType.Keyword && keywords.includes(token.value)) {
87
+ break;
88
+ }
89
+ }
90
+ };
91
+ const parseArray = (type) => {
92
+ consume(TokenType.LeftSquareBracket);
93
+ const array = [];
94
+ while (!match(TokenType.RightSquareBracket)) {
95
+ const { value } = consume(type);
96
+ array.push(value);
97
+ if (match(TokenType.Comma)) {
98
+ consume(TokenType.Comma);
99
+ }
100
+ }
101
+ consume(TokenType.RightSquareBracket);
102
+ return array;
103
+ };
104
+ const parseArrayBlock = (value) => {
105
+ const array = [];
106
+ const symbols = [];
107
+ consume(TokenType.LeftBracket);
108
+ while (!match(TokenType.RightBracket)) {
109
+ const { value: identifier, location } = consume(TokenType.Identifier, value);
110
+ const elemSymbol = Symbol();
111
+ array.push(identifier);
112
+ symbols.push(elemSymbol);
113
+ locationMap.set(elemSymbol, location);
114
+ }
115
+ consume(TokenType.RightBracket);
116
+ return {
117
+ value: array,
118
+ symbols
119
+ };
120
+ };
121
+ const parseArrayBlockWithAttributes = () => {
122
+ const array = {};
123
+ let hasAttributes = false;
124
+ consume(TokenType.LeftBracket);
125
+ while (!match(TokenType.RightBracket)) {
126
+ const { value: identifier } = consume(TokenType.Identifier);
127
+ array[identifier] = {};
128
+ while (match(TokenType.AttributeName)) {
129
+ hasAttributes = true;
130
+ const { value: attributeName } = consume(TokenType.AttributeName);
131
+ if (match(TokenType.LeftParens)) {
132
+ consume(TokenType.LeftParens);
133
+ consume(TokenType.RightParens);
134
+ } else {
135
+ array[identifier][attributeName] = true;
136
+ }
137
+ }
138
+ }
139
+ consume(TokenType.RightBracket);
140
+ return hasAttributes ? array : Object.keys(array);
141
+ };
142
+ const parsePropertyAttributeValue = (attributeName, property, location) => {
143
+ const consumeBoolean = () => {
144
+ if (match(TokenType.Boolean)) {
145
+ const { value } = consume(TokenType.Boolean);
146
+ return value;
147
+ }
148
+ return true;
149
+ };
150
+ if ("enum" in property && attributeName === "values") {
151
+ property.enum = parseArray(TokenType.QuotedString);
152
+ return;
153
+ }
154
+ switch (attributeName) {
155
+ case "icon": {
156
+ const { value } = consume(TokenType.QuotedString, ICON_NAMES);
157
+ property[attributeName] = value;
158
+ return;
159
+ }
160
+ case "hint":
161
+ case "description": {
162
+ const { value } = consume(TokenType.QuotedString);
163
+ property[attributeName] = value;
164
+ return;
165
+ }
166
+ }
167
+ if ("$ref" in property) {
168
+ switch (attributeName) {
169
+ case "purge":
170
+ case "inline": {
171
+ property[attributeName] = consumeBoolean();
172
+ return;
173
+ }
174
+ case "select":
175
+ case "form":
176
+ case "populate":
177
+ case "indexes": {
178
+ property[attributeName] = parseArray(TokenType.Identifier);
179
+ return;
180
+ }
181
+ case "populateDepth": {
182
+ const { value } = consume(TokenType.Number);
183
+ property[attributeName] = value;
184
+ return;
185
+ }
186
+ }
187
+ if (isFileProperty(property)) {
188
+ switch (attributeName) {
189
+ case "extensions":
190
+ case "accept": {
191
+ property[attributeName] = parseArray(TokenType.QuotedString);
192
+ return;
193
+ }
194
+ }
195
+ }
196
+ }
197
+ if ("type" in property) {
198
+ switch (property.type) {
199
+ case "string": {
200
+ switch (attributeName) {
201
+ case "format": {
202
+ const { value } = consume(TokenType.QuotedString, PROPERTY_FORMATS);
203
+ property[attributeName] = value;
204
+ return;
205
+ }
206
+ case "mask": {
207
+ if (match(TokenType.LeftSquareBracket)) {
208
+ property[attributeName] = parseArray(TokenType.QuotedString);
209
+ return;
210
+ } else {
211
+ const { value } = consume(TokenType.QuotedString);
212
+ property[attributeName] = value;
213
+ return;
214
+ }
215
+ }
216
+ case "maskedValue": {
217
+ const { value } = consume(TokenType.Boolean);
218
+ property[attributeName] = value;
219
+ return;
220
+ }
221
+ case "minLength":
222
+ case "maxLength": {
223
+ const { value } = consume(TokenType.Number);
224
+ property[attributeName] = value;
225
+ return;
226
+ }
227
+ case "inputType": {
228
+ const { value } = consume(TokenType.QuotedString, PROPERTY_INPUT_TYPES);
229
+ property[attributeName] = value;
230
+ return;
231
+ }
232
+ case "element": {
233
+ const { value } = consume(TokenType.QuotedString, PROPERTY_INPUT_ELEMENTS);
234
+ property[attributeName] = value;
235
+ return;
236
+ }
237
+ case "placeholder": {
238
+ const { value } = consume(TokenType.QuotedString);
239
+ property[attributeName] = value;
240
+ return;
241
+ }
242
+ }
243
+ break;
244
+ }
245
+ case "integer":
246
+ case "number": {
247
+ switch (attributeName) {
248
+ case "exclusiveMinimum":
249
+ case "exclusiveMaximum":
250
+ case "minimum":
251
+ case "maximum": {
252
+ const { value } = consume(TokenType.Number);
253
+ property[attributeName] = value;
254
+ return;
255
+ }
256
+ case "placeholder": {
257
+ const { value } = consume(TokenType.QuotedString);
258
+ property[attributeName] = value;
259
+ return;
260
+ }
261
+ }
262
+ break;
263
+ }
264
+ case "array": {
265
+ switch (attributeName) {
266
+ case "uniqueItems": {
267
+ const { value } = consume(TokenType.Boolean);
268
+ property[attributeName] = value;
269
+ return;
270
+ }
271
+ case "element": {
272
+ const { value } = consume(TokenType.QuotedString, PROPERTY_ARRAY_ELEMENTS);
273
+ property[attributeName] = value;
274
+ return;
275
+ }
276
+ }
277
+ }
278
+ }
279
+ }
280
+ throw new Diagnostic(`invalid attribute name "${attributeName}"`, location);
281
+ };
282
+ const parsePropertyType = (options = {
283
+ allowModifiers: false
284
+ }) => {
285
+ let property;
286
+ let nestedProperties;
287
+ let modifierToken;
288
+ if (match(TokenType.LeftSquareBracket)) {
289
+ consume(TokenType.LeftSquareBracket);
290
+ const arrayProperty = {
291
+ type: "array"
292
+ };
293
+ while (!match(TokenType.RightSquareBracket)) {
294
+ const attributeSymbol = Symbol();
295
+ arrayProperty[AST.LOCATION_SYMBOL] ??= {
296
+ attributes: {},
297
+ arrays: {}
298
+ };
299
+ if (match(TokenType.Range)) {
300
+ const { value: rangeSeparator } = consume(TokenType.Range);
301
+ let attributeName2;
302
+ const minItems = rangeSeparator[0];
303
+ if (!isNaN(minItems)) {
304
+ attributeName2 = "minItems";
305
+ arrayProperty[attributeName2] = minItems, arrayProperty[AST.LOCATION_SYMBOL].attributes[attributeName2] = attributeSymbol;
306
+ }
307
+ const maxItems = rangeSeparator[1];
308
+ if (!isNaN(maxItems)) {
309
+ attributeName2 = "maxItems";
310
+ arrayProperty[attributeName2] = maxItems;
311
+ arrayProperty[AST.LOCATION_SYMBOL].attributes[attributeName2] = attributeSymbol;
312
+ }
313
+ continue;
314
+ }
315
+ const { value: attributeName, location } = consume(TokenType.AttributeName);
316
+ if (match(TokenType.LeftParens)) {
317
+ consume(TokenType.LeftParens);
318
+ locationMap.set(attributeSymbol, next().location);
319
+ arrayProperty[AST.LOCATION_SYMBOL].attributes[attributeName] = attributeSymbol;
320
+ parsePropertyAttributeValue(attributeName, arrayProperty, location);
321
+ consume(TokenType.RightParens);
322
+ } else {
323
+ parsePropertyAttributeValue(attributeName, arrayProperty, location);
324
+ }
325
+ }
326
+ consume(TokenType.RightSquareBracket);
327
+ const { property: items, nestedProperties: nestedProperties2 } = parsePropertyType(options);
328
+ property = {
329
+ ...arrayProperty,
330
+ items
331
+ };
332
+ return {
333
+ kind: "property",
334
+ property,
335
+ nestedProperties: nestedProperties2
336
+ };
337
+ }
338
+ if (options.allowModifiers) {
339
+ const nextToken = next();
340
+ const currentTokenValue = current().value;
341
+ if (match(TokenType.Identifier) && typeof currentTokenValue === "string" && guards.isValidPropertyModifier(currentTokenValue) && (nextToken.type === TokenType.LeftBracket || nextToken.type === TokenType.Identifier)) {
342
+ modifierToken = consume(TokenType.Identifier);
343
+ }
344
+ }
345
+ if (match(TokenType.LeftBracket)) {
346
+ consume(TokenType.LeftBracket);
347
+ property = {
348
+ type: "object",
349
+ properties: {},
350
+ [AST.LOCATION_SYMBOL]: {
351
+ attributes: {},
352
+ arrays: {}
353
+ }
354
+ };
355
+ while (!match(TokenType.RightBracket)) {
356
+ const { value: keyword, location } = current();
357
+ switch (keyword) {
358
+ case "writable":
359
+ case "required": {
360
+ consume(TokenType.Keyword);
361
+ const { value, symbols } = parseArrayBlock();
362
+ property[keyword] = value;
363
+ property[AST.LOCATION_SYMBOL].arrays[keyword] = symbols;
364
+ break;
365
+ }
366
+ case "properties": {
367
+ consume(TokenType.Keyword);
368
+ nestedProperties = parsePropertiesBlock(options);
369
+ break;
370
+ }
371
+ default:
372
+ throw new Diagnostic(`invalid keyword "${keyword}"`, location);
373
+ }
374
+ }
375
+ consume(TokenType.RightBracket);
376
+ } else {
377
+ const { value: identifier, location } = consume(TokenType.Identifier);
378
+ if (guards.isNativePropertyType(identifier)) {
379
+ switch (identifier) {
380
+ case "enum": {
381
+ property = {
382
+ enum: []
383
+ };
384
+ break;
385
+ }
386
+ case "date": {
387
+ property = {
388
+ type: "string",
389
+ format: "date"
390
+ };
391
+ break;
392
+ }
393
+ case "datetime": {
394
+ property = {
395
+ type: "string",
396
+ format: "date-time"
397
+ };
398
+ break;
399
+ }
400
+ default:
401
+ property = {
402
+ type: AST.PropertyType[identifier]
403
+ };
404
+ }
405
+ } else {
406
+ const collection = ast.collections.find((node2) => node2.name === identifier);
407
+ if (!collection) {
408
+ throw new Diagnostic(`invalid reference "${identifier}"`, location);
409
+ }
410
+ property = {
411
+ $ref: identifier
412
+ };
413
+ }
414
+ }
415
+ while (match(TokenType.AttributeName)) {
416
+ const { value: attributeName, location } = consume(TokenType.AttributeName);
417
+ if (match(TokenType.LeftParens)) {
418
+ consume(TokenType.LeftParens);
419
+ const attributeSymbol = Symbol();
420
+ locationMap.set(attributeSymbol, next().location);
421
+ property[AST.LOCATION_SYMBOL] ??= {
422
+ attributes: {},
423
+ arrays: {}
424
+ };
425
+ property[AST.LOCATION_SYMBOL].attributes[attributeName] = attributeSymbol;
426
+ parsePropertyAttributeValue(attributeName, property, location);
427
+ consume(TokenType.RightParens);
428
+ } else {
429
+ parsePropertyAttributeValue(attributeName, property, location);
430
+ }
431
+ }
432
+ const node = {
433
+ kind: "property",
434
+ property,
435
+ nestedProperties
436
+ };
437
+ if (modifierToken) {
438
+ node.modifier = modifierToken.value;
439
+ }
440
+ return node;
441
+ };
442
+ const parsePropertiesBlock = (options = {
443
+ allowModifiers: false
444
+ }) => {
445
+ consume(TokenType.LeftBracket);
446
+ const properties = {};
447
+ while (!match(TokenType.RightBracket)) {
448
+ try {
449
+ const { value: propName } = consume(TokenType.Identifier);
450
+ properties[propName] = parsePropertyType(options);
451
+ if (match(TokenType.Comma)) {
452
+ consume(TokenType.Comma);
453
+ }
454
+ } catch (err) {
455
+ if (err instanceof Diagnostic) {
456
+ errors.push(err);
457
+ recoverLoop: for (; ; ) {
458
+ switch (current().type) {
459
+ case TokenType.RightBracket:
460
+ case TokenType.Identifier: {
461
+ break recoverLoop;
462
+ }
463
+ }
464
+ while (match(TokenType.AttributeName)) {
465
+ advance();
466
+ if (match(TokenType.LeftParens)) {
467
+ advance();
468
+ while (!match(TokenType.RightParens)) {
469
+ advance();
470
+ }
471
+ }
472
+ }
473
+ advance();
474
+ foldBrackets();
475
+ }
476
+ continue;
477
+ }
478
+ }
479
+ }
480
+ consume(TokenType.RightBracket);
481
+ return properties;
482
+ };
483
+ const parseMultiplePropertyTypes = (options = {
484
+ allowModifiers: false
485
+ }) => {
486
+ if (match(TokenType.Pipe)) {
487
+ consume(TokenType.Pipe);
488
+ const properties = [];
489
+ while (index < tokens.length) {
490
+ properties.push(parsePropertyType(options));
491
+ if (match(TokenType.Pipe)) {
492
+ consume(TokenType.Pipe);
493
+ } else {
494
+ break;
495
+ }
496
+ }
497
+ return properties;
498
+ }
499
+ return parsePropertyType(options);
500
+ };
501
+ const parseCollection = (ast2) => {
502
+ consume(TokenType.Keyword, "collection");
503
+ const { value: name } = consume(TokenType.Identifier);
504
+ const node = {
505
+ kind: "collection",
506
+ name,
507
+ properties: {},
508
+ [AST.LOCATION_SYMBOL]: {
509
+ arrays: {}
510
+ }
511
+ };
512
+ if (match(TokenType.Keyword, "extends")) {
513
+ consume(TokenType.Keyword);
514
+ const { value: packageName } = consume(TokenType.Identifier);
515
+ consume(TokenType.Dot);
516
+ const { value: symbolName } = consume(TokenType.Identifier);
517
+ node.extends = {
518
+ packageName,
519
+ symbolName: symbolName[0].toLowerCase() + symbolName.slice(1)
520
+ };
521
+ }
522
+ consume(TokenType.LeftBracket);
523
+ while (!match(TokenType.RightBracket)) {
524
+ const { value: keyword } = consume(TokenType.Keyword, lexer.COLLECTION_KEYWORDS);
525
+ try {
526
+ switch (keyword) {
527
+ case "owned": {
528
+ if (match(TokenType.QuotedString, [
529
+ "always",
530
+ "on-write"
531
+ ])) {
532
+ node.owned = consume(TokenType.QuotedString).value;
533
+ }
534
+ break;
535
+ }
536
+ case "icon": {
537
+ const { value } = consume(TokenType.QuotedString, ICON_NAMES);
538
+ node.icon = value;
539
+ break;
540
+ }
541
+ case "properties": {
542
+ node.properties = parsePropertiesBlock();
543
+ break;
544
+ }
545
+ case "functions": {
546
+ node.functions = parseFunctionsBlock(ast2);
547
+ break;
548
+ }
549
+ case "individualActions":
550
+ case "actions": {
551
+ node[keyword] = parseActionsBlock();
552
+ break;
553
+ }
554
+ case "required": {
555
+ node.required = parseArrayBlockWithAttributes();
556
+ break;
557
+ }
558
+ case "presets": {
559
+ const { value, symbols } = parseArrayBlock(DESCRIPTION_PRESETS);
560
+ node[keyword] = value;
561
+ node[AST.LOCATION_SYMBOL].arrays[keyword] = symbols;
562
+ break;
563
+ }
564
+ case "indexes":
565
+ case "form":
566
+ case "table":
567
+ case "filters": {
568
+ const { value, symbols } = parseArrayBlock();
569
+ node[keyword] = value;
570
+ node[AST.LOCATION_SYMBOL].arrays[keyword] = symbols;
571
+ break;
572
+ }
573
+ case "search": {
574
+ node[keyword] = parseSearchBlock();
575
+ break;
576
+ }
577
+ }
578
+ } catch (err) {
579
+ if (err instanceof Diagnostic) {
580
+ errors.push(err);
581
+ recover(lexer.COLLECTION_KEYWORDS);
582
+ continue;
583
+ }
584
+ }
585
+ }
586
+ consume(TokenType.RightBracket);
587
+ return node;
588
+ };
589
+ const parseContract = () => {
590
+ consume(TokenType.Keyword, "contract");
591
+ const { value: name } = consume(TokenType.Identifier);
592
+ consume(TokenType.LeftBracket);
593
+ const node = {
594
+ kind: "contract",
595
+ name
596
+ };
597
+ while (!match(TokenType.RightBracket)) {
598
+ const { value: keyword } = consume(TokenType.Keyword, lexer.CONTRACT_KEYWORDS);
599
+ switch (keyword) {
600
+ case "payload": {
601
+ node.payload = parsePropertyType({
602
+ allowModifiers: true
603
+ });
604
+ break;
605
+ }
606
+ case "query": {
607
+ node.query = parsePropertyType({
608
+ allowModifiers: true
609
+ });
610
+ break;
611
+ }
612
+ case "response": {
613
+ node.response = parseMultiplePropertyTypes({
614
+ allowModifiers: true
615
+ });
616
+ break;
617
+ }
618
+ }
619
+ }
620
+ consume(TokenType.RightBracket);
621
+ return node;
622
+ };
623
+ const parseFunctionsBlock = (ast2) => {
624
+ consume(TokenType.LeftBracket);
625
+ const functions = {};
626
+ while (!match(TokenType.RightBracket)) {
627
+ if (match(TokenType.MacroName)) {
628
+ const { value: macroName } = consume(TokenType.MacroName, ["include"]);
629
+ switch (macroName) {
630
+ case "include": {
631
+ const { value: functionSetName, location } = consume(TokenType.Identifier);
632
+ const functionset = ast2.functionsets.find((node) => node.name === functionSetName);
633
+ if (!functionset) {
634
+ throw new Diagnostic(`functionset "${functionSetName}" not found`, location);
635
+ }
636
+ Object.assign(functions, functionset.functions);
637
+ consume(TokenType.RightParens);
638
+ }
639
+ }
640
+ continue;
641
+ }
642
+ const { value: functionName } = consume(TokenType.Identifier);
643
+ functions[functionName] = {
644
+ accessCondition: false
645
+ };
646
+ while (match(TokenType.AttributeName, "expose")) {
647
+ consume(TokenType.AttributeName, "expose");
648
+ if (match(TokenType.LeftParens)) {
649
+ consume(TokenType.LeftParens);
650
+ if (match(TokenType.Boolean)) {
651
+ const { value } = consume(TokenType.Boolean);
652
+ functions[functionName] = {
653
+ accessCondition: value
654
+ };
655
+ } else if (match(TokenType.QuotedString, [
656
+ "unauthenticated",
657
+ "unauthenticated-only"
658
+ ])) {
659
+ const { value } = consume(TokenType.QuotedString, [
660
+ "unauthenticated",
661
+ "unauthenticated-only"
662
+ ]);
663
+ functions[functionName] = {
664
+ accessCondition: value
665
+ };
666
+ } else {
667
+ const value = parseArray(TokenType.QuotedString);
668
+ functions[functionName] = {
669
+ accessCondition: value
670
+ };
671
+ }
672
+ consume(TokenType.RightParens);
673
+ } else {
674
+ functions[functionName] = {
675
+ accessCondition: true
676
+ };
677
+ }
678
+ }
679
+ }
680
+ consume(TokenType.RightBracket);
681
+ return functions;
682
+ };
683
+ const parseFunctionSet = (ast2) => {
684
+ consume(TokenType.Keyword, "functionset");
685
+ const { value: name } = consume(TokenType.Identifier);
686
+ const node = {
687
+ kind: "functionset",
688
+ name,
689
+ functions: parseFunctionsBlock(ast2)
690
+ };
691
+ return node;
692
+ };
693
+ const parseActionsBlock = () => {
694
+ const actions = {};
695
+ consume(TokenType.LeftBracket);
696
+ while (!match(TokenType.RightBracket)) {
697
+ const { value: actionName } = consume(TokenType.Identifier);
698
+ consume(TokenType.LeftBracket);
699
+ const baseSlots = {};
700
+ const slots = {
701
+ route: {
702
+ route: {
703
+ name: ""
704
+ }
705
+ },
706
+ function: {},
707
+ event: {}
708
+ };
709
+ let actionType;
710
+ while (!match(TokenType.RightBracket)) {
711
+ const { value: keyword } = consume(TokenType.Keyword, lexer.COLLECTION_ACTIONS_KEYWORDS);
712
+ switch (keyword) {
713
+ case "icon": {
714
+ const { value } = consume(TokenType.QuotedString, ICON_NAMES);
715
+ baseSlots[keyword] = value;
716
+ break;
717
+ }
718
+ case "label": {
719
+ const { value } = consume(TokenType.QuotedString);
720
+ baseSlots[keyword] = value;
721
+ break;
722
+ }
723
+ case "ask":
724
+ case "button":
725
+ case "translate": {
726
+ const { value } = consume(TokenType.Boolean);
727
+ baseSlots[keyword] = value;
728
+ break;
729
+ }
730
+ case "roles":
731
+ case "requires": {
732
+ const value = parseArray(TokenType.Identifier);
733
+ baseSlots[keyword] = value;
734
+ break;
735
+ }
736
+ case "route": {
737
+ const { value } = consume(TokenType.QuotedString);
738
+ actionType = "route";
739
+ slots.route.route.name = value;
740
+ break;
741
+ }
742
+ case "setItem":
743
+ case "fetchItem":
744
+ case "clearItem": {
745
+ const { value } = consume(TokenType.Boolean);
746
+ slots.route.route[keyword] = value;
747
+ break;
748
+ }
749
+ case "function": {
750
+ const { value } = consume(TokenType.QuotedString);
751
+ actionType = "function";
752
+ slots.function.function = value;
753
+ break;
754
+ }
755
+ case "effect": {
756
+ const { value } = consume(TokenType.QuotedString);
757
+ slots.function.effect = value;
758
+ break;
759
+ }
760
+ case "selection": {
761
+ const { value } = consume(TokenType.Boolean);
762
+ slots.function.selection = value;
763
+ break;
764
+ }
765
+ case "event": {
766
+ const { value } = consume(TokenType.QuotedString);
767
+ actionType = "event";
768
+ slots.event.event = value;
769
+ break;
770
+ }
771
+ }
772
+ }
773
+ if (actionType) {
774
+ actions[actionName] = {
775
+ ...baseSlots,
776
+ ...slots[actionType]
777
+ };
778
+ } else {
779
+ actions[actionName] = baseSlots;
780
+ }
781
+ consume(TokenType.RightBracket);
782
+ }
783
+ consume(TokenType.RightBracket);
784
+ return actions;
785
+ };
786
+ const parseSearchBlock = () => {
787
+ const searchSlots = {};
788
+ const { location } = consume(TokenType.LeftBracket);
789
+ while (!match(TokenType.RightBracket)) {
790
+ const { value: keyword } = consume(TokenType.Keyword, lexer.COLLECTION_SEARCH_KEYWORDS);
791
+ switch (keyword) {
792
+ case "placeholder": {
793
+ const { value } = consume(TokenType.QuotedString);
794
+ searchSlots[keyword] = value;
795
+ break;
796
+ }
797
+ case "exactMatches": {
798
+ const { value } = consume(TokenType.Boolean);
799
+ searchSlots[keyword] = value;
800
+ break;
801
+ }
802
+ case "indexes": {
803
+ const { value } = parseArrayBlock();
804
+ searchSlots[keyword] = value;
805
+ break;
806
+ }
807
+ }
808
+ }
809
+ const { indexes } = searchSlots;
810
+ if (!indexes) {
811
+ throw new Diagnostic('"indexes" option is required', location);
812
+ }
813
+ consume(TokenType.RightBracket);
814
+ return {
815
+ ...searchSlots,
816
+ indexes
817
+ };
818
+ };
819
+ while (index < tokens.length) {
820
+ const { value: declType, location } = current();
821
+ try {
822
+ switch (declType) {
823
+ case "collection": {
824
+ ast.collections.push(parseCollection(ast));
825
+ break;
826
+ }
827
+ case "contract": {
828
+ ast.contracts.push(parseContract());
829
+ break;
830
+ }
831
+ case "functionset": {
832
+ ast.functionsets.push(parseFunctionSet(ast));
833
+ break;
834
+ }
835
+ default:
836
+ throw new Diagnostic(`invalid declaration type: "${declType}"`, location);
837
+ }
838
+ } catch (err) {
839
+ if (err instanceof Diagnostic) {
840
+ errors.push(err);
841
+ recover(lexer.TOPLEVEL_KEYWORDS);
842
+ continue;
843
+ }
844
+ throw err;
845
+ }
846
+ }
847
+ return {
848
+ ast,
849
+ errors
850
+ };
851
+ };