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.
- package/LICENSE.md +9 -0
- package/bin/cli +1 -1
- package/bundle/asset/logo-dark-256.png +0 -0
- package/bundle/asset/logo-dark.png +0 -0
- package/bundle/asset/logo-light-256.png +0 -0
- package/bundle/asset/logo-light.png +0 -0
- package/bundle/cli/index.js +6849 -0
- package/bundle/cli/index.js.map +7 -0
- package/bundle/extension.js +39 -0
- package/bundle/extension.js.map +7 -0
- package/bundle/language-server/main.js +6105 -0
- package/bundle/language-server/main.js.map +7 -0
- package/{out/generator → bundle/res}/package.template.json +0 -0
- package/bundle/res/stdlib.zmodel +101 -0
- package/{out/generator → bundle/res}/tsconfig.template.json +0 -0
- package/package.json +38 -14
- package/src/cli/cli-util.ts +71 -0
- package/src/cli/index.ts +182 -0
- package/src/extension.ts +76 -0
- package/src/generator/constants.ts +5 -0
- package/src/generator/index.ts +102 -0
- package/{out/generator/next-auth/index.js → src/generator/next-auth/index.ts} +49 -58
- package/src/generator/prisma/expression-writer.ts +360 -0
- package/src/generator/prisma/index.ts +35 -0
- package/src/generator/prisma/prisma-builder.ts +370 -0
- package/src/generator/prisma/query-gard-generator.ts +213 -0
- package/src/generator/prisma/schema-generator.ts +305 -0
- package/src/generator/prisma/typescript-expression-transformer.ts +108 -0
- package/src/generator/react-hooks/index.ts +184 -0
- package/src/generator/service/index.ts +110 -0
- package/src/generator/types.ts +17 -0
- package/src/generator/utils.ts +18 -0
- package/src/language-server/constants.ts +28 -0
- package/src/language-server/generated/ast.ts +616 -0
- package/{out/language-server/generated/grammar.js → src/language-server/generated/grammar.ts} +5 -8
- package/src/language-server/generated/module.ts +24 -0
- package/src/language-server/langium-ext.d.ts +10 -0
- package/src/language-server/lsp/zmodel-definition-provider.ts +87 -0
- package/src/language-server/main.ts +13 -0
- package/src/language-server/types.ts +25 -0
- package/src/language-server/validator/attribute-validator.ts +11 -0
- package/src/language-server/validator/datamodel-validator.ts +311 -0
- package/src/language-server/validator/datasource-validator.ts +102 -0
- package/src/language-server/validator/enum-validator.ts +14 -0
- package/src/language-server/validator/schema-validator.ts +31 -0
- package/src/language-server/validator/utils.ts +158 -0
- package/src/language-server/validator/zmodel-validator.ts +84 -0
- package/src/language-server/zmodel-linker.ts +446 -0
- package/src/language-server/zmodel-module.ts +136 -0
- package/src/language-server/zmodel-scope.ts +45 -0
- package/src/language-server/zmodel-workspace-manager.ts +23 -0
- package/src/language-server/zmodel.langium +197 -0
- package/{out/cli → src/res}/package.template.json +2 -3
- package/src/res/stdlib.zmodel +101 -0
- package/{out/cli → src/res}/tsconfig.template.json +1 -1
- package/src/utils/exec-utils.ts +8 -0
- package/src/utils/indent-string.ts +9 -0
- package/LICENSE +0 -21
- package/out/cli/cli-util.js +0 -64
- package/out/cli/cli-util.js.map +0 -1
- package/out/cli/generator.js +0 -1
- package/out/cli/generator.js.map +0 -1
- package/out/cli/index.js +0 -124
- package/out/cli/index.js.map +0 -1
- package/out/extension.js +0 -81
- package/out/extension.js.map +0 -1
- package/out/generator/constants.js +0 -9
- package/out/generator/constants.js.map +0 -1
- package/out/generator/data-server/index.js +0 -1
- package/out/generator/data-server/index.js.map +0 -1
- package/out/generator/index.js +0 -98
- package/out/generator/index.js.map +0 -1
- package/out/generator/next-auth/index.js.map +0 -1
- package/out/generator/prisma/expression-writer.js +0 -287
- package/out/generator/prisma/expression-writer.js.map +0 -1
- package/out/generator/prisma/index.js +0 -44
- package/out/generator/prisma/index.js.map +0 -1
- package/out/generator/prisma/plain-expression-builder.js +0 -69
- package/out/generator/prisma/plain-expression-builder.js.map +0 -1
- package/out/generator/prisma/prisma-builder.js +0 -307
- package/out/generator/prisma/prisma-builder.js.map +0 -1
- package/out/generator/prisma/query-gard-generator.js +0 -159
- package/out/generator/prisma/query-gard-generator.js.map +0 -1
- package/out/generator/prisma/schema-generator.js +0 -193
- package/out/generator/prisma/schema-generator.js.map +0 -1
- package/out/generator/query-guard/index.js +0 -2
- package/out/generator/query-guard/index.js.map +0 -1
- package/out/generator/react-hooks/index.js +0 -179
- package/out/generator/react-hooks/index.js.map +0 -1
- package/out/generator/server/data/data-generator.js +0 -376
- package/out/generator/server/data/data-generator.js.map +0 -1
- package/out/generator/server/data/expression-writer.js +0 -287
- package/out/generator/server/data/expression-writer.js.map +0 -1
- package/out/generator/server/data/plain-expression-builder.js +0 -69
- package/out/generator/server/data/plain-expression-builder.js.map +0 -1
- package/out/generator/server/data-generator.js +0 -82
- package/out/generator/server/data-generator.js.map +0 -1
- package/out/generator/server/expression-writer.js +0 -1
- package/out/generator/server/expression-writer.js.map +0 -1
- package/out/generator/server/function/function-generator.js +0 -50
- package/out/generator/server/function/function-generator.js.map +0 -1
- package/out/generator/server/function-generator.js +0 -13
- package/out/generator/server/function-generator.js.map +0 -1
- package/out/generator/server/index.js +0 -88
- package/out/generator/server/index.js.map +0 -1
- package/out/generator/server/js-expression-builder.js +0 -1
- package/out/generator/server/js-expression-builder.js.map +0 -1
- package/out/generator/server/plain-expression-builder.js +0 -1
- package/out/generator/server/plain-expression-builder.js.map +0 -1
- package/out/generator/server/server-code-generator.js +0 -3
- package/out/generator/server/server-code-generator.js.map +0 -1
- package/out/generator/server/server-code-writer.js +0 -1
- package/out/generator/server/server-code-writer.js.map +0 -1
- package/out/generator/service/index.js +0 -133
- package/out/generator/service/index.js.map +0 -1
- package/out/generator/types.js +0 -10
- package/out/generator/types.js.map +0 -1
- package/out/generator/utils.js +0 -10
- package/out/generator/utils.js.map +0 -1
- package/out/langium-ext.js +0 -3
- package/out/langium-ext.js.map +0 -1
- package/out/language-server/constants.js +0 -20
- package/out/language-server/constants.js.map +0 -1
- package/out/language-server/generated/ast.js +0 -390
- package/out/language-server/generated/ast.js.map +0 -1
- package/out/language-server/generated/grammar.js.map +0 -1
- package/out/language-server/generated/module.js +0 -23
- package/out/language-server/generated/module.js.map +0 -1
- package/out/language-server/langium-ext.js +0 -3
- package/out/language-server/langium-ext.js.map +0 -1
- package/out/language-server/main.js +0 -13
- package/out/language-server/main.js.map +0 -1
- package/out/language-server/stdlib.zmodel +0 -23
- package/out/language-server/types.js +0 -3
- package/out/language-server/types.js.map +0 -1
- package/out/language-server/validator/attribute-validator copy.js +0 -12
- package/out/language-server/validator/attribute-validator copy.js.map +0 -1
- package/out/language-server/validator/attribute-validator.js +0 -7
- package/out/language-server/validator/attribute-validator.js.map +0 -1
- package/out/language-server/validator/datamodel-validator.js +0 -199
- package/out/language-server/validator/datamodel-validator.js.map +0 -1
- package/out/language-server/validator/datasource-validator copy.js +0 -77
- package/out/language-server/validator/datasource-validator copy.js.map +0 -1
- package/out/language-server/validator/datasource-validator.js +0 -77
- package/out/language-server/validator/datasource-validator.js.map +0 -1
- package/out/language-server/validator/enum-validator.js +0 -10
- package/out/language-server/validator/enum-validator.js.map +0 -1
- package/out/language-server/validator/model-validator.js +0 -21
- package/out/language-server/validator/model-validator.js.map +0 -1
- package/out/language-server/validator/schema-validator.js +0 -21
- package/out/language-server/validator/schema-validator.js.map +0 -1
- package/out/language-server/validator/utils.js +0 -106
- package/out/language-server/validator/utils.js.map +0 -1
- package/out/language-server/validator/zmodel-validator.js +0 -52
- package/out/language-server/validator/zmodel-validator.js.map +0 -1
- package/out/language-server/zmodel-index.js +0 -11
- package/out/language-server/zmodel-index.js.map +0 -1
- package/out/language-server/zmodel-linker.js +0 -249
- package/out/language-server/zmodel-linker.js.map +0 -1
- package/out/language-server/zmodel-module.js +0 -46
- package/out/language-server/zmodel-module.js.map +0 -1
- package/out/language-server/zmodel-scope.js +0 -41
- package/out/language-server/zmodel-scope.js.map +0 -1
- package/out/language-server/zmodel-validator.js +0 -35
- package/out/language-server/zmodel-validator.js.map +0 -1
- package/out/utils/exec-utils.js +0 -9
- package/out/utils/exec-utils.js.map +0 -1
- package/out/utils/indent-string.js +0 -9
- 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
|
+
}
|