@aeriajs/compiler 0.0.62 → 0.0.63

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/dist/parser.mjs DELETED
@@ -1,1304 +0,0 @@
1
- "use strict";
2
- import { DESCRIPTION_PRESETS, LAYOUT_NAMES, PROPERTY_ARRAY_ELEMENTS, PROPERTY_FORMATS, PROPERTY_INPUT_ELEMENTS, PROPERTY_INPUT_TYPES } from "@aeriajs/types";
3
- import { icons } from "@phosphor-icons/core";
4
- import * as AST from "./ast.mjs";
5
- import * as guards from "./guards.mjs";
6
- import * as lexer from "./lexer.mjs";
7
- import { TokenType } from "./token.mjs";
8
- import { Diagnostic } from "./diagnostic.mjs";
9
- import { DEFAULT_EXPORT_SYMBOLS } from "./utils.mjs";
10
- const MAX_ERROR_MESSAGE_ITEMS = 20;
11
- const ICON_NAMES = icons.map((icon) => icon.name);
12
- export const locationMap = /* @__PURE__ */ new WeakMap();
13
- export const memoTable = {
14
- defaultExportSymbols: DEFAULT_EXPORT_SYMBOLS
15
- };
16
- const isFileProperty = (property) => {
17
- return property.$ref === "File";
18
- };
19
- const checkForValidRoles = (roles, symbols) => {
20
- if (memoTable.roles) {
21
- for (const [i, role] of roles.entries()) {
22
- const symbol = symbols[i];
23
- if (!memoTable.roles.includes(role)) {
24
- const location = locationMap.get(symbol);
25
- throw new Diagnostic(`invalid role "${role}"`, location);
26
- }
27
- }
28
- }
29
- return roles;
30
- };
31
- export const parse = (tokens) => {
32
- let index = 0;
33
- const ast = {
34
- kind: "program",
35
- collections: [],
36
- contracts: [],
37
- functionsets: []
38
- };
39
- const errors = [];
40
- const advance = () => index++;
41
- const rollback = () => index--;
42
- const next = () => {
43
- const token = tokens[index + 1];
44
- if (!token) {
45
- throw new Diagnostic("unexpected EOF", current().location);
46
- }
47
- return token;
48
- };
49
- const previous = () => {
50
- const token = tokens[index - 1];
51
- if (!token) {
52
- throw new Diagnostic("invalid position");
53
- }
54
- return token;
55
- };
56
- const current = () => {
57
- const token = tokens[index];
58
- if (!token) {
59
- throw new Diagnostic("unexpected EOF", previous().location);
60
- }
61
- return token;
62
- };
63
- const foldBrackets = () => {
64
- if (match(TokenType.LeftBracket)) {
65
- advance();
66
- while (!match(TokenType.RightBracket)) {
67
- foldBrackets();
68
- advance();
69
- }
70
- }
71
- };
72
- const match = (expected, value) => {
73
- const token = current();
74
- if (token.type === expected) {
75
- if (value !== void 0) {
76
- return Array.isArray(value) ? value.includes(token.value) : token.value === value;
77
- }
78
- return true;
79
- }
80
- return false;
81
- };
82
- const consume = (expected, value) => {
83
- const token = current();
84
- if (match(expected, value)) {
85
- advance();
86
- return token;
87
- }
88
- let expectedValue;
89
- if (value) {
90
- expectedValue = Array.isArray(value) ? value.slice(0, MAX_ERROR_MESSAGE_ITEMS).map((elem) => `"${elem}"`).join(" | ") : `"${value}"`;
91
- }
92
- if (Array.isArray(value) && value.length > MAX_ERROR_MESSAGE_ITEMS) {
93
- expectedValue += " | ...";
94
- }
95
- throw new Diagnostic(
96
- expectedValue ? `expected ${expected} with value ${expectedValue} but found ${token.type} with value "${token.value}" instead` : `expected ${expected} but found ${token.type} instead`,
97
- token.location
98
- );
99
- };
100
- const recover = (keywords) => {
101
- let token;
102
- while (token = tokens[++index]) {
103
- if (token.type === TokenType.Keyword && keywords.includes(token.value)) {
104
- break;
105
- }
106
- }
107
- };
108
- const parseArray = (types) => {
109
- const array = [];
110
- const symbols = [];
111
- const { location: openingLocation } = consume(TokenType.LeftSquareBracket);
112
- if (match(TokenType.RightSquareBracket)) {
113
- consume(TokenType.RightSquareBracket);
114
- return {
115
- value: [],
116
- symbols: []
117
- };
118
- }
119
- let type;
120
- for (const typeCandidate of types) {
121
- if (match(typeCandidate)) {
122
- type = typeCandidate;
123
- break;
124
- }
125
- }
126
- if (!type) {
127
- throw new Diagnostic(`array got an invalid type, accepted ones are: ${types.join(" | ")}`, openingLocation);
128
- }
129
- while (!match(TokenType.RightSquareBracket)) {
130
- const { value, location } = consume(type);
131
- const elemSymbol = Symbol();
132
- array.push(value);
133
- symbols.push(elemSymbol);
134
- locationMap.set(elemSymbol, location);
135
- if (match(TokenType.Comma)) {
136
- consume(TokenType.Comma);
137
- }
138
- }
139
- consume(TokenType.RightSquareBracket);
140
- return {
141
- value: array,
142
- symbols
143
- };
144
- };
145
- const parseArrayBlock = (value) => {
146
- const array = [];
147
- const symbols = [];
148
- consume(TokenType.LeftBracket);
149
- while (!match(TokenType.RightBracket)) {
150
- const { value: identifier, location } = consume(TokenType.Identifier, value);
151
- const elemSymbol = Symbol();
152
- array.push(identifier);
153
- symbols.push(elemSymbol);
154
- locationMap.set(elemSymbol, location);
155
- if (match(TokenType.Comma)) {
156
- consume(TokenType.Comma);
157
- }
158
- }
159
- consume(TokenType.RightBracket);
160
- return {
161
- value: array,
162
- symbols
163
- };
164
- };
165
- const parseArrayBlockWithAttributes = (allowedAttributes, cb) => {
166
- const array = {};
167
- const symbols = [];
168
- let hasAttributes = false;
169
- consume(TokenType.LeftBracket);
170
- while (!match(TokenType.RightBracket)) {
171
- const { value: identifier, location } = consume(TokenType.Identifier);
172
- array[identifier] = true;
173
- const elemSymbol = Symbol();
174
- symbols.push(elemSymbol);
175
- locationMap.set(elemSymbol, location);
176
- if (match(TokenType.AttributeName)) {
177
- hasAttributes = true;
178
- }
179
- while (match(TokenType.AttributeName)) {
180
- array[identifier] = {};
181
- const { value: attributeName } = consume(TokenType.AttributeName, allowedAttributes);
182
- cb(attributeName, array, identifier);
183
- }
184
- }
185
- consume(TokenType.RightBracket);
186
- const value = hasAttributes ? array : Object.keys(array);
187
- return {
188
- value,
189
- symbols
190
- };
191
- };
192
- const parsePropertyAttributeValue = (attributeName, property, location) => {
193
- const consumeBoolean = () => {
194
- if (match(TokenType.Boolean)) {
195
- const { value } = consume(TokenType.Boolean);
196
- return value;
197
- }
198
- return true;
199
- };
200
- if ("enum" in property && attributeName === "values") {
201
- property.enum = parseArray([
202
- TokenType.QuotedString,
203
- TokenType.Number
204
- ]).value;
205
- return;
206
- }
207
- if ("const" in property && attributeName === "value") {
208
- const token = current();
209
- advance();
210
- switch (token.type) {
211
- case TokenType.Number:
212
- case TokenType.Boolean:
213
- case TokenType.Null:
214
- case TokenType.QuotedString: {
215
- property.const = token.value;
216
- return;
217
- }
218
- default: {
219
- throw new Diagnostic(`const received invalid value: "${token.value}"`, location);
220
- }
221
- }
222
- }
223
- switch (attributeName) {
224
- case "icon": {
225
- const { value } = consume(TokenType.QuotedString, ICON_NAMES);
226
- property[attributeName] = value;
227
- return;
228
- }
229
- case "hint":
230
- case "description": {
231
- const { value } = consume(TokenType.QuotedString);
232
- property[attributeName] = value;
233
- return;
234
- }
235
- case "hidden":
236
- case "translate": {
237
- property[attributeName] = consumeBoolean();
238
- return;
239
- }
240
- }
241
- if ("$ref" in property) {
242
- switch (attributeName) {
243
- case "purge":
244
- case "inline": {
245
- property[attributeName] = consumeBoolean();
246
- return;
247
- }
248
- case "select":
249
- case "form":
250
- case "populate":
251
- case "indexes": {
252
- property[attributeName] = parseArray([TokenType.Identifier]).value;
253
- return;
254
- }
255
- case "populateDepth": {
256
- const { value } = consume(TokenType.Number);
257
- property[attributeName] = value;
258
- return;
259
- }
260
- case "constraints": {
261
- const constraintTerms = [];
262
- rollback();
263
- property[attributeName] = parseCondition(constraintTerms);
264
- property[AST.LOCATION_SYMBOL].contraintTerms = constraintTerms;
265
- rollback();
266
- return;
267
- }
268
- }
269
- if (isFileProperty(property)) {
270
- switch (attributeName) {
271
- case "extensions":
272
- case "accept": {
273
- property[attributeName] = parseArray([TokenType.QuotedString]).value;
274
- return;
275
- }
276
- }
277
- }
278
- }
279
- if ("type" in property) {
280
- switch (property.type) {
281
- case "string": {
282
- switch (attributeName) {
283
- case "format": {
284
- const { value } = consume(TokenType.QuotedString, PROPERTY_FORMATS);
285
- property[attributeName] = value;
286
- return;
287
- }
288
- case "mask": {
289
- if (match(TokenType.LeftSquareBracket)) {
290
- property[attributeName] = parseArray([TokenType.QuotedString]).value;
291
- return;
292
- } else {
293
- const { value } = consume(TokenType.QuotedString);
294
- property[attributeName] = value;
295
- return;
296
- }
297
- }
298
- case "maskedValue": {
299
- const { value } = consume(TokenType.Boolean);
300
- property[attributeName] = value;
301
- return;
302
- }
303
- case "minLength":
304
- case "maxLength": {
305
- const { value } = consume(TokenType.Number);
306
- property[attributeName] = value;
307
- return;
308
- }
309
- case "inputType": {
310
- const { value } = consume(TokenType.QuotedString, PROPERTY_INPUT_TYPES);
311
- property[attributeName] = value;
312
- return;
313
- }
314
- case "element": {
315
- const { value } = consume(TokenType.QuotedString, PROPERTY_INPUT_ELEMENTS);
316
- property[attributeName] = value;
317
- return;
318
- }
319
- case "placeholder": {
320
- const { value } = consume(TokenType.QuotedString);
321
- property[attributeName] = value;
322
- return;
323
- }
324
- }
325
- break;
326
- }
327
- case "integer":
328
- case "number": {
329
- switch (attributeName) {
330
- case "exclusiveMinimum":
331
- case "exclusiveMaximum":
332
- case "minimum":
333
- case "maximum": {
334
- const { value } = consume(TokenType.Number);
335
- property[attributeName] = value;
336
- return;
337
- }
338
- case "placeholder": {
339
- const { value } = consume(TokenType.QuotedString);
340
- property[attributeName] = value;
341
- return;
342
- }
343
- }
344
- break;
345
- }
346
- case "array": {
347
- switch (attributeName) {
348
- case "uniqueItems": {
349
- const { value } = consume(TokenType.Boolean);
350
- property[attributeName] = value;
351
- return;
352
- }
353
- case "element": {
354
- const { value } = consume(TokenType.QuotedString, PROPERTY_ARRAY_ELEMENTS);
355
- property[attributeName] = value;
356
- return;
357
- }
358
- }
359
- }
360
- }
361
- }
362
- throw new Diagnostic(`invalid attribute name "${attributeName}"`, location);
363
- };
364
- const parsePropertyType = (options = {
365
- allowModifiers: false
366
- }) => {
367
- let property;
368
- let nestedProperties;
369
- let nestedAdditionalProperties;
370
- let modifierToken;
371
- const typeSymbol = Symbol();
372
- if (options.allowModifiers) {
373
- const nextToken = next();
374
- const currentTokenValue = current().value;
375
- if (match(TokenType.Identifier) && typeof currentTokenValue === "string" && guards.isValidPropertyModifier(currentTokenValue) && (nextToken.type === TokenType.LeftBracket || nextToken.type === TokenType.LeftSquareBracket || nextToken.type === TokenType.Identifier)) {
376
- modifierToken = consume(TokenType.Identifier);
377
- }
378
- }
379
- if (match(TokenType.LeftSquareBracket)) {
380
- consume(TokenType.LeftSquareBracket);
381
- const arrayProperty = {
382
- type: "array"
383
- };
384
- while (!match(TokenType.RightSquareBracket)) {
385
- const attributeSymbol = Symbol();
386
- arrayProperty[AST.LOCATION_SYMBOL] ??= {
387
- type: typeSymbol,
388
- attributes: {},
389
- arrays: {}
390
- };
391
- if (match(TokenType.Range)) {
392
- const { value: rangeSeparator } = consume(TokenType.Range);
393
- let attributeName2;
394
- const minItems = rangeSeparator[0];
395
- if (!isNaN(minItems)) {
396
- attributeName2 = "minItems";
397
- arrayProperty[attributeName2] = minItems, arrayProperty[AST.LOCATION_SYMBOL].attributes[attributeName2] = attributeSymbol;
398
- }
399
- const maxItems = rangeSeparator[1];
400
- if (!isNaN(maxItems)) {
401
- attributeName2 = "maxItems";
402
- arrayProperty[attributeName2] = maxItems;
403
- arrayProperty[AST.LOCATION_SYMBOL].attributes[attributeName2] = attributeSymbol;
404
- }
405
- continue;
406
- }
407
- const { value: attributeName, location } = consume(TokenType.AttributeName);
408
- if (match(TokenType.LeftParens)) {
409
- consume(TokenType.LeftParens);
410
- locationMap.set(attributeSymbol, next().location);
411
- arrayProperty[AST.LOCATION_SYMBOL].attributes[attributeName] = attributeSymbol;
412
- parsePropertyAttributeValue(attributeName, arrayProperty, location);
413
- consume(TokenType.RightParens);
414
- } else {
415
- parsePropertyAttributeValue(attributeName, arrayProperty, location);
416
- }
417
- }
418
- consume(TokenType.RightSquareBracket);
419
- const { property: items, nestedProperties: nestedProperties2 } = parsePropertyType(options);
420
- property = {
421
- ...arrayProperty,
422
- items
423
- };
424
- locationMap.set(typeSymbol, current().location);
425
- return {
426
- kind: "property",
427
- property,
428
- nestedProperties: nestedProperties2
429
- };
430
- }
431
- locationMap.set(typeSymbol, current().location);
432
- if (match(TokenType.LeftBracket)) {
433
- consume(TokenType.LeftBracket);
434
- property = {
435
- type: "object",
436
- properties: {},
437
- [AST.LOCATION_SYMBOL]: {
438
- type: typeSymbol,
439
- attributes: {},
440
- arrays: {}
441
- }
442
- };
443
- while (!match(TokenType.RightBracket)) {
444
- const { value: keyword, location } = consume(TokenType.Keyword, lexer.COLLECTION_KEYWORDS);
445
- switch (keyword) {
446
- case "writable":
447
- case "required": {
448
- const { value, symbols } = parseArrayBlock();
449
- property[keyword] = value;
450
- property[AST.LOCATION_SYMBOL].arrays[keyword] = symbols;
451
- break;
452
- }
453
- case "properties": {
454
- nestedProperties = parsePropertiesBlock(options);
455
- break;
456
- }
457
- case "additionalProperties": {
458
- if (match(TokenType.Boolean)) {
459
- nestedAdditionalProperties = consume(TokenType.Boolean).value;
460
- } else {
461
- nestedAdditionalProperties = parsePropertyType();
462
- }
463
- break;
464
- }
465
- default:
466
- throw new Diagnostic(`invalid keyword "${keyword}"`, location);
467
- }
468
- }
469
- consume(TokenType.RightBracket);
470
- } else {
471
- const { value: identifier } = consume(TokenType.Identifier);
472
- if (guards.isNativePropertyType(identifier)) {
473
- switch (identifier) {
474
- case "enum": {
475
- property = {
476
- enum: []
477
- };
478
- break;
479
- }
480
- case "const": {
481
- property = {
482
- const: null
483
- };
484
- break;
485
- }
486
- case "date": {
487
- property = {
488
- type: "string",
489
- format: "date"
490
- };
491
- break;
492
- }
493
- case "datetime": {
494
- property = {
495
- type: "string",
496
- format: "date-time"
497
- };
498
- break;
499
- }
500
- case "objectid": {
501
- property = {
502
- type: "string",
503
- format: "objectid"
504
- };
505
- break;
506
- }
507
- default:
508
- property = {
509
- type: AST.PropertyType[identifier]
510
- };
511
- }
512
- } else {
513
- property = {
514
- $ref: identifier,
515
- [AST.LOCATION_SYMBOL]: {
516
- type: typeSymbol,
517
- attributes: {},
518
- arrays: {}
519
- }
520
- };
521
- }
522
- }
523
- while (match(TokenType.AttributeName)) {
524
- const { value: attributeName, location } = consume(TokenType.AttributeName);
525
- if (match(TokenType.LeftParens)) {
526
- consume(TokenType.LeftParens);
527
- const attributeSymbol = Symbol();
528
- locationMap.set(attributeSymbol, next().location);
529
- property[AST.LOCATION_SYMBOL] ??= {
530
- type: typeSymbol,
531
- attributes: {},
532
- arrays: {}
533
- };
534
- property[AST.LOCATION_SYMBOL].attributes[attributeName] = attributeSymbol;
535
- parsePropertyAttributeValue(attributeName, property, location);
536
- consume(TokenType.RightParens);
537
- } else {
538
- parsePropertyAttributeValue(attributeName, property, location);
539
- }
540
- }
541
- const node = {
542
- kind: "property",
543
- property,
544
- nestedProperties,
545
- nestedAdditionalProperties
546
- };
547
- if (modifierToken) {
548
- node.modifier = modifierToken.value;
549
- }
550
- return node;
551
- };
552
- const parsePropertiesBlock = (options = {
553
- allowModifiers: false
554
- }) => {
555
- consume(TokenType.LeftBracket);
556
- const properties = {};
557
- while (!match(TokenType.RightBracket)) {
558
- try {
559
- const { value: propName } = consume(TokenType.Identifier);
560
- properties[propName] = parsePropertyType(options);
561
- if (match(TokenType.Comma)) {
562
- consume(TokenType.Comma);
563
- }
564
- } catch (err) {
565
- if (err instanceof Diagnostic) {
566
- errors.push(err);
567
- recoverLoop: for (; ; ) {
568
- switch (current().type) {
569
- case TokenType.RightBracket:
570
- case TokenType.Identifier: {
571
- break recoverLoop;
572
- }
573
- }
574
- while (match(TokenType.AttributeName)) {
575
- advance();
576
- if (match(TokenType.LeftParens)) {
577
- advance();
578
- while (!match(TokenType.RightParens)) {
579
- advance();
580
- }
581
- }
582
- }
583
- advance();
584
- foldBrackets();
585
- }
586
- continue;
587
- }
588
- throw err;
589
- }
590
- }
591
- consume(TokenType.RightBracket);
592
- return properties;
593
- };
594
- const parseMultiplePropertyTypes = (options = {
595
- allowModifiers: false
596
- }) => {
597
- if (match(TokenType.Pipe)) {
598
- consume(TokenType.Pipe);
599
- const properties = [];
600
- while (index < tokens.length) {
601
- properties.push(parsePropertyType(options));
602
- if (match(TokenType.Pipe)) {
603
- consume(TokenType.Pipe);
604
- } else {
605
- break;
606
- }
607
- }
608
- return properties;
609
- }
610
- return parsePropertyType(options);
611
- };
612
- const parseAccessCondition = (options = {
613
- arrayBlock: false
614
- }) => {
615
- if (match(TokenType.Boolean)) {
616
- const { value } = consume(TokenType.Boolean);
617
- return value;
618
- } else if (match(TokenType.QuotedString, [
619
- "unauthenticated",
620
- "unauthenticated-only"
621
- ])) {
622
- const { value } = consume(TokenType.QuotedString, [
623
- "unauthenticated",
624
- "unauthenticated-only"
625
- ]);
626
- return value;
627
- } else {
628
- const { value, symbols } = options.arrayBlock ? parseArrayBlock() : parseArray([TokenType.QuotedString]);
629
- return checkForValidRoles(value, symbols);
630
- }
631
- };
632
- const parseCollection = () => {
633
- consume(TokenType.Keyword, "collection");
634
- const { value: name } = consume(TokenType.Identifier);
635
- const node = {
636
- kind: "collection",
637
- name,
638
- properties: {},
639
- [AST.LOCATION_SYMBOL]: {
640
- arrays: {}
641
- }
642
- };
643
- if (match(TokenType.Keyword, "extends")) {
644
- consume(TokenType.Keyword);
645
- const { value: packageName } = match(TokenType.QuotedString) ? consume(TokenType.QuotedString) : consume(TokenType.Identifier);
646
- consume(TokenType.Dot);
647
- const { value: symbolName } = consume(TokenType.Identifier);
648
- node.extends = {
649
- packageName,
650
- importPath: packageName,
651
- symbolName: symbolName[0].toLowerCase() + symbolName.slice(1)
652
- };
653
- }
654
- consume(TokenType.LeftBracket);
655
- while (!match(TokenType.RightBracket)) {
656
- const { value: keyword } = consume(TokenType.Keyword, lexer.COLLECTION_KEYWORDS);
657
- try {
658
- switch (keyword) {
659
- case "middlewares": {
660
- node.middlewares = parseArrayBlock().value;
661
- break;
662
- }
663
- case "owned": {
664
- if (match(TokenType.Boolean)) {
665
- node.owned = consume(TokenType.Boolean).value;
666
- } else {
667
- node.owned = consume(TokenType.QuotedString, [
668
- "always",
669
- "on-write"
670
- ]).value;
671
- }
672
- break;
673
- }
674
- case "icon": {
675
- const { value } = consume(TokenType.QuotedString, ICON_NAMES);
676
- node[keyword] = value;
677
- break;
678
- }
679
- case "properties": {
680
- node[keyword] = parsePropertiesBlock();
681
- break;
682
- }
683
- case "functions": {
684
- const { functions, functionSets } = parseFunctionsBlock();
685
- node.functions = functions;
686
- node.functionSets = functionSets;
687
- break;
688
- }
689
- case "individualActions":
690
- case "actions": {
691
- node[keyword] = parseActionsBlock();
692
- break;
693
- }
694
- case "required": {
695
- const { value, symbols } = parseArrayBlockWithAttributes(["if"], (attributeName, array, identifier) => {
696
- switch (attributeName) {
697
- case "if": {
698
- const ifTerms = [];
699
- array[identifier] = parseCondition(ifTerms);
700
- node[AST.LOCATION_SYMBOL].requiredTerms = ifTerms;
701
- break;
702
- }
703
- }
704
- });
705
- node.required = value;
706
- node[AST.LOCATION_SYMBOL].required = symbols;
707
- break;
708
- }
709
- case "presets": {
710
- const { value, symbols } = parseArrayBlock(DESCRIPTION_PRESETS);
711
- node[keyword] = value;
712
- node[AST.LOCATION_SYMBOL].arrays[keyword] = symbols;
713
- break;
714
- }
715
- case "indexes":
716
- case "form":
717
- case "table":
718
- case "tableMeta":
719
- case "unique":
720
- case "filters": {
721
- const { value, symbols } = parseArrayBlock();
722
- node[keyword] = value;
723
- node[AST.LOCATION_SYMBOL].arrays[keyword] = symbols;
724
- break;
725
- }
726
- case "search": {
727
- const { options, indexesSymbols } = parseSearchBlock();
728
- node[keyword] = options;
729
- node[AST.LOCATION_SYMBOL].searchIndexes = indexesSymbols;
730
- break;
731
- }
732
- case "layout": {
733
- node[keyword] = parseLayoutBlock();
734
- break;
735
- }
736
- case "formLayout": {
737
- node[keyword] = parseFormLayoutBlock();
738
- break;
739
- }
740
- }
741
- } catch (err) {
742
- if (err instanceof Diagnostic) {
743
- errors.push(err);
744
- recover(lexer.COLLECTION_KEYWORDS);
745
- continue;
746
- }
747
- throw err;
748
- }
749
- }
750
- consume(TokenType.RightBracket);
751
- return node;
752
- };
753
- const parseContract = () => {
754
- consume(TokenType.Keyword, "contract");
755
- const { value: name } = consume(TokenType.Identifier);
756
- consume(TokenType.LeftBracket);
757
- const node = {
758
- kind: "contract",
759
- name
760
- };
761
- while (!match(TokenType.RightBracket)) {
762
- const { value: keyword } = consume(TokenType.Keyword, lexer.CONTRACT_KEYWORDS);
763
- switch (keyword) {
764
- case "roles": {
765
- node.roles = parseAccessCondition({
766
- arrayBlock: true
767
- });
768
- break;
769
- }
770
- case "payload": {
771
- node.payload = parsePropertyType({
772
- allowModifiers: true
773
- });
774
- break;
775
- }
776
- case "query": {
777
- node.query = parsePropertyType({
778
- allowModifiers: true
779
- });
780
- break;
781
- }
782
- case "response": {
783
- node.response = parseMultiplePropertyTypes({
784
- allowModifiers: true
785
- });
786
- break;
787
- }
788
- }
789
- }
790
- consume(TokenType.RightBracket);
791
- return node;
792
- };
793
- const parseFunctionsBlock = () => {
794
- consume(TokenType.LeftBracket);
795
- const functions = [];
796
- const functionSets = [];
797
- while (!match(TokenType.RightBracket)) {
798
- try {
799
- if (match(TokenType.MacroName)) {
800
- const { value: macroName } = consume(TokenType.MacroName, ["include"]);
801
- switch (macroName) {
802
- case "include": {
803
- const { value: functionSetName, location } = consume(TokenType.Identifier);
804
- const functionSetSymbol = Symbol();
805
- locationMap.set(functionSetSymbol, location);
806
- functionSets.push([
807
- functionSetName,
808
- functionSetSymbol
809
- ]);
810
- consume(TokenType.RightParens);
811
- break;
812
- }
813
- }
814
- continue;
815
- }
816
- let functionNode;
817
- if (current().type === TokenType.Identifier && next().type === TokenType.Dot) {
818
- const { value: packageName } = match(TokenType.QuotedString) ? consume(TokenType.QuotedString) : consume(TokenType.Identifier);
819
- consume(TokenType.Dot);
820
- const { value: symbolName } = consume(TokenType.Identifier);
821
- functionNode = {
822
- kind: "function",
823
- name: symbolName,
824
- exportSymbol: {
825
- packageName,
826
- importPath: packageName,
827
- symbolName
828
- }
829
- };
830
- } else {
831
- const { value: functionName } = consume(TokenType.Identifier);
832
- let exportSymbol;
833
- if (memoTable.defaultExportSymbols && functionName in memoTable.defaultExportSymbols) {
834
- const packageName = memoTable.defaultExportSymbols[functionName];
835
- exportSymbol = {
836
- packageName,
837
- importPath: packageName,
838
- symbolName: functionName
839
- };
840
- }
841
- functionNode = {
842
- kind: "function",
843
- name: functionName,
844
- exportSymbol
845
- };
846
- }
847
- functions.push(functionNode);
848
- while (match(TokenType.AttributeName, "expose")) {
849
- consume(TokenType.AttributeName, "expose");
850
- if (match(TokenType.LeftParens)) {
851
- consume(TokenType.LeftParens);
852
- functionNode.accessCondition = parseAccessCondition();
853
- consume(TokenType.RightParens);
854
- } else {
855
- functionNode.accessCondition = true;
856
- }
857
- }
858
- } catch (err) {
859
- if (err instanceof Diagnostic) {
860
- let token;
861
- while (token = tokens[++index]) {
862
- if (token.type === TokenType.Identifier || token.type === TokenType.RightBracket) {
863
- break;
864
- }
865
- }
866
- errors.push(err);
867
- continue;
868
- }
869
- throw err;
870
- }
871
- }
872
- consume(TokenType.RightBracket);
873
- return {
874
- functions,
875
- functionSets
876
- };
877
- };
878
- const parseFunctionSet = () => {
879
- consume(TokenType.Keyword, "functionset");
880
- const { value: name } = consume(TokenType.Identifier);
881
- const { functions, functionSets } = parseFunctionsBlock();
882
- const node = {
883
- kind: "functionset",
884
- name,
885
- functions,
886
- functionSets
887
- };
888
- return node;
889
- };
890
- const parseActionsBlock = () => {
891
- const actions = {};
892
- consume(TokenType.LeftBracket);
893
- while (!match(TokenType.RightBracket)) {
894
- const { value: actionName } = consume(TokenType.Identifier);
895
- consume(TokenType.LeftBracket);
896
- const baseSlots = {};
897
- const slots = {
898
- route: {
899
- route: {
900
- name: ""
901
- }
902
- },
903
- function: {},
904
- event: {}
905
- };
906
- let actionType;
907
- while (!match(TokenType.RightBracket)) {
908
- const { value: keyword } = consume(TokenType.Keyword, lexer.COLLECTION_ACTIONS_KEYWORDS);
909
- switch (keyword) {
910
- case "icon": {
911
- const { value } = consume(TokenType.QuotedString, ICON_NAMES);
912
- baseSlots[keyword] = value;
913
- break;
914
- }
915
- case "label": {
916
- const { value } = consume(TokenType.QuotedString);
917
- baseSlots[keyword] = value;
918
- break;
919
- }
920
- case "ask":
921
- case "button":
922
- case "translate": {
923
- const { value } = consume(TokenType.Boolean);
924
- baseSlots[keyword] = value;
925
- break;
926
- }
927
- case "roles": {
928
- const { value, symbols } = parseArray([TokenType.Identifier]);
929
- const roles = checkForValidRoles(value, symbols);
930
- baseSlots[keyword] = roles;
931
- break;
932
- }
933
- case "requires": {
934
- const { value } = parseArrayBlock();
935
- baseSlots[keyword] = value;
936
- break;
937
- }
938
- case "route": {
939
- const { value } = consume(TokenType.QuotedString);
940
- actionType = "route";
941
- slots.route.route.name = value;
942
- break;
943
- }
944
- case "setItem":
945
- case "fetchItem":
946
- case "clearItem": {
947
- const { value } = consume(TokenType.Boolean);
948
- slots.route.route[keyword] = value;
949
- break;
950
- }
951
- case "function": {
952
- const { value } = consume(TokenType.QuotedString);
953
- actionType = "function";
954
- slots.function.function = value;
955
- break;
956
- }
957
- case "effect": {
958
- const { value } = consume(TokenType.QuotedString);
959
- slots.function.effect = value;
960
- break;
961
- }
962
- case "selection": {
963
- const { value } = consume(TokenType.Boolean);
964
- slots.function.selection = value;
965
- break;
966
- }
967
- case "event": {
968
- const { value } = consume(TokenType.QuotedString);
969
- actionType = "event";
970
- slots.event.event = value;
971
- break;
972
- }
973
- }
974
- }
975
- if (actionType) {
976
- actions[actionName] = {
977
- ...baseSlots,
978
- ...slots[actionType]
979
- };
980
- } else {
981
- actions[actionName] = baseSlots;
982
- }
983
- consume(TokenType.RightBracket);
984
- }
985
- consume(TokenType.RightBracket);
986
- return actions;
987
- };
988
- const parseSearchBlock = () => {
989
- const searchSlots = {};
990
- const { location } = consume(TokenType.LeftBracket);
991
- let indexesSymbols;
992
- while (!match(TokenType.RightBracket)) {
993
- const { value: keyword } = consume(TokenType.Keyword, lexer.COLLECTION_SEARCH_KEYWORDS);
994
- switch (keyword) {
995
- case "indexes": {
996
- const { value, symbols } = parseArrayBlock();
997
- searchSlots[keyword] = value;
998
- indexesSymbols = symbols;
999
- break;
1000
- }
1001
- case "placeholder": {
1002
- const { value } = consume(TokenType.QuotedString);
1003
- searchSlots[keyword] = value;
1004
- break;
1005
- }
1006
- case "exactMatches": {
1007
- const { value } = consume(TokenType.Boolean);
1008
- searchSlots[keyword] = value;
1009
- break;
1010
- }
1011
- }
1012
- }
1013
- const { indexes } = searchSlots;
1014
- if (!indexes) {
1015
- throw new Diagnostic('"indexes" option is required', location);
1016
- }
1017
- consume(TokenType.RightBracket);
1018
- const options = {
1019
- ...searchSlots,
1020
- indexes
1021
- };
1022
- return {
1023
- options,
1024
- indexesSymbols
1025
- };
1026
- };
1027
- const parseLayoutBlock = () => {
1028
- let name;
1029
- const options = {};
1030
- const optionsSymbols = {};
1031
- const { location } = consume(TokenType.LeftBracket);
1032
- while (!match(TokenType.RightBracket)) {
1033
- const { value: keyword } = consume(TokenType.Keyword, lexer.COLLECTION_LAYOUT_KEYWORDS);
1034
- switch (keyword) {
1035
- case "name": {
1036
- name = consume(TokenType.QuotedString, LAYOUT_NAMES).value;
1037
- break;
1038
- }
1039
- case "options":
1040
- {
1041
- consume(TokenType.LeftBracket);
1042
- while (!match(TokenType.RightBracket)) {
1043
- const { value: optionsKeyword } = consume(TokenType.Keyword, lexer.COLLECTION_LAYOUT_OPTIONS_KEYWORDS);
1044
- switch (optionsKeyword) {
1045
- case "active":
1046
- case "title":
1047
- case "picture":
1048
- case "badge": {
1049
- const { value, location: location2 } = consume(TokenType.Identifier);
1050
- const symbol = Symbol();
1051
- options[optionsKeyword] = value;
1052
- optionsSymbols[optionsKeyword] = symbol;
1053
- locationMap.set(symbol, location2);
1054
- break;
1055
- }
1056
- case "information": {
1057
- if (match(TokenType.LeftBracket)) {
1058
- const { value, symbols } = parseArrayBlock();
1059
- options[optionsKeyword] = value;
1060
- optionsSymbols[optionsKeyword] = symbols;
1061
- } else {
1062
- const { value, location: location2 } = consume(TokenType.Identifier);
1063
- const symbol = Symbol();
1064
- options[optionsKeyword] = value;
1065
- optionsSymbols[optionsKeyword] = symbol;
1066
- locationMap.set(symbol, location2);
1067
- }
1068
- break;
1069
- }
1070
- case "translateBadge": {
1071
- const { value } = consume(TokenType.Boolean);
1072
- options[optionsKeyword] = value;
1073
- break;
1074
- }
1075
- }
1076
- }
1077
- }
1078
- consume(TokenType.RightBracket);
1079
- break;
1080
- }
1081
- }
1082
- if (!name) {
1083
- throw new Diagnostic('layout must have a "name" property', location);
1084
- }
1085
- consume(TokenType.RightBracket);
1086
- return {
1087
- kind: "layout",
1088
- name,
1089
- options,
1090
- [AST.LOCATION_SYMBOL]: {
1091
- options: optionsSymbols
1092
- }
1093
- };
1094
- };
1095
- const parseFormLayoutBlock = () => {
1096
- const fields = {};
1097
- const node = {
1098
- kind: "formLayout",
1099
- fields,
1100
- [AST.LOCATION_SYMBOL]: {
1101
- fields: {}
1102
- }
1103
- };
1104
- consume(TokenType.LeftBracket);
1105
- while (!match(TokenType.RightBracket)) {
1106
- const { value: keyword, location: keywordLocation } = consume(TokenType.Keyword, lexer.COLLECTION_FORM_LAYOUT_KEYWORDS);
1107
- switch (keyword) {
1108
- case "fields": {
1109
- consume(TokenType.LeftBracket);
1110
- while (!match(TokenType.RightBracket)) {
1111
- const { value: identifier, location: identifierLocation } = consume(TokenType.Identifier);
1112
- const identifierSymbol = Symbol();
1113
- locationMap.set(identifierSymbol, identifierLocation);
1114
- fields[identifier] ??= {};
1115
- node[AST.LOCATION_SYMBOL].fields[identifier] = {
1116
- name: identifierSymbol,
1117
- field: {}
1118
- };
1119
- consume(TokenType.LeftBracket);
1120
- while (!match(TokenType.RightBracket)) {
1121
- const { value: keyword2, location: keywordLocation2 } = consume(TokenType.Keyword, lexer.COLLECTION_FORM_LAYOUT_KEYWORDS);
1122
- switch (keyword2) {
1123
- case "if": {
1124
- const ifTerms = [];
1125
- fields[identifier].if = parseCondition(ifTerms);
1126
- node[AST.LOCATION_SYMBOL].terms = ifTerms;
1127
- break;
1128
- }
1129
- case "span":
1130
- case "verticalSpacing": {
1131
- fields[identifier].span = consume(TokenType.Number).value;
1132
- break;
1133
- }
1134
- case "separator": {
1135
- fields[identifier].separator = match(TokenType.Boolean) ? consume(TokenType.Boolean).value : consume(TokenType.QuotedString, [
1136
- "top",
1137
- "bottom"
1138
- ]).value;
1139
- break;
1140
- }
1141
- default: {
1142
- throw new Diagnostic(`invalid keyword "${keyword2}"`, keywordLocation2);
1143
- }
1144
- }
1145
- }
1146
- consume(TokenType.RightBracket);
1147
- }
1148
- consume(TokenType.RightBracket);
1149
- break;
1150
- }
1151
- default: {
1152
- throw new Diagnostic(`invalid keyword "${keyword}"`, keywordLocation);
1153
- }
1154
- }
1155
- }
1156
- consume(TokenType.RightBracket);
1157
- return node;
1158
- };
1159
- const parseCondition = (symbols = []) => {
1160
- if (match(TokenType.LeftParens)) {
1161
- consume(TokenType.LeftParens);
1162
- let operatorType, newOp = operatorType;
1163
- const conditions = [];
1164
- while (!match(TokenType.RightParens)) {
1165
- conditions.push(parseCondition(symbols));
1166
- if (match(TokenType.RightParens)) {
1167
- break;
1168
- }
1169
- const { value: operatorSymbol2, location: location2 } = consume(TokenType.Operator);
1170
- switch (operatorSymbol2) {
1171
- case "&&":
1172
- newOp = "and";
1173
- break;
1174
- case "||":
1175
- newOp = "or";
1176
- break;
1177
- default: {
1178
- throw new Diagnostic(`unsupported operator: "${operatorSymbol2}"`, location2);
1179
- }
1180
- }
1181
- if (operatorType && operatorType !== newOp) {
1182
- throw new Diagnostic('having "and" or "or" in the same expression is not supported, please use parenthesis', location2);
1183
- }
1184
- operatorType = newOp;
1185
- }
1186
- consume(TokenType.RightParens);
1187
- switch (operatorType) {
1188
- case "and": {
1189
- return {
1190
- and: conditions
1191
- };
1192
- }
1193
- case "or": {
1194
- return {
1195
- or: conditions
1196
- };
1197
- }
1198
- default: {
1199
- return conditions[0];
1200
- }
1201
- }
1202
- }
1203
- if (match(TokenType.Operator, "!")) {
1204
- consume(TokenType.Operator);
1205
- return {
1206
- not: parseCondition(symbols)
1207
- };
1208
- }
1209
- const { value: term1, location: term1Location } = consume(TokenType.Identifier);
1210
- const term1Symbol = Symbol();
1211
- locationMap.set(term1Symbol, term1Location);
1212
- symbols.push([
1213
- term1,
1214
- term1Symbol
1215
- ]);
1216
- if (!match(TokenType.Operator, lexer.FINAL_OPERATORS)) {
1217
- return {
1218
- operator: "truthy",
1219
- term1
1220
- };
1221
- }
1222
- const { value: operatorSymbol, location } = consume(TokenType.Operator);
1223
- let term2;
1224
- if (match(TokenType.LeftParens)) {
1225
- term2 = parseCondition(symbols);
1226
- } else if (match(TokenType.LeftSquareBracket)) {
1227
- term2 = parseArray([
1228
- TokenType.QuotedString,
1229
- TokenType.Number
1230
- ]).value;
1231
- } else {
1232
- term2 = current().value;
1233
- advance();
1234
- }
1235
- let operator;
1236
- switch (operatorSymbol) {
1237
- case "==":
1238
- operator = "equal";
1239
- break;
1240
- case "in":
1241
- operator = "in";
1242
- break;
1243
- case ">=":
1244
- operator = "gte";
1245
- break;
1246
- case "<=":
1247
- operator = "lte";
1248
- break;
1249
- case ">":
1250
- operator = "gt";
1251
- break;
1252
- case "<":
1253
- operator = "lt";
1254
- break;
1255
- default: {
1256
- throw new Diagnostic(`unsupported operator: "${operatorSymbol}"`, location);
1257
- }
1258
- }
1259
- return {
1260
- operator,
1261
- term1,
1262
- term2
1263
- };
1264
- };
1265
- while (index < tokens.length) {
1266
- const { value: declType, location } = current();
1267
- try {
1268
- switch (declType) {
1269
- case "collection": {
1270
- const collection = parseCollection();
1271
- if (collection.name === "User") {
1272
- const { properties } = collection;
1273
- if ("roles" in properties && "items" in properties.roles.property && "enum" in properties.roles.property.items) {
1274
- memoTable.roles = properties.roles.property.items.enum;
1275
- }
1276
- }
1277
- ast.collections.push(collection);
1278
- break;
1279
- }
1280
- case "contract": {
1281
- ast.contracts.push(parseContract());
1282
- break;
1283
- }
1284
- case "functionset": {
1285
- ast.functionsets.push(parseFunctionSet());
1286
- break;
1287
- }
1288
- default:
1289
- throw new Diagnostic(`invalid declaration type: "${declType}"`, location);
1290
- }
1291
- } catch (err) {
1292
- if (err instanceof Diagnostic) {
1293
- errors.push(err);
1294
- recover(lexer.TOPLEVEL_KEYWORDS);
1295
- continue;
1296
- }
1297
- throw err;
1298
- }
1299
- }
1300
- return {
1301
- ast,
1302
- errors
1303
- };
1304
- };