diff options
Diffstat (limited to 'subprojects/language/src')
39 files changed, 1693 insertions, 365 deletions
diff --git a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext index f0d6c38c..08f7a585 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext +++ b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext | |||
@@ -17,7 +17,8 @@ enum ModuleKind: | |||
17 | 17 | ||
18 | Statement: | 18 | Statement: |
19 | ImportStatement | Assertion | ClassDeclaration | EnumDeclaration | | 19 | ImportStatement | Assertion | ClassDeclaration | EnumDeclaration | |
20 | PredicateDefinition | /* FunctionDefinition | RuleDefinition | */ | 20 | DatatypeDeclaration | AggregatorDeclaration | PredicateDefinition | |
21 | /* FunctionDefinition | RuleDefinition | */ | ||
21 | ScopeDeclaration | NodeDeclaration; | 22 | ScopeDeclaration | NodeDeclaration; |
22 | 23 | ||
23 | ImportStatement: | 24 | ImportStatement: |
@@ -27,7 +28,7 @@ ClassDeclaration: | |||
27 | abstract?="abstract"? "class" | 28 | abstract?="abstract"? "class" |
28 | name=Identifier | 29 | name=Identifier |
29 | ("extends" superTypes+=[Relation|QualifiedName] ("," superTypes+=[Relation|QualifiedName])*)? | 30 | ("extends" superTypes+=[Relation|QualifiedName] ("," superTypes+=[Relation|QualifiedName])*)? |
30 | ("{" (featureDeclarations+=FeatureDeclaration ";"?)* "}" | "."); | 31 | ("{" (featureDeclarations+=ReferenceDeclaration ";"?)* "}" | "."); |
31 | 32 | ||
32 | EnumDeclaration: | 33 | EnumDeclaration: |
33 | "enum" | 34 | "enum" |
@@ -37,8 +38,11 @@ EnumDeclaration: | |||
37 | EnumLiteral returns Node: | 38 | EnumLiteral returns Node: |
38 | name=Identifier; | 39 | name=Identifier; |
39 | 40 | ||
40 | FeatureDeclaration: | 41 | DatatypeDeclaration: |
41 | ReferenceDeclaration /* | AttributeDeclaration | FlagDeclaration */; | 42 | "extern" "datatype" name=Identifier "."; |
43 | |||
44 | AggregatorDeclaration: | ||
45 | "extern" "aggregator" name=Identifier "."; | ||
42 | 46 | ||
43 | enum ReferenceKind: | 47 | enum ReferenceKind: |
44 | REFERENCE="refers" | CONTAINMENT="contains" | CONTAINER="container"; | 48 | REFERENCE="refers" | CONTAINMENT="contains" | CONTAINER="container"; |
@@ -53,15 +57,6 @@ ReferenceDeclaration: | |||
53 | ReferenceMultiplicity returns Multiplicity: | 57 | ReferenceMultiplicity returns Multiplicity: |
54 | "[" Multiplicity "]"; | 58 | "[" Multiplicity "]"; |
55 | 59 | ||
56 | //enum PrimitiveType: | ||
57 | // INT="int" | REAL="real" | STRING="string"; | ||
58 | // | ||
59 | //AttributeDeclaration: | ||
60 | // attributeType=PrimitiveType name=Identifier; | ||
61 | // | ||
62 | //FlagDeclaration: | ||
63 | // "bool" name=Identifier; | ||
64 | |||
65 | PredicateDefinition: | 60 | PredicateDefinition: |
66 | ("pred" | error?="error" "pred"?) | 61 | ("pred" | error?="error" "pred"?) |
67 | name=Identifier | 62 | name=Identifier |
@@ -73,14 +68,13 @@ Conjunction: | |||
73 | literals+=Expr ("," literals+=Expr)*; | 68 | literals+=Expr ("," literals+=Expr)*; |
74 | 69 | ||
75 | //FunctionDefinition: | 70 | //FunctionDefinition: |
76 | // "fn" functionType=PrimitiveType name=Identifier | 71 | // "fn" functionType=[DatatypeDefinition|QualifiedName] name=Identifier |
77 | // "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")" | 72 | // "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")" |
78 | // ("=" cases+=Case (";" cases+=Case)*)? | 73 | // ("=" cases+=Case (";" cases+=Case)*)? |
79 | // "."; | 74 | // "."; |
80 | // | 75 | // |
81 | //Case: | 76 | //Case: |
82 | // Conjunction ({Match.condition=current} "->" value=Expr)?; | 77 | // Conjunction ({Match.condition=current} "->" value=Expr)?; |
83 | |||
84 | //RuleDefinition: | 78 | //RuleDefinition: |
85 | // "rule" | 79 | // "rule" |
86 | // name=Identifier | 80 | // name=Identifier |
@@ -112,25 +106,32 @@ Parameter: | |||
112 | // name=Identifier; | 106 | // name=Identifier; |
113 | 107 | ||
114 | Expr: | 108 | Expr: |
115 | ComparisonExpr; | 109 | AssignmentExpr; |
110 | |||
111 | AssignmentExpr returns Expr: | ||
112 | BooleanExpr ({AssignmentExpr.left=current} "is" right=BooleanExpr)*; | ||
113 | |||
114 | enum BooleanBinaryOp returns BinaryOp: | ||
115 | AND="&&" | OR="||" | XOR="^^"; | ||
116 | |||
117 | BooleanExpr returns Expr: | ||
118 | ComparisonExpr ({ArithmeticBinaryExpr.left=current} | ||
119 | op=BooleanBinaryOp right=ComparisonExpr)*; | ||
116 | 120 | ||
117 | enum ComparisonOp: | 121 | enum ComparisonOp: |
118 | LESS="<" | LESS_EQ="<=" | GREATER=">" | GREATER_EQ=">=" | EQ="==" | NOT_EQ="!=" | | 122 | LESS="<" | LESS_EQ="<=" | GREATER=">" | GREATER_EQ=">=" | EQ="===" | NOT_EQ="!==" | |
119 | IN="in" | SUBSUMES=":>" | SUBSUMED_BY="<:" | ABS_EQ="===" | ABS_NOT_EQ="!=="; | 123 | IN="in" | NODE_EQ="==" | NODE_NOT_EQ="!="; |
120 | 124 | ||
121 | ComparisonExpr returns Expr: | 125 | ComparisonExpr returns Expr: |
122 | LatticeExpr ({ComparisonExpr.left=current} | 126 | LatticeExpr ({ComparisonExpr.left=current} |
123 | op=ComparisonOp right=LatticeExpr)*; | 127 | op=ComparisonOp right=LatticeExpr)*; |
124 | 128 | ||
125 | enum LatticeOp returns BinaryOp: | 129 | enum LatticeBinaryOp: |
126 | MEET="/\\" | JOIN="\\/"; | 130 | MEET="/\\" | JOIN="\\/"; |
127 | 131 | ||
128 | LatticeExpr returns Expr: | 132 | LatticeExpr returns Expr: |
129 | RangeExpr ({ArithmeticBinaryExpr.left=current} | 133 | AdditiveExpr ({LatticeBinaryExpr.left=current} |
130 | op=LatticeOp right=RangeExpr)*; | 134 | op=LatticeBinaryOp right=AdditiveExpr)*; |
131 | |||
132 | RangeExpr returns Expr: | ||
133 | AdditiveExpr ({RangeExpr.left=current} ".." right=AdditiveExpr)*; | ||
134 | 135 | ||
135 | enum AdditiveOp returns BinaryOp: | 136 | enum AdditiveOp returns BinaryOp: |
136 | ADD="+" | SUB="-"; | 137 | ADD="+" | SUB="-"; |
@@ -150,12 +151,15 @@ enum ExponentialOp returns BinaryOp: | |||
150 | POW="**"; | 151 | POW="**"; |
151 | 152 | ||
152 | ExponentialExpr returns Expr: | 153 | ExponentialExpr returns Expr: |
153 | UnaryExpr ({ArithmeticBinaryExpr.left=current} | 154 | RangeExpr ({ArithmeticBinaryExpr.left=current} |
154 | op=ExponentialOp right=ExponentialExpr)?; | 155 | op=ExponentialOp right=ExponentialExpr)?; |
155 | 156 | ||
157 | RangeExpr returns Expr: | ||
158 | UnaryExpr ({RangeExpr.left=current} ".." right=UnaryExpr)*; | ||
159 | |||
156 | UnaryExpr returns Expr: | 160 | UnaryExpr returns Expr: |
157 | ArithmeticUnaryExpr | ModalExpr | NegationExpr | CountExpr | AggregationExpr | | 161 | ArithmeticUnaryExpr | NegationExpr | |
158 | Atom | VariableOrNodeExpr | Constant | "(" Expr ")"; | 162 | CountExpr | AggregationExpr | CastExpr; |
159 | 163 | ||
160 | enum UnaryOp: | 164 | enum UnaryOp: |
161 | PLUS="+" | MINUS="-"; | 165 | PLUS="+" | MINUS="-"; |
@@ -163,23 +167,21 @@ enum UnaryOp: | |||
163 | ArithmeticUnaryExpr: | 167 | ArithmeticUnaryExpr: |
164 | op=UnaryOp body=UnaryExpr; | 168 | op=UnaryOp body=UnaryExpr; |
165 | 169 | ||
166 | enum Modality: | ||
167 | MAY="may" | MUST="must" | CURRENT="current"; | ||
168 | |||
169 | ModalExpr: | ||
170 | modality=Modality body=UnaryExpr; | ||
171 | |||
172 | NegationExpr: | 170 | NegationExpr: |
173 | "!" body=UnaryExpr; | 171 | "!" body=UnaryExpr; |
174 | 172 | ||
175 | CountExpr: | 173 | CountExpr: |
176 | "count" body=UnaryExpr; | 174 | "count" body=UnaryExpr; |
177 | 175 | ||
178 | enum AggregationOp: | ||
179 | SUM="sum" | PROD="prod" | MIN="min" | MAX="max"; | ||
180 | |||
181 | AggregationExpr: | 176 | AggregationExpr: |
182 | op=AggregationOp "{" value=Expr "|" condition=Expr "}"; | 177 | aggregator=[AggregatorDeclaration|QualifiedName] |
178 | "{" value=Expr "|" condition=ComparisonExpr "}"; | ||
179 | |||
180 | CastExpr returns Expr: | ||
181 | CastExprBody ({CastExpr.body=current} "as" targetType=[Relation|QualifiedName])?; | ||
182 | |||
183 | CastExprBody returns Expr: | ||
184 | Atom | VariableOrNodeExpr | Constant | "(" Expr ")"; | ||
183 | 185 | ||
184 | Atom: | 186 | Atom: |
185 | relation=[Relation|QualifiedName] | 187 | relation=[Relation|QualifiedName] |
@@ -190,7 +192,7 @@ VariableOrNodeExpr: | |||
190 | variableOrNode=[VariableOrNode|QualifiedName]; | 192 | variableOrNode=[VariableOrNode|QualifiedName]; |
191 | 193 | ||
192 | Constant: | 194 | Constant: |
193 | RealConstant | IntConstant | InfConstant | StringConstant | LogicConstant; | 195 | RealConstant | IntConstant | StringConstant | InfiniteConstant | LogicConstant; |
194 | 196 | ||
195 | IntConstant: | 197 | IntConstant: |
196 | intValue=INT; | 198 | intValue=INT; |
@@ -198,12 +200,12 @@ IntConstant: | |||
198 | RealConstant: | 200 | RealConstant: |
199 | realValue=Real; | 201 | realValue=Real; |
200 | 202 | ||
201 | InfConstant: | ||
202 | {InfConstant} "*"; | ||
203 | |||
204 | StringConstant: | 203 | StringConstant: |
205 | stringValue=STRING; | 204 | stringValue=STRING; |
206 | 205 | ||
206 | InfiniteConstant: | ||
207 | {InfiniteConstant} "*"; | ||
208 | |||
207 | enum LogicValue: | 209 | enum LogicValue: |
208 | TRUE="true" | FALSE="false" | UNKNOWN="unknown" | ERROR="error"; | 210 | TRUE="true" | FALSE="false" | UNKNOWN="unknown" | ERROR="error"; |
209 | 211 | ||
@@ -278,8 +280,8 @@ Identifier: | |||
278 | NonContainmentIdentifier | "contains" | "container"; | 280 | NonContainmentIdentifier | "contains" | "container"; |
279 | 281 | ||
280 | NonContainmentIdentifier: | 282 | NonContainmentIdentifier: |
281 | ID | "atom" | "multi" | "contained" | | 283 | ID | "atom" | "multi" | "contained" | "problem" | "module" | |
282 | "sum" | "prod" | "min" | "max" | "problem" | "module"; | 284 | "datatype" | "aggregator"; |
283 | 285 | ||
284 | Real returns ecore::EDouble: | 286 | Real returns ecore::EDouble: |
285 | EXPONENTIAL | INT "." (INT | EXPONENTIAL); | 287 | EXPONENTIAL | INT "." (INT | EXPONENTIAL); |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java b/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java index f7039027..955a89f9 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java +++ b/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java | |||
@@ -30,9 +30,8 @@ import org.eclipse.xtext.validation.IResourceValidator; | |||
30 | import org.eclipse.xtext.xbase.annotations.validation.DerivedStateAwareResourceValidator; | 30 | import org.eclipse.xtext.xbase.annotations.validation.DerivedStateAwareResourceValidator; |
31 | import tools.refinery.language.conversion.ProblemValueConverterService; | 31 | import tools.refinery.language.conversion.ProblemValueConverterService; |
32 | import tools.refinery.language.linking.ProblemLinkingService; | 32 | import tools.refinery.language.linking.ProblemLinkingService; |
33 | import tools.refinery.language.naming.ProblemDelegateQualifiedNameProvider; | ||
34 | import tools.refinery.language.naming.ProblemQualifiedNameConverter; | ||
35 | import tools.refinery.language.naming.ProblemQualifiedNameProvider; | 33 | import tools.refinery.language.naming.ProblemQualifiedNameProvider; |
34 | import tools.refinery.language.naming.ProblemQualifiedNameConverter; | ||
36 | import tools.refinery.language.parser.ProblemEcoreElementFactory; | 35 | import tools.refinery.language.parser.ProblemEcoreElementFactory; |
37 | import tools.refinery.language.parser.antlr.TokenSourceInjectingProblemParser; | 36 | import tools.refinery.language.parser.antlr.TokenSourceInjectingProblemParser; |
38 | import tools.refinery.language.resource.ProblemLocationInFileProvider; | 37 | import tools.refinery.language.resource.ProblemLocationInFileProvider; |
@@ -68,12 +67,6 @@ public class ProblemRuntimeModule extends AbstractProblemRuntimeModule { | |||
68 | return ProblemQualifiedNameConverter.class; | 67 | return ProblemQualifiedNameConverter.class; |
69 | } | 68 | } |
70 | 69 | ||
71 | public void configureIQualifiedNameProviderDelegate(Binder binder) { | ||
72 | binder.bind(IQualifiedNameProvider.class) | ||
73 | .annotatedWith(Names.named(ProblemQualifiedNameProvider.NAMED_DELEGATE)) | ||
74 | .to(ProblemDelegateQualifiedNameProvider.class); | ||
75 | } | ||
76 | |||
77 | @Override | 70 | @Override |
78 | public Class<? extends IQualifiedNameProvider> bindIQualifiedNameProvider() { | 71 | public Class<? extends IQualifiedNameProvider> bindIQualifiedNameProvider() { |
79 | return ProblemQualifiedNameProvider.class; | 72 | return ProblemQualifiedNameProvider.class; |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/expressions/AbstractTermInterpreter.java b/subprojects/language/src/main/java/tools/refinery/language/expressions/AbstractTermInterpreter.java new file mode 100644 index 00000000..2530b707 --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/expressions/AbstractTermInterpreter.java | |||
@@ -0,0 +1,131 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.expressions; | ||
7 | |||
8 | import tools.refinery.language.model.problem.BinaryOp; | ||
9 | import tools.refinery.language.model.problem.UnaryOp; | ||
10 | import tools.refinery.language.typesystem.AggregatorName; | ||
11 | import tools.refinery.language.typesystem.DataExprType; | ||
12 | |||
13 | import java.util.*; | ||
14 | |||
15 | // This class is used to configure term interpreters by clients with various arguments. | ||
16 | @SuppressWarnings("SameParameterValue") | ||
17 | public abstract class AbstractTermInterpreter implements TermInterpreter { | ||
18 | private final Map<DataExprType, DataExprType> negations = new HashMap<>(); | ||
19 | private final Map<UnaryKey, DataExprType> unaryOperators = new HashMap<>(); | ||
20 | private final Set<DataExprType> comparisons = new HashSet<>(); | ||
21 | private final Set<DataExprType> ranges = new HashSet<>(); | ||
22 | private final Map<BinaryKey, DataExprType> binaryOperators = new HashMap<>(); | ||
23 | private final Set<CastKey> casts = new HashSet<>(); | ||
24 | private final Map<AggregatorKey, DataExprType> aggregators = new HashMap<>(); | ||
25 | |||
26 | protected AbstractTermInterpreter() { | ||
27 | } | ||
28 | |||
29 | protected void addNegation(DataExprType type, DataExprType result) { | ||
30 | negations.put(type, result); | ||
31 | } | ||
32 | |||
33 | protected void addNegation(DataExprType type) { | ||
34 | addNegation(type, type); | ||
35 | } | ||
36 | |||
37 | protected void addUnaryOperator(UnaryOp op, DataExprType type, DataExprType result) { | ||
38 | unaryOperators.put(new UnaryKey(op, type), result); | ||
39 | } | ||
40 | |||
41 | protected void addUnaryOperator(UnaryOp op, DataExprType type) { | ||
42 | addUnaryOperator(op, type, type); | ||
43 | } | ||
44 | |||
45 | protected void addComparison(DataExprType type) { | ||
46 | comparisons.add(type); | ||
47 | } | ||
48 | |||
49 | protected void addRange(DataExprType type) { | ||
50 | ranges.add(type); | ||
51 | } | ||
52 | |||
53 | protected void addBinaryOperator(BinaryOp op, DataExprType leftType, DataExprType rightType, DataExprType result) { | ||
54 | binaryOperators.put(new BinaryKey(op, leftType, rightType), result); | ||
55 | } | ||
56 | |||
57 | protected void addBinaryOperator(BinaryOp op, DataExprType type) { | ||
58 | addBinaryOperator(op, type, type, type); | ||
59 | } | ||
60 | |||
61 | protected void addCast(DataExprType fromType, DataExprType toType) { | ||
62 | if (fromType.equals(toType)) { | ||
63 | throw new IllegalArgumentException("The fromType and toType of a cast operator must be different"); | ||
64 | } | ||
65 | casts.add(new CastKey(fromType, toType)); | ||
66 | } | ||
67 | |||
68 | protected void addAggregator(AggregatorName aggregator, DataExprType type, DataExprType result) { | ||
69 | aggregators.put(new AggregatorKey(aggregator, type), result); | ||
70 | } | ||
71 | |||
72 | protected void addAggregator(AggregatorName aggregator, DataExprType type) { | ||
73 | addAggregator(aggregator, type, type); | ||
74 | } | ||
75 | |||
76 | @Override | ||
77 | public Optional<DataExprType> getNegationType(DataExprType type) { | ||
78 | return Optional.ofNullable(negations.get(type)); | ||
79 | } | ||
80 | |||
81 | @Override | ||
82 | public Optional<DataExprType> getUnaryOperationType(UnaryOp op, DataExprType type) { | ||
83 | if (unaryOperators.isEmpty()) { | ||
84 | return Optional.empty(); | ||
85 | } | ||
86 | return Optional.ofNullable(unaryOperators.get(new UnaryKey(op, type))); | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public boolean isComparisonSupported(DataExprType type) { | ||
91 | return comparisons.contains(type); | ||
92 | } | ||
93 | |||
94 | @Override | ||
95 | public boolean isRangeSupported(DataExprType type) { | ||
96 | return ranges.contains(type); | ||
97 | } | ||
98 | |||
99 | @Override | ||
100 | public Optional<DataExprType> getBinaryOperationType(BinaryOp op, DataExprType leftType, DataExprType rightType) { | ||
101 | if (binaryOperators.isEmpty()) { | ||
102 | return Optional.empty(); | ||
103 | } | ||
104 | return Optional.ofNullable(binaryOperators.get(new BinaryKey(op, leftType, rightType))); | ||
105 | } | ||
106 | |||
107 | @Override | ||
108 | public Optional<DataExprType> getAggregationType(AggregatorName aggregator, DataExprType type) { | ||
109 | if (aggregators.isEmpty()) { | ||
110 | return Optional.empty(); | ||
111 | } | ||
112 | return Optional.ofNullable(aggregators.get(new AggregatorKey(aggregator, type))); | ||
113 | } | ||
114 | |||
115 | @Override | ||
116 | public boolean isCastSupported(DataExprType fromType, DataExprType toType) { | ||
117 | return casts.contains(new CastKey(fromType, toType)); | ||
118 | } | ||
119 | |||
120 | private record UnaryKey(UnaryOp op, DataExprType type) { | ||
121 | } | ||
122 | |||
123 | private record BinaryKey(BinaryOp op, DataExprType leftType, DataExprType rightType) { | ||
124 | } | ||
125 | |||
126 | private record CastKey(DataExprType fromType, DataExprType toType) { | ||
127 | } | ||
128 | |||
129 | private record AggregatorKey(AggregatorName aggregator, DataExprType type) { | ||
130 | } | ||
131 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/expressions/BuiltinTermInterpreter.java b/subprojects/language/src/main/java/tools/refinery/language/expressions/BuiltinTermInterpreter.java new file mode 100644 index 00000000..412ed8ba --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/expressions/BuiltinTermInterpreter.java | |||
@@ -0,0 +1,54 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.expressions; | ||
7 | |||
8 | import tools.refinery.language.library.BuiltinLibrary; | ||
9 | import tools.refinery.language.model.problem.BinaryOp; | ||
10 | import tools.refinery.language.model.problem.UnaryOp; | ||
11 | import tools.refinery.language.typesystem.AggregatorName; | ||
12 | import tools.refinery.language.typesystem.DataExprType; | ||
13 | |||
14 | public final class BuiltinTermInterpreter extends AbstractTermInterpreter { | ||
15 | public static final DataExprType BOOLEAN_TYPE = new DataExprType(BuiltinLibrary.BUILTIN_LIBRARY_NAME, "boolean"); | ||
16 | public static final DataExprType INT_TYPE = new DataExprType(BuiltinLibrary.BUILTIN_LIBRARY_NAME, "int"); | ||
17 | public static final DataExprType REAL_TYPE = new DataExprType(BuiltinLibrary.BUILTIN_LIBRARY_NAME, "real"); | ||
18 | public static final DataExprType STRING_TYPE = new DataExprType(BuiltinLibrary.BUILTIN_LIBRARY_NAME, "string"); | ||
19 | public static final AggregatorName MIN_AGGREGATOR = new AggregatorName(BuiltinLibrary.BUILTIN_LIBRARY_NAME, "min"); | ||
20 | public static final AggregatorName MAX_AGGREGATOR = new AggregatorName(BuiltinLibrary.BUILTIN_LIBRARY_NAME, "max"); | ||
21 | public static final AggregatorName SUM_AGGREGATOR = new AggregatorName(BuiltinLibrary.BUILTIN_LIBRARY_NAME, "sum"); | ||
22 | |||
23 | public BuiltinTermInterpreter() { | ||
24 | addNegation(BOOLEAN_TYPE); | ||
25 | addBinaryOperator(BinaryOp.AND, BOOLEAN_TYPE); | ||
26 | addBinaryOperator(BinaryOp.OR, BOOLEAN_TYPE); | ||
27 | addBinaryOperator(BinaryOp.XOR, BOOLEAN_TYPE); | ||
28 | |||
29 | addUnaryOperator(UnaryOp.PLUS, INT_TYPE); | ||
30 | addUnaryOperator(UnaryOp.MINUS, INT_TYPE); | ||
31 | addComparison(INT_TYPE); | ||
32 | addRange(INT_TYPE); | ||
33 | addBinaryOperator(BinaryOp.ADD, INT_TYPE); | ||
34 | addBinaryOperator(BinaryOp.SUB, INT_TYPE); | ||
35 | addBinaryOperator(BinaryOp.MUL, INT_TYPE); | ||
36 | addAggregator(MIN_AGGREGATOR, INT_TYPE); | ||
37 | addAggregator(MAX_AGGREGATOR, INT_TYPE); | ||
38 | addAggregator(SUM_AGGREGATOR, INT_TYPE); | ||
39 | |||
40 | addUnaryOperator(UnaryOp.PLUS, REAL_TYPE); | ||
41 | addUnaryOperator(UnaryOp.MINUS, REAL_TYPE); | ||
42 | addCast(INT_TYPE, REAL_TYPE); | ||
43 | addComparison(REAL_TYPE); | ||
44 | addRange(REAL_TYPE); | ||
45 | addBinaryOperator(BinaryOp.ADD, REAL_TYPE); | ||
46 | addBinaryOperator(BinaryOp.SUB, REAL_TYPE); | ||
47 | addBinaryOperator(BinaryOp.MUL, REAL_TYPE); | ||
48 | addBinaryOperator(BinaryOp.DIV, REAL_TYPE); | ||
49 | addBinaryOperator(BinaryOp.POW, REAL_TYPE); | ||
50 | addAggregator(MIN_AGGREGATOR, REAL_TYPE); | ||
51 | addAggregator(MAX_AGGREGATOR, REAL_TYPE); | ||
52 | addAggregator(SUM_AGGREGATOR, REAL_TYPE); | ||
53 | } | ||
54 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/expressions/CompositeTermInterpreter.java b/subprojects/language/src/main/java/tools/refinery/language/expressions/CompositeTermInterpreter.java new file mode 100644 index 00000000..b337e5dd --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/expressions/CompositeTermInterpreter.java | |||
@@ -0,0 +1,99 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.expressions; | ||
7 | |||
8 | import tools.refinery.language.model.problem.BinaryOp; | ||
9 | import tools.refinery.language.model.problem.UnaryOp; | ||
10 | import tools.refinery.language.typesystem.AggregatorName; | ||
11 | import tools.refinery.language.typesystem.DataExprType; | ||
12 | |||
13 | import java.util.List; | ||
14 | import java.util.Optional; | ||
15 | |||
16 | public class CompositeTermInterpreter implements TermInterpreter { | ||
17 | private final List<TermInterpreter> interpreters; | ||
18 | |||
19 | public CompositeTermInterpreter(List<TermInterpreter> interpreters) { | ||
20 | this.interpreters = interpreters; | ||
21 | } | ||
22 | |||
23 | @Override | ||
24 | public Optional<DataExprType> getNegationType(DataExprType type) { | ||
25 | for (var interpreter : interpreters) { | ||
26 | var result = interpreter.getNegationType(type); | ||
27 | if (result.isPresent()) { | ||
28 | return result; | ||
29 | } | ||
30 | } | ||
31 | return Optional.empty(); | ||
32 | } | ||
33 | |||
34 | @Override | ||
35 | public Optional<DataExprType> getUnaryOperationType(UnaryOp op, DataExprType type) { | ||
36 | for (var interpreter : interpreters) { | ||
37 | var result = interpreter.getUnaryOperationType(op, type); | ||
38 | if (result.isPresent()) { | ||
39 | return result; | ||
40 | } | ||
41 | } | ||
42 | return Optional.empty(); | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public boolean isComparisonSupported(DataExprType type) { | ||
47 | for (var interpreter : interpreters) { | ||
48 | var result = interpreter.isComparisonSupported(type); | ||
49 | if (result) { | ||
50 | return true; | ||
51 | } | ||
52 | } | ||
53 | return false; | ||
54 | } | ||
55 | |||
56 | @Override | ||
57 | public boolean isRangeSupported(DataExprType type) { | ||
58 | for (var interpreter : interpreters) { | ||
59 | var result = interpreter.isRangeSupported(type); | ||
60 | if (result) { | ||
61 | return true; | ||
62 | } | ||
63 | } | ||
64 | return false; | ||
65 | } | ||
66 | |||
67 | @Override | ||
68 | public Optional<DataExprType> getBinaryOperationType(BinaryOp op, DataExprType leftType, DataExprType rightType) { | ||
69 | for (var interpreter : interpreters) { | ||
70 | var result = interpreter.getBinaryOperationType(op, leftType, rightType); | ||
71 | if (result.isPresent()) { | ||
72 | return result; | ||
73 | } | ||
74 | } | ||
75 | return Optional.empty(); | ||
76 | } | ||
77 | |||
78 | @Override | ||
79 | public boolean isCastSupported(DataExprType fromType, DataExprType toType) { | ||
80 | for (var interpreter : interpreters) { | ||
81 | var result = interpreter.isCastSupported(fromType, toType); | ||
82 | if (result) { | ||
83 | return true; | ||
84 | } | ||
85 | } | ||
86 | return false; | ||
87 | } | ||
88 | |||
89 | @Override | ||
90 | public Optional<DataExprType> getAggregationType(AggregatorName aggregator, DataExprType type) { | ||
91 | for (var interpreter : interpreters) { | ||
92 | var result = interpreter.getAggregationType(aggregator, type); | ||
93 | if (result.isPresent()) { | ||
94 | return result; | ||
95 | } | ||
96 | } | ||
97 | return Optional.empty(); | ||
98 | } | ||
99 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/expressions/TermInterpreter.java b/subprojects/language/src/main/java/tools/refinery/language/expressions/TermInterpreter.java new file mode 100644 index 00000000..122785f2 --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/expressions/TermInterpreter.java | |||
@@ -0,0 +1,29 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.expressions; | ||
7 | |||
8 | import tools.refinery.language.model.problem.BinaryOp; | ||
9 | import tools.refinery.language.model.problem.UnaryOp; | ||
10 | import tools.refinery.language.typesystem.AggregatorName; | ||
11 | import tools.refinery.language.typesystem.DataExprType; | ||
12 | |||
13 | import java.util.Optional; | ||
14 | |||
15 | public interface TermInterpreter { | ||
16 | Optional<DataExprType> getNegationType(DataExprType type); | ||
17 | |||
18 | Optional<DataExprType> getUnaryOperationType(UnaryOp op, DataExprType type); | ||
19 | |||
20 | boolean isComparisonSupported(DataExprType type); | ||
21 | |||
22 | boolean isRangeSupported(DataExprType type); | ||
23 | |||
24 | Optional<DataExprType> getBinaryOperationType(BinaryOp op, DataExprType leftType, DataExprType rightType); | ||
25 | |||
26 | boolean isCastSupported(DataExprType fromType, DataExprType toType); | ||
27 | |||
28 | Optional<DataExprType> getAggregationType(AggregatorName aggregator, DataExprType type); | ||
29 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemDelegateQualifiedNameProvider.java b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemDelegateQualifiedNameProvider.java deleted file mode 100644 index b3931401..00000000 --- a/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemDelegateQualifiedNameProvider.java +++ /dev/null | |||
@@ -1,44 +0,0 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.naming; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import org.eclipse.xtext.naming.DefaultDeclarativeQualifiedNameProvider; | ||
10 | import org.eclipse.xtext.naming.IQualifiedNameConverter; | ||
11 | import org.eclipse.xtext.naming.QualifiedName; | ||
12 | import tools.refinery.language.model.problem.Problem; | ||
13 | import tools.refinery.language.scoping.imports.ImportAdapter; | ||
14 | import tools.refinery.language.utils.ProblemUtil; | ||
15 | |||
16 | public class ProblemDelegateQualifiedNameProvider extends DefaultDeclarativeQualifiedNameProvider { | ||
17 | @Inject | ||
18 | private IQualifiedNameConverter qualifiedNameConverter; | ||
19 | |||
20 | protected QualifiedName qualifiedName(Problem problem) { | ||
21 | var qualifiedNameString = problem.getName(); | ||
22 | if (qualifiedNameString != null) { | ||
23 | return NamingUtil.stripRootPrefix(qualifiedNameConverter.toQualifiedName(qualifiedNameString)); | ||
24 | } | ||
25 | if (!ProblemUtil.isModule(problem)) { | ||
26 | return null; | ||
27 | } | ||
28 | var resource = problem.eResource(); | ||
29 | if (resource == null) { | ||
30 | return null; | ||
31 | } | ||
32 | var resourceUri = resource.getURI(); | ||
33 | if (resourceUri == null) { | ||
34 | return null; | ||
35 | } | ||
36 | var resourceSet = resource.getResourceSet(); | ||
37 | if (resourceSet == null) { | ||
38 | return null; | ||
39 | } | ||
40 | var adapter = ImportAdapter.getOrInstall(resourceSet); | ||
41 | // If a module has no explicitly specified name, return the qualified name it was resolved under. | ||
42 | return adapter.getQualifiedName(resourceUri); | ||
43 | } | ||
44 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameProvider.java b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameProvider.java index 5b682058..2a4df4d0 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameProvider.java +++ b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameProvider.java | |||
@@ -6,36 +6,44 @@ | |||
6 | package tools.refinery.language.naming; | 6 | package tools.refinery.language.naming; |
7 | 7 | ||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import com.google.inject.name.Named; | 9 | import com.google.inject.Singleton; |
10 | import org.eclipse.emf.ecore.EObject; | 10 | import org.eclipse.xtext.naming.DefaultDeclarativeQualifiedNameProvider; |
11 | import org.eclipse.xtext.naming.IQualifiedNameProvider; | 11 | import org.eclipse.xtext.naming.IQualifiedNameConverter; |
12 | import org.eclipse.xtext.naming.QualifiedName; | 12 | import org.eclipse.xtext.naming.QualifiedName; |
13 | import org.eclipse.xtext.util.IResourceScopeCache; | 13 | import tools.refinery.language.model.problem.Problem; |
14 | import org.eclipse.xtext.util.Tuples; | 14 | import tools.refinery.language.scoping.imports.ImportAdapterProvider; |
15 | import tools.refinery.language.resource.ProblemResourceDescriptionStrategy; | 15 | import tools.refinery.language.utils.ProblemUtil; |
16 | |||
17 | public class ProblemQualifiedNameProvider extends IQualifiedNameProvider.AbstractImpl { | ||
18 | private static final String PREFIX = "tools.refinery.language.naming.ProblemQualifiedNameProvider."; | ||
19 | public static final String NAMED_DELEGATE = PREFIX + "NAMED_DELEGATE"; | ||
20 | public static final String CACHE_KEY = PREFIX + "CACHE_KEY"; | ||
21 | 16 | ||
17 | @Singleton | ||
18 | public class ProblemQualifiedNameProvider extends DefaultDeclarativeQualifiedNameProvider { | ||
22 | @Inject | 19 | @Inject |
23 | @Named(NAMED_DELEGATE) | 20 | private IQualifiedNameConverter qualifiedNameConverter; |
24 | private IQualifiedNameProvider delegate; | ||
25 | 21 | ||
26 | @Inject | 22 | @Inject |
27 | private IResourceScopeCache cache = IResourceScopeCache.NullImpl.INSTANCE; | 23 | private ImportAdapterProvider importAdapterProvider; |
28 | |||
29 | @Override | ||
30 | public QualifiedName getFullyQualifiedName(EObject obj) { | ||
31 | return cache.get(Tuples.pair(obj, CACHE_KEY), obj.eResource(), () -> computeFullyQualifiedName(obj)); | ||
32 | } | ||
33 | 24 | ||
34 | public QualifiedName computeFullyQualifiedName(EObject obj) { | 25 | protected QualifiedName qualifiedName(Problem problem) { |
35 | var qualifiedName = delegate.getFullyQualifiedName(obj); | 26 | var qualifiedNameString = problem.getName(); |
36 | if (qualifiedName != null && ProblemResourceDescriptionStrategy.shouldExport(obj)) { | 27 | if (qualifiedNameString != null) { |
37 | return NamingUtil.addRootPrefix(qualifiedName); | 28 | return NamingUtil.stripRootPrefix(qualifiedNameConverter.toQualifiedName(qualifiedNameString)); |
29 | } | ||
30 | if (!ProblemUtil.isModule(problem)) { | ||
31 | return null; | ||
32 | } | ||
33 | var resource = problem.eResource(); | ||
34 | if (resource == null) { | ||
35 | return null; | ||
36 | } | ||
37 | var resourceUri = resource.getURI(); | ||
38 | if (resourceUri == null) { | ||
39 | return null; | ||
40 | } | ||
41 | var resourceSet = resource.getResourceSet(); | ||
42 | if (resourceSet == null) { | ||
43 | return null; | ||
38 | } | 44 | } |
39 | return qualifiedName; | 45 | var adapter = importAdapterProvider.getOrInstall(resourceSet); |
46 | // If a module has no explicitly specified name, return the qualified name it was resolved under. | ||
47 | return adapter.getQualifiedName(resourceUri); | ||
40 | } | 48 | } |
41 | } | 49 | } |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java index 3dcf6b1f..8fd60364 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java +++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java | |||
@@ -8,7 +8,6 @@ package tools.refinery.language.resource; | |||
8 | import com.google.common.collect.ImmutableMap; | 8 | import com.google.common.collect.ImmutableMap; |
9 | import com.google.inject.Inject; | 9 | import com.google.inject.Inject; |
10 | import com.google.inject.Singleton; | 10 | import com.google.inject.Singleton; |
11 | import com.google.inject.name.Named; | ||
12 | import org.eclipse.emf.ecore.EObject; | 11 | import org.eclipse.emf.ecore.EObject; |
13 | import org.eclipse.xtext.EcoreUtil2; | 12 | import org.eclipse.xtext.EcoreUtil2; |
14 | import org.eclipse.xtext.naming.IQualifiedNameConverter; | 13 | import org.eclipse.xtext.naming.IQualifiedNameConverter; |
@@ -19,10 +18,9 @@ import org.eclipse.xtext.resource.IEObjectDescription; | |||
19 | import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy; | 18 | import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy; |
20 | import org.eclipse.xtext.util.IAcceptor; | 19 | import org.eclipse.xtext.util.IAcceptor; |
21 | import tools.refinery.language.documentation.DocumentationCommentParser; | 20 | import tools.refinery.language.documentation.DocumentationCommentParser; |
22 | import tools.refinery.language.naming.ProblemQualifiedNameProvider; | ||
23 | import tools.refinery.language.scoping.imports.ImportCollector; | ||
24 | import tools.refinery.language.model.problem.*; | 21 | import tools.refinery.language.model.problem.*; |
25 | import tools.refinery.language.naming.NamingUtil; | 22 | import tools.refinery.language.naming.NamingUtil; |
23 | import tools.refinery.language.scoping.imports.ImportCollector; | ||
26 | import tools.refinery.language.utils.ProblemUtil; | 24 | import tools.refinery.language.utils.ProblemUtil; |
27 | 25 | ||
28 | import java.util.Map; | 26 | import java.util.Map; |
@@ -32,13 +30,15 @@ import java.util.stream.Collectors; | |||
32 | public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy { | 30 | public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy { |
33 | private static final String DATA_PREFIX = "tools.refinery.language.resource.ProblemResourceDescriptionStrategy."; | 31 | private static final String DATA_PREFIX = "tools.refinery.language.resource.ProblemResourceDescriptionStrategy."; |
34 | 32 | ||
35 | public static final String ARITY = DATA_PREFIX + "ARITY"; | 33 | public static final String TYPE_LIKE = DATA_PREFIX + "ARITY"; |
34 | public static final String TYPE_LIKE_TRUE = "true"; | ||
36 | public static final String ERROR_PREDICATE = DATA_PREFIX + "ERROR_PREDICATE"; | 35 | public static final String ERROR_PREDICATE = DATA_PREFIX + "ERROR_PREDICATE"; |
37 | public static final String ERROR_PREDICATE_TRUE = "true"; | 36 | public static final String ERROR_PREDICATE_TRUE = "true"; |
38 | public static final String SHADOWING_KEY = DATA_PREFIX + "SHADOWING_KEY"; | 37 | public static final String SHADOWING_KEY = DATA_PREFIX + "SHADOWING_KEY"; |
39 | public static final String SHADOWING_KEY_PROBLEM = "problem"; | 38 | public static final String SHADOWING_KEY_PROBLEM = "problem"; |
40 | public static final String SHADOWING_KEY_NODE = "node"; | 39 | public static final String SHADOWING_KEY_NODE = "node"; |
41 | public static final String SHADOWING_KEY_RELATION = "relation"; | 40 | public static final String SHADOWING_KEY_RELATION = "relation"; |
41 | public static final String SHADOWING_KEY_AGGREGATOR = "aggregator"; | ||
42 | public static final String PREFERRED_NAME = DATA_PREFIX + "PREFERRED_NAME"; | 42 | public static final String PREFERRED_NAME = DATA_PREFIX + "PREFERRED_NAME"; |
43 | public static final String PREFERRED_NAME_TRUE = "true"; | 43 | public static final String PREFERRED_NAME_TRUE = "true"; |
44 | public static final String IMPORTS = DATA_PREFIX + "IMPORTS"; | 44 | public static final String IMPORTS = DATA_PREFIX + "IMPORTS"; |
@@ -51,8 +51,7 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti | |||
51 | private IQualifiedNameConverter qualifiedNameConverter; | 51 | private IQualifiedNameConverter qualifiedNameConverter; |
52 | 52 | ||
53 | @Inject | 53 | @Inject |
54 | @Named(ProblemQualifiedNameProvider.NAMED_DELEGATE) | 54 | private IQualifiedNameProvider qualifiedNameProvider; |
55 | private IQualifiedNameProvider delegateQualifiedNameProvider; | ||
56 | 55 | ||
57 | @Inject | 56 | @Inject |
58 | private ImportCollector importCollector; | 57 | private ImportCollector importCollector; |
@@ -123,7 +122,7 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti | |||
123 | if (problem == null) { | 122 | if (problem == null) { |
124 | return QualifiedName.EMPTY; | 123 | return QualifiedName.EMPTY; |
125 | } | 124 | } |
126 | var qualifiedName = delegateQualifiedNameProvider.getFullyQualifiedName(problem); | 125 | var qualifiedName = qualifiedNameProvider.getFullyQualifiedName(problem); |
127 | return qualifiedName == null ? QualifiedName.EMPTY : qualifiedName; | 126 | return qualifiedName == null ? QualifiedName.EMPTY : qualifiedName; |
128 | } | 127 | } |
129 | 128 | ||
@@ -152,8 +151,11 @@ public class ProblemResourceDescriptionStrategy extends DefaultResourceDescripti | |||
152 | builder.put(SHADOWING_KEY, SHADOWING_KEY_NODE); | 151 | builder.put(SHADOWING_KEY, SHADOWING_KEY_NODE); |
153 | } else if (eObject instanceof Relation relation) { | 152 | } else if (eObject instanceof Relation relation) { |
154 | builder.put(SHADOWING_KEY, SHADOWING_KEY_RELATION); | 153 | builder.put(SHADOWING_KEY, SHADOWING_KEY_RELATION); |
155 | int arity = ProblemUtil.getArity(relation); | 154 | if (ProblemUtil.isTypeLike(relation)) { |
156 | builder.put(ARITY, Integer.toString(arity)); | 155 | builder.put(TYPE_LIKE, TYPE_LIKE_TRUE); |
156 | } | ||
157 | } else if (eObject instanceof AggregatorDeclaration) { | ||
158 | builder.put(SHADOWING_KEY, SHADOWING_KEY_AGGREGATOR); | ||
157 | } | 159 | } |
158 | if (eObject instanceof PredicateDefinition predicateDefinition && predicateDefinition.isError()) { | 160 | if (eObject instanceof PredicateDefinition predicateDefinition && predicateDefinition.isError()) { |
159 | builder.put(ERROR_PREDICATE, ERROR_PREDICATE_TRUE); | 161 | builder.put(ERROR_PREDICATE, ERROR_PREDICATE_TRUE); |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/state/DerivedVariableComputer.java b/subprojects/language/src/main/java/tools/refinery/language/resource/state/DerivedVariableComputer.java index f0baf35f..f096264b 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/resource/state/DerivedVariableComputer.java +++ b/subprojects/language/src/main/java/tools/refinery/language/resource/state/DerivedVariableComputer.java | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
@@ -44,15 +44,14 @@ public class DerivedVariableComputer { | |||
44 | knownVariables.add(name); | 44 | knownVariables.add(name); |
45 | } | 45 | } |
46 | } | 46 | } |
47 | if (definition instanceof PredicateDefinition predicateDefinition) { | 47 | switch (definition) { |
48 | installDerivedPredicateDefinitionState(predicateDefinition, knownVariables); | 48 | case PredicateDefinition predicateDefinition -> |
49 | } else if (definition instanceof FunctionDefinition functionDefinition) { | 49 | installDerivedPredicateDefinitionState(predicateDefinition, knownVariables); |
50 | installDerivedFunctionDefinitionState(functionDefinition, knownVariables); | 50 | case FunctionDefinition functionDefinition -> |
51 | } else if (definition instanceof RuleDefinition ruleDefinition) { | 51 | installDerivedFunctionDefinitionState(functionDefinition, knownVariables); |
52 | installDerivedRuleDefinitionState(ruleDefinition, knownVariables); | 52 | case RuleDefinition ruleDefinition -> installDerivedRuleDefinitionState(ruleDefinition, knownVariables); |
53 | } else { | 53 | default -> throw new IllegalArgumentException("Unknown ParametricDefinition: " + definition); |
54 | throw new IllegalArgumentException("Unknown ParametricDefinition: " + definition); | 54 | } |
55 | } | ||
56 | } | 55 | } |
57 | 56 | ||
58 | protected void installDerivedPredicateDefinitionState(PredicateDefinition definition, Set<String> knownVariables) { | 57 | protected void installDerivedPredicateDefinitionState(PredicateDefinition definition, Set<String> knownVariables) { |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/state/ImplicitVariableScope.java b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ImplicitVariableScope.java index e25887ad..c8e01724 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/resource/state/ImplicitVariableScope.java +++ b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ImplicitVariableScope.java | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
@@ -16,10 +16,7 @@ import org.eclipse.xtext.scoping.IScopeProvider; | |||
16 | import tools.refinery.language.model.problem.*; | 16 | import tools.refinery.language.model.problem.*; |
17 | import tools.refinery.language.naming.NamingUtil; | 17 | import tools.refinery.language.naming.NamingUtil; |
18 | 18 | ||
19 | import java.util.Deque; | 19 | import java.util.*; |
20 | import java.util.HashSet; | ||
21 | import java.util.List; | ||
22 | import java.util.Set; | ||
23 | 20 | ||
24 | public class ImplicitVariableScope { | 21 | public class ImplicitVariableScope { |
25 | private final EObject root; | 22 | private final EObject root; |
@@ -71,13 +68,12 @@ public class ImplicitVariableScope { | |||
71 | if ((hasKnownVariables && hasParent) || (!hasKnownVariables && !hasParent)) { | 68 | if ((hasKnownVariables && hasParent) || (!hasKnownVariables && !hasParent)) { |
72 | throw new IllegalStateException("Either known variables or parent must be provided, but not both"); | 69 | throw new IllegalStateException("Either known variables or parent must be provided, but not both"); |
73 | } | 70 | } |
74 | if (hasKnownVariables) { | 71 | if (!hasKnownVariables) { |
75 | return; | 72 | if (parent.knownVariables == null) { |
76 | } | 73 | throw new IllegalStateException("Parent scope must be processed before current scope"); |
77 | if (parent.knownVariables == null) { | 74 | } |
78 | throw new IllegalStateException("Parent scope must be processed before current scope"); | 75 | knownVariables = new HashSet<>(parent.knownVariables); |
79 | } | 76 | } |
80 | knownVariables = new HashSet<>(parent.knownVariables); | ||
81 | } | 77 | } |
82 | 78 | ||
83 | private void processEObject(EObject eObject, IScopeProvider scopeProvider, LinkingHelper linkingHelper, | 79 | private void processEObject(EObject eObject, IScopeProvider scopeProvider, LinkingHelper linkingHelper, |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java index d905aa9a..efa77c50 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java +++ b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
@@ -51,7 +51,7 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer { | |||
51 | if (contents.isEmpty()) { | 51 | if (contents.isEmpty()) { |
52 | return null; | 52 | return null; |
53 | } | 53 | } |
54 | EObject object = contents.get(0); | 54 | EObject object = contents.getFirst(); |
55 | if (object instanceof Problem problem) { | 55 | if (object instanceof Problem problem) { |
56 | return problem; | 56 | return problem; |
57 | } | 57 | } |
@@ -71,10 +71,8 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer { | |||
71 | for (var statement : problem.getStatements()) { | 71 | for (var statement : problem.getStatements()) { |
72 | if (statement instanceof ClassDeclaration classDeclaration) { | 72 | if (statement instanceof ClassDeclaration classDeclaration) { |
73 | installOrRemoveNewNode(adapter, classDeclaration); | 73 | installOrRemoveNewNode(adapter, classDeclaration); |
74 | for (var featureDeclaration : classDeclaration.getFeatureDeclarations()) { | 74 | for (var referenceDeclaration : classDeclaration.getFeatureDeclarations()) { |
75 | if (featureDeclaration instanceof ReferenceDeclaration referenceDeclaration) { | 75 | installOrRemoveInvalidMultiplicityPredicate(adapter, classDeclaration, referenceDeclaration); |
76 | installOrRemoveInvalidMultiplicityPredicate(adapter, classDeclaration, referenceDeclaration); | ||
77 | } | ||
78 | } | 76 | } |
79 | } | 77 | } |
80 | } | 78 | } |
@@ -157,9 +155,8 @@ public class ProblemDerivedStateComputer implements IDerivedStateComputer { | |||
157 | if (classDeclaration.isAbstract()) { | 155 | if (classDeclaration.isAbstract()) { |
158 | abstractClassDeclarations.add(classDeclaration); | 156 | abstractClassDeclarations.add(classDeclaration); |
159 | } | 157 | } |
160 | for (var featureDeclaration : classDeclaration.getFeatureDeclarations()) { | 158 | for (var referenceDeclaration : classDeclaration.getFeatureDeclarations()) { |
161 | if (featureDeclaration instanceof ReferenceDeclaration referenceDeclaration && | 159 | if (ProblemUtil.hasMultiplicityConstraint(referenceDeclaration)) { |
162 | ProblemUtil.hasMultiplicityConstraint(referenceDeclaration)) { | ||
163 | referenceDeclarationsWithMultiplicity.add(referenceDeclaration); | 160 | referenceDeclarationsWithMultiplicity.add(referenceDeclaration); |
164 | } | 161 | } |
165 | } | 162 | } |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java index 0067bf94..3b94423a 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java +++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java | |||
@@ -6,7 +6,6 @@ | |||
6 | package tools.refinery.language.scoping; | 6 | package tools.refinery.language.scoping; |
7 | 7 | ||
8 | import com.google.inject.Inject; | 8 | import com.google.inject.Inject; |
9 | import com.google.inject.name.Named; | ||
10 | import org.eclipse.emf.ecore.EObject; | 9 | import org.eclipse.emf.ecore.EObject; |
11 | import org.eclipse.emf.ecore.EReference; | 10 | import org.eclipse.emf.ecore.EReference; |
12 | import org.eclipse.emf.ecore.resource.Resource; | 11 | import org.eclipse.emf.ecore.resource.Resource; |
@@ -17,14 +16,12 @@ import org.eclipse.xtext.resource.ISelectable; | |||
17 | import org.eclipse.xtext.scoping.IScope; | 16 | import org.eclipse.xtext.scoping.IScope; |
18 | import org.eclipse.xtext.scoping.impl.AbstractGlobalScopeDelegatingScopeProvider; | 17 | import org.eclipse.xtext.scoping.impl.AbstractGlobalScopeDelegatingScopeProvider; |
19 | import org.eclipse.xtext.util.IResourceScopeCache; | 18 | import org.eclipse.xtext.util.IResourceScopeCache; |
20 | import tools.refinery.language.naming.ProblemQualifiedNameProvider; | ||
21 | 19 | ||
22 | public class ProblemLocalScopeProvider extends AbstractGlobalScopeDelegatingScopeProvider { | 20 | public class ProblemLocalScopeProvider extends AbstractGlobalScopeDelegatingScopeProvider { |
23 | private static final String CACHE_KEY = "tools.refinery.language.scoping.ProblemLocalScopeProvider.CACHE_KEY"; | 21 | private static final String CACHE_KEY = "tools.refinery.language.scoping.ProblemLocalScopeProvider.CACHE_KEY"; |
24 | 22 | ||
25 | @Inject | 23 | @Inject |
26 | @Named(ProblemQualifiedNameProvider.NAMED_DELEGATE) | 24 | private IQualifiedNameProvider qualifiedNameProvider; |
27 | private IQualifiedNameProvider delegateQualifiedNameProvider; | ||
28 | 25 | ||
29 | @Inject | 26 | @Inject |
30 | private IResourceDescriptionsProvider resourceDescriptionsProvider; | 27 | private IResourceDescriptionsProvider resourceDescriptionsProvider; |
@@ -59,7 +56,7 @@ public class ProblemLocalScopeProvider extends AbstractGlobalScopeDelegatingScop | |||
59 | if (rootElement == null) { | 56 | if (rootElement == null) { |
60 | return new NoFullyQualifiedNamesSelectable(resourceDescription); | 57 | return new NoFullyQualifiedNamesSelectable(resourceDescription); |
61 | } | 58 | } |
62 | var rootName = delegateQualifiedNameProvider.getFullyQualifiedName(rootElement); | 59 | var rootName = qualifiedNameProvider.getFullyQualifiedName(rootElement); |
63 | if (rootName == null) { | 60 | if (rootName == null) { |
64 | return new NoFullyQualifiedNamesSelectable(resourceDescription); | 61 | return new NoFullyQualifiedNamesSelectable(resourceDescription); |
65 | } | 62 | } |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java index a4437ba6..d94c9a13 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java +++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
@@ -9,17 +9,15 @@ | |||
9 | */ | 9 | */ |
10 | package tools.refinery.language.scoping; | 10 | package tools.refinery.language.scoping; |
11 | 11 | ||
12 | import com.google.inject.Inject; | ||
13 | import org.eclipse.emf.ecore.EObject; | 12 | import org.eclipse.emf.ecore.EObject; |
14 | import org.eclipse.emf.ecore.EReference; | 13 | import org.eclipse.emf.ecore.EReference; |
15 | import org.eclipse.xtext.EcoreUtil2; | 14 | import org.eclipse.xtext.EcoreUtil2; |
16 | import org.eclipse.xtext.scoping.IScope; | 15 | import org.eclipse.xtext.scoping.IScope; |
17 | import org.eclipse.xtext.scoping.Scopes; | 16 | import org.eclipse.xtext.scoping.Scopes; |
18 | import tools.refinery.language.model.problem.*; | 17 | import tools.refinery.language.model.problem.*; |
19 | import tools.refinery.language.utils.ProblemDesugarer; | ||
20 | 18 | ||
21 | import java.util.ArrayList; | 19 | import java.util.Collection; |
22 | import java.util.List; | 20 | import java.util.LinkedHashSet; |
23 | 21 | ||
24 | /** | 22 | /** |
25 | * This class contains custom scoping description. | 23 | * This class contains custom scoping description. |
@@ -29,9 +27,6 @@ import java.util.List; | |||
29 | * on how and when to use it. | 27 | * on how and when to use it. |
30 | */ | 28 | */ |
31 | public class ProblemScopeProvider extends AbstractProblemScopeProvider { | 29 | public class ProblemScopeProvider extends AbstractProblemScopeProvider { |
32 | @Inject | ||
33 | private ProblemDesugarer desugarer; | ||
34 | |||
35 | @Override | 30 | @Override |
36 | public IScope getScope(EObject context, EReference reference) { | 31 | public IScope getScope(EObject context, EReference reference) { |
37 | var scope = super.getScope(context, reference); | 32 | var scope = super.getScope(context, reference); |
@@ -58,7 +53,7 @@ public class ProblemScopeProvider extends AbstractProblemScopeProvider { | |||
58 | } | 53 | } |
59 | 54 | ||
60 | protected IScope getVariableScope(EObject context, IScope delegateScope) { | 55 | protected IScope getVariableScope(EObject context, IScope delegateScope) { |
61 | List<Variable> variables = new ArrayList<>(); | 56 | Collection<Variable> variables = new LinkedHashSet<>(); |
62 | addSingletonVariableToScope(context, variables); | 57 | addSingletonVariableToScope(context, variables); |
63 | EObject currentContext = context; | 58 | EObject currentContext = context; |
64 | while (currentContext != null && !(currentContext instanceof ParametricDefinition)) { | 59 | while (currentContext != null && !(currentContext instanceof ParametricDefinition)) { |
@@ -73,7 +68,7 @@ public class ProblemScopeProvider extends AbstractProblemScopeProvider { | |||
73 | return Scopes.scopeFor(variables, parentScope); | 68 | return Scopes.scopeFor(variables, parentScope); |
74 | } | 69 | } |
75 | 70 | ||
76 | protected void addSingletonVariableToScope(EObject context, List<Variable> variables) { | 71 | protected void addSingletonVariableToScope(EObject context, Collection<Variable> variables) { |
77 | if (context instanceof VariableOrNodeExpr expr) { | 72 | if (context instanceof VariableOrNodeExpr expr) { |
78 | Variable singletonVariable = expr.getSingletonVariable(); | 73 | Variable singletonVariable = expr.getSingletonVariable(); |
79 | if (singletonVariable != null) { | 74 | if (singletonVariable != null) { |
@@ -82,18 +77,21 @@ public class ProblemScopeProvider extends AbstractProblemScopeProvider { | |||
82 | } | 77 | } |
83 | } | 78 | } |
84 | 79 | ||
85 | protected void addExistentiallyQualifiedVariableToScope(EObject currentContext, List<Variable> variables) { | 80 | protected void addExistentiallyQualifiedVariableToScope(EObject currentContext, Collection<Variable> variables) { |
86 | if (currentContext instanceof ExistentialQuantifier quantifier) { | 81 | switch (currentContext) { |
87 | variables.addAll(quantifier.getImplicitVariables()); | 82 | case ExistentialQuantifier quantifier -> variables.addAll(quantifier.getImplicitVariables()); |
88 | } else if (currentContext instanceof Match match) { | 83 | case Match match -> variables.addAll(match.getCondition().getImplicitVariables()); |
89 | variables.addAll(match.getCondition().getImplicitVariables()); | 84 | case Consequent consequent -> { |
90 | } else if (currentContext instanceof Consequent consequent) { | ||
91 | for (var literal : consequent.getActions()) { | 85 | for (var literal : consequent.getActions()) { |
92 | if (literal instanceof NewAction newAction && newAction.getVariable() != null) { | 86 | if (literal instanceof NewAction newAction && newAction.getVariable() != null) { |
93 | variables.add(newAction.getVariable()); | 87 | variables.add(newAction.getVariable()); |
94 | } | 88 | } |
95 | } | 89 | } |
96 | } | 90 | } |
91 | default -> { | ||
92 | // Nothing to add. | ||
93 | } | ||
94 | } | ||
97 | } | 95 | } |
98 | 96 | ||
99 | protected IScope getOppositeScope(EObject context) { | 97 | protected IScope getOppositeScope(EObject context) { |
@@ -105,10 +103,7 @@ public class ProblemScopeProvider extends AbstractProblemScopeProvider { | |||
105 | if (!(relation instanceof ClassDeclaration classDeclaration)) { | 103 | if (!(relation instanceof ClassDeclaration classDeclaration)) { |
106 | return IScope.NULLSCOPE; | 104 | return IScope.NULLSCOPE; |
107 | } | 105 | } |
108 | var referenceDeclarations = classDeclaration.getFeatureDeclarations() | 106 | var referenceDeclarations = classDeclaration.getFeatureDeclarations(); |
109 | .stream() | ||
110 | .filter(ReferenceDeclaration.class::isInstance) | ||
111 | .toList(); | ||
112 | return Scopes.scopeFor(referenceDeclarations); | 107 | return Scopes.scopeFor(referenceDeclarations); |
113 | } | 108 | } |
114 | } | 109 | } |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportAdapter.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportAdapter.java index d7a5304f..753c8565 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportAdapter.java +++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportAdapter.java | |||
@@ -16,7 +16,12 @@ import org.eclipse.emf.ecore.resource.Resource; | |||
16 | import org.eclipse.emf.ecore.resource.ResourceSet; | 16 | import org.eclipse.emf.ecore.resource.ResourceSet; |
17 | import org.eclipse.emf.ecore.util.EcoreUtil; | 17 | import org.eclipse.emf.ecore.util.EcoreUtil; |
18 | import org.eclipse.xtext.naming.QualifiedName; | 18 | import org.eclipse.xtext.naming.QualifiedName; |
19 | import tools.refinery.language.expressions.CompositeTermInterpreter; | ||
20 | import tools.refinery.language.expressions.TermInterpreter; | ||
21 | import tools.refinery.language.library.BuiltinLibrary; | ||
19 | import tools.refinery.language.library.RefineryLibrary; | 22 | import tools.refinery.language.library.RefineryLibrary; |
23 | import tools.refinery.language.model.problem.Problem; | ||
24 | import tools.refinery.language.utils.BuiltinSymbols; | ||
20 | 25 | ||
21 | import java.io.File; | 26 | import java.io.File; |
22 | import java.nio.file.Path; | 27 | import java.nio.file.Path; |
@@ -25,15 +30,12 @@ import java.util.*; | |||
25 | public class ImportAdapter extends AdapterImpl { | 30 | public class ImportAdapter extends AdapterImpl { |
26 | private static final Logger LOG = Logger.getLogger(ImportAdapter.class); | 31 | private static final Logger LOG = Logger.getLogger(ImportAdapter.class); |
27 | private static final List<RefineryLibrary> DEFAULT_LIBRARIES; | 32 | private static final List<RefineryLibrary> DEFAULT_LIBRARIES; |
33 | private static final List<TermInterpreter> DEFAULT_TERM_INTERPRETERS; | ||
28 | private static final List<Path> DEFAULT_PATHS; | 34 | private static final List<Path> DEFAULT_PATHS; |
29 | 35 | ||
30 | static { | 36 | static { |
31 | var serviceLoader = ServiceLoader.load(RefineryLibrary.class); | 37 | DEFAULT_LIBRARIES = loadServices(RefineryLibrary.class); |
32 | var defaultLibraries = new ArrayList<RefineryLibrary>(); | 38 | DEFAULT_TERM_INTERPRETERS = loadServices(TermInterpreter.class); |
33 | for (var service : serviceLoader) { | ||
34 | defaultLibraries.add(service); | ||
35 | } | ||
36 | DEFAULT_LIBRARIES = List.copyOf(defaultLibraries); | ||
37 | var pathEnv = System.getenv("REFINERY_LIBRARY_PATH"); | 39 | var pathEnv = System.getenv("REFINERY_LIBRARY_PATH"); |
38 | if (pathEnv == null) { | 40 | if (pathEnv == null) { |
39 | DEFAULT_PATHS = List.of(); | 41 | DEFAULT_PATHS = List.of(); |
@@ -45,16 +47,29 @@ public class ImportAdapter extends AdapterImpl { | |||
45 | } | 47 | } |
46 | } | 48 | } |
47 | 49 | ||
48 | private final List<RefineryLibrary> libraries; | 50 | private static <T> List<T> loadServices(Class<T> serviceClass) { |
49 | private final List<Path> libraryPaths; | 51 | var serviceLoader = ServiceLoader.load(serviceClass); |
52 | var services = new ArrayList<T>(); | ||
53 | for (var service : serviceLoader) { | ||
54 | services.add(service); | ||
55 | } | ||
56 | return List.copyOf(services); | ||
57 | } | ||
58 | |||
59 | private ResourceSet resourceSet; | ||
60 | private final List<RefineryLibrary> libraries = new ArrayList<>(DEFAULT_LIBRARIES); | ||
61 | private final List<TermInterpreter> termInterpreters = new ArrayList<>(DEFAULT_TERM_INTERPRETERS); | ||
62 | private final TermInterpreter termInterpreter = new CompositeTermInterpreter(termInterpreters); | ||
63 | private final List<Path> libraryPaths = new ArrayList<>(DEFAULT_PATHS); | ||
50 | private final Cache<QualifiedName, QualifiedName> failedResolutions = | 64 | private final Cache<QualifiedName, QualifiedName> failedResolutions = |
51 | CacheBuilder.newBuilder().maximumSize(100).build(); | 65 | CacheBuilder.newBuilder().maximumSize(100).build(); |
52 | private final Map<QualifiedName, URI> qualifiedNameToUriMap = new LinkedHashMap<>(); | 66 | private final Map<QualifiedName, URI> qualifiedNameToUriMap = new LinkedHashMap<>(); |
53 | private final Map<URI, QualifiedName> uriToQualifiedNameMap = new LinkedHashMap<>(); | 67 | private final Map<URI, QualifiedName> uriToQualifiedNameMap = new LinkedHashMap<>(); |
68 | private Problem builtinProblem; | ||
69 | private BuiltinSymbols builtinSymbols; | ||
54 | 70 | ||
55 | private ImportAdapter(ResourceSet resourceSet) { | 71 | void setResourceSet(ResourceSet resourceSet) { |
56 | libraries = new ArrayList<>(DEFAULT_LIBRARIES); | 72 | this.resourceSet = resourceSet; |
57 | libraryPaths = new ArrayList<>(DEFAULT_PATHS); | ||
58 | for (var resource : resourceSet.getResources()) { | 73 | for (var resource : resourceSet.getResources()) { |
59 | resourceAdded(resource); | 74 | resourceAdded(resource); |
60 | } | 75 | } |
@@ -69,6 +84,14 @@ public class ImportAdapter extends AdapterImpl { | |||
69 | return libraries; | 84 | return libraries; |
70 | } | 85 | } |
71 | 86 | ||
87 | public List<TermInterpreter> getTermInterpreters() { | ||
88 | return termInterpreters; | ||
89 | } | ||
90 | |||
91 | public TermInterpreter getTermInterpreter() { | ||
92 | return termInterpreter; | ||
93 | } | ||
94 | |||
72 | public List<Path> getLibraryPaths() { | 95 | public List<Path> getLibraryPaths() { |
73 | return libraryPaths; | 96 | return libraryPaths; |
74 | } | 97 | } |
@@ -190,16 +213,26 @@ public class ImportAdapter extends AdapterImpl { | |||
190 | } | 213 | } |
191 | } | 214 | } |
192 | 215 | ||
193 | public static ImportAdapter getOrInstall(ResourceSet resourceSet) { | 216 | public Problem getBuiltinProblem() { |
194 | var adapter = getAdapter(resourceSet); | 217 | if (builtinProblem == null) { |
195 | if (adapter == null) { | 218 | var builtinResource = resourceSet.getResource(BuiltinLibrary.BUILTIN_LIBRARY_URI, true); |
196 | adapter = new ImportAdapter(resourceSet); | 219 | if (builtinResource == null) { |
197 | resourceSet.eAdapters().add(adapter); | 220 | throw new IllegalStateException("Failed to load builtin resource"); |
221 | } | ||
222 | var contents = builtinResource.getContents(); | ||
223 | if (contents.isEmpty()) { | ||
224 | throw new IllegalStateException("builtin resource is empty"); | ||
225 | } | ||
226 | builtinProblem = (Problem) contents.getFirst(); | ||
227 | EcoreUtil.resolveAll(builtinResource); | ||
198 | } | 228 | } |
199 | return adapter; | 229 | return builtinProblem; |
200 | } | 230 | } |
201 | 231 | ||
202 | private static ImportAdapter getAdapter(ResourceSet resourceSet) { | 232 | public BuiltinSymbols getBuiltinSymbols() { |
203 | return (ImportAdapter) EcoreUtil.getAdapter(resourceSet.eAdapters(), ImportAdapter.class); | 233 | if (builtinSymbols == null) { |
234 | builtinSymbols = new BuiltinSymbols(getBuiltinProblem()); | ||
235 | } | ||
236 | return builtinSymbols; | ||
204 | } | 237 | } |
205 | } | 238 | } |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportAdapterProvider.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportAdapterProvider.java new file mode 100644 index 00000000..5ab3a851 --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportAdapterProvider.java | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.scoping.imports; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import com.google.inject.Provider; | ||
10 | import com.google.inject.Singleton; | ||
11 | import org.eclipse.emf.ecore.EObject; | ||
12 | import org.eclipse.emf.ecore.resource.Resource; | ||
13 | import org.eclipse.emf.ecore.resource.ResourceSet; | ||
14 | import org.eclipse.emf.ecore.util.EcoreUtil; | ||
15 | import org.jetbrains.annotations.NotNull; | ||
16 | import tools.refinery.language.expressions.TermInterpreter; | ||
17 | import tools.refinery.language.utils.BuiltinSymbols; | ||
18 | |||
19 | @Singleton | ||
20 | public class ImportAdapterProvider { | ||
21 | @Inject | ||
22 | private Provider<ImportAdapter> delegateProvider; | ||
23 | |||
24 | public BuiltinSymbols getBuiltinSymbols(@NotNull EObject context) { | ||
25 | var adapter = getOrInstall(context); | ||
26 | return adapter.getBuiltinSymbols(); | ||
27 | } | ||
28 | |||
29 | public BuiltinSymbols getBuiltinSymbols(@NotNull Resource context) { | ||
30 | var adapter = getOrInstall(context); | ||
31 | return adapter.getBuiltinSymbols(); | ||
32 | } | ||
33 | |||
34 | public TermInterpreter getTermInterpreter(@NotNull EObject context) { | ||
35 | var adapter = getOrInstall(context); | ||
36 | return adapter.getTermInterpreter(); | ||
37 | } | ||
38 | |||
39 | public ImportAdapter getOrInstall(@NotNull EObject context) { | ||
40 | var resource = context.eResource(); | ||
41 | if (resource == null) { | ||
42 | throw new IllegalArgumentException("context is not in a resource"); | ||
43 | } | ||
44 | return getOrInstall(resource); | ||
45 | } | ||
46 | |||
47 | public ImportAdapter getOrInstall(@NotNull Resource context) { | ||
48 | var resourceSet = context.getResourceSet(); | ||
49 | if (resourceSet == null) { | ||
50 | throw new IllegalArgumentException("context is not in a resource set"); | ||
51 | } | ||
52 | return getOrInstall(resourceSet); | ||
53 | } | ||
54 | |||
55 | public ImportAdapter getOrInstall(@NotNull ResourceSet resourceSet) { | ||
56 | var adapter = getAdapter(resourceSet); | ||
57 | if (adapter == null) { | ||
58 | adapter = delegateProvider.get(); | ||
59 | adapter.setResourceSet(resourceSet); | ||
60 | resourceSet.eAdapters().add(adapter); | ||
61 | } | ||
62 | return adapter; | ||
63 | } | ||
64 | |||
65 | public static ImportAdapter getAdapter(@NotNull ResourceSet resourceSet) { | ||
66 | return (ImportAdapter) EcoreUtil.getAdapter(resourceSet.eAdapters(), ImportAdapter.class); | ||
67 | } | ||
68 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollector.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollector.java index ac5a92ba..f3ab54ae 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollector.java +++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/imports/ImportCollector.java | |||
@@ -46,6 +46,9 @@ public class ImportCollector { | |||
46 | @Inject | 46 | @Inject |
47 | private Provider<LoadOnDemandResourceDescriptionProvider> loadOnDemandProvider; | 47 | private Provider<LoadOnDemandResourceDescriptionProvider> loadOnDemandProvider; |
48 | 48 | ||
49 | @Inject | ||
50 | private ImportAdapterProvider importAdapterProvider; | ||
51 | |||
49 | public ImportCollection getDirectImports(Resource resource) { | 52 | public ImportCollection getDirectImports(Resource resource) { |
50 | return cache.get(DIRECT_IMPORTS_KEY, resource, () -> this.computeDirectImports(resource)); | 53 | return cache.get(DIRECT_IMPORTS_KEY, resource, () -> this.computeDirectImports(resource)); |
51 | } | 54 | } |
@@ -58,7 +61,7 @@ public class ImportCollector { | |||
58 | if (resourceSet == null) { | 61 | if (resourceSet == null) { |
59 | return ImportCollection.EMPTY; | 62 | return ImportCollection.EMPTY; |
60 | } | 63 | } |
61 | var adapter = ImportAdapter.getOrInstall(resourceSet); | 64 | var adapter = importAdapterProvider.getOrInstall(resourceSet); |
62 | var collection = new ImportCollection(); | 65 | var collection = new ImportCollection(); |
63 | collectAutomaticImports(collection, adapter); | 66 | collectAutomaticImports(collection, adapter); |
64 | collectExplicitImports(problem, collection, adapter); | 67 | collectExplicitImports(problem, collection, adapter); |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/typesystem/AggregatorName.java b/subprojects/language/src/main/java/tools/refinery/language/typesystem/AggregatorName.java new file mode 100644 index 00000000..5939865a --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/typesystem/AggregatorName.java | |||
@@ -0,0 +1,19 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.typesystem; | ||
7 | |||
8 | import org.eclipse.xtext.naming.QualifiedName; | ||
9 | |||
10 | public record AggregatorName(QualifiedName qualifiedName) { | ||
11 | public AggregatorName(QualifiedName prefix, String name) { | ||
12 | this(prefix.append(name)); | ||
13 | } | ||
14 | |||
15 | @Override | ||
16 | public String toString() { | ||
17 | return qualifiedName.isEmpty() ? "" : qualifiedName.getLastSegment(); | ||
18 | } | ||
19 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/typesystem/DataExprType.java b/subprojects/language/src/main/java/tools/refinery/language/typesystem/DataExprType.java new file mode 100644 index 00000000..9bc3e6aa --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/typesystem/DataExprType.java | |||
@@ -0,0 +1,19 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.typesystem; | ||
7 | |||
8 | import org.eclipse.xtext.naming.QualifiedName; | ||
9 | |||
10 | public record DataExprType(QualifiedName qualifiedName) implements FixedType { | ||
11 | public DataExprType(QualifiedName prefix, String name) { | ||
12 | this(prefix.append(name)); | ||
13 | } | ||
14 | |||
15 | @Override | ||
16 | public String toString() { | ||
17 | return qualifiedName.isEmpty() ? "" : qualifiedName.getLastSegment(); | ||
18 | } | ||
19 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/typesystem/ExprType.java b/subprojects/language/src/main/java/tools/refinery/language/typesystem/ExprType.java new file mode 100644 index 00000000..9e44063d --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/typesystem/ExprType.java | |||
@@ -0,0 +1,16 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.typesystem; | ||
7 | |||
8 | public sealed interface ExprType permits FixedType, MutableType { | ||
9 | NodeType NODE = new NodeType(); | ||
10 | LiteralType LITERAL = new LiteralType(); | ||
11 | InvalidType INVALID = new InvalidType(); | ||
12 | |||
13 | FixedType getActualType(); | ||
14 | |||
15 | ExprType unwrapIfSet(); | ||
16 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/typesystem/FixedType.java b/subprojects/language/src/main/java/tools/refinery/language/typesystem/FixedType.java new file mode 100644 index 00000000..1b2ded48 --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/typesystem/FixedType.java | |||
@@ -0,0 +1,18 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.typesystem; | ||
7 | |||
8 | public sealed interface FixedType extends ExprType permits NodeType, LiteralType, InvalidType, DataExprType { | ||
9 | @Override | ||
10 | default FixedType getActualType() { | ||
11 | return this; | ||
12 | } | ||
13 | |||
14 | @Override | ||
15 | default FixedType unwrapIfSet() { | ||
16 | return this; | ||
17 | } | ||
18 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/typesystem/InvalidType.java b/subprojects/language/src/main/java/tools/refinery/language/typesystem/InvalidType.java new file mode 100644 index 00000000..c18612bc --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/typesystem/InvalidType.java | |||
@@ -0,0 +1,16 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.typesystem; | ||
7 | |||
8 | public final class InvalidType implements FixedType { | ||
9 | InvalidType() { | ||
10 | } | ||
11 | |||
12 | @Override | ||
13 | public String toString() { | ||
14 | return "invalid"; | ||
15 | } | ||
16 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/typesystem/LiteralType.java b/subprojects/language/src/main/java/tools/refinery/language/typesystem/LiteralType.java new file mode 100644 index 00000000..7fd33553 --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/typesystem/LiteralType.java | |||
@@ -0,0 +1,16 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.typesystem; | ||
7 | |||
8 | public final class LiteralType implements FixedType { | ||
9 | LiteralType() { | ||
10 | } | ||
11 | |||
12 | @Override | ||
13 | public String toString() { | ||
14 | return "constraint"; | ||
15 | } | ||
16 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/typesystem/MutableType.java b/subprojects/language/src/main/java/tools/refinery/language/typesystem/MutableType.java new file mode 100644 index 00000000..78fdf884 --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/typesystem/MutableType.java | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.typesystem; | ||
7 | |||
8 | public final class MutableType implements ExprType { | ||
9 | private DataExprType actualType; | ||
10 | |||
11 | @Override | ||
12 | public FixedType getActualType() { | ||
13 | return actualType == null ? INVALID : actualType; | ||
14 | } | ||
15 | |||
16 | public void setActualType(DataExprType actualType) { | ||
17 | if (this.actualType != null) { | ||
18 | throw new IllegalStateException("Actual type was already set"); | ||
19 | } | ||
20 | this.actualType = actualType; | ||
21 | } | ||
22 | |||
23 | @Override | ||
24 | public ExprType unwrapIfSet() { | ||
25 | return actualType == null ? this : actualType; | ||
26 | } | ||
27 | |||
28 | @Override | ||
29 | public String toString() { | ||
30 | return getActualType().toString(); | ||
31 | } | ||
32 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/typesystem/NodeType.java b/subprojects/language/src/main/java/tools/refinery/language/typesystem/NodeType.java new file mode 100644 index 00000000..1baff212 --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/typesystem/NodeType.java | |||
@@ -0,0 +1,16 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.typesystem; | ||
7 | |||
8 | public final class NodeType implements FixedType { | ||
9 | NodeType() { | ||
10 | } | ||
11 | |||
12 | @Override | ||
13 | public String toString() { | ||
14 | return "node"; | ||
15 | } | ||
16 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/typesystem/ProblemTypeAnalyzer.java b/subprojects/language/src/main/java/tools/refinery/language/typesystem/ProblemTypeAnalyzer.java new file mode 100644 index 00000000..fcf99ad8 --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/typesystem/ProblemTypeAnalyzer.java | |||
@@ -0,0 +1,32 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.typesystem; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import com.google.inject.Provider; | ||
10 | import com.google.inject.Singleton; | ||
11 | import org.eclipse.xtext.util.IResourceScopeCache; | ||
12 | import tools.refinery.language.model.problem.Problem; | ||
13 | |||
14 | @Singleton | ||
15 | public class ProblemTypeAnalyzer { | ||
16 | private static final String CACHE_KEY = "tools.refinery.language.typesystem.ProblemTypeAnalyzer.CACHE_KEY"; | ||
17 | |||
18 | @Inject | ||
19 | private IResourceScopeCache resourceScopeCache; | ||
20 | |||
21 | @Inject | ||
22 | private Provider<TypedModule> typedModuleProvider; | ||
23 | |||
24 | public TypedModule getOrComputeTypes(Problem problem) { | ||
25 | var resource = problem.eResource(); | ||
26 | return resourceScopeCache.get(CACHE_KEY, resource, () -> { | ||
27 | var typedModule = typedModuleProvider.get(); | ||
28 | typedModule.setProblem(problem); | ||
29 | return typedModule; | ||
30 | }); | ||
31 | } | ||
32 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/typesystem/Signature.java b/subprojects/language/src/main/java/tools/refinery/language/typesystem/Signature.java new file mode 100644 index 00000000..8e72c185 --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/typesystem/Signature.java | |||
@@ -0,0 +1,11 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.typesystem; | ||
7 | |||
8 | import java.util.List; | ||
9 | |||
10 | public record Signature(List<FixedType> parameterTypes, FixedType resultType) { | ||
11 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/typesystem/SignatureProvider.java b/subprojects/language/src/main/java/tools/refinery/language/typesystem/SignatureProvider.java new file mode 100644 index 00000000..3e25a0f5 --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/typesystem/SignatureProvider.java | |||
@@ -0,0 +1,107 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.typesystem; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import com.google.inject.Singleton; | ||
10 | import org.eclipse.xtext.naming.IQualifiedNameProvider; | ||
11 | import org.eclipse.xtext.util.IResourceScopeCache; | ||
12 | import tools.refinery.language.model.problem.*; | ||
13 | |||
14 | import java.util.ArrayList; | ||
15 | import java.util.HashMap; | ||
16 | import java.util.List; | ||
17 | |||
18 | @Singleton | ||
19 | public class SignatureProvider { | ||
20 | private static final String PREFIX = "tools.refinery.language.typesystem.SignatureProvider."; | ||
21 | private static final String SIGNATURE_CACHE = PREFIX + "SIGNATURE_CACHE"; | ||
22 | private static final String DATATYPE_CACHE = PREFIX + "DATATYPE_CACHE"; | ||
23 | private static final String AGGREGATOR_CACHE = PREFIX + "AGGREGATOR_CACHE"; | ||
24 | |||
25 | @Inject | ||
26 | private IQualifiedNameProvider qualifiedNameProvider; | ||
27 | |||
28 | @Inject | ||
29 | private IResourceScopeCache cache; | ||
30 | |||
31 | public Signature getSignature(Relation relation) { | ||
32 | var signatures = cache.get(SIGNATURE_CACHE, relation.eResource(), () -> new HashMap<Relation, Signature>()); | ||
33 | return signatures.computeIfAbsent(relation, this::computeSignature); | ||
34 | } | ||
35 | |||
36 | public int getArity(Relation relation) { | ||
37 | return getSignature(relation).parameterTypes().size(); | ||
38 | } | ||
39 | |||
40 | private Signature computeSignature(Relation relation) { | ||
41 | return new Signature(getParameterTypes(relation), getResultType(relation)); | ||
42 | } | ||
43 | |||
44 | private List<FixedType> getParameterTypes(Relation relation) { | ||
45 | return switch (relation) { | ||
46 | case ClassDeclaration ignored -> List.of(ExprType.NODE); | ||
47 | case EnumDeclaration ignored -> List.of(ExprType.NODE); | ||
48 | case DatatypeDeclaration datatypeDeclaration -> List.of(getDataType(datatypeDeclaration)); | ||
49 | case ReferenceDeclaration referenceDeclaration -> { | ||
50 | if (referenceDeclaration.getReferenceType() instanceof DatatypeDeclaration) { | ||
51 | yield List.of(ExprType.NODE); | ||
52 | } | ||
53 | yield List.of(ExprType.NODE, ExprType.NODE); | ||
54 | } | ||
55 | case ParametricDefinition parametricDefinition -> { | ||
56 | var parameters = parametricDefinition.getParameters(); | ||
57 | var exprTypes = new ArrayList<FixedType>(parameters.size()); | ||
58 | for (var parameter : parameters) { | ||
59 | if (parameter.getParameterType() instanceof DatatypeDeclaration datatypeDeclaration) { | ||
60 | exprTypes.add(getDataType(datatypeDeclaration)); | ||
61 | } else { | ||
62 | exprTypes.add(ExprType.NODE); | ||
63 | } | ||
64 | } | ||
65 | yield List.copyOf(exprTypes); | ||
66 | } | ||
67 | default -> throw new IllegalArgumentException("Unknown Relation: " + relation); | ||
68 | }; | ||
69 | } | ||
70 | |||
71 | private FixedType getResultType(Relation relation) { | ||
72 | if (relation instanceof ReferenceDeclaration referenceDeclaration && | ||
73 | referenceDeclaration.getReferenceType() instanceof DatatypeDeclaration datatypeDeclaration) { | ||
74 | return getDataType(datatypeDeclaration); | ||
75 | } | ||
76 | return ExprType.LITERAL; | ||
77 | } | ||
78 | |||
79 | public DataExprType getDataType(DatatypeDeclaration datatypeDeclaration) { | ||
80 | var dataTypes = cache.get(DATATYPE_CACHE, datatypeDeclaration.eResource(), | ||
81 | () -> new HashMap<DatatypeDeclaration, DataExprType>()); | ||
82 | return dataTypes.computeIfAbsent(datatypeDeclaration, this::computeDataType); | ||
83 | } | ||
84 | |||
85 | private DataExprType computeDataType(DatatypeDeclaration datatypeDeclaration) { | ||
86 | var qualifiedName = qualifiedNameProvider.getFullyQualifiedName(datatypeDeclaration); | ||
87 | if (qualifiedName == null) { | ||
88 | throw new IllegalArgumentException("Datatype declaration has no qualified name: " + datatypeDeclaration); | ||
89 | } | ||
90 | return new DataExprType(qualifiedName); | ||
91 | } | ||
92 | |||
93 | public AggregatorName getAggregatorName(AggregatorDeclaration aggregatorDeclaration) { | ||
94 | var dataTypes = cache.get(AGGREGATOR_CACHE, aggregatorDeclaration.eResource(), | ||
95 | () -> new HashMap<AggregatorDeclaration, AggregatorName>()); | ||
96 | return dataTypes.computeIfAbsent(aggregatorDeclaration, this::computeAggregatorName); | ||
97 | } | ||
98 | |||
99 | private AggregatorName computeAggregatorName(AggregatorDeclaration aggregatorDeclaration) { | ||
100 | var qualifiedName = qualifiedNameProvider.getFullyQualifiedName(aggregatorDeclaration); | ||
101 | if (qualifiedName == null) { | ||
102 | throw new IllegalArgumentException( | ||
103 | "Aggregator declaration has no qualified name: " + aggregatorDeclaration); | ||
104 | } | ||
105 | return new AggregatorName(qualifiedName); | ||
106 | } | ||
107 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/typesystem/TypedModule.java b/subprojects/language/src/main/java/tools/refinery/language/typesystem/TypedModule.java new file mode 100644 index 00000000..de923e0d --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/typesystem/TypedModule.java | |||
@@ -0,0 +1,568 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.typesystem; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import org.eclipse.emf.common.util.Diagnostic; | ||
10 | import org.eclipse.emf.ecore.EObject; | ||
11 | import org.eclipse.emf.ecore.EStructuralFeature; | ||
12 | import org.eclipse.xtext.validation.CheckType; | ||
13 | import org.eclipse.xtext.validation.FeatureBasedDiagnostic; | ||
14 | import tools.refinery.language.expressions.BuiltinTermInterpreter; | ||
15 | import tools.refinery.language.expressions.TermInterpreter; | ||
16 | import tools.refinery.language.model.problem.*; | ||
17 | import tools.refinery.language.scoping.imports.ImportAdapterProvider; | ||
18 | import tools.refinery.language.validation.ProblemValidator; | ||
19 | |||
20 | import java.util.*; | ||
21 | |||
22 | public class TypedModule { | ||
23 | private static final String OPERAND_TYPE_ERROR_MESSAGE = "Cannot determine operand type."; | ||
24 | |||
25 | @Inject | ||
26 | private SignatureProvider signatureProvider; | ||
27 | |||
28 | @Inject | ||
29 | private ImportAdapterProvider importAdapterProvider; | ||
30 | |||
31 | private TermInterpreter interpreter; | ||
32 | private final Map<Variable, List<AssignmentExpr>> assignments = new LinkedHashMap<>(); | ||
33 | private final Map<Variable, FixedType> variableTypes = new HashMap<>(); | ||
34 | private final Map<Expr, ExprType> expressionTypes = new HashMap<>(); | ||
35 | private final Set<Variable> variablesToProcess = new LinkedHashSet<>(); | ||
36 | private final List<FeatureBasedDiagnostic> diagnostics = new ArrayList<>(); | ||
37 | |||
38 | void setProblem(Problem problem) { | ||
39 | interpreter = importAdapterProvider.getTermInterpreter(problem); | ||
40 | gatherAssignments(problem); | ||
41 | checkTypes(problem); | ||
42 | } | ||
43 | |||
44 | private void gatherAssignments(Problem problem) { | ||
45 | var iterator = problem.eAllContents(); | ||
46 | while (iterator.hasNext()) { | ||
47 | var eObject = iterator.next(); | ||
48 | if (!(eObject instanceof AssignmentExpr assignmentExpr)) { | ||
49 | continue; | ||
50 | } | ||
51 | if (assignmentExpr.getLeft() instanceof VariableOrNodeExpr variableOrNodeExpr && | ||
52 | variableOrNodeExpr.getVariableOrNode() instanceof Variable variable) { | ||
53 | var assignmentList = assignments.computeIfAbsent(variable, ignored -> new ArrayList<>(1)); | ||
54 | assignmentList.add(assignmentExpr); | ||
55 | } | ||
56 | iterator.prune(); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | private void checkTypes(Problem problem) { | ||
61 | for (var statement : problem.getStatements()) { | ||
62 | switch (statement) { | ||
63 | case PredicateDefinition predicateDefinition -> checkTypes(predicateDefinition); | ||
64 | case Assertion assertion -> checkTypes(assertion); | ||
65 | default -> { | ||
66 | // Nothing to type check. | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | |||
72 | private void checkTypes(PredicateDefinition predicateDefinition) { | ||
73 | for (var conjunction : predicateDefinition.getBodies()) { | ||
74 | for (var literal : conjunction.getLiterals()) { | ||
75 | coerceIntoLiteral(literal); | ||
76 | } | ||
77 | } | ||
78 | } | ||
79 | |||
80 | private void checkTypes(Assertion assertion) { | ||
81 | var relation = assertion.getRelation(); | ||
82 | var value = assertion.getValue(); | ||
83 | if (relation == null) { | ||
84 | return; | ||
85 | } | ||
86 | var type = signatureProvider.getSignature(relation).resultType(); | ||
87 | if (type == ExprType.LITERAL) { | ||
88 | if (value == null) { | ||
89 | return; | ||
90 | } | ||
91 | expectType(value, BuiltinTermInterpreter.BOOLEAN_TYPE); | ||
92 | return; | ||
93 | } | ||
94 | if (value == null) { | ||
95 | var message = "Assertion value of type %s is required.".formatted(type); | ||
96 | error(message, assertion, ProblemPackage.Literals.ASSERTION__RELATION, 0, ProblemValidator.TYPE_ERROR); | ||
97 | } | ||
98 | expectType(value, type); | ||
99 | } | ||
100 | |||
101 | public List<FeatureBasedDiagnostic> getDiagnostics() { | ||
102 | return diagnostics; | ||
103 | } | ||
104 | |||
105 | public FixedType getVariableType(Variable variable) { | ||
106 | // We can't use computeIfAbsent here, because translating referenced queries calls this method in a reentrant | ||
107 | // way, which would cause a ConcurrentModificationException with computeIfAbsent. | ||
108 | @SuppressWarnings("squid:S3824") | ||
109 | var type = variableTypes.get(variable); | ||
110 | //noinspection Java8MapApi | ||
111 | if (type == null) { | ||
112 | type = computeVariableType(variable); | ||
113 | variableTypes.put(variable, type); | ||
114 | } | ||
115 | return type; | ||
116 | } | ||
117 | |||
118 | private FixedType computeVariableType(Variable variable) { | ||
119 | if (variable instanceof Parameter) { | ||
120 | return computeUnassignedVariableType(variable); | ||
121 | } | ||
122 | var assignmnentList = assignments.get(variable); | ||
123 | if (assignmnentList == null || assignmnentList.isEmpty()) { | ||
124 | return computeUnassignedVariableType(variable); | ||
125 | } | ||
126 | if (variablesToProcess.contains(variable)) { | ||
127 | throw new IllegalStateException("Circular reference to variable: " + variable.getName()); | ||
128 | } | ||
129 | if (assignmnentList.size() > 1) { | ||
130 | var message = "Multiple assignments for variable '%s'.".formatted(variable.getName()); | ||
131 | for (var assignment : assignmnentList) { | ||
132 | error(message, assignment, ProblemPackage.Literals.BINARY_EXPR__LEFT, 0, | ||
133 | ProblemValidator.INVALID_ASSIGNMENT_ISSUE); | ||
134 | } | ||
135 | return ExprType.INVALID; | ||
136 | } | ||
137 | var assignment = assignmnentList.getFirst(); | ||
138 | variablesToProcess.add(variable); | ||
139 | try { | ||
140 | var assignedType = getExpressionType(assignment.getRight()); | ||
141 | if (assignedType instanceof MutableType) { | ||
142 | var message = "Cannot determine type of variable '%s'.".formatted(variable.getName()); | ||
143 | error(message, assignment, ProblemPackage.Literals.BINARY_EXPR__RIGHT, 0, ProblemValidator.TYPE_ERROR); | ||
144 | return ExprType.INVALID; | ||
145 | } | ||
146 | if (assignedType instanceof DataExprType dataExprType) { | ||
147 | return dataExprType; | ||
148 | } | ||
149 | if (assignedType != ExprType.INVALID) { | ||
150 | var message = "Expected data expression for variable '%s', got %s instead." | ||
151 | .formatted(variable.getName(), assignedType); | ||
152 | error(message, assignment, ProblemPackage.Literals.BINARY_EXPR__RIGHT, 0, ProblemValidator.TYPE_ERROR); | ||
153 | } | ||
154 | return ExprType.INVALID; | ||
155 | } finally { | ||
156 | variablesToProcess.remove(variable); | ||
157 | } | ||
158 | } | ||
159 | |||
160 | private FixedType computeUnassignedVariableType(Variable variable) { | ||
161 | if (variable instanceof Parameter parameter && | ||
162 | parameter.getParameterType() instanceof DatatypeDeclaration datatypeDeclaration) { | ||
163 | return signatureProvider.getDataType(datatypeDeclaration); | ||
164 | } | ||
165 | // Parameters without an explicit datatype annotation are node variables. | ||
166 | return ExprType.NODE; | ||
167 | } | ||
168 | |||
169 | public ExprType getExpressionType(Expr expr) { | ||
170 | // We can't use computeIfAbsent here, because translating referenced queries calls this method in a reentrant | ||
171 | // way, which would cause a ConcurrentModificationException with computeIfAbsent. | ||
172 | @SuppressWarnings("squid:S3824") | ||
173 | var type = expressionTypes.get(expr); | ||
174 | //noinspection Java8MapApi | ||
175 | if (type == null) { | ||
176 | type = computeExpressionType(expr); | ||
177 | expressionTypes.put(expr, type); | ||
178 | } | ||
179 | return type.unwrapIfSet(); | ||
180 | } | ||
181 | |||
182 | private ExprType computeExpressionType(Expr expr) { | ||
183 | return switch (expr) { | ||
184 | case LogicConstant logicConstant -> computeExpressionType(logicConstant); | ||
185 | case IntConstant ignored -> BuiltinTermInterpreter.INT_TYPE; | ||
186 | case RealConstant ignored -> BuiltinTermInterpreter.REAL_TYPE; | ||
187 | case StringConstant ignored -> BuiltinTermInterpreter.STRING_TYPE; | ||
188 | case InfiniteConstant ignored -> new MutableType(); | ||
189 | case VariableOrNodeExpr variableOrNodeExpr -> computeExpressionType(variableOrNodeExpr); | ||
190 | case AssignmentExpr assignmentExpr -> computeExpressionType(assignmentExpr); | ||
191 | case Atom atom -> computeExpressionType(atom); | ||
192 | case NegationExpr negationExpr -> computeExpressionType(negationExpr); | ||
193 | case ArithmeticUnaryExpr arithmeticUnaryExpr -> computeExpressionType(arithmeticUnaryExpr); | ||
194 | case CountExpr countExpr -> computeExpressionType(countExpr); | ||
195 | case AggregationExpr aggregationExpr -> computeExpressionType(aggregationExpr); | ||
196 | case ComparisonExpr comparisonExpr -> computeExpressionType(comparisonExpr); | ||
197 | case LatticeBinaryExpr latticeBinaryExpr -> computeExpressionType(latticeBinaryExpr); | ||
198 | case RangeExpr rangeExpr -> computeExpressionType(rangeExpr); | ||
199 | case ArithmeticBinaryExpr arithmeticBinaryExpr -> computeExpressionType(arithmeticBinaryExpr); | ||
200 | case CastExpr castExpr -> computeExpressionType(castExpr); | ||
201 | default -> { | ||
202 | error("Unknown expression: " + expr.getClass().getSimpleName(), expr, null, 0, | ||
203 | ProblemValidator.UNKNOWN_EXPRESSION_ISSUE); | ||
204 | yield ExprType.INVALID; | ||
205 | } | ||
206 | }; | ||
207 | } | ||
208 | |||
209 | private ExprType computeExpressionType(LogicConstant expr) { | ||
210 | return switch (expr.getLogicValue()) { | ||
211 | case TRUE, FALSE -> BuiltinTermInterpreter.BOOLEAN_TYPE; | ||
212 | case UNKNOWN, ERROR -> new MutableType(); | ||
213 | case null -> ExprType.INVALID; | ||
214 | }; | ||
215 | } | ||
216 | |||
217 | private ExprType computeExpressionType(VariableOrNodeExpr expr) { | ||
218 | var target = expr.getVariableOrNode(); | ||
219 | if (target == null || target.eIsProxy()) { | ||
220 | return ExprType.INVALID; | ||
221 | } | ||
222 | return switch (target) { | ||
223 | case Node ignored -> ExprType.NODE; | ||
224 | case Variable variable -> { | ||
225 | if (variablesToProcess.contains(variable)) { | ||
226 | var message = "Circular reference to variable '%s'.".formatted(variable.getName()); | ||
227 | error(message, expr, ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE, 0, | ||
228 | ProblemValidator.INVALID_ASSIGNMENT_ISSUE); | ||
229 | yield ExprType.INVALID; | ||
230 | } | ||
231 | yield getVariableType(variable); | ||
232 | } | ||
233 | default -> { | ||
234 | error("Unknown variable: " + target.getName(), expr, | ||
235 | ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE, 0, | ||
236 | ProblemValidator.UNKNOWN_EXPRESSION_ISSUE); | ||
237 | yield ExprType.INVALID; | ||
238 | } | ||
239 | }; | ||
240 | } | ||
241 | |||
242 | private ExprType computeExpressionType(AssignmentExpr expr) { | ||
243 | // Force the left side to type check. Since the left side is a variable, it will force the right side to also | ||
244 | // type check in order to infer the variable type. | ||
245 | return getExpressionType(expr.getLeft()) == ExprType.INVALID ? ExprType.INVALID : ExprType.LITERAL; | ||
246 | } | ||
247 | |||
248 | private ExprType computeExpressionType(Atom atom) { | ||
249 | var relation = atom.getRelation(); | ||
250 | if (relation == null || relation.eIsProxy()) { | ||
251 | return ExprType.INVALID; | ||
252 | } | ||
253 | if (relation instanceof DatatypeDeclaration) { | ||
254 | var message = "Invalid call to data type. Use 'as %s' for casting.".formatted( | ||
255 | relation.getName()); | ||
256 | error(message, atom, ProblemPackage.Literals.ATOM__RELATION, 0, ProblemValidator.TYPE_ERROR); | ||
257 | } | ||
258 | var signature = signatureProvider.getSignature(relation); | ||
259 | var parameterTypes = signature.parameterTypes(); | ||
260 | var arguments = atom.getArguments(); | ||
261 | int size = Math.min(parameterTypes.size(), arguments.size()); | ||
262 | boolean ok = parameterTypes.size() == arguments.size(); | ||
263 | for (int i = 0; i < size; i++) { | ||
264 | var parameterType = parameterTypes.get(i); | ||
265 | var argument = arguments.get(i); | ||
266 | if (!expectType(argument, parameterType)) { | ||
267 | // Avoid short-circuiting to let us type check all arguments. | ||
268 | ok = false; | ||
269 | } | ||
270 | } | ||
271 | return ok ? signature.resultType() : ExprType.INVALID; | ||
272 | } | ||
273 | |||
274 | private ExprType computeExpressionType(NegationExpr negationExpr) { | ||
275 | var body = negationExpr.getBody(); | ||
276 | if (body == null) { | ||
277 | return ExprType.INVALID; | ||
278 | } | ||
279 | var actualType = getExpressionType(body); | ||
280 | if (actualType == ExprType.LITERAL) { | ||
281 | // Negation of literals yields another (non-enumerable) literal. | ||
282 | return ExprType.LITERAL; | ||
283 | } | ||
284 | if (actualType == DataExprType.INVALID) { | ||
285 | return ExprType.INVALID; | ||
286 | } | ||
287 | if (actualType instanceof MutableType) { | ||
288 | error(OPERAND_TYPE_ERROR_MESSAGE, body, null, 0, ProblemValidator.TYPE_ERROR); | ||
289 | return ExprType.INVALID; | ||
290 | } | ||
291 | if (actualType instanceof DataExprType dataExprType) { | ||
292 | var result = interpreter.getNegationType(dataExprType); | ||
293 | if (result.isPresent()) { | ||
294 | return result.get(); | ||
295 | } | ||
296 | } | ||
297 | var message = "Data type %s does not support negation.".formatted(actualType); | ||
298 | error(message, negationExpr, null, 0, ProblemValidator.TYPE_ERROR); | ||
299 | return ExprType.INVALID; | ||
300 | } | ||
301 | |||
302 | private ExprType computeExpressionType(ArithmeticUnaryExpr expr) { | ||
303 | var op = expr.getOp(); | ||
304 | var body = expr.getBody(); | ||
305 | if (op == null || body == null) { | ||
306 | return ExprType.INVALID; | ||
307 | } | ||
308 | var actualType = getExpressionType(body); | ||
309 | if (actualType == DataExprType.INVALID) { | ||
310 | return ExprType.INVALID; | ||
311 | } | ||
312 | if (actualType instanceof MutableType) { | ||
313 | error(OPERAND_TYPE_ERROR_MESSAGE, body, null, 0, ProblemValidator.TYPE_ERROR); | ||
314 | return ExprType.INVALID; | ||
315 | } | ||
316 | if (actualType instanceof DataExprType dataExprType) { | ||
317 | var result = interpreter.getUnaryOperationType(op, dataExprType); | ||
318 | if (result.isPresent()) { | ||
319 | return result.get(); | ||
320 | } | ||
321 | } | ||
322 | var message = "Unsupported operator for data type %s.".formatted(actualType); | ||
323 | error(message, expr, null, 0, ProblemValidator.TYPE_ERROR); | ||
324 | return ExprType.INVALID; | ||
325 | } | ||
326 | |||
327 | private ExprType computeExpressionType(CountExpr countExpr) { | ||
328 | return coerceIntoLiteral(countExpr.getBody()) ? BuiltinTermInterpreter.INT_TYPE : ExprType.INVALID; | ||
329 | } | ||
330 | |||
331 | private ExprType computeExpressionType(AggregationExpr expr) { | ||
332 | var aggregator = expr.getAggregator(); | ||
333 | if (aggregator == null || aggregator.eIsProxy()) { | ||
334 | return null; | ||
335 | } | ||
336 | // Avoid short-circuiting to let us type check both the value and the condition. | ||
337 | boolean ok = coerceIntoLiteral(expr.getCondition()); | ||
338 | var value = expr.getValue(); | ||
339 | var actualType = getExpressionType(value); | ||
340 | if (actualType == ExprType.INVALID) { | ||
341 | return ExprType.INVALID; | ||
342 | } | ||
343 | if (actualType instanceof MutableType) { | ||
344 | error(OPERAND_TYPE_ERROR_MESSAGE, value, null, 0, ProblemValidator.TYPE_ERROR); | ||
345 | return ExprType.INVALID; | ||
346 | } | ||
347 | if (actualType instanceof DataExprType dataExprType) { | ||
348 | var aggregatorName = signatureProvider.getAggregatorName(aggregator); | ||
349 | var result = interpreter.getAggregationType(aggregatorName, dataExprType); | ||
350 | if (result.isPresent()) { | ||
351 | return ok ? result.get() : ExprType.INVALID; | ||
352 | } | ||
353 | } | ||
354 | var message = "Unsupported aggregator for type %s.".formatted(actualType); | ||
355 | error(message, expr, ProblemPackage.Literals.AGGREGATION_EXPR__AGGREGATOR, 0, ProblemValidator.TYPE_ERROR); | ||
356 | return ExprType.INVALID; | ||
357 | } | ||
358 | |||
359 | private ExprType computeExpressionType(ComparisonExpr expr) { | ||
360 | var left = expr.getLeft(); | ||
361 | var right = expr.getRight(); | ||
362 | var op = expr.getOp(); | ||
363 | if (op == ComparisonOp.NODE_EQ || op == ComparisonOp.NODE_NOT_EQ) { | ||
364 | // Avoid short-circuiting to let us type check both arguments. | ||
365 | boolean leftOk = expectType(left, ExprType.NODE); | ||
366 | boolean rightOk = expectType(right, ExprType.NODE); | ||
367 | return leftOk && rightOk ? ExprType.LITERAL : ExprType.INVALID; | ||
368 | } | ||
369 | if (!(getCommonDataType(expr) instanceof DataExprType commonType)) { | ||
370 | return ExprType.INVALID; | ||
371 | } | ||
372 | // Data equality and inequality are always supported for data types. | ||
373 | if (op != ComparisonOp.EQ && op != ComparisonOp.NOT_EQ && !interpreter.isComparisonSupported(commonType)) { | ||
374 | var message = "Data type %s does not support comparison.".formatted(commonType); | ||
375 | error(message, expr, null, 0, ProblemValidator.TYPE_ERROR); | ||
376 | return ExprType.INVALID; | ||
377 | } | ||
378 | return BuiltinTermInterpreter.BOOLEAN_TYPE; | ||
379 | } | ||
380 | |||
381 | private ExprType computeExpressionType(LatticeBinaryExpr expr) { | ||
382 | // Lattice operations are always supported for data types. | ||
383 | return getCommonDataType(expr); | ||
384 | } | ||
385 | |||
386 | private ExprType computeExpressionType(RangeExpr expr) { | ||
387 | var left = expr.getLeft(); | ||
388 | var right = expr.getRight(); | ||
389 | if (left instanceof InfiniteConstant && right instanceof InfiniteConstant) { | ||
390 | // `*..*` is equivalent to `unknown` if neither subexpression have been typed yet. | ||
391 | var mutableType = new MutableType(); | ||
392 | if (expressionTypes.putIfAbsent(left, mutableType) == null && | ||
393 | expressionTypes.put(right, mutableType) == null) { | ||
394 | return mutableType; | ||
395 | } | ||
396 | } | ||
397 | if (!(getCommonDataType(expr) instanceof DataExprType commonType)) { | ||
398 | return ExprType.INVALID; | ||
399 | } | ||
400 | if (!interpreter.isRangeSupported(commonType)) { | ||
401 | var message = "Data type %s does not support ranges.".formatted(commonType); | ||
402 | error(message, expr, null, 0, ProblemValidator.TYPE_ERROR); | ||
403 | return ExprType.INVALID; | ||
404 | } | ||
405 | return commonType; | ||
406 | } | ||
407 | |||
408 | private ExprType computeExpressionType(ArithmeticBinaryExpr expr) { | ||
409 | var op = expr.getOp(); | ||
410 | var left = expr.getLeft(); | ||
411 | var right = expr.getRight(); | ||
412 | if (op == null || left == null || right == null) { | ||
413 | return ExprType.INVALID; | ||
414 | } | ||
415 | // Avoid short-circuiting to let us type check both arguments. | ||
416 | var leftType = getExpressionType(left); | ||
417 | var rightType = getExpressionType(right); | ||
418 | if (leftType == ExprType.INVALID || rightType == ExprType.INVALID) { | ||
419 | return ExprType.INVALID; | ||
420 | } | ||
421 | if (rightType instanceof MutableType rightMutableType) { | ||
422 | if (leftType instanceof DataExprType leftExprType) { | ||
423 | rightMutableType.setActualType(leftExprType); | ||
424 | rightType = leftExprType; | ||
425 | } else { | ||
426 | error(OPERAND_TYPE_ERROR_MESSAGE, right, null, 0, ProblemValidator.TYPE_ERROR); | ||
427 | return ExprType.INVALID; | ||
428 | } | ||
429 | } | ||
430 | if (leftType instanceof MutableType leftMutableType) { | ||
431 | if (rightType instanceof DataExprType rightExprType) { | ||
432 | leftMutableType.setActualType(rightExprType); | ||
433 | leftType = rightExprType; | ||
434 | } else { | ||
435 | error(OPERAND_TYPE_ERROR_MESSAGE, left, null, 0, ProblemValidator.TYPE_ERROR); | ||
436 | return ExprType.INVALID; | ||
437 | } | ||
438 | } | ||
439 | if (leftType instanceof DataExprType leftExprType && rightType instanceof DataExprType rightExprType) { | ||
440 | var result = interpreter.getBinaryOperationType(op, leftExprType, rightExprType); | ||
441 | if (result.isPresent()) { | ||
442 | return result.get(); | ||
443 | } | ||
444 | } | ||
445 | var messageBuilder = new StringBuilder("Unsupported operator for "); | ||
446 | if (leftType.equals(rightType)) { | ||
447 | messageBuilder.append("data type ").append(leftType); | ||
448 | } else { | ||
449 | messageBuilder.append("data types ").append(leftType).append(" and ").append(rightType); | ||
450 | } | ||
451 | messageBuilder.append("."); | ||
452 | error(messageBuilder.toString(), expr, null, 0, ProblemValidator.TYPE_ERROR); | ||
453 | return ExprType.INVALID; | ||
454 | } | ||
455 | |||
456 | private ExprType computeExpressionType(CastExpr expr) { | ||
457 | var body = expr.getBody(); | ||
458 | var targetRelation = expr.getTargetType(); | ||
459 | if (body == null || !(targetRelation instanceof DatatypeDeclaration targetDeclaration)) { | ||
460 | return null; | ||
461 | } | ||
462 | var actualType = getExpressionType(body); | ||
463 | if (actualType == ExprType.INVALID) { | ||
464 | return ExprType.INVALID; | ||
465 | } | ||
466 | var targetType = signatureProvider.getDataType(targetDeclaration); | ||
467 | if (actualType instanceof MutableType mutableType) { | ||
468 | // Type ascription for polymorphic literal (e.g., `unknown as int` for the set of all integers). | ||
469 | mutableType.setActualType(targetType); | ||
470 | return targetType; | ||
471 | } | ||
472 | if (actualType.equals(targetType)) { | ||
473 | return targetType; | ||
474 | } | ||
475 | if (actualType instanceof DataExprType dataExprType && interpreter.isCastSupported(dataExprType, targetType)) { | ||
476 | return targetType; | ||
477 | } | ||
478 | var message = "Casting from %s to %s is not supported.".formatted(actualType, targetType); | ||
479 | error(message, expr, null, 0, ProblemValidator.TYPE_ERROR); | ||
480 | return ExprType.INVALID; | ||
481 | } | ||
482 | |||
483 | private FixedType getCommonDataType(BinaryExpr expr) { | ||
484 | var commonType = getCommonType(expr); | ||
485 | if (!(commonType instanceof DataExprType) && commonType != ExprType.INVALID) { | ||
486 | var message = "Expected data expression, got %s instead.".formatted(commonType); | ||
487 | error(message, expr, null, 0, ProblemValidator.TYPE_ERROR); | ||
488 | return ExprType.INVALID; | ||
489 | } | ||
490 | return commonType; | ||
491 | } | ||
492 | |||
493 | private FixedType getCommonType(BinaryExpr expr) { | ||
494 | var left = expr.getLeft(); | ||
495 | var right = expr.getRight(); | ||
496 | if (left == null || right == null) { | ||
497 | return ExprType.INVALID; | ||
498 | } | ||
499 | var leftType = getExpressionType(left); | ||
500 | if (leftType instanceof FixedType fixedLeftType) { | ||
501 | return expectType(right, fixedLeftType) ? fixedLeftType : ExprType.INVALID; | ||
502 | } else { | ||
503 | var rightType = getExpressionType(right); | ||
504 | if (rightType instanceof FixedType fixedRightType) { | ||
505 | return expectType(left, leftType, fixedRightType) ? fixedRightType : ExprType.INVALID; | ||
506 | } else { | ||
507 | error(OPERAND_TYPE_ERROR_MESSAGE, left, null, 0, ProblemValidator.TYPE_ERROR); | ||
508 | error(OPERAND_TYPE_ERROR_MESSAGE, right, null, 0, ProblemValidator.TYPE_ERROR); | ||
509 | return ExprType.INVALID; | ||
510 | } | ||
511 | } | ||
512 | } | ||
513 | |||
514 | private boolean coerceIntoLiteral(Expr expr) { | ||
515 | if (expr == null) { | ||
516 | return false; | ||
517 | } | ||
518 | var actualType = getExpressionType(expr); | ||
519 | if (actualType == ExprType.LITERAL) { | ||
520 | return true; | ||
521 | } | ||
522 | return expectType(expr, actualType, BuiltinTermInterpreter.BOOLEAN_TYPE); | ||
523 | } | ||
524 | |||
525 | private boolean expectType(Expr expr, FixedType expectedType) { | ||
526 | if (expr == null) { | ||
527 | return false; | ||
528 | } | ||
529 | var actualType = getExpressionType(expr); | ||
530 | return expectType(expr, actualType, expectedType); | ||
531 | } | ||
532 | |||
533 | private boolean expectType(Expr expr, ExprType actualType, FixedType expectedType) { | ||
534 | if (expectedType == ExprType.INVALID) { | ||
535 | // Silence any further errors is the expected type failed to compute. | ||
536 | return false; | ||
537 | } | ||
538 | if (actualType.equals(expectedType)) { | ||
539 | return true; | ||
540 | } | ||
541 | if (actualType == ExprType.INVALID) { | ||
542 | // We have already emitted an error previously. | ||
543 | return false; | ||
544 | } | ||
545 | if (actualType instanceof MutableType mutableType && expectedType instanceof DataExprType dataExprType) { | ||
546 | mutableType.setActualType(dataExprType); | ||
547 | return true; | ||
548 | } | ||
549 | var builder = new StringBuilder() | ||
550 | .append("Expected ") | ||
551 | .append(expectedType) | ||
552 | .append(" expression"); | ||
553 | if (!(actualType instanceof MutableType)) { | ||
554 | builder.append(", got ") | ||
555 | .append(actualType) | ||
556 | .append(" instead"); | ||
557 | } | ||
558 | builder.append("."); | ||
559 | error(builder.toString(), expr, null, 0, ProblemValidator.TYPE_ERROR); | ||
560 | return false; | ||
561 | } | ||
562 | |||
563 | private void error(String message, EObject object, EStructuralFeature feature, int index, String code, | ||
564 | String... issueData) { | ||
565 | diagnostics.add(new FeatureBasedDiagnostic(Diagnostic.ERROR, message, object, feature, index, | ||
566 | CheckType.NORMAL, code, issueData)); | ||
567 | } | ||
568 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java b/subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java index c87fa044..72f23e85 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java +++ b/subprojects/language/src/main/java/tools/refinery/language/utils/BuiltinSymbols.java | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
@@ -7,7 +7,56 @@ package tools.refinery.language.utils; | |||
7 | 7 | ||
8 | import tools.refinery.language.model.problem.*; | 8 | import tools.refinery.language.model.problem.*; |
9 | 9 | ||
10 | public record BuiltinSymbols(Problem problem, ClassDeclaration node, PredicateDefinition equals, | 10 | public final class BuiltinSymbols { |
11 | PredicateDefinition exists, ClassDeclaration contained, PredicateDefinition contains, | 11 | private final Problem problem; |
12 | PredicateDefinition invalidContainer) { | 12 | private final ClassDeclaration node; |
13 | private final PredicateDefinition equals; | ||
14 | private final PredicateDefinition exists; | ||
15 | private final ClassDeclaration contained; | ||
16 | private final PredicateDefinition contains; | ||
17 | private final PredicateDefinition invalidContainer; | ||
18 | |||
19 | public BuiltinSymbols(Problem problem) { | ||
20 | this.problem = problem; | ||
21 | node = getDeclaration(ClassDeclaration.class, "node"); | ||
22 | equals = getDeclaration(PredicateDefinition.class, "equals"); | ||
23 | exists = getDeclaration(PredicateDefinition.class, "exists"); | ||
24 | contained = getDeclaration(ClassDeclaration.class, "contained"); | ||
25 | contains = getDeclaration(PredicateDefinition.class, "contains"); | ||
26 | invalidContainer = getDeclaration(PredicateDefinition.class, "invalidContainer"); | ||
27 | } | ||
28 | |||
29 | public Problem problem() { | ||
30 | return problem; | ||
31 | } | ||
32 | |||
33 | public ClassDeclaration node() { | ||
34 | return node; | ||
35 | } | ||
36 | |||
37 | public PredicateDefinition equals() { | ||
38 | return equals; | ||
39 | } | ||
40 | |||
41 | public PredicateDefinition exists() { | ||
42 | return exists; | ||
43 | } | ||
44 | |||
45 | public ClassDeclaration contained() { | ||
46 | return contained; | ||
47 | } | ||
48 | |||
49 | public PredicateDefinition contains() { | ||
50 | return contains; | ||
51 | } | ||
52 | |||
53 | public PredicateDefinition invalidContainer() { | ||
54 | return invalidContainer; | ||
55 | } | ||
56 | |||
57 | private <T extends Statement & NamedElement> T getDeclaration(Class<T> type, String name) { | ||
58 | return problem.getStatements().stream().filter(type::isInstance).map(type::cast) | ||
59 | .filter(declaration -> name.equals(declaration.getName())).findFirst() | ||
60 | .orElseThrow(() -> new IllegalArgumentException("Built-in declaration " + name + " was not found")); | ||
61 | } | ||
13 | } | 62 | } |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemDesugarer.java b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemDesugarer.java deleted file mode 100644 index 0bd1e50b..00000000 --- a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemDesugarer.java +++ /dev/null | |||
@@ -1,102 +0,0 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.utils; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import com.google.inject.Singleton; | ||
10 | import org.eclipse.emf.ecore.EObject; | ||
11 | import org.eclipse.emf.ecore.resource.Resource; | ||
12 | import org.eclipse.xtext.util.IResourceScopeCache; | ||
13 | import org.eclipse.xtext.util.Tuples; | ||
14 | import tools.refinery.language.library.BuiltinLibrary; | ||
15 | import tools.refinery.language.model.problem.*; | ||
16 | |||
17 | import java.util.*; | ||
18 | |||
19 | @Singleton | ||
20 | public class ProblemDesugarer { | ||
21 | @Inject | ||
22 | private IResourceScopeCache cache = IResourceScopeCache.NullImpl.INSTANCE; | ||
23 | |||
24 | public Optional<Problem> getBuiltinProblem(EObject context) { | ||
25 | return Optional.ofNullable(context).map(EObject::eResource).flatMap(resource -> | ||
26 | cache.get("builtinProblem", resource, () -> doGetBuiltinProblem(resource))); | ||
27 | } | ||
28 | |||
29 | private Optional<Problem> doGetBuiltinProblem(Resource resource) { | ||
30 | return Optional.ofNullable(resource).map(Resource::getResourceSet) | ||
31 | .map(resourceSet -> resourceSet.getResource(BuiltinLibrary.BUILTIN_LIBRARY_URI, true)) | ||
32 | .map(Resource::getContents).filter(contents -> !contents.isEmpty()).map(List::getFirst) | ||
33 | .filter(Problem.class::isInstance).map(Problem.class::cast); | ||
34 | } | ||
35 | |||
36 | public Optional<BuiltinSymbols> getBuiltinSymbols(EObject context) { | ||
37 | return getBuiltinProblem(context).map(builtin -> | ||
38 | cache.get("builtinSymbols", builtin.eResource(), () -> doGetBuiltinSymbols(builtin))); | ||
39 | } | ||
40 | |||
41 | private BuiltinSymbols doGetBuiltinSymbols(Problem builtin) { | ||
42 | var node = doGetDeclaration(builtin, ClassDeclaration.class, "node"); | ||
43 | var equals = doGetDeclaration(builtin, PredicateDefinition.class, "equals"); | ||
44 | var exists = doGetDeclaration(builtin, PredicateDefinition.class, "exists"); | ||
45 | var contained = doGetDeclaration(builtin, ClassDeclaration.class, "contained"); | ||
46 | var contains = doGetDeclaration(builtin, PredicateDefinition.class, "contains"); | ||
47 | var invalidContainer = doGetDeclaration(builtin, PredicateDefinition.class, "invalidContainer"); | ||
48 | return new BuiltinSymbols(builtin, node, equals, exists, contained, contains, invalidContainer); | ||
49 | } | ||
50 | |||
51 | private <T extends Statement & NamedElement> T doGetDeclaration(Problem builtin, Class<T> type, String name) { | ||
52 | return builtin.getStatements().stream().filter(type::isInstance).map(type::cast) | ||
53 | .filter(declaration -> name.equals(declaration.getName())).findFirst() | ||
54 | .orElseThrow(() -> new IllegalArgumentException("Built-in declaration " + name + " was not found")); | ||
55 | } | ||
56 | |||
57 | public Collection<ClassDeclaration> getSuperclassesAndSelf(ClassDeclaration classDeclaration) { | ||
58 | return cache.get(Tuples.create(classDeclaration, "superclassesAndSelf"), classDeclaration.eResource(), | ||
59 | () -> doGetSuperclassesAndSelf(classDeclaration)); | ||
60 | } | ||
61 | |||
62 | private Collection<ClassDeclaration> doGetSuperclassesAndSelf(ClassDeclaration classDeclaration) { | ||
63 | var builtinSymbols = getBuiltinSymbols(classDeclaration); | ||
64 | Set<ClassDeclaration> found = new HashSet<>(); | ||
65 | builtinSymbols.ifPresent(symbols -> found.add(symbols.node())); | ||
66 | Deque<ClassDeclaration> queue = new ArrayDeque<>(); | ||
67 | queue.addLast(classDeclaration); | ||
68 | while (!queue.isEmpty()) { | ||
69 | ClassDeclaration current = queue.removeFirst(); | ||
70 | if (!found.contains(current)) { | ||
71 | found.add(current); | ||
72 | for (Relation superType : current.getSuperTypes()) { | ||
73 | if (superType instanceof ClassDeclaration superDeclaration) { | ||
74 | queue.addLast(superDeclaration); | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | } | ||
79 | return found; | ||
80 | } | ||
81 | |||
82 | public Collection<ReferenceDeclaration> getAllReferenceDeclarations(ClassDeclaration classDeclaration) { | ||
83 | return cache.get(Tuples.create(classDeclaration, "allReferenceDeclarations"), classDeclaration.eResource(), | ||
84 | () -> doGetAllReferenceDeclarations(classDeclaration)); | ||
85 | } | ||
86 | |||
87 | private Collection<ReferenceDeclaration> doGetAllReferenceDeclarations(ClassDeclaration classDeclaration) { | ||
88 | Set<ReferenceDeclaration> referenceDeclarations = new HashSet<>(); | ||
89 | for (ClassDeclaration superclass : getSuperclassesAndSelf(classDeclaration)) { | ||
90 | for (FeatureDeclaration featureDeclaration : superclass.getFeatureDeclarations()) { | ||
91 | if (featureDeclaration instanceof ReferenceDeclaration referenceDeclaration) { | ||
92 | referenceDeclarations.add(referenceDeclaration); | ||
93 | } | ||
94 | } | ||
95 | } | ||
96 | return referenceDeclarations; | ||
97 | } | ||
98 | |||
99 | public boolean isContainmentReference(ReferenceDeclaration referenceDeclaration) { | ||
100 | return referenceDeclaration.getKind() == ReferenceKind.CONTAINMENT; | ||
101 | } | ||
102 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java index f70893e0..9daa8f61 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java +++ b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
@@ -81,6 +81,9 @@ public final class ProblemUtil { | |||
81 | } | 81 | } |
82 | 82 | ||
83 | public static boolean hasMultiplicityConstraint(ReferenceDeclaration referenceDeclaration) { | 83 | public static boolean hasMultiplicityConstraint(ReferenceDeclaration referenceDeclaration) { |
84 | if (referenceDeclaration.getReferenceType() instanceof DatatypeDeclaration) { | ||
85 | return false; | ||
86 | } | ||
84 | var opposite = referenceDeclaration.getOpposite(); | 87 | var opposite = referenceDeclaration.getOpposite(); |
85 | if (opposite != null && opposite.getKind() == ReferenceKind.CONTAINMENT) { | 88 | if (opposite != null && opposite.getKind() == ReferenceKind.CONTAINMENT) { |
86 | return false; | 89 | return false; |
@@ -95,17 +98,19 @@ public final class ProblemUtil { | |||
95 | return true; | 98 | return true; |
96 | } | 99 | } |
97 | 100 | ||
98 | public static int getArity(Relation relation) { | 101 | public static boolean isTypeLike(Relation relation) { |
99 | if (relation instanceof ClassDeclaration || relation instanceof EnumDeclaration) { | 102 | if (relation instanceof ClassDeclaration || relation instanceof EnumDeclaration || |
100 | return 1; | 103 | relation instanceof DatatypeDeclaration) { |
101 | } | 104 | return true; |
102 | if (relation instanceof ReferenceDeclaration) { | ||
103 | return 2; | ||
104 | } | 105 | } |
105 | if (relation instanceof PredicateDefinition predicateDefinition) { | 106 | if (relation instanceof PredicateDefinition predicateDefinition) { |
106 | return predicateDefinition.getParameters().size(); | 107 | return predicateDefinition.getParameters().size() == 1; |
107 | } | 108 | } |
108 | throw new IllegalArgumentException("Unknown Relation: " + relation); | 109 | return false; |
110 | } | ||
111 | |||
112 | public static boolean isContainmentReference(ReferenceDeclaration referenceDeclaration) { | ||
113 | return referenceDeclaration.getKind() == ReferenceKind.CONTAINMENT; | ||
109 | } | 114 | } |
110 | 115 | ||
111 | public static boolean isContainerReference(ReferenceDeclaration referenceDeclaration) { | 116 | public static boolean isContainerReference(ReferenceDeclaration referenceDeclaration) { |
@@ -116,7 +121,7 @@ public final class ProblemUtil { | |||
116 | return switch (kind) { | 121 | return switch (kind) { |
117 | case CONTAINMENT -> false; | 122 | case CONTAINMENT -> false; |
118 | case CONTAINER -> true; | 123 | case CONTAINER -> true; |
119 | case REFERENCE -> { | 124 | case DEFAULT, REFERENCE -> { |
120 | var opposite = referenceDeclaration.getOpposite(); | 125 | var opposite = referenceDeclaration.getOpposite(); |
121 | if (opposite == null) { | 126 | if (opposite == null) { |
122 | yield false; | 127 | yield false; |
diff --git a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java index d9eb5fd3..745e2d2b 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java +++ b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java | |||
@@ -10,6 +10,7 @@ | |||
10 | package tools.refinery.language.validation; | 10 | package tools.refinery.language.validation; |
11 | 11 | ||
12 | import com.google.inject.Inject; | 12 | import com.google.inject.Inject; |
13 | import org.eclipse.emf.common.util.Diagnostic; | ||
13 | import org.eclipse.emf.ecore.EObject; | 14 | import org.eclipse.emf.ecore.EObject; |
14 | import org.eclipse.emf.ecore.EReference; | 15 | import org.eclipse.emf.ecore.EReference; |
15 | import org.eclipse.xtext.EcoreUtil2; | 16 | import org.eclipse.xtext.EcoreUtil2; |
@@ -18,11 +19,15 @@ import org.eclipse.xtext.validation.Check; | |||
18 | import org.jetbrains.annotations.Nullable; | 19 | import org.jetbrains.annotations.Nullable; |
19 | import tools.refinery.language.model.problem.*; | 20 | import tools.refinery.language.model.problem.*; |
20 | import tools.refinery.language.naming.NamingUtil; | 21 | import tools.refinery.language.naming.NamingUtil; |
21 | import tools.refinery.language.scoping.imports.ImportAdapter; | 22 | import tools.refinery.language.scoping.imports.ImportAdapterProvider; |
22 | import tools.refinery.language.utils.ProblemDesugarer; | 23 | import tools.refinery.language.typesystem.ProblemTypeAnalyzer; |
24 | import tools.refinery.language.typesystem.SignatureProvider; | ||
23 | import tools.refinery.language.utils.ProblemUtil; | 25 | import tools.refinery.language.utils.ProblemUtil; |
24 | 26 | ||
25 | import java.util.*; | 27 | import java.util.ArrayList; |
28 | import java.util.LinkedHashMap; | ||
29 | import java.util.LinkedHashSet; | ||
30 | import java.util.Set; | ||
26 | 31 | ||
27 | /** | 32 | /** |
28 | * This class contains custom validation rules. | 33 | * This class contains custom validation rules. |
@@ -32,45 +37,39 @@ import java.util.*; | |||
32 | */ | 37 | */ |
33 | public class ProblemValidator extends AbstractProblemValidator { | 38 | public class ProblemValidator extends AbstractProblemValidator { |
34 | private static final String ISSUE_PREFIX = "tools.refinery.language.validation.ProblemValidator."; | 39 | private static final String ISSUE_PREFIX = "tools.refinery.language.validation.ProblemValidator."; |
35 | |||
36 | public static final String UNEXPECTED_MODULE_NAME_ISSUE = ISSUE_PREFIX + "UNEXPECTED_MODULE_NAME"; | 40 | public static final String UNEXPECTED_MODULE_NAME_ISSUE = ISSUE_PREFIX + "UNEXPECTED_MODULE_NAME"; |
37 | |||
38 | public static final String INVALID_IMPORT_ISSUE = ISSUE_PREFIX + "INVALID_IMPORT"; | 41 | public static final String INVALID_IMPORT_ISSUE = ISSUE_PREFIX + "INVALID_IMPORT"; |
39 | |||
40 | public static final String SINGLETON_VARIABLE_ISSUE = ISSUE_PREFIX + "SINGLETON_VARIABLE"; | 42 | public static final String SINGLETON_VARIABLE_ISSUE = ISSUE_PREFIX + "SINGLETON_VARIABLE"; |
41 | |||
42 | public static final String NODE_CONSTANT_ISSUE = ISSUE_PREFIX + "NODE_CONSTANT_ISSUE"; | 43 | public static final String NODE_CONSTANT_ISSUE = ISSUE_PREFIX + "NODE_CONSTANT_ISSUE"; |
43 | |||
44 | public static final String DUPLICATE_NAME_ISSUE = ISSUE_PREFIX + "DUPLICATE_NAME"; | 44 | public static final String DUPLICATE_NAME_ISSUE = ISSUE_PREFIX + "DUPLICATE_NAME"; |
45 | |||
46 | public static final String INVALID_MULTIPLICITY_ISSUE = ISSUE_PREFIX + "INVALID_MULTIPLICITY"; | 45 | public static final String INVALID_MULTIPLICITY_ISSUE = ISSUE_PREFIX + "INVALID_MULTIPLICITY"; |
47 | |||
48 | public static final String ZERO_MULTIPLICITY_ISSUE = ISSUE_PREFIX + "ZERO_MULTIPLICITY"; | 46 | public static final String ZERO_MULTIPLICITY_ISSUE = ISSUE_PREFIX + "ZERO_MULTIPLICITY"; |
49 | |||
50 | public static final String MISSING_OPPOSITE_ISSUE = ISSUE_PREFIX + "MISSING_OPPOSITE"; | 47 | public static final String MISSING_OPPOSITE_ISSUE = ISSUE_PREFIX + "MISSING_OPPOSITE"; |
51 | |||
52 | public static final String INVALID_OPPOSITE_ISSUE = ISSUE_PREFIX + "INVALID_OPPOSITE"; | 48 | public static final String INVALID_OPPOSITE_ISSUE = ISSUE_PREFIX + "INVALID_OPPOSITE"; |
53 | |||
54 | public static final String INVALID_SUPERTYPE_ISSUE = ISSUE_PREFIX + "INVALID_SUPERTYPE"; | 49 | public static final String INVALID_SUPERTYPE_ISSUE = ISSUE_PREFIX + "INVALID_SUPERTYPE"; |
55 | |||
56 | public static final String INVALID_REFERENCE_TYPE_ISSUE = ISSUE_PREFIX + "INVALID_REFERENCE_TYPE"; | 50 | public static final String INVALID_REFERENCE_TYPE_ISSUE = ISSUE_PREFIX + "INVALID_REFERENCE_TYPE"; |
57 | |||
58 | public static final String INVALID_ARITY_ISSUE = ISSUE_PREFIX + "INVALID_ARITY"; | 51 | public static final String INVALID_ARITY_ISSUE = ISSUE_PREFIX + "INVALID_ARITY"; |
59 | |||
60 | public static final String INVALID_TRANSITIVE_CLOSURE_ISSUE = ISSUE_PREFIX + "INVALID_TRANSITIVE_CLOSURE"; | 52 | public static final String INVALID_TRANSITIVE_CLOSURE_ISSUE = ISSUE_PREFIX + "INVALID_TRANSITIVE_CLOSURE"; |
61 | |||
62 | public static final String INVALID_VALUE_ISSUE = ISSUE_PREFIX + "INVALID_VALUE"; | 53 | public static final String INVALID_VALUE_ISSUE = ISSUE_PREFIX + "INVALID_VALUE"; |
63 | |||
64 | public static final String UNSUPPORTED_ASSERTION_ISSUE = ISSUE_PREFIX + "UNSUPPORTED_ASSERTION"; | 54 | public static final String UNSUPPORTED_ASSERTION_ISSUE = ISSUE_PREFIX + "UNSUPPORTED_ASSERTION"; |
55 | public static final String UNKNOWN_EXPRESSION_ISSUE = ISSUE_PREFIX + "UNKNOWN_EXPRESSION"; | ||
56 | public static final String INVALID_ASSIGNMENT_ISSUE = ISSUE_PREFIX + "INVALID_ASSIGNMENT"; | ||
57 | public static final String TYPE_ERROR = ISSUE_PREFIX + "TYPE_ERROR"; | ||
65 | 58 | ||
66 | @Inject | 59 | @Inject |
67 | private ReferenceCounter referenceCounter; | 60 | private ReferenceCounter referenceCounter; |
68 | 61 | ||
69 | @Inject | 62 | @Inject |
70 | private ProblemDesugarer desugarer; | 63 | private IQualifiedNameConverter qualifiedNameConverter; |
64 | |||
65 | @Inject | ||
66 | private ImportAdapterProvider importAdapterProvider; | ||
71 | 67 | ||
72 | @Inject | 68 | @Inject |
73 | private IQualifiedNameConverter qualifiedNameConverter; | 69 | private SignatureProvider signatureProvider; |
70 | |||
71 | @Inject | ||
72 | private ProblemTypeAnalyzer typeAnalyzer; | ||
74 | 73 | ||
75 | @Check | 74 | @Check |
76 | public void checkModuleName(Problem problem) { | 75 | public void checkModuleName(Problem problem) { |
@@ -86,7 +85,7 @@ public class ProblemValidator extends AbstractProblemValidator { | |||
86 | if (resourceSet == null) { | 85 | if (resourceSet == null) { |
87 | return; | 86 | return; |
88 | } | 87 | } |
89 | var adapter = ImportAdapter.getOrInstall(resourceSet); | 88 | var adapter = importAdapterProvider.getOrInstall(resourceSet); |
90 | var expectedName = adapter.getQualifiedName(resource.getURI()); | 89 | var expectedName = adapter.getQualifiedName(resource.getURI()); |
91 | if (expectedName == null) { | 90 | if (expectedName == null) { |
92 | return; | 91 | return; |
@@ -156,15 +155,19 @@ public class ProblemValidator extends AbstractProblemValidator { | |||
156 | public void checkUniqueDeclarations(Problem problem) { | 155 | public void checkUniqueDeclarations(Problem problem) { |
157 | var relations = new ArrayList<Relation>(); | 156 | var relations = new ArrayList<Relation>(); |
158 | var nodes = new ArrayList<Node>(); | 157 | var nodes = new ArrayList<Node>(); |
158 | var aggregators = new ArrayList<AggregatorDeclaration>(); | ||
159 | for (var statement : problem.getStatements()) { | 159 | for (var statement : problem.getStatements()) { |
160 | if (statement instanceof Relation relation) { | 160 | if (statement instanceof Relation relation) { |
161 | relations.add(relation); | 161 | relations.add(relation); |
162 | } else if (statement instanceof NodeDeclaration nodeDeclaration) { | 162 | } else if (statement instanceof NodeDeclaration nodeDeclaration) { |
163 | nodes.addAll(nodeDeclaration.getNodes()); | 163 | nodes.addAll(nodeDeclaration.getNodes()); |
164 | } else if (statement instanceof AggregatorDeclaration aggregatorDeclaration) { | ||
165 | aggregators.add(aggregatorDeclaration); | ||
164 | } | 166 | } |
165 | } | 167 | } |
166 | checkUniqueSimpleNames(relations); | 168 | checkUniqueSimpleNames(relations); |
167 | checkUniqueSimpleNames(nodes); | 169 | checkUniqueSimpleNames(nodes); |
170 | checkUniqueSimpleNames(aggregators); | ||
168 | } | 171 | } |
169 | 172 | ||
170 | @Check | 173 | @Check |
@@ -315,8 +318,9 @@ public class ProblemValidator extends AbstractProblemValidator { | |||
315 | 318 | ||
316 | @Check | 319 | @Check |
317 | public void checkReferenceType(ReferenceDeclaration referenceDeclaration) { | 320 | public void checkReferenceType(ReferenceDeclaration referenceDeclaration) { |
318 | if (referenceDeclaration.getKind() == ReferenceKind.REFERENCE && | 321 | boolean isDefaultReference = referenceDeclaration.getKind() == ReferenceKind.DEFAULT && |
319 | !ProblemUtil.isContainerReference(referenceDeclaration)) { | 322 | !ProblemUtil.isContainerReference(referenceDeclaration); |
323 | if (isDefaultReference || referenceDeclaration.getKind() == ReferenceKind.REFERENCE) { | ||
320 | checkArity(referenceDeclaration, ProblemPackage.Literals.REFERENCE_DECLARATION__REFERENCE_TYPE, 1); | 324 | checkArity(referenceDeclaration, ProblemPackage.Literals.REFERENCE_DECLARATION__REFERENCE_TYPE, 1); |
321 | return; | 325 | return; |
322 | } | 326 | } |
@@ -350,11 +354,14 @@ public class ProblemValidator extends AbstractProblemValidator { | |||
350 | 354 | ||
351 | @Check | 355 | @Check |
352 | public void checkAssertion(Assertion assertion) { | 356 | public void checkAssertion(Assertion assertion) { |
353 | int argumentCount = assertion.getArguments().size(); | 357 | var relation = assertion.getRelation(); |
354 | if (!(assertion.getValue() instanceof LogicConstant)) { | 358 | if (relation instanceof DatatypeDeclaration) { |
355 | var message = "Assertion value must be one of 'true', 'false', 'unknown', or 'error'."; | 359 | var message = "Assertions for data types are not supported."; |
356 | acceptError(message, assertion, ProblemPackage.Literals.ASSERTION__VALUE, 0, INVALID_VALUE_ISSUE); | 360 | acceptError(message, assertion, ProblemPackage.Literals.ASSERTION__RELATION, 0, |
361 | UNSUPPORTED_ASSERTION_ISSUE); | ||
362 | return; | ||
357 | } | 363 | } |
364 | int argumentCount = assertion.getArguments().size(); | ||
358 | checkArity(assertion, ProblemPackage.Literals.ASSERTION__RELATION, argumentCount); | 365 | checkArity(assertion, ProblemPackage.Literals.ASSERTION__RELATION, argumentCount); |
359 | } | 366 | } |
360 | 367 | ||
@@ -373,7 +380,7 @@ public class ProblemValidator extends AbstractProblemValidator { | |||
373 | // Feature does not point to a {@link Relation}, we are probably already emitting another error. | 380 | // Feature does not point to a {@link Relation}, we are probably already emitting another error. |
374 | return; | 381 | return; |
375 | } | 382 | } |
376 | int arity = ProblemUtil.getArity(relation); | 383 | int arity = signatureProvider.getArity(relation); |
377 | if (arity == expectedArity) { | 384 | if (arity == expectedArity) { |
378 | return; | 385 | return; |
379 | } | 386 | } |
@@ -384,11 +391,7 @@ public class ProblemValidator extends AbstractProblemValidator { | |||
384 | 391 | ||
385 | @Check | 392 | @Check |
386 | public void checkMultiObjectAssertion(Assertion assertion) { | 393 | public void checkMultiObjectAssertion(Assertion assertion) { |
387 | var builtinSymbolsOption = desugarer.getBuiltinSymbols(assertion); | 394 | var builtinSymbols = importAdapterProvider.getBuiltinSymbols(assertion); |
388 | if (builtinSymbolsOption.isEmpty()) { | ||
389 | return; | ||
390 | } | ||
391 | var builtinSymbols = builtinSymbolsOption.get(); | ||
392 | var relation = assertion.getRelation(); | 395 | var relation = assertion.getRelation(); |
393 | boolean isExists = builtinSymbols.exists().equals(relation); | 396 | boolean isExists = builtinSymbols.exists().equals(relation); |
394 | boolean isEquals = builtinSymbols.equals().equals(relation); | 397 | boolean isEquals = builtinSymbols.equals().equals(relation); |
@@ -492,4 +495,66 @@ public class ProblemValidator extends AbstractProblemValidator { | |||
492 | } | 495 | } |
493 | } | 496 | } |
494 | } | 497 | } |
498 | |||
499 | @Check | ||
500 | private void checkAssignmentExpr(AssignmentExpr assignmentExpr) { | ||
501 | var left = assignmentExpr.getLeft(); | ||
502 | if (left == null) { | ||
503 | // Syntactically invalid, so we already emit an error. | ||
504 | return; | ||
505 | } | ||
506 | if (!(left instanceof VariableOrNodeExpr variableOrNodeExpr)) { | ||
507 | var message = "Left side of an assignment must be variable."; | ||
508 | acceptError(message, assignmentExpr, ProblemPackage.Literals.BINARY_EXPR__LEFT, | ||
509 | 0, INVALID_ASSIGNMENT_ISSUE); | ||
510 | return; | ||
511 | } | ||
512 | var target = variableOrNodeExpr.getVariableOrNode(); | ||
513 | if (target == null) { | ||
514 | // Syntactically invalid, so we already emit an error. | ||
515 | return; | ||
516 | } | ||
517 | if (target instanceof Parameter) { | ||
518 | var message = "Parameters cannot be assigned."; | ||
519 | acceptError(message, variableOrNodeExpr, ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE, | ||
520 | 0, INVALID_ASSIGNMENT_ISSUE); | ||
521 | } | ||
522 | if (target instanceof Node) { | ||
523 | var message = "Nodes cannot be assigned."; | ||
524 | acceptError(message, variableOrNodeExpr, ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE, | ||
525 | 0, INVALID_ASSIGNMENT_ISSUE); | ||
526 | } | ||
527 | if (!(assignmentExpr.eContainer() instanceof Conjunction)) { | ||
528 | var message = "Assignments may only appear as top-level expressions."; | ||
529 | acceptError(message, assignmentExpr, null, 0, INVALID_ASSIGNMENT_ISSUE); | ||
530 | } | ||
531 | } | ||
532 | |||
533 | @Check | ||
534 | private void checkInfiniteConstant(InfiniteConstant infiniteConstant) { | ||
535 | if (!(infiniteConstant.eContainer() instanceof RangeExpr)) { | ||
536 | var message = "Negative and positive infinity literals may only appear in '..' range expressions."; | ||
537 | acceptError(message, infiniteConstant, null, 0, TYPE_ERROR); | ||
538 | } | ||
539 | } | ||
540 | |||
541 | @Check | ||
542 | private void checkTypes(Problem problem) { | ||
543 | var diagnostics = typeAnalyzer.getOrComputeTypes(problem).getDiagnostics(); | ||
544 | for (var diagnostic : diagnostics) { | ||
545 | switch (diagnostic.getSeverity()) { | ||
546 | case Diagnostic.INFO -> info(diagnostic.getMessage(), diagnostic.getSourceEObject(), | ||
547 | diagnostic.getFeature(), diagnostic.getIndex(), diagnostic.getIssueCode(), | ||
548 | diagnostic.getIssueData()); | ||
549 | case Diagnostic.WARNING -> warning(diagnostic.getMessage(), diagnostic.getSourceEObject(), | ||
550 | diagnostic.getFeature(), diagnostic.getIndex(), diagnostic.getIssueCode(), | ||
551 | diagnostic.getIssueData()); | ||
552 | case Diagnostic.ERROR -> error(diagnostic.getMessage(), diagnostic.getSourceEObject(), | ||
553 | diagnostic.getFeature(), diagnostic.getIndex(), diagnostic.getIssueCode(), | ||
554 | diagnostic.getIssueData()); | ||
555 | default -> throw new IllegalStateException("Unknown severity %s of %s" | ||
556 | .formatted(diagnostic.getSeverity(), diagnostic)); | ||
557 | } | ||
558 | } | ||
559 | } | ||
495 | } | 560 | } |
diff --git a/subprojects/language/src/main/resources/META-INF/services/tools.refinery.language.expressions.TermInterpreter b/subprojects/language/src/main/resources/META-INF/services/tools.refinery.language.expressions.TermInterpreter new file mode 100644 index 00000000..11b6ccae --- /dev/null +++ b/subprojects/language/src/main/resources/META-INF/services/tools.refinery.language.expressions.TermInterpreter | |||
@@ -0,0 +1,4 @@ | |||
1 | # SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
2 | # | ||
3 | # SPDX-License-Identifier: EPL-2.0 | ||
4 | tools.refinery.language.expressions.BuiltinTermInterpreter | ||
diff --git a/subprojects/language/src/main/resources/tools/refinery/language/library/builtin.refinery b/subprojects/language/src/main/resources/tools/refinery/language/library/builtin.refinery index 4e74ca03..09c7d92b 100644 --- a/subprojects/language/src/main/resources/tools/refinery/language/library/builtin.refinery +++ b/subprojects/language/src/main/resources/tools/refinery/language/library/builtin.refinery | |||
@@ -13,3 +13,17 @@ abstract class contained extends node. | |||
13 | pred contains(container, contained contained). | 13 | pred contains(container, contained contained). |
14 | 14 | ||
15 | error invalidContainer(contained contained). | 15 | error invalidContainer(contained contained). |
16 | |||
17 | extern datatype boolean. | ||
18 | |||
19 | extern datatype int. | ||
20 | |||
21 | extern datatype real. | ||
22 | |||
23 | extern datatype string. | ||
24 | |||
25 | extern aggregator min. | ||
26 | |||
27 | extern aggregator max. | ||
28 | |||
29 | extern aggregator sum. | ||
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssertionValidationTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssertionValidationTest.java index 1fb08845..b995d0bb 100644 --- a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssertionValidationTest.java +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssertionValidationTest.java | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2023-2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
@@ -35,7 +35,7 @@ class AssertionValidationTest { | |||
35 | """); | 35 | """); |
36 | var issues = problem.validate(); | 36 | var issues = problem.validate(); |
37 | assertThat(issues, hasItem(hasProperty("issueCode", | 37 | assertThat(issues, hasItem(hasProperty("issueCode", |
38 | is(ProblemValidator.INVALID_VALUE_ISSUE)))); | 38 | is(ProblemValidator.TYPE_ERROR)))); |
39 | } | 39 | } |
40 | 40 | ||
41 | @ParameterizedTest | 41 | @ParameterizedTest |
diff --git a/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssignmentValidationTest.java b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssignmentValidationTest.java new file mode 100644 index 00000000..a9e0e311 --- /dev/null +++ b/subprojects/language/src/test/java/tools/refinery/language/tests/validation/AssignmentValidationTest.java | |||
@@ -0,0 +1,61 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.tests.validation; | ||
7 | |||
8 | |||
9 | import com.google.inject.Inject; | ||
10 | import org.eclipse.xtext.testing.InjectWith; | ||
11 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
12 | import org.junit.jupiter.api.Test; | ||
13 | import org.junit.jupiter.api.extension.ExtendWith; | ||
14 | import org.junit.jupiter.params.ParameterizedTest; | ||
15 | import org.junit.jupiter.params.provider.ValueSource; | ||
16 | import tools.refinery.language.model.tests.utils.ProblemParseHelper; | ||
17 | import tools.refinery.language.tests.ProblemInjectorProvider; | ||
18 | import tools.refinery.language.validation.ProblemValidator; | ||
19 | |||
20 | import static org.hamcrest.MatcherAssert.assertThat; | ||
21 | import static org.hamcrest.Matchers.*; | ||
22 | |||
23 | @ExtendWith(InjectionExtension.class) | ||
24 | @InjectWith(ProblemInjectorProvider.class) | ||
25 | class AssignmentValidationTest { | ||
26 | @Inject | ||
27 | private ProblemParseHelper parseHelper; | ||
28 | |||
29 | @ParameterizedTest | ||
30 | @ValueSource(strings = {""" | ||
31 | pred foo(node a) <-> 5 is 5. | ||
32 | """, """ | ||
33 | pred foo(node a) <-> b + 2 is 5. | ||
34 | """, """ | ||
35 | pred foo(node a) <-> a is 5. | ||
36 | """, """ | ||
37 | node(n). | ||
38 | pred foo(node a) <-> n is 5. | ||
39 | """, """ | ||
40 | enum E { A, B } | ||
41 | pred foo(node a) <-> B is 5. | ||
42 | """}) | ||
43 | void invalidAssignmentTest(String text) { | ||
44 | var problem = parseHelper.parse(text); | ||
45 | var issues = problem.validate(); | ||
46 | assertThat(issues, hasItem(hasProperty("issueCode", is( | ||
47 | ProblemValidator.INVALID_ASSIGNMENT_ISSUE | ||
48 | )))); | ||
49 | } | ||
50 | |||
51 | @Test | ||
52 | void validAssignmentTest() { | ||
53 | var problem = parseHelper.parse(""" | ||
54 | pred foo(node a) <-> b is 5. | ||
55 | """); | ||
56 | var issues = problem.validate(); | ||
57 | assertThat(issues, not(hasItem(hasProperty("issueCode", is( | ||
58 | ProblemValidator.INVALID_ASSIGNMENT_ISSUE | ||
59 | ))))); | ||
60 | } | ||
61 | } | ||
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedClassDeclaration.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedClassDeclaration.java index a228137c..14ac7bfc 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedClassDeclaration.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedClassDeclaration.java | |||
@@ -1,19 +1,19 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
6 | package tools.refinery.language.model.tests.utils; | 6 | package tools.refinery.language.model.tests.utils; |
7 | 7 | ||
8 | import tools.refinery.language.model.problem.ClassDeclaration; | 8 | import tools.refinery.language.model.problem.ClassDeclaration; |
9 | import tools.refinery.language.model.problem.FeatureDeclaration; | 9 | import tools.refinery.language.model.problem.ReferenceDeclaration; |
10 | 10 | ||
11 | public record WrappedClassDeclaration(ClassDeclaration classDeclaration) { | 11 | public record WrappedClassDeclaration(ClassDeclaration classDeclaration) { |
12 | public ClassDeclaration get() { | 12 | public ClassDeclaration get() { |
13 | return classDeclaration; | 13 | return classDeclaration; |
14 | } | 14 | } |
15 | 15 | ||
16 | public FeatureDeclaration feature(String name) { | 16 | public ReferenceDeclaration feature(String name) { |
17 | return ProblemNavigationUtil.named(classDeclaration.getFeatureDeclarations(), name); | 17 | return ProblemNavigationUtil.named(classDeclaration.getFeatureDeclarations(), name); |
18 | } | 18 | } |
19 | } | 19 | } |
diff --git a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedProblem.java b/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedProblem.java index 58bfce44..b31eed6d 100644 --- a/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedProblem.java +++ b/subprojects/language/src/testFixtures/java/tools/refinery/language/model/tests/utils/WrappedProblem.java | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | 2 | * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/> |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: EPL-2.0 | 4 | * SPDX-License-Identifier: EPL-2.0 |
5 | */ | 5 | */ |
@@ -7,9 +7,9 @@ package tools.refinery.language.model.tests.utils; | |||
7 | 7 | ||
8 | import org.eclipse.emf.ecore.resource.Resource.Diagnostic; | 8 | import org.eclipse.emf.ecore.resource.Resource.Diagnostic; |
9 | import org.eclipse.emf.ecore.util.Diagnostician; | 9 | import org.eclipse.emf.ecore.util.Diagnostician; |
10 | import org.eclipse.emf.ecore.util.EcoreUtil; | ||
11 | import tools.refinery.language.library.BuiltinLibrary; | ||
10 | import tools.refinery.language.model.problem.*; | 12 | import tools.refinery.language.model.problem.*; |
11 | import tools.refinery.language.utils.BuiltinSymbols; | ||
12 | import tools.refinery.language.utils.ProblemDesugarer; | ||
13 | 13 | ||
14 | import java.util.List; | 14 | import java.util.List; |
15 | import java.util.stream.Stream; | 15 | import java.util.stream.Stream; |
@@ -32,11 +32,11 @@ public record WrappedProblem(Problem problem) { | |||
32 | } | 32 | } |
33 | 33 | ||
34 | public WrappedProblem builtin() { | 34 | public WrappedProblem builtin() { |
35 | return new WrappedProblem(new ProblemDesugarer().getBuiltinProblem(problem).orElseThrow()); | 35 | var resourceSet = problem.eResource().getResourceSet(); |
36 | } | 36 | var builtinResource = resourceSet.getResource(BuiltinLibrary.BUILTIN_LIBRARY_URI, true); |
37 | 37 | EcoreUtil.resolveAll(builtinResource); | |
38 | public BuiltinSymbols builtinSymbols() { | 38 | var builtinProblem = (Problem) builtinResource.getContents().getFirst(); |
39 | return new ProblemDesugarer().getBuiltinSymbols(problem).orElseThrow(); | 39 | return new WrappedProblem(builtinProblem); |
40 | } | 40 | } |
41 | 41 | ||
42 | public List<String> nodeNames() { | 42 | public List<String> nodeNames() { |