Skip to content

Commit f2b9423

Browse files
authored
Merge pull request #681 from jvalue/rfc18-implementation
Multi attribute value types (Part 1)
2 parents 1531d0b + 24bf1f7 commit f2b9423

24 files changed

+9380
-6639
lines changed

libs/execution/src/lib/constraints/constraint-executor.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
ERROR_TYPEGUARD,
1212
type InternalErrorValueRepresentation,
1313
type InternalValidValueRepresentation,
14-
type ValueTypeAttribute,
14+
type ValueTypeProperty,
1515
type ValueTypeConstraintInlineDefinition,
1616
evaluateExpression,
1717
} from '@jvalue/jayvee-language-server';
@@ -27,16 +27,19 @@ export class ConstraintExecutor<
2727
isValid(
2828
value: InternalValidValueRepresentation | InternalErrorValueRepresentation,
2929
context: ExecutionContext,
30-
attribute: T extends ValueTypeConstraintInlineDefinition
31-
? ValueTypeAttribute
30+
properties: T extends ValueTypeConstraintInlineDefinition
31+
? ValueTypeProperty[]
3232
: void,
3333
): boolean {
3434
const expression = this.astNode.expression;
3535

36-
if (attribute === undefined) {
36+
if (properties === undefined) {
3737
context.evaluationContext.setValueForValueKeyword(value);
3838
} else {
39-
context.evaluationContext.setValueForReference(attribute.name, value);
39+
const assignmentForTypeSystem: ValueTypeProperty[] = properties;
40+
for (const property of assignmentForTypeSystem) {
41+
context.evaluationContext.setValueForReference(property.name, value);
42+
}
4043
}
4144

4245
const result = evaluateExpression(
@@ -54,10 +57,13 @@ export class ConstraintExecutor<
5457
),
5558
);
5659

57-
if (attribute === undefined) {
60+
if (properties === undefined) {
5861
context.evaluationContext.deleteValueForValueKeyword();
5962
} else {
60-
context.evaluationContext.deleteValueForReference(attribute.name);
63+
const assignment_for_type_system: ValueTypeProperty[] = properties;
64+
for (const property of assignment_for_type_system) {
65+
context.evaluationContext.deleteValueForReference(property.name);
66+
}
6167
}
6268

6369
return result;

libs/execution/src/lib/types/value-types/internal-representation-parsing.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
type TextValuetype,
1818
type ValueType,
1919
ValueTypeVisitor,
20+
onlyElementOrUndefined,
2021
internalValueToString,
2122
isCellRangeLiteral,
2223
} from '@jvalue/jayvee-language-server';
@@ -104,10 +105,15 @@ class InternalRepresentationParserVisitor extends ValueTypeVisitor<
104105
visitAtomicValueType(
105106
valueType: AtomicValueType,
106107
): InternalValidValueRepresentation | InvalidValue {
107-
const contained = valueType.getContainedType();
108-
assert(contained !== undefined);
109-
110-
return contained.acceptVisitor(this);
108+
const containedTypes = valueType.getContainedTypes();
109+
assert(containedTypes !== undefined);
110+
const containedType = onlyElementOrUndefined(containedTypes);
111+
if (containedType === undefined) {
112+
return new InvalidValue(
113+
`Cannot parse value types with multiple properties`,
114+
);
115+
}
116+
return containedType.acceptVisitor(this);
111117
}
112118

113119
visitCellRange(): InvalidValue {

libs/execution/src/lib/types/value-types/value-representation-validity.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,24 +45,24 @@ class ValueRepresentationValidityVisitor extends ValueTypeVisitor<boolean> {
4545
}
4646

4747
override visitAtomicValueType(valueType: AtomicValueType): boolean {
48-
const contained = valueType.getContainedType();
49-
assert(contained !== undefined);
50-
if (!contained.acceptVisitor(this)) {
48+
const containedTypes = valueType.getContainedTypes();
49+
assert(containedTypes !== undefined);
50+
const allPropertiesValid = containedTypes.every((containedType) =>
51+
containedType.acceptVisitor(this),
52+
);
53+
if (!allPropertiesValid) {
5154
return false;
5255
}
5356

54-
const attribute = valueType.getAttribute();
55-
assert(attribute !== undefined);
56-
const constraints = valueType.getConstraints();
57-
for (const constraint of constraints) {
57+
for (const constraint of valueType.getConstraints()) {
5858
this.context.enterNode(constraint);
5959

6060
const valueFulfilledConstraint = isConstraintDefinition(constraint)
6161
? new ConstraintExecutor(constraint).isValid(this.value, this.context)
6262
: new ConstraintExecutor(constraint).isValid(
6363
this.value,
6464
this.context,
65-
attribute,
65+
valueType.getProperties(),
6666
);
6767

6868
this.context.exitNode(constraint);

libs/execution/src/lib/types/value-types/visitors/sql-column-type-visitor.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { strict as assert } from 'assert';
77

88
import {
99
type AtomicValueType,
10+
onlyElementOrUndefined,
1011
ValueTypeVisitor,
1112
} from '@jvalue/jayvee-language-server';
1213

@@ -28,9 +29,15 @@ export class SQLColumnTypeVisitor extends ValueTypeVisitor<string> {
2829
}
2930

3031
override visitAtomicValueType(valueType: AtomicValueType): string {
31-
const contained = valueType.getContainedType();
32-
assert(contained !== undefined);
33-
return contained.acceptVisitor(this);
32+
const containedTypes = valueType.getContainedTypes();
33+
assert(containedTypes !== undefined);
34+
const containedType = onlyElementOrUndefined(containedTypes);
35+
if (containedType === undefined) {
36+
throw new Error(
37+
'Can only determine sql column type for value types with one property',
38+
);
39+
}
40+
return containedType.acceptVisitor(this);
3441
}
3542

3643
override visitRegex(): string {

libs/execution/src/lib/types/value-types/visitors/sql-value-representation-visitor.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { strict as assert } from 'assert';
88
import {
99
type AtomicValueType,
1010
type BooleanValuetype,
11+
onlyElementOrUndefined,
1112
type DecimalValuetype,
1213
ERROR_TYPEGUARD,
1314
type IntegerValuetype,
@@ -69,9 +70,16 @@ export class SQLValueRepresentationVisitor extends ValueTypeVisitor<
6970
): (
7071
value: InternalValidValueRepresentation | InternalErrorValueRepresentation,
7172
) => string {
72-
const contained = valueType.getContainedType();
73-
assert(contained !== undefined);
74-
return contained.acceptVisitor(this);
73+
const containedTypes = valueType.getContainedTypes();
74+
assert(containedTypes !== undefined);
75+
const containedType = onlyElementOrUndefined(containedTypes);
76+
if (containedType === undefined) {
77+
throw new Error(
78+
'Can only determine sql value representation for value types with one' +
79+
' property',
80+
);
81+
}
82+
return containedType.acceptVisitor(this);
7583
}
7684

7785
override visitRegex(): (

libs/language-server/src/grammar/expression.langium

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,4 @@ Referencable:
8888
| BlockTypeProperty
8989
| TransformDefinition
9090
| TransformPortDefinition
91-
| ValueTypeAttribute;
91+
| ValueTypeProperty;

libs/language-server/src/grammar/value-type.langium

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,18 @@ CustomValuetypeDefinition infers ValuetypeDefinition:
1414
'valuetype' name=ID
1515
(genericDefinition=ValuetypeGenericsDefinition)?
1616
'{'
17-
attribute=ValueTypeAttribute
18-
(constraints+=ValueTypeConstraintReference | constraints+=ValueTypeConstraintInlineDefinition)*
17+
properties+=ValueTypeProperty
18+
(properties+=ValueTypeProperty
19+
| constraints+=ValueTypeConstraintReference
20+
| constraints+=ValueTypeConstraintInlineDefinition)*
1921
'}';
2022

21-
ValueTypeAttribute:
23+
ValueTypeProperty:
2224
'property' name=ID 'oftype' valueType=ValueTypeReference ';';
2325

2426
ValueTypeConstraintReference:
25-
'constraint' name=ID ':' definition=[ConstraintDefinition] 'on' attribute=[ValueTypeAttribute] ';';
27+
'constraint' name=ID ':' definition=[ConstraintDefinition] 'on'
28+
property=[ValueTypeProperty] ';';
2629

2730
ValueTypeConstraintInlineDefinition:
2831
'constraint' name=ID ':' expression=Expression ';';

libs/language-server/src/lib/ast/expressions/evaluation-context.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
isTransformDefinition,
1919
isTransformPortDefinition,
2020
isValueKeywordLiteral,
21-
isValueTypeAttribute,
21+
isValueTypeProperty,
2222
} from '../generated/ast';
2323
import { type ValueTypeProvider } from '../wrappers';
2424
import { type ValueType } from '../wrappers/value-type/value-type';
@@ -103,11 +103,11 @@ export class EvaluationContext {
103103
)
104104
);
105105
}
106-
if (isValueTypeAttribute(dereferenced)) {
106+
if (isValueTypeProperty(dereferenced)) {
107107
return (
108108
this.variableValues.get(dereferenced.name) ??
109109
new MissingValue(
110-
`Could not find value for value type attribute ${dereferenced.name}`,
110+
`Could not find value for value type property ${dereferenced.name}`,
111111
)
112112
);
113113
}

libs/language-server/src/lib/ast/expressions/type-inference.ts

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
import { assertUnreachable } from 'langium';
66

7+
// eslint-disable-next-line unicorn/prefer-node-protocol
8+
import assert from 'assert';
9+
710
import { type ValidationContext } from '../../validation/validation-context';
811
import {
912
type CollectionLiteral,
@@ -31,7 +34,7 @@ import {
3134
isUnaryExpression,
3235
isValueKeywordLiteral,
3336
isValueLiteral,
34-
isValueTypeAttribute,
37+
isValueTypeProperty,
3538
isValuetypeAssignmentLiteral,
3639
} from '../generated/ast';
3740
import { getNextAstNodeContainer } from '../model-util';
@@ -41,10 +44,10 @@ import {
4144
type WrapperFactoryProvider,
4245
} from '../wrappers';
4346
import {
44-
getValuetypeHierarchyStack,
4547
pickCommonAtomicValueType,
4648
pickCommonPrimitiveValuetype,
4749
} from '../wrappers/util/value-type-util';
50+
import { onlyElementOrUndefined } from '../../util';
4851

4952
/**
5053
* @returns The inferred ValueType. `undefined` means that any type could be
@@ -233,23 +236,17 @@ function inferCollectionType(
233236
return undefined;
234237
}
235238

236-
const stacks = elementValuetypes.map(getValuetypeHierarchyStack);
237-
238-
if (stacks.length === 0) {
239+
if (elementValuetypes.length === 0) {
239240
return valueTypeProvider.EmptyCollection;
240241
}
241-
if (stacks.length === 1) {
242-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
243-
const stack = stacks[0]!;
244-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
245-
const resultingInnerType = stack[stack.length - 1]!;
246-
return valueTypeProvider.createCollectionValueTypeOf(resultingInnerType);
242+
if (elementValuetypes.length === 1) {
243+
const elementValueType = onlyElementOrUndefined(elementValuetypes);
244+
assert(elementValueType !== undefined);
245+
return valueTypeProvider.createCollectionValueTypeOf(elementValueType);
247246
}
248247

249-
const primitiveValuetypes = stacks.map((stack) => stack[0]);
250-
251248
const commonPrimitiveValuetype =
252-
pickCommonPrimitiveValuetype(primitiveValuetypes);
249+
pickCommonPrimitiveValuetype(elementValuetypes);
253250

254251
if (commonPrimitiveValuetype === undefined) {
255252
validationContext.accept(
@@ -262,7 +259,7 @@ function inferCollectionType(
262259
return undefined;
263260
}
264261

265-
const commonAtomicValueType = pickCommonAtomicValueType(stacks);
262+
const commonAtomicValueType = pickCommonAtomicValueType(elementValuetypes);
266263
if (commonAtomicValueType === undefined) {
267264
return valueTypeProvider.createCollectionValueTypeOf(
268265
commonPrimitiveValuetype,
@@ -342,7 +339,7 @@ function inferTypeFromReferenceLiteral(
342339
if (
343340
isTransformPortDefinition(referenced) ||
344341
isBlockTypeProperty(referenced) ||
345-
isValueTypeAttribute(referenced)
342+
isValueTypeProperty(referenced)
346343
) {
347344
const valueType = referenced.valueType;
348345

0 commit comments

Comments
 (0)