zenstack 0.1.47 → 0.1.50

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 (169) hide show
  1. package/LICENSE.md +9 -0
  2. package/bin/cli +1 -1
  3. package/bundle/asset/logo-dark-256.png +0 -0
  4. package/bundle/asset/logo-dark.png +0 -0
  5. package/bundle/asset/logo-light-256.png +0 -0
  6. package/bundle/asset/logo-light.png +0 -0
  7. package/bundle/cli/index.js +6849 -0
  8. package/bundle/cli/index.js.map +7 -0
  9. package/bundle/extension.js +39 -0
  10. package/bundle/extension.js.map +7 -0
  11. package/bundle/language-server/main.js +6105 -0
  12. package/bundle/language-server/main.js.map +7 -0
  13. package/{out/generator → bundle/res}/package.template.json +0 -0
  14. package/bundle/res/stdlib.zmodel +101 -0
  15. package/{out/generator → bundle/res}/tsconfig.template.json +0 -0
  16. package/package.json +38 -14
  17. package/src/cli/cli-util.ts +71 -0
  18. package/src/cli/index.ts +182 -0
  19. package/src/extension.ts +76 -0
  20. package/src/generator/constants.ts +5 -0
  21. package/src/generator/index.ts +102 -0
  22. package/{out/generator/next-auth/index.js → src/generator/next-auth/index.ts} +49 -58
  23. package/src/generator/prisma/expression-writer.ts +360 -0
  24. package/src/generator/prisma/index.ts +35 -0
  25. package/src/generator/prisma/prisma-builder.ts +370 -0
  26. package/src/generator/prisma/query-gard-generator.ts +213 -0
  27. package/src/generator/prisma/schema-generator.ts +305 -0
  28. package/src/generator/prisma/typescript-expression-transformer.ts +108 -0
  29. package/src/generator/react-hooks/index.ts +184 -0
  30. package/src/generator/service/index.ts +110 -0
  31. package/src/generator/types.ts +17 -0
  32. package/src/generator/utils.ts +18 -0
  33. package/src/language-server/constants.ts +28 -0
  34. package/src/language-server/generated/ast.ts +616 -0
  35. package/{out/language-server/generated/grammar.js → src/language-server/generated/grammar.ts} +5 -8
  36. package/src/language-server/generated/module.ts +24 -0
  37. package/src/language-server/langium-ext.d.ts +10 -0
  38. package/src/language-server/lsp/zmodel-definition-provider.ts +87 -0
  39. package/src/language-server/main.ts +13 -0
  40. package/src/language-server/types.ts +25 -0
  41. package/src/language-server/validator/attribute-validator.ts +11 -0
  42. package/src/language-server/validator/datamodel-validator.ts +311 -0
  43. package/src/language-server/validator/datasource-validator.ts +102 -0
  44. package/src/language-server/validator/enum-validator.ts +14 -0
  45. package/src/language-server/validator/schema-validator.ts +31 -0
  46. package/src/language-server/validator/utils.ts +158 -0
  47. package/src/language-server/validator/zmodel-validator.ts +84 -0
  48. package/src/language-server/zmodel-linker.ts +446 -0
  49. package/src/language-server/zmodel-module.ts +136 -0
  50. package/src/language-server/zmodel-scope.ts +45 -0
  51. package/src/language-server/zmodel-workspace-manager.ts +23 -0
  52. package/src/language-server/zmodel.langium +197 -0
  53. package/{out/cli → src/res}/package.template.json +2 -3
  54. package/src/res/stdlib.zmodel +101 -0
  55. package/{out/cli → src/res}/tsconfig.template.json +1 -1
  56. package/src/utils/exec-utils.ts +8 -0
  57. package/src/utils/indent-string.ts +9 -0
  58. package/LICENSE +0 -21
  59. package/out/cli/cli-util.js +0 -64
  60. package/out/cli/cli-util.js.map +0 -1
  61. package/out/cli/generator.js +0 -1
  62. package/out/cli/generator.js.map +0 -1
  63. package/out/cli/index.js +0 -124
  64. package/out/cli/index.js.map +0 -1
  65. package/out/extension.js +0 -81
  66. package/out/extension.js.map +0 -1
  67. package/out/generator/constants.js +0 -9
  68. package/out/generator/constants.js.map +0 -1
  69. package/out/generator/data-server/index.js +0 -1
  70. package/out/generator/data-server/index.js.map +0 -1
  71. package/out/generator/index.js +0 -98
  72. package/out/generator/index.js.map +0 -1
  73. package/out/generator/next-auth/index.js.map +0 -1
  74. package/out/generator/prisma/expression-writer.js +0 -287
  75. package/out/generator/prisma/expression-writer.js.map +0 -1
  76. package/out/generator/prisma/index.js +0 -44
  77. package/out/generator/prisma/index.js.map +0 -1
  78. package/out/generator/prisma/plain-expression-builder.js +0 -69
  79. package/out/generator/prisma/plain-expression-builder.js.map +0 -1
  80. package/out/generator/prisma/prisma-builder.js +0 -307
  81. package/out/generator/prisma/prisma-builder.js.map +0 -1
  82. package/out/generator/prisma/query-gard-generator.js +0 -159
  83. package/out/generator/prisma/query-gard-generator.js.map +0 -1
  84. package/out/generator/prisma/schema-generator.js +0 -193
  85. package/out/generator/prisma/schema-generator.js.map +0 -1
  86. package/out/generator/query-guard/index.js +0 -2
  87. package/out/generator/query-guard/index.js.map +0 -1
  88. package/out/generator/react-hooks/index.js +0 -179
  89. package/out/generator/react-hooks/index.js.map +0 -1
  90. package/out/generator/server/data/data-generator.js +0 -376
  91. package/out/generator/server/data/data-generator.js.map +0 -1
  92. package/out/generator/server/data/expression-writer.js +0 -287
  93. package/out/generator/server/data/expression-writer.js.map +0 -1
  94. package/out/generator/server/data/plain-expression-builder.js +0 -69
  95. package/out/generator/server/data/plain-expression-builder.js.map +0 -1
  96. package/out/generator/server/data-generator.js +0 -82
  97. package/out/generator/server/data-generator.js.map +0 -1
  98. package/out/generator/server/expression-writer.js +0 -1
  99. package/out/generator/server/expression-writer.js.map +0 -1
  100. package/out/generator/server/function/function-generator.js +0 -50
  101. package/out/generator/server/function/function-generator.js.map +0 -1
  102. package/out/generator/server/function-generator.js +0 -13
  103. package/out/generator/server/function-generator.js.map +0 -1
  104. package/out/generator/server/index.js +0 -88
  105. package/out/generator/server/index.js.map +0 -1
  106. package/out/generator/server/js-expression-builder.js +0 -1
  107. package/out/generator/server/js-expression-builder.js.map +0 -1
  108. package/out/generator/server/plain-expression-builder.js +0 -1
  109. package/out/generator/server/plain-expression-builder.js.map +0 -1
  110. package/out/generator/server/server-code-generator.js +0 -3
  111. package/out/generator/server/server-code-generator.js.map +0 -1
  112. package/out/generator/server/server-code-writer.js +0 -1
  113. package/out/generator/server/server-code-writer.js.map +0 -1
  114. package/out/generator/service/index.js +0 -133
  115. package/out/generator/service/index.js.map +0 -1
  116. package/out/generator/types.js +0 -10
  117. package/out/generator/types.js.map +0 -1
  118. package/out/generator/utils.js +0 -10
  119. package/out/generator/utils.js.map +0 -1
  120. package/out/langium-ext.js +0 -3
  121. package/out/langium-ext.js.map +0 -1
  122. package/out/language-server/constants.js +0 -20
  123. package/out/language-server/constants.js.map +0 -1
  124. package/out/language-server/generated/ast.js +0 -390
  125. package/out/language-server/generated/ast.js.map +0 -1
  126. package/out/language-server/generated/grammar.js.map +0 -1
  127. package/out/language-server/generated/module.js +0 -23
  128. package/out/language-server/generated/module.js.map +0 -1
  129. package/out/language-server/langium-ext.js +0 -3
  130. package/out/language-server/langium-ext.js.map +0 -1
  131. package/out/language-server/main.js +0 -13
  132. package/out/language-server/main.js.map +0 -1
  133. package/out/language-server/stdlib.zmodel +0 -23
  134. package/out/language-server/types.js +0 -3
  135. package/out/language-server/types.js.map +0 -1
  136. package/out/language-server/validator/attribute-validator copy.js +0 -12
  137. package/out/language-server/validator/attribute-validator copy.js.map +0 -1
  138. package/out/language-server/validator/attribute-validator.js +0 -7
  139. package/out/language-server/validator/attribute-validator.js.map +0 -1
  140. package/out/language-server/validator/datamodel-validator.js +0 -199
  141. package/out/language-server/validator/datamodel-validator.js.map +0 -1
  142. package/out/language-server/validator/datasource-validator copy.js +0 -77
  143. package/out/language-server/validator/datasource-validator copy.js.map +0 -1
  144. package/out/language-server/validator/datasource-validator.js +0 -77
  145. package/out/language-server/validator/datasource-validator.js.map +0 -1
  146. package/out/language-server/validator/enum-validator.js +0 -10
  147. package/out/language-server/validator/enum-validator.js.map +0 -1
  148. package/out/language-server/validator/model-validator.js +0 -21
  149. package/out/language-server/validator/model-validator.js.map +0 -1
  150. package/out/language-server/validator/schema-validator.js +0 -21
  151. package/out/language-server/validator/schema-validator.js.map +0 -1
  152. package/out/language-server/validator/utils.js +0 -106
  153. package/out/language-server/validator/utils.js.map +0 -1
  154. package/out/language-server/validator/zmodel-validator.js +0 -52
  155. package/out/language-server/validator/zmodel-validator.js.map +0 -1
  156. package/out/language-server/zmodel-index.js +0 -11
  157. package/out/language-server/zmodel-index.js.map +0 -1
  158. package/out/language-server/zmodel-linker.js +0 -249
  159. package/out/language-server/zmodel-linker.js.map +0 -1
  160. package/out/language-server/zmodel-module.js +0 -46
  161. package/out/language-server/zmodel-module.js.map +0 -1
  162. package/out/language-server/zmodel-scope.js +0 -41
  163. package/out/language-server/zmodel-scope.js.map +0 -1
  164. package/out/language-server/zmodel-validator.js +0 -35
  165. package/out/language-server/zmodel-validator.js.map +0 -1
  166. package/out/utils/exec-utils.js +0 -9
  167. package/out/utils/exec-utils.js.map +0 -1
  168. package/out/utils/indent-string.js +0 -9
  169. package/out/utils/indent-string.js.map +0 -1
@@ -0,0 +1,158 @@
1
+ import {
2
+ AttributeArg,
3
+ AttributeParam,
4
+ BuiltinType,
5
+ DataModelAttribute,
6
+ DataModelFieldAttribute,
7
+ ExpressionType,
8
+ isArrayExpr,
9
+ isDataModelField,
10
+ isLiteralExpr,
11
+ isReferenceExpr,
12
+ } from '@lang/generated/ast';
13
+ import { AstNode, ValidationAcceptor } from 'langium';
14
+
15
+ /**
16
+ * Checks if the given declarations have duplicated names
17
+ */
18
+ export function validateDuplicatedDeclarations(
19
+ decls: Array<AstNode & { name: string }>,
20
+ accept: ValidationAcceptor
21
+ ): void {
22
+ const groupByName = decls.reduce<
23
+ Record<string, Array<AstNode & { name: string }>>
24
+ >((group, decl) => {
25
+ group[decl.name] = group[decl.name] ?? [];
26
+ group[decl.name].push(decl);
27
+ return group;
28
+ }, {});
29
+
30
+ for (const [name, decls] of Object.entries<AstNode[]>(groupByName)) {
31
+ if (decls.length > 1) {
32
+ accept('error', `Duplicated declaration name "${name}"`, {
33
+ node: decls[1],
34
+ });
35
+ }
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Try getting string value from a potential string literal expression
41
+ */
42
+ export function getStringLiteral(node: AstNode): string | undefined {
43
+ if (isLiteralExpr(node) && typeof node.value === 'string') {
44
+ return node.value;
45
+ } else {
46
+ return undefined;
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Determines if the given sourceType is assignable to a destination of destType
52
+ */
53
+ export function typeAssignable(
54
+ destType: ExpressionType,
55
+ sourceType: ExpressionType
56
+ ): boolean {
57
+ switch (destType) {
58
+ case 'Any':
59
+ return true;
60
+ case 'Float':
61
+ return (
62
+ sourceType === 'Any' ||
63
+ sourceType === 'Int' ||
64
+ sourceType === 'Float'
65
+ );
66
+ default:
67
+ return sourceType === 'Any' || sourceType === destType;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Maps a ZModel builtin type to expression type
73
+ */
74
+ export function mapBuiltinTypeToExpressionType(
75
+ type: BuiltinType | 'Any' | 'Null'
76
+ ): ExpressionType | 'Any' {
77
+ switch (type) {
78
+ case 'Any':
79
+ case 'Boolean':
80
+ case 'String':
81
+ case 'DateTime':
82
+ case 'Int':
83
+ case 'Float':
84
+ case 'Null':
85
+ return type;
86
+ case 'BigInt':
87
+ return 'Int';
88
+ case 'Decimal':
89
+ return 'Float';
90
+ case 'Json':
91
+ case 'Bytes':
92
+ return 'Any';
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Determines if the given attribute argument is assignable to the given attribute parameter
98
+ */
99
+ export function assignableToAttributeParam(
100
+ arg: AttributeArg,
101
+ param: AttributeParam,
102
+ attr: DataModelAttribute | DataModelFieldAttribute
103
+ ): boolean {
104
+ const argResolvedType = arg.$resolvedType;
105
+ if (!argResolvedType) {
106
+ return false;
107
+ }
108
+
109
+ let dstType = param.type.type;
110
+ const dstIsArray = param.type.array;
111
+ const dstRef = param.type.reference;
112
+
113
+ if (dstType) {
114
+ if (typeof argResolvedType?.decl !== 'string') {
115
+ // destination type is not a reference, so argument type must be a plain expression
116
+ return false;
117
+ }
118
+
119
+ if (dstType === 'FieldReference') {
120
+ if (dstIsArray) {
121
+ return (
122
+ isArrayExpr(arg.value) &&
123
+ !arg.value.items.find(
124
+ (item) =>
125
+ !isReferenceExpr(item) ||
126
+ !isDataModelField(item.target.ref)
127
+ )
128
+ );
129
+ } else {
130
+ return (
131
+ isReferenceExpr(arg.value) &&
132
+ isDataModelField(arg.value.target.ref)
133
+ );
134
+ }
135
+ } else if (dstType === 'ContextType') {
136
+ if (isDataModelField(attr.$container)) {
137
+ if (!attr.$container?.type?.type) {
138
+ return false;
139
+ }
140
+ dstType = mapBuiltinTypeToExpressionType(
141
+ attr.$container.type.type
142
+ );
143
+ } else {
144
+ dstType = 'Any';
145
+ }
146
+ }
147
+
148
+ return (
149
+ typeAssignable(dstType, argResolvedType.decl) &&
150
+ dstIsArray === argResolvedType.array
151
+ );
152
+ } else {
153
+ return (
154
+ dstRef?.ref === argResolvedType.decl &&
155
+ dstIsArray === argResolvedType.array
156
+ );
157
+ }
158
+ }
@@ -0,0 +1,84 @@
1
+ import {
2
+ AstNode,
3
+ LangiumDocument,
4
+ ValidationAcceptor,
5
+ ValidationChecks,
6
+ ValidationRegistry,
7
+ } from 'langium';
8
+ import {
9
+ Attribute,
10
+ DataModel,
11
+ DataSource,
12
+ Enum,
13
+ Model,
14
+ ZModelAstType,
15
+ } from '../generated/ast';
16
+ import type { ZModelServices } from '../zmodel-module';
17
+ import SchemaValidator from './schema-validator';
18
+ import DataSourceValidator from './datasource-validator';
19
+ import DataModelValidator from './datamodel-validator';
20
+ import AttributeValidator from './attribute-validator';
21
+ import EnumValidator from './enum-validator';
22
+
23
+ /**
24
+ * Registry for validation checks.
25
+ */
26
+ export class ZModelValidationRegistry extends ValidationRegistry {
27
+ constructor(services: ZModelServices) {
28
+ super(services);
29
+ const validator = services.validation.ZModelValidator;
30
+ const checks: ValidationChecks<ZModelAstType> = {
31
+ Model: validator.checkModel,
32
+ DataSource: validator.checkDataSource,
33
+ DataModel: validator.checkDataModel,
34
+ Enum: validator.checkEnum,
35
+ Attribute: validator.checkAttribute,
36
+ };
37
+ this.register(checks, validator);
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Implementation of custom validations.
43
+ */
44
+ export class ZModelValidator {
45
+ private shouldCheck(node: AstNode) {
46
+ let doc: LangiumDocument | undefined;
47
+ let currNode: AstNode | undefined = node;
48
+ while (currNode) {
49
+ if (currNode.$document) {
50
+ doc = currNode.$document;
51
+ break;
52
+ }
53
+ currNode = currNode.$container;
54
+ }
55
+
56
+ return (
57
+ doc?.parseResult.lexerErrors.length === 0 &&
58
+ doc?.parseResult.parserErrors.length === 0
59
+ );
60
+ }
61
+
62
+ checkModel(node: Model, accept: ValidationAcceptor): void {
63
+ this.shouldCheck(node) && new SchemaValidator().validate(node, accept);
64
+ }
65
+
66
+ checkDataSource(node: DataSource, accept: ValidationAcceptor): void {
67
+ this.shouldCheck(node) &&
68
+ new DataSourceValidator().validate(node, accept);
69
+ }
70
+
71
+ checkDataModel(node: DataModel, accept: ValidationAcceptor): void {
72
+ this.shouldCheck(node) &&
73
+ new DataModelValidator().validate(node, accept);
74
+ }
75
+
76
+ checkEnum(node: Enum, accept: ValidationAcceptor): void {
77
+ this.shouldCheck(node) && new EnumValidator().validate(node, accept);
78
+ }
79
+
80
+ checkAttribute(node: Attribute, accept: ValidationAcceptor): void {
81
+ this.shouldCheck(node) &&
82
+ new AttributeValidator().validate(node, accept);
83
+ }
84
+ }
@@ -0,0 +1,446 @@
1
+ import {
2
+ AstNode,
3
+ AstNodeDescription,
4
+ AstNodeDescriptionProvider,
5
+ DefaultLinker,
6
+ DocumentState,
7
+ interruptAndCheck,
8
+ isReference,
9
+ LangiumDocument,
10
+ LangiumServices,
11
+ LinkingError,
12
+ Reference,
13
+ streamContents,
14
+ } from 'langium';
15
+ import { CancellationToken } from 'vscode-jsonrpc';
16
+ import {
17
+ ArrayExpr,
18
+ AttributeArg,
19
+ BinaryExpr,
20
+ DataModel,
21
+ DataModelField,
22
+ DataModelFieldType,
23
+ EnumField,
24
+ Function,
25
+ FunctionParam,
26
+ FunctionParamType,
27
+ InvocationExpr,
28
+ isDataModel,
29
+ LiteralExpr,
30
+ MemberAccessExpr,
31
+ NullExpr,
32
+ ReferenceExpr,
33
+ ReferenceTarget,
34
+ ThisExpr,
35
+ UnaryExpr,
36
+ } from './generated/ast';
37
+ import { ResolvedShape } from './types';
38
+ import { mapBuiltinTypeToExpressionType } from './validator/utils';
39
+
40
+ interface DefaultReference extends Reference {
41
+ _ref?: AstNode | LinkingError;
42
+ _nodeDescription?: AstNodeDescription;
43
+ }
44
+
45
+ type ScopeProvider = (name: string) => ReferenceTarget | undefined;
46
+
47
+ /**
48
+ * Langium linker implementation which links references and resolves expression types
49
+ */
50
+ export class ZModelLinker extends DefaultLinker {
51
+ private readonly descriptions: AstNodeDescriptionProvider;
52
+
53
+ constructor(services: LangiumServices) {
54
+ super(services);
55
+ this.descriptions = services.workspace.AstNodeDescriptionProvider;
56
+ }
57
+
58
+ //#region Reference linking
59
+
60
+ async link(
61
+ document: LangiumDocument,
62
+ cancelToken = CancellationToken.None
63
+ ): Promise<void> {
64
+ if (
65
+ document.parseResult.lexerErrors?.length > 0 ||
66
+ document.parseResult.parserErrors?.length > 0
67
+ ) {
68
+ return;
69
+ }
70
+
71
+ for (const node of streamContents(document.parseResult.value)) {
72
+ await interruptAndCheck(cancelToken);
73
+ this.resolve(node, document);
74
+ }
75
+ document.state = DocumentState.Linked;
76
+ }
77
+
78
+ private linkReference(
79
+ container: AstNode,
80
+ property: string,
81
+ document: LangiumDocument,
82
+ extraScopes: ScopeProvider[]
83
+ ) {
84
+ if (
85
+ !this.resolveFromScopeProviders(
86
+ container,
87
+ property,
88
+ document,
89
+ extraScopes
90
+ )
91
+ ) {
92
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
+ const reference: Reference<AstNode> = (container as any)[property];
94
+ this.doLink({ reference, container, property }, document);
95
+ }
96
+ }
97
+
98
+ //#endregion
99
+
100
+ //#region Expression type resolving
101
+
102
+ private resolveFromScopeProviders(
103
+ node: AstNode,
104
+ property: string,
105
+ document: LangiumDocument,
106
+ providers: ScopeProvider[]
107
+ ) {
108
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
109
+ const reference: DefaultReference = (node as any)[property];
110
+ for (const provider of providers) {
111
+ const target = provider(reference.$refText);
112
+ if (target) {
113
+ reference._ref = target;
114
+ reference._nodeDescription =
115
+ this.descriptions.createDescription(
116
+ target,
117
+ target.name,
118
+ document
119
+ );
120
+ return target;
121
+ }
122
+ }
123
+ return null;
124
+ }
125
+
126
+ private resolve(
127
+ node: AstNode,
128
+ document: LangiumDocument,
129
+ extraScopes: ScopeProvider[] = []
130
+ ) {
131
+ if (node.$resolvedType) {
132
+ return;
133
+ }
134
+
135
+ switch (node.$type) {
136
+ case LiteralExpr:
137
+ this.resolveLiteral(node as LiteralExpr);
138
+ break;
139
+
140
+ case InvocationExpr:
141
+ this.resolveInvocation(
142
+ node as InvocationExpr,
143
+ document,
144
+ extraScopes
145
+ );
146
+ break;
147
+
148
+ case ArrayExpr:
149
+ this.resolveArray(node as ArrayExpr, document, extraScopes);
150
+ break;
151
+
152
+ case ReferenceExpr:
153
+ this.resolveReference(
154
+ node as ReferenceExpr,
155
+ document,
156
+ extraScopes
157
+ );
158
+ break;
159
+
160
+ case MemberAccessExpr:
161
+ this.resolveMemberAccess(
162
+ node as MemberAccessExpr,
163
+ document,
164
+ extraScopes
165
+ );
166
+ break;
167
+
168
+ case UnaryExpr:
169
+ this.resolveUnary(node as UnaryExpr, document, extraScopes);
170
+ break;
171
+
172
+ case BinaryExpr:
173
+ this.resolveBinary(node as BinaryExpr, document, extraScopes);
174
+ break;
175
+
176
+ case ThisExpr:
177
+ this.resolveThis(node as ThisExpr, document, extraScopes);
178
+ break;
179
+
180
+ case NullExpr:
181
+ this.resolveNull(node as NullExpr, document, extraScopes);
182
+ break;
183
+
184
+ case AttributeArg:
185
+ this.resolveAttributeArg(
186
+ node as AttributeArg,
187
+ document,
188
+ extraScopes
189
+ );
190
+ break;
191
+
192
+ default:
193
+ this.resolveDefault(node, document, extraScopes);
194
+ break;
195
+ }
196
+ }
197
+
198
+ private resolveBinary(
199
+ node: BinaryExpr,
200
+ document: LangiumDocument<AstNode>,
201
+ extraScopes: ScopeProvider[]
202
+ ) {
203
+ switch (node.operator) {
204
+ // TODO: support arithmetics?
205
+ // case '+':
206
+ // case '-':
207
+ // case '*':
208
+ // case '/':
209
+ // this.resolve(node.left, document, extraScopes);
210
+ // this.resolve(node.right, document, extraScopes);
211
+ // this.resolveToBuiltinTypeOrDecl(node, 'Int');
212
+ // break;
213
+
214
+ case '>':
215
+ case '>=':
216
+ case '<':
217
+ case '<=':
218
+ case '==':
219
+ case '!=':
220
+ case '&&':
221
+ case '||':
222
+ this.resolve(node.left, document, extraScopes);
223
+ this.resolve(node.right, document, extraScopes);
224
+ this.resolveToBuiltinTypeOrDecl(node, 'Boolean');
225
+ break;
226
+
227
+ case '?':
228
+ case '!':
229
+ case '^':
230
+ this.resolveCollectionPredicate(node, document, extraScopes);
231
+ break;
232
+
233
+ default:
234
+ throw Error(`Unsupported binary operator: ${node.operator}`);
235
+ }
236
+ }
237
+
238
+ private resolveUnary(
239
+ node: UnaryExpr,
240
+ document: LangiumDocument<AstNode>,
241
+ extraScopes: ScopeProvider[]
242
+ ) {
243
+ this.resolve(node.operand, document, extraScopes);
244
+ node.$resolvedType = node.operand.$resolvedType;
245
+ }
246
+
247
+ private resolveReference(
248
+ node: ReferenceExpr,
249
+ document: LangiumDocument<AstNode>,
250
+ extraScopes: ScopeProvider[]
251
+ ) {
252
+ this.linkReference(node, 'target', document, extraScopes);
253
+ node.args.forEach((arg) => this.resolve(arg, document, extraScopes));
254
+
255
+ if (node.target.ref) {
256
+ // resolve type
257
+ if (node.target.ref.$type === EnumField) {
258
+ this.resolveToBuiltinTypeOrDecl(
259
+ node,
260
+ node.target.ref.$container
261
+ );
262
+ } else {
263
+ this.resolveToDeclaredType(
264
+ node,
265
+ (node.target.ref as DataModelField | FunctionParam).type
266
+ );
267
+ }
268
+ }
269
+ }
270
+
271
+ private resolveArray(
272
+ node: ArrayExpr,
273
+ document: LangiumDocument<AstNode>,
274
+ extraScopes: ScopeProvider[]
275
+ ) {
276
+ node.items.forEach((item) => this.resolve(item, document, extraScopes));
277
+
278
+ const itemType = node.items[0].$resolvedType;
279
+ if (itemType?.decl) {
280
+ this.resolveToBuiltinTypeOrDecl(node, itemType.decl, true);
281
+ }
282
+ }
283
+
284
+ private resolveInvocation(
285
+ node: InvocationExpr,
286
+ document: LangiumDocument,
287
+ extraScopes: ScopeProvider[]
288
+ ) {
289
+ this.linkReference(node, 'function', document, extraScopes);
290
+ node.args.forEach((arg) => this.resolve(arg, document, extraScopes));
291
+ if (node.function.ref) {
292
+ // eslint-disable-next-line @typescript-eslint/ban-types
293
+ const funcDecl = node.function.ref as Function;
294
+ this.resolveToDeclaredType(node, funcDecl.returnType);
295
+ }
296
+ }
297
+
298
+ private resolveLiteral(node: LiteralExpr) {
299
+ const type =
300
+ typeof node.value === 'string'
301
+ ? 'String'
302
+ : typeof node.value === 'boolean'
303
+ ? 'Boolean'
304
+ : typeof node.value === 'number'
305
+ ? 'Int'
306
+ : undefined;
307
+
308
+ if (type) {
309
+ this.resolveToBuiltinTypeOrDecl(node, type);
310
+ }
311
+ }
312
+
313
+ private resolveMemberAccess(
314
+ node: MemberAccessExpr,
315
+ document: LangiumDocument<AstNode>,
316
+ extraScopes: ScopeProvider[]
317
+ ) {
318
+ this.resolve(node.operand, document, extraScopes);
319
+ const operandResolved = node.operand.$resolvedType;
320
+
321
+ if (
322
+ operandResolved &&
323
+ !operandResolved.array &&
324
+ isDataModel(operandResolved.decl)
325
+ ) {
326
+ const modelDecl = operandResolved.decl as DataModel;
327
+ const provider = (name: string) =>
328
+ modelDecl.fields.find((f) => f.name === name);
329
+ extraScopes = [provider, ...extraScopes];
330
+ }
331
+
332
+ this.linkReference(node, 'member', document, extraScopes);
333
+ if (node.member.ref) {
334
+ this.resolveToDeclaredType(node, node.member.ref.type);
335
+ }
336
+ }
337
+
338
+ private resolveCollectionPredicate(
339
+ node: BinaryExpr,
340
+ document: LangiumDocument,
341
+ extraScopes: ScopeProvider[]
342
+ ) {
343
+ this.resolve(node.left, document, extraScopes);
344
+
345
+ const resolvedType = node.left.$resolvedType;
346
+ if (
347
+ resolvedType &&
348
+ isDataModel(resolvedType.decl) &&
349
+ resolvedType.array
350
+ ) {
351
+ const dataModelDecl = resolvedType.decl;
352
+ const provider = (name: string) =>
353
+ dataModelDecl.fields.find((f) => f.name === name);
354
+ extraScopes = [provider, ...extraScopes];
355
+ this.resolve(node.right, document, extraScopes);
356
+ this.resolveToBuiltinTypeOrDecl(node, 'Boolean');
357
+ } else {
358
+ // TODO: handle this during validation
359
+ console.warn(`Unresolved collection predicate`);
360
+ }
361
+ }
362
+
363
+ private resolveThis(
364
+ node: ThisExpr,
365
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
366
+ document: LangiumDocument<AstNode>,
367
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
368
+ extraScopes: ScopeProvider[]
369
+ ) {
370
+ let decl: AstNode | undefined = node.$container;
371
+
372
+ while (decl && !isDataModel(decl)) {
373
+ decl = decl.$container;
374
+ }
375
+
376
+ if (decl) {
377
+ this.resolveToBuiltinTypeOrDecl(node, decl);
378
+ }
379
+ }
380
+
381
+ private resolveNull(
382
+ node: NullExpr,
383
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
384
+ document: LangiumDocument<AstNode>,
385
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
386
+ extraScopes: ScopeProvider[]
387
+ ) {
388
+ // TODO: how to really resolve null?
389
+ this.resolveToBuiltinTypeOrDecl(node, 'Null');
390
+ }
391
+
392
+ private resolveAttributeArg(
393
+ node: AttributeArg,
394
+ document: LangiumDocument<AstNode>,
395
+ extraScopes: ScopeProvider[]
396
+ ) {
397
+ this.resolve(node.value, document, extraScopes);
398
+ node.$resolvedType = node.value.$resolvedType;
399
+ }
400
+
401
+ private resolveDefault(
402
+ node: AstNode,
403
+ document: LangiumDocument<AstNode>,
404
+ extraScopes: ScopeProvider[]
405
+ ) {
406
+ for (const [property, value] of Object.entries(node)) {
407
+ if (!property.startsWith('$')) {
408
+ if (isReference(value)) {
409
+ this.linkReference(node, property, document, extraScopes);
410
+ }
411
+ }
412
+ }
413
+ for (const child of streamContents(node)) {
414
+ this.resolve(child, document, extraScopes);
415
+ }
416
+ }
417
+
418
+ //#endregion
419
+
420
+ //#region Utils
421
+
422
+ private resolveToDeclaredType(
423
+ node: AstNode,
424
+ type: FunctionParamType | DataModelFieldType
425
+ ) {
426
+ if (type.type) {
427
+ const mappedType = mapBuiltinTypeToExpressionType(type.type);
428
+ node.$resolvedType = { decl: mappedType, array: type.array };
429
+ } else if (type.reference) {
430
+ node.$resolvedType = {
431
+ decl: type.reference.ref,
432
+ array: type.array,
433
+ };
434
+ }
435
+ }
436
+
437
+ private resolveToBuiltinTypeOrDecl(
438
+ node: AstNode,
439
+ type: ResolvedShape,
440
+ array = false
441
+ ) {
442
+ node.$resolvedType = { decl: type, array };
443
+ }
444
+
445
+ //#endregion
446
+ }