aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language/src/main/java
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-11-19 14:00:12 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-11-22 16:40:03 +0100
commit383137c190cab040d2609f8295ef822c3917b88d (patch)
treed0d88528de2020da51cb6e92459b84cf63609feb /subprojects/language/src/main/java
parentchore(deps): bump dependencies (diff)
downloadrefinery-383137c190cab040d2609f8295ef822c3917b88d.tar.gz
refinery-383137c190cab040d2609f8295ef822c3917b88d.tar.zst
refinery-383137c190cab040d2609f8295ef822c3917b88d.zip
feat(language): numeric expressions
Diffstat (limited to 'subprojects/language/src/main/java')
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/Problem.xtext137
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java7
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java20
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/parser/antlr/IdentifierTokenProvider.java89
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/parser/antlr/ProblemTokenSource.java125
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/parser/antlr/TokenSourceInjectingProblemParser.java18
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java181
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ImplicitVariableScope.java138
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemXmiResourceFactory.java16
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java36
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java2
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java30
12 files changed, 560 insertions, 239 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 2a8429a3..bc1ee465 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
+++ b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
@@ -8,9 +8,10 @@ Problem:
8 statements+=Statement*; 8 statements+=Statement*;
9 9
10Statement: 10Statement:
11 ClassDeclaration | EnumDeclaration | PredicateDefinition | /* RuleDefinition | */ Assertion | NodeValueAssertion | 11 ClassDeclaration | EnumDeclaration |
12 ScopeDeclaration | 12 PredicateDefinition | FunctionDefinition | /* RuleDefinition | */
13 IndividualDeclaration; 13 Assertion | NodeValueAssertion |
14 ScopeDeclaration | IndividualDeclaration;
14 15
15ClassDeclaration: 16ClassDeclaration:
16 abstract?="abstract"? "class" 17 abstract?="abstract"? "class"
@@ -30,53 +31,51 @@ enum ReferenceKind:
30 REFERENCE="refers" | CONTAINMENT="contains" | CONTAINER="container"; 31 REFERENCE="refers" | CONTAINMENT="contains" | CONTAINER="container";
31 32
32ReferenceDeclaration: 33ReferenceDeclaration:
33 ( 34 (kind=ReferenceKind referenceType=[Relation|QualifiedName] |
34 kind=ReferenceKind referenceType=[Relation|QualifiedName] | 35 referenceType=[Relation|NonRelationKindQualifiedName])
35 referenceType=[Relation|NonRelationKindQualifiedName]
36 )
37 ("[" multiplicity=Multiplicity "]")? 36 ("[" multiplicity=Multiplicity "]")?
38 name=Identifier 37 name=Identifier
39 ("opposite" opposite=[ReferenceDeclaration|QualifiedName])?; 38 ("opposite" opposite=[ReferenceDeclaration|QualifiedName])?;
40 39
40enum ErrorKind returns PredicateKind:
41 ERROR="error";
42
41enum PredicateKind: 43enum PredicateKind:
42 ERROR="error" | CONTAINED="contained" | CONTAINMENT="containment"; 44 ERROR="error" | CONTAINED="contained" | CONTAINMENT="containment";
43 45
44PredicateDefinition: 46PredicateDefinition:
45 (kind=PredicateKind "pred"? | "pred") 47 (kind=ErrorKind | kind=PredicateKind? "pred")
46 name=Identifier 48 name=Identifier
47 "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")" 49 "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")"
48 ("<->" bodies+=Conjunction (";" bodies+=Conjunction)*)? 50 ("<->" bodies+=Conjunction (";" bodies+=Conjunction)*)?
49 "."; 51 ".";
50 52
53Conjunction:
54 literals+=Expr ("," literals+=Expr)*;
55
56FunctionDefinition:
57 functionType=[Relation|QualifiedName] name=Identifier
58 "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")"
59 ("=" cases+=Case (";" cases+=Case)*)?
60 ".";
61
62Case:
63 Conjunction ({Match.condition=current} "->" value=Expr)?;
64
51//RuleDefinition: 65//RuleDefinition:
52// "rule" 66// "rule"
53// name=Identifier 67// name=Identifier
54// "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")" 68// "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")"
55// (":" bodies+=Conjunction (";" bodies+=Conjunction)* 69// (":" preconditions+=Conjunction (";" preconditions+=Conjunction)*)?
56// "==>" consequents+=Consequent (";" consequents+=Consequent)*)? 70// "==>" consequents+=Consequent (";" consequents+=Consequent)*)?
57// "."; 71// ".";
58 72
59Parameter: 73Parameter:
60 (modality=Modality? parameterType=[Relation|QualifiedName])? name=Identifier; 74 (modality=Modality? parameterType=[Relation|QualifiedName])? name=Identifier;
61 75
62Conjunction:
63 literals+=Literal ("," literals+=Literal)*;
64
65//Consequent: 76//Consequent:
66// actions+=Action ("," actions+=Action)*; 77// actions+=Action ("," actions+=Action)*;
67 78//
68Literal:
69 Atom | NegativeLiteral | CountLiteral;
70
71NegativeLiteral:
72 modality=Modality? "!" atom=Atom;
73
74enum ComparisonOp:
75 LESS="<" | LESS_EQ="<=" | GREATER=">" | GREATER_EQ=">=" | EQ="==" | NOT_EQ="!=";
76
77CountLiteral:
78 modality=Modality? "count" atom=Atom op=ComparisonOp threshold=INT;
79
80//Action: 79//Action:
81// AssertionAction | DeleteAction | NewAction; 80// AssertionAction | DeleteAction | NewAction;
82// 81//
@@ -93,22 +92,74 @@ CountLiteral:
93//NewVariable: 92//NewVariable:
94// name=Identifier; 93// name=Identifier;
95 94
95Expr:
96 ComparisonExpr;
97
98enum ComparisonOp:
99 LESS="<" | LESS_EQ="<=" | GREATER=">" | GREATER_EQ=">=" | EQ="==" | NOT_EQ="!=";
100
101ComparisonExpr returns Expr:
102 AdditiveExpr ({ComparisonExpr.left=current}
103 op=ComparisonOp right=AdditiveExpr)*;
104
105enum AdditiveOp returns BinaryOp:
106 ADD="+" | SUB="-";
107
108AdditiveExpr returns Expr:
109 MultiplicativeExpr ({ArithmeticBinaryExpr.left=current}
110 op=AdditiveOp right=MultiplicativeExpr)*;
111
112enum MultiplicativeOp returns BinaryOp:
113 MUL="*" | DIV="/";
114
115MultiplicativeExpr returns Expr:
116 ExponentialExpr ({ArithmeticBinaryExpr.left=current}
117 op=MultiplicativeOp right=ExponentialExpr)*;
118
119enum ExponentialOp returns BinaryOp:
120 POW="**";
121
122ExponentialExpr returns Expr:
123 UnaryExpr ({ArithmeticBinaryExpr.left=current}
124 op=ExponentialOp right=ExponentialExpr)?;
125
126UnaryExpr returns Expr:
127 ArithmeticUnaryExpr | ModalExpr | NegationExpr | CountExpr | AggregationExpr |
128 Atom | VariableOrNodeExpr | ConstantExpr | "(" Expr ")";
129
130enum UnaryOp:
131 PLUS="+" | MINUS="-";
132
133ArithmeticUnaryExpr:
134 op=UnaryOp body=UnaryExpr;
135
96enum Modality: 136enum Modality:
97 MAY="may" | MUST="must" | CURRENT="current"; 137 MAY="may" | MUST="must" | CURRENT="current";
98 138
139ModalExpr:
140 modality=Modality body=UnaryExpr;
141
142NegationExpr:
143 "!" body=UnaryExpr;
144
145CountExpr:
146 "#" body=UnaryExpr;
147
148enum AggregationOp:
149 SUM="sum" | PROD="prod" | MIN="min" | MAX="max";
150
151AggregationExpr:
152 op=AggregationOp "{" value=Expr "|" condition=Expr "}";
153
99Atom: 154Atom:
100 modality=Modality?
101 relation=[Relation|QualifiedName] 155 relation=[Relation|QualifiedName]
102 transitiveClosure?="+"? 156 transitiveClosure?=TRANSITIVE_CLOSURE?
103 "(" (arguments+=Argument ("," arguments+=Argument)*)? ")"; 157 "(" (arguments+=Expr ("," arguments+=Expr)*)? ")";
104
105Argument:
106 VariableOrNodeArgument | ConstantArgument;
107 158
108VariableOrNodeArgument: 159VariableOrNodeExpr:
109 variableOrNode=[VariableOrNode|QualifiedName]; 160 variableOrNode=[VariableOrNode|QualifiedName];
110 161
111ConstantArgument: 162ConstantExpr:
112 constant=Constant; 163 constant=Constant;
113 164
114Assertion: 165Assertion:
@@ -131,7 +182,7 @@ WildcardAssertionArgument:
131 {WildcardAssertionArgument} "*"; 182 {WildcardAssertionArgument} "*";
132 183
133ConstantAssertionArgument: 184ConstantAssertionArgument:
134 constant=Constant; 185 negative?="-"? constant=Constant;
135 186
136enum LogicValue: 187enum LogicValue:
137 TRUE="true" | FALSE="false" | UNKNOWN="unknown" | ERROR="error"; 188 TRUE="true" | FALSE="false" | UNKNOWN="unknown" | ERROR="error";
@@ -146,7 +197,7 @@ Constant:
146 RealConstant | IntConstant | StringConstant; 197 RealConstant | IntConstant | StringConstant;
147 198
148IntConstant: 199IntConstant:
149 intValue=Integer; 200 intValue=INT;
150 201
151RealConstant: 202RealConstant:
152 realValue=Real; 203 realValue=Real;
@@ -178,7 +229,7 @@ ExactMultiplicity:
178 exactValue=INT; 229 exactValue=INT;
179 230
180IndividualDeclaration: 231IndividualDeclaration:
181 "indiv" nodes+=EnumLiteral ("," nodes+=EnumLiteral)* "."; 232 "individual" nodes+=EnumLiteral ("," nodes+=EnumLiteral)* ".";
182 233
183UpperBound returns ecore::EInt: 234UpperBound returns ecore::EInt:
184 INT | "*"; 235 INT | "*";
@@ -190,18 +241,16 @@ QualifiedName hidden():
190 Identifier ("::" Identifier)*; 241 Identifier ("::" Identifier)*;
191 242
192NonRelationKindIdentifier: 243NonRelationKindIdentifier:
193 ID | "true" | "false" | "unknown" | "error" | "class" | "abstract" | "extends" | "enum" | 244 ID | "true" | "false" | "contained" | "sum" | "prod" | "min" | "max";
194 "pred" | "indiv" | "problem" | /* "new" | "delete" | "rule" | */ "may" | "must" | "current" |
195 "count" | "default" | "scope" | "contained" | "containment";
196 245
197Identifier: 246Identifier:
198 NonRelationKindIdentifier | "refers" | "contains" | "container"; 247 NonRelationKindIdentifier | "contains";
199
200Integer returns ecore::EInt hidden():
201 "-"? INT;
202 248
203Real returns ecore::EDouble: 249Real returns ecore::EDouble:
204 "-"? (EXPONENTIAL | INT "." (INT | EXPONENTIAL)); 250 EXPONENTIAL | INT "." (INT | EXPONENTIAL);
251
252terminal TRANSITIVE_CLOSURE:
253 "synthetic:TRANSITIVE_CLOSURE";
205 254
206@Override 255@Override
207terminal ID: 256terminal ID:
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 dd7731b4..c0777038 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java
@@ -5,6 +5,7 @@ package tools.refinery.language;
5 5
6import org.eclipse.xtext.conversion.IValueConverterService; 6import org.eclipse.xtext.conversion.IValueConverterService;
7import org.eclipse.xtext.naming.IQualifiedNameConverter; 7import org.eclipse.xtext.naming.IQualifiedNameConverter;
8import org.eclipse.xtext.parser.IParser;
8import org.eclipse.xtext.resource.DerivedStateAwareResource; 9import org.eclipse.xtext.resource.DerivedStateAwareResource;
9import org.eclipse.xtext.resource.DerivedStateAwareResourceDescriptionManager; 10import org.eclipse.xtext.resource.DerivedStateAwareResourceDescriptionManager;
10import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy; 11import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy;
@@ -23,6 +24,7 @@ import com.google.inject.name.Names;
23 24
24import tools.refinery.language.conversion.ProblemValueConverterService; 25import tools.refinery.language.conversion.ProblemValueConverterService;
25import tools.refinery.language.naming.ProblemQualifiedNameConverter; 26import tools.refinery.language.naming.ProblemQualifiedNameConverter;
27import tools.refinery.language.parser.antlr.TokenSourceInjectingProblemParser;
26import tools.refinery.language.resource.ProblemDerivedStateComputer; 28import tools.refinery.language.resource.ProblemDerivedStateComputer;
27import tools.refinery.language.resource.ProblemLocationInFileProvider; 29import tools.refinery.language.resource.ProblemLocationInFileProvider;
28import tools.refinery.language.resource.ProblemResourceDescriptionStrategy; 30import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
@@ -34,6 +36,11 @@ import tools.refinery.language.scoping.ProblemLocalScopeProvider;
34 * Equinox extension registry. 36 * Equinox extension registry.
35 */ 37 */
36public class ProblemRuntimeModule extends AbstractProblemRuntimeModule { 38public class ProblemRuntimeModule extends AbstractProblemRuntimeModule {
39 @Override
40 public Class<? extends IParser> bindIParser() {
41 return TokenSourceInjectingProblemParser.class;
42 }
43
37 public Class<? extends IQualifiedNameConverter> bindIQualifiedNameConverter() { 44 public Class<? extends IQualifiedNameConverter> bindIQualifiedNameConverter() {
38 return ProblemQualifiedNameConverter.class; 45 return ProblemQualifiedNameConverter.class;
39 } 46 }
diff --git a/subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java b/subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java
index df5d2090..a65e0750 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java
@@ -11,17 +11,9 @@ import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionsFinder;
11import org.eclipse.xtext.formatting2.regionaccess.ISequentialRegion; 11import org.eclipse.xtext.formatting2.regionaccess.ISequentialRegion;
12import org.eclipse.xtext.xbase.lib.Procedures.Procedure1; 12import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
13 13
14import tools.refinery.language.model.problem.Assertion; 14import tools.refinery.language.model.problem.*;
15import tools.refinery.language.model.problem.Atom;
16import tools.refinery.language.model.problem.ClassDeclaration;
17import tools.refinery.language.model.problem.Conjunction;
18import tools.refinery.language.model.problem.IndividualDeclaration;
19import tools.refinery.language.model.problem.NegativeLiteral;
20import tools.refinery.language.model.problem.Parameter;
21import tools.refinery.language.model.problem.PredicateDefinition;
22import tools.refinery.language.model.problem.Problem;
23import tools.refinery.language.model.problem.ProblemPackage;
24 15
16@SuppressWarnings("UnstableApiUsage")
25public class ProblemFormatter extends AbstractJavaFormatter { 17public class ProblemFormatter extends AbstractJavaFormatter {
26 18
27 protected void format(Problem problem, IFormattableDocument doc) { 19 protected void format(Problem problem, IFormattableDocument doc) {
@@ -95,16 +87,14 @@ public class ProblemFormatter extends AbstractJavaFormatter {
95 } 87 }
96 } 88 }
97 89
98 protected void format(NegativeLiteral literal, IFormattableDocument doc) { 90 protected void format(NegationExpr literal, IFormattableDocument doc) {
99 var region = regionFor(literal); 91 var region = regionFor(literal);
100 doc.append(region.feature(ProblemPackage.Literals.LITERAL__MODALITY), this::oneSpace);
101 doc.append(region.keyword("!"), this::noSpace); 92 doc.append(region.keyword("!"), this::noSpace);
102 doc.format(literal.getAtom()); 93 doc.format(literal.getBody());
103 } 94 }
104 95
105 protected void format(Atom atom, IFormattableDocument doc) { 96 protected void format(Atom atom, IFormattableDocument doc) {
106 var region = regionFor(atom); 97 var region = regionFor(atom);
107 doc.append(region.feature(ProblemPackage.Literals.LITERAL__MODALITY), this::oneSpace);
108 doc.append(region.feature(ProblemPackage.Literals.ATOM__RELATION), this::noSpace); 98 doc.append(region.feature(ProblemPackage.Literals.ATOM__RELATION), this::noSpace);
109 doc.append(region.feature(ProblemPackage.Literals.ATOM__TRANSITIVE_CLOSURE), this::noSpace); 99 doc.append(region.feature(ProblemPackage.Literals.ATOM__TRANSITIVE_CLOSURE), this::noSpace);
110 formatParenthesizedList(region, doc); 100 formatParenthesizedList(region, doc);
@@ -116,7 +106,7 @@ public class ProblemFormatter extends AbstractJavaFormatter {
116 protected void format(IndividualDeclaration individualDeclaration, IFormattableDocument doc) { 106 protected void format(IndividualDeclaration individualDeclaration, IFormattableDocument doc) {
117 surroundNewLines(doc, individualDeclaration, this::singleNewLine); 107 surroundNewLines(doc, individualDeclaration, this::singleNewLine);
118 var region = regionFor(individualDeclaration); 108 var region = regionFor(individualDeclaration);
119 doc.append(region.keyword("indiv"), this::oneSpace); 109 doc.append(region.keyword("individual"), this::oneSpace);
120 formatList(region, ",", doc); 110 formatList(region, ",", doc);
121 doc.prepend(region.keyword("."), this::noSpace); 111 doc.prepend(region.keyword("."), this::noSpace);
122 } 112 }
diff --git a/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/IdentifierTokenProvider.java b/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/IdentifierTokenProvider.java
new file mode 100644
index 00000000..ab133a90
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/IdentifierTokenProvider.java
@@ -0,0 +1,89 @@
1package tools.refinery.language.parser.antlr;
2
3import com.google.inject.Inject;
4import com.google.inject.Singleton;
5import org.eclipse.xtext.*;
6import org.eclipse.xtext.parser.antlr.ITokenDefProvider;
7import tools.refinery.language.services.ProblemGrammarAccess;
8
9import java.util.HashMap;
10import java.util.HashSet;
11import java.util.Set;
12
13@Singleton
14public class IdentifierTokenProvider {
15 private final int[] identifierTokensArray;
16
17 @Inject
18 private IdentifierTokenProvider(Initializer initializer) {
19 this.identifierTokensArray = initializer.getIdentifierTokesArray();
20 }
21
22 public boolean isIdentifierToken(int tokenId) {
23 for (int identifierTokenId : identifierTokensArray) {
24 if (identifierTokenId == tokenId) {
25 return true;
26 }
27 }
28 return false;
29 }
30
31 private static class Initializer {
32 @Inject
33 private ITokenDefProvider tokenDefProvider;
34
35 @Inject
36 private ProblemGrammarAccess problemGrammarAccess;
37
38 private HashMap<String, Integer> valueToTokenIdMap;
39
40 private Set<Integer> identifierTokens;
41
42 public int[] getIdentifierTokesArray() {
43 createValueToTokenIdMap();
44 identifierTokens = new HashSet<>();
45 collectIdentifierTokensFromRule(problemGrammarAccess.getIdentifierRule());
46 var identifierTokensArray = new int[identifierTokens.size()];
47 int i = 0;
48 for (var tokenId : identifierTokens) {
49 identifierTokensArray[i] = tokenId;
50 i++;
51 }
52 return identifierTokensArray;
53 }
54
55 private void createValueToTokenIdMap() {
56 var tokenIdToValueMap = tokenDefProvider.getTokenDefMap();
57 valueToTokenIdMap = new HashMap<>(tokenIdToValueMap.size());
58 for (var entry : tokenIdToValueMap.entrySet()) {
59 valueToTokenIdMap.put(entry.getValue(), entry.getKey());
60 }
61 }
62
63 private void collectIdentifierTokensFromRule(AbstractRule rule) {
64 if (rule instanceof TerminalRule) {
65 collectToken("RULE_" + rule.getName());
66 return;
67 }
68 collectIdentifierTokensFromElement(rule.getAlternatives());
69 }
70
71 private void collectIdentifierTokensFromElement(AbstractElement element) {
72 if (element instanceof Alternatives alternatives) {
73 for (var alternative : alternatives.getElements()) {
74 collectIdentifierTokensFromElement(alternative);
75 }
76 } else if (element instanceof RuleCall ruleCall) {
77 collectIdentifierTokensFromRule(ruleCall.getRule());
78 } else if (element instanceof Keyword keyword) {
79 collectToken("'" + keyword.getValue() + "'");
80 } else {
81 throw new IllegalArgumentException("Unknown Xtext grammar element: " + element);
82 }
83 }
84
85 private void collectToken(String value) {
86 identifierTokens.add(valueToTokenIdMap.get(value));
87 }
88 }
89}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/ProblemTokenSource.java b/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/ProblemTokenSource.java
new file mode 100644
index 00000000..0b4e7185
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/ProblemTokenSource.java
@@ -0,0 +1,125 @@
1/*
2 * generated by Xtext 2.29.0.M2
3 */
4package tools.refinery.language.parser.antlr;
5
6import com.google.inject.Inject;
7import org.antlr.runtime.Token;
8import org.antlr.runtime.TokenSource;
9import tools.refinery.language.parser.antlr.internal.InternalProblemParser;
10
11import java.util.ArrayDeque;
12import java.util.Deque;
13
14public class ProblemTokenSource implements TokenSource {
15 private IdentifierTokenProvider identifierTokenProvider;
16
17 private final TokenSource delegate;
18
19 private final Deque<Token> buffer = new ArrayDeque<>();
20
21 private boolean recursive;
22
23 private boolean seenId;
24
25 public ProblemTokenSource(TokenSource delegate) {
26 this.delegate = delegate;
27 }
28
29 @Inject
30 public void setIdentifierTokenProvider(IdentifierTokenProvider identifierTokenProvider) {
31 this.identifierTokenProvider = identifierTokenProvider;
32 }
33
34 public boolean isRecursive() {
35 return recursive;
36 }
37
38 public void setRecursive(boolean recursive) {
39 this.recursive = recursive;
40 }
41
42 @Override
43 public Token nextToken() {
44 if (!buffer.isEmpty()) {
45 return buffer.removeFirst();
46 }
47 var token = delegate.nextToken();
48 if (isIdentifier(token)) {
49 seenId = true;
50 } else if (seenId && isPlusOrTransitiveClosure(token)) {
51 if (peekForTransitiveClosure()) {
52 token.setType(InternalProblemParser.RULE_TRANSITIVE_CLOSURE);
53 }
54 } else if (isVisibleToken(token)) {
55 seenId = false;
56 }
57 return token;
58 }
59
60 @Override
61 public String getSourceName() {
62 return "[%s]%s".formatted(this.getClass().getSimpleName(), delegate.getSourceName());
63 }
64
65 protected boolean isIdentifier(Token token) {
66 return identifierTokenProvider.isIdentifierToken(token.getType());
67 }
68
69 protected boolean isPlusOrTransitiveClosure(Token token) {
70 return token.getType() == InternalProblemParser.PlusSign;
71 }
72
73 protected boolean isVisibleToken(Token token) {
74 int tokenId = token.getType();
75 return tokenId != InternalProblemParser.RULE_WS && tokenId != InternalProblemParser.RULE_SL_COMMENT &&
76 tokenId != InternalProblemParser.RULE_ML_COMMENT;
77 }
78
79 protected boolean peekForTransitiveClosure() {
80 Token token = peekWithSkipWhitespace();
81 if (token.getType() != InternalProblemParser.LeftParenthesis) {
82 return false;
83 }
84 while (true) {
85 token = peekWithSkipWhitespace();
86 if (!isIdentifier(token)) {
87 return false;
88 }
89 token = peekWithSkipWhitespace();
90 switch (token.getType()) {
91 case InternalProblemParser.Comma:
92 return true;
93 case InternalProblemParser.ColonColon:
94 break;
95 default:
96 // By default, we do not peek at inner plus signs to limit recursion depth.
97 // Such expressions are never valid, so we don't have to parse them correctly.
98 if (recursive && isPlusOrTransitiveClosure(token) && peekForTransitiveClosure()) {
99 token.setType(InternalProblemParser.RULE_TRANSITIVE_CLOSURE);
100 }
101 // Not a transitive closure for the initial position where we started peeking.
102 return false;
103 }
104 }
105 }
106
107 protected Token peekToken() {
108 var token = delegate.nextToken();
109 if (isIdentifier(token)) {
110 seenId = true;
111 } else if (isVisibleToken(token)) {
112 seenId = false;
113 }
114 buffer.addLast(token);
115 return token;
116 }
117
118 protected Token peekWithSkipWhitespace() {
119 Token token;
120 do {
121 token = peekToken();
122 } while (token != null && !isVisibleToken(token));
123 return token;
124 }
125}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/TokenSourceInjectingProblemParser.java b/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/TokenSourceInjectingProblemParser.java
new file mode 100644
index 00000000..0cdd38d8
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/parser/antlr/TokenSourceInjectingProblemParser.java
@@ -0,0 +1,18 @@
1package tools.refinery.language.parser.antlr;
2
3import com.google.inject.Inject;
4import com.google.inject.Injector;
5import org.antlr.runtime.CharStream;
6import org.antlr.runtime.TokenSource;
7
8public class TokenSourceInjectingProblemParser extends ProblemParser {
9 @Inject
10 private Injector injector;
11
12 @Override
13 protected TokenSource createLexer(CharStream stream) {
14 var tokenSource = super.createLexer(stream);
15 injector.injectMembers(tokenSource);
16 return tokenSource;
17 }
18}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java b/subprojects/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java
index b76c4bf7..6176b0c4 100644
--- a/subprojects/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java
@@ -1,36 +1,15 @@
1package tools.refinery.language.resource; 1package tools.refinery.language.resource;
2 2
3import java.util.HashSet; 3import com.google.inject.Inject;
4import java.util.List; 4import com.google.inject.Singleton;
5import java.util.Set; 5import com.google.inject.name.Named;
6
7import org.eclipse.xtext.linking.impl.LinkingHelper; 6import org.eclipse.xtext.linking.impl.LinkingHelper;
8import org.eclipse.xtext.naming.IQualifiedNameConverter; 7import org.eclipse.xtext.naming.IQualifiedNameConverter;
9import org.eclipse.xtext.nodemodel.INode;
10import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
11import org.eclipse.xtext.scoping.IScope;
12import org.eclipse.xtext.scoping.IScopeProvider; 8import org.eclipse.xtext.scoping.IScopeProvider;
13import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider; 9import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
10import tools.refinery.language.model.problem.*;
14 11
15import com.google.inject.Inject; 12import java.util.*;
16import com.google.inject.Singleton;
17import com.google.inject.name.Named;
18
19import tools.refinery.language.model.problem.Argument;
20import tools.refinery.language.model.problem.Atom;
21import tools.refinery.language.model.problem.CompoundLiteral;
22import tools.refinery.language.model.problem.Conjunction;
23import tools.refinery.language.model.problem.ExistentialQuantifier;
24import tools.refinery.language.model.problem.ImplicitVariable;
25import tools.refinery.language.model.problem.Literal;
26import tools.refinery.language.model.problem.Parameter;
27import tools.refinery.language.model.problem.ParametricDefinition;
28import tools.refinery.language.model.problem.Problem;
29import tools.refinery.language.model.problem.ProblemFactory;
30import tools.refinery.language.model.problem.ProblemPackage;
31import tools.refinery.language.model.problem.Statement;
32import tools.refinery.language.model.problem.VariableOrNodeArgument;
33import tools.refinery.language.naming.NamingUtil;
34 13
35@Singleton 14@Singleton
36public class DerivedVariableComputer { 15public class DerivedVariableComputer {
@@ -53,107 +32,60 @@ public class DerivedVariableComputer {
53 } 32 }
54 33
55 protected void installDerivedParametricDefinitionState(ParametricDefinition definition, Set<String> nodeNames) { 34 protected void installDerivedParametricDefinitionState(ParametricDefinition definition, Set<String> nodeNames) {
56 Set<String> knownVariables = new HashSet<>(); 35 Set<String> knownVariables = new HashSet<>(nodeNames);
57 knownVariables.addAll(nodeNames);
58 for (Parameter parameter : definition.getParameters()) { 36 for (Parameter parameter : definition.getParameters()) {
59 String name = parameter.getName(); 37 String name = parameter.getName();
60 if (name != null) { 38 if (name != null) {
61 knownVariables.add(name); 39 knownVariables.add(name);
62 } 40 }
63 } 41 }
64 for (Conjunction body : definition.getBodies()) { 42 if (definition instanceof PredicateDefinition predicateDefinition) {
65 installDeriveConjunctionState(body, knownVariables); 43 installDerivedPredicateDefinitionState(predicateDefinition, knownVariables);
44 } else if (definition instanceof FunctionDefinition functionDefinition) {
45 installDerivedFunctionDefinitionState(functionDefinition, knownVariables);
46 } else if (definition instanceof RuleDefinition ruleDefinition) {
47 installDerivedRuleDefinitionState(ruleDefinition, knownVariables);
48 } else {
49 throw new IllegalArgumentException("Unknown ParametricDefinition: " + definition);
66 } 50 }
67 } 51 }
68 52
69 protected void installDeriveConjunctionState(Conjunction conjunction, Set<String> knownVariables) { 53 protected void installDerivedPredicateDefinitionState(PredicateDefinition definition, Set<String> knownVariables) {
70 Set<String> newVariables = new HashSet<>(); 54 for (Conjunction body : definition.getBodies()) {
71 for (Literal literal : conjunction.getLiterals()) { 55 createVariablesForScope(new ImplicitVariableScope(body, knownVariables));
72 if (literal instanceof Atom atom) {
73 createSigletonVariablesAndCollectVariables(atom, knownVariables, newVariables);
74 }
75 }
76 createVariables(conjunction, newVariables);
77 newVariables.addAll(knownVariables);
78 for (Literal literal : conjunction.getLiterals()) {
79 if (literal instanceof CompoundLiteral compoundLiteral) {
80 installDerivedCompoundLiteralState(compoundLiteral, newVariables);
81 }
82 } 56 }
83 } 57 }
84 58
85 protected void installDerivedCompoundLiteralState(CompoundLiteral compoundLiteral, Set<String> knownVariables) { 59 protected void installDerivedFunctionDefinitionState(FunctionDefinition definition, Set<String> knownVariables) {
86 Set<String> newVariables = new HashSet<>(); 60 for (Case body : definition.getCases()) {
87 createSigletonVariablesAndCollectVariables(compoundLiteral.getAtom(), knownVariables, newVariables); 61 if (body instanceof Conjunction conjunction) {
88 createVariables(compoundLiteral, newVariables); 62 createVariablesForScope(new ImplicitVariableScope(conjunction, knownVariables));
89 } 63 } else if (body instanceof Match match) {
90 64 var condition = match.getCondition();
91 protected void createSigletonVariablesAndCollectVariables(Atom atom, Set<String> knownVariables, 65 if (condition != null) {
92 Set<String> newVariables) { 66 createVariablesForScope(new ImplicitVariableScope(match, match.getCondition(), knownVariables));
93 for (Argument argument : atom.getArguments()) {
94 if (argument instanceof VariableOrNodeArgument variableOrNodeArgument) {
95 IScope scope = scopeProvider.getScope(variableOrNodeArgument,
96 ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE);
97 List<INode> nodes = NodeModelUtils.findNodesForFeature(variableOrNodeArgument,
98 ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE);
99 for (INode node : nodes) {
100 var variableName = linkingHelper.getCrossRefNodeAsString(node, true);
101 var created = tryCreateVariableForArgument(variableOrNodeArgument, variableName, scope,
102 knownVariables, newVariables);
103 if (created) {
104 break;
105 }
106 } 67 }
68 } else {
69 throw new IllegalArgumentException("Unknown Case: " + body);
107 } 70 }
108 } 71 }
109 } 72 }
110 73
111 protected boolean tryCreateVariableForArgument(VariableOrNodeArgument variableOrNodeArgument, String variableName, 74 protected void installDerivedRuleDefinitionState(RuleDefinition definition, Set<String> knownVariables) {
112 IScope scope, Set<String> knownVariables, Set<String> newVariables) { 75 for (Conjunction precondition : definition.getPreconditions()) {
113 if (!NamingUtil.isValidId(variableName)) { 76 createVariablesForScope(new ImplicitVariableScope(precondition, knownVariables));
114 return false;
115 }
116 var qualifiedName = qualifiedNameConverter.toQualifiedName(variableName);
117 if (scope.getSingleElement(qualifiedName) != null) {
118 return false;
119 }
120 if (NamingUtil.isSingletonVariableName(variableName)) {
121 createSingletonVariable(variableOrNodeArgument, variableName);
122 return true;
123 }
124 if (!knownVariables.contains(variableName)) {
125 newVariables.add(variableName);
126 return true;
127 }
128 return false;
129 }
130
131 protected void createVariables(ExistentialQuantifier quantifier, Set<String> newVariables) {
132 for (String variableName : newVariables) {
133 createVariable(quantifier, variableName);
134 }
135 }
136
137 protected void createVariable(ExistentialQuantifier quantifier, String variableName) {
138 if (NamingUtil.isValidId(variableName)) {
139 ImplicitVariable variable = createNamedVariable(variableName);
140 quantifier.getImplicitVariables().add(variable);
141 } 77 }
142 } 78 }
143 79
144 protected void createSingletonVariable(VariableOrNodeArgument argument, String variableName) { 80 protected void createVariablesForScope(ImplicitVariableScope scope) {
145 if (NamingUtil.isValidId(variableName)) { 81 var queue = new ArrayDeque<ImplicitVariableScope>();
146 ImplicitVariable variable = createNamedVariable(variableName); 82 queue.addLast(scope);
147 argument.setSingletonVariable(variable); 83 while (!queue.isEmpty()) {
84 var nextScope = queue.removeFirst();
85 nextScope.createVariables(scopeProvider, linkingHelper, qualifiedNameConverter, queue);
148 } 86 }
149 } 87 }
150 88
151 protected ImplicitVariable createNamedVariable(String variableName) {
152 var variable = ProblemFactory.eINSTANCE.createImplicitVariable();
153 variable.setName(variableName);
154 return variable;
155 }
156
157 public void discardDerivedVariables(Problem problem) { 89 public void discardDerivedVariables(Problem problem) {
158 for (Statement statement : problem.getStatements()) { 90 for (Statement statement : problem.getStatements()) {
159 if (statement instanceof ParametricDefinition parametricDefinition) { 91 if (statement instanceof ParametricDefinition parametricDefinition) {
@@ -163,28 +95,31 @@ public class DerivedVariableComputer {
163 } 95 }
164 96
165 protected void discardParametricDefinitionState(ParametricDefinition definition) { 97 protected void discardParametricDefinitionState(ParametricDefinition definition) {
166 for (Conjunction body : definition.getBodies()) { 98 List<ExistentialQuantifier> existentialQuantifiers = new ArrayList<>();
167 body.getImplicitVariables().clear(); 99 List<VariableOrNodeExpr> variableOrNodeExprs = new ArrayList<>();
168 for (Literal literal : body.getLiterals()) { 100 var treeIterator = definition.eAllContents();
169 if (literal instanceof Atom atom) { 101 // We must collect the nodes where we are discarding derived state and only discard them after the iteration,
170 discardDerivedAtomState(atom); 102 // because modifying the containment hierarchy during iteration causes the TreeIterator to fail with
171 } 103 // IndexOutOfBoundsException.
172 if (literal instanceof CompoundLiteral compoundLiteral) { 104 while (treeIterator.hasNext()) {
173 compoundLiteral.getImplicitVariables().clear(); 105 var child = treeIterator.next();
174 discardDerivedAtomState(compoundLiteral.getAtom()); 106 var containingFeature = child.eContainingFeature();
175 } 107 if (containingFeature == ProblemPackage.Literals.EXISTENTIAL_QUANTIFIER__IMPLICIT_VARIABLES ||
108 containingFeature == ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__SINGLETON_VARIABLE) {
109 treeIterator.prune();
110 } else if (child instanceof ExistentialQuantifier existentialQuantifier &&
111 !existentialQuantifier.getImplicitVariables().isEmpty()) {
112 existentialQuantifiers.add(existentialQuantifier);
113 } else if (child instanceof VariableOrNodeExpr variableOrNodeExpr &&
114 variableOrNodeExpr.getSingletonVariable() != null) {
115 variableOrNodeExprs.add(variableOrNodeExpr);
176 } 116 }
177 } 117 }
178 } 118 for (var existentialQuantifier : existentialQuantifiers) {
179 119 existentialQuantifier.getImplicitVariables().clear();
180 protected void discardDerivedAtomState(Atom atom) {
181 if (atom == null) {
182 return;
183 } 120 }
184 for (Argument argument : atom.getArguments()) { 121 for (var variableOrNodeExpr : variableOrNodeExprs) {
185 if (argument instanceof VariableOrNodeArgument variableOrNodeArgument) { 122 variableOrNodeExpr.setSingletonVariable(null);
186 variableOrNodeArgument.setSingletonVariable(null);
187 }
188 } 123 }
189 } 124 }
190} 125}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ImplicitVariableScope.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ImplicitVariableScope.java
new file mode 100644
index 00000000..b0ac2ab6
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ImplicitVariableScope.java
@@ -0,0 +1,138 @@
1package tools.refinery.language.resource;
2
3import org.eclipse.emf.ecore.EObject;
4import org.eclipse.xtext.linking.impl.LinkingHelper;
5import org.eclipse.xtext.naming.IQualifiedNameConverter;
6import org.eclipse.xtext.naming.QualifiedName;
7import org.eclipse.xtext.nodemodel.INode;
8import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
9import org.eclipse.xtext.scoping.IScope;
10import org.eclipse.xtext.scoping.IScopeProvider;
11import tools.refinery.language.model.problem.*;
12import tools.refinery.language.naming.NamingUtil;
13
14import java.util.Deque;
15import java.util.HashSet;
16import java.util.List;
17import java.util.Set;
18
19public class ImplicitVariableScope {
20 private final EObject root;
21
22 private final ExistentialQuantifier quantifier;
23
24 private final ImplicitVariableScope parent;
25
26 private Set<String> knownVariables;
27
28 private ImplicitVariableScope(ExistentialQuantifier quantifier, ImplicitVariableScope parent) {
29 this.root = quantifier;
30 this.quantifier = quantifier;
31 this.parent = parent;
32 this.knownVariables = null;
33 }
34
35 public ImplicitVariableScope(EObject root, ExistentialQuantifier quantifier, Set<String> knownVariables) {
36 this.root = root;
37 this.quantifier = quantifier;
38 this.parent = null;
39 this.knownVariables = new HashSet<>(knownVariables);
40 }
41
42 public ImplicitVariableScope(ExistentialQuantifier root, Set<String> knownVariables) {
43 this(root, root, knownVariables);
44 }
45
46 public void createVariables(IScopeProvider scopeProvider, LinkingHelper linkingHelper,
47 IQualifiedNameConverter qualifiedNameConverter,
48 Deque<ImplicitVariableScope> scopeQueue) {
49 initializeKnownVariables();
50 processEObject(root, scopeProvider, linkingHelper, qualifiedNameConverter);
51 var treeIterator = root.eAllContents();
52 while (treeIterator.hasNext()) {
53 var child = treeIterator.next();
54 if (child instanceof ExistentialQuantifier nestedQuantifier) {
55 scopeQueue.addLast(new ImplicitVariableScope(nestedQuantifier, this));
56 treeIterator.prune();
57 } else {
58 processEObject(child, scopeProvider, linkingHelper, qualifiedNameConverter);
59 }
60 }
61 }
62
63 private void initializeKnownVariables() {
64 boolean hasKnownVariables = knownVariables != null;
65 boolean hasParent = parent != null;
66 if ((hasKnownVariables && hasParent) || (!hasKnownVariables && !hasParent)) {
67 throw new IllegalStateException("Either known variables or parent must be provided, but not both");
68 }
69 if (hasKnownVariables) {
70 return;
71 }
72 if (parent.knownVariables == null) {
73 throw new IllegalStateException("Parent scope must be processed before current scope");
74 }
75 knownVariables = new HashSet<>(parent.knownVariables);
76 }
77
78 private void processEObject(EObject eObject, IScopeProvider scopeProvider, LinkingHelper linkingHelper,
79 IQualifiedNameConverter qualifiedNameConverter) {
80 if (!(eObject instanceof VariableOrNodeExpr variableOrNodeExpr)) {
81 return;
82 }
83 IScope scope = scopeProvider.getScope(variableOrNodeExpr,
84 ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE);
85 List<INode> nodes = NodeModelUtils.findNodesForFeature(variableOrNodeExpr,
86 ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE);
87 for (INode node : nodes) {
88 var variableName = linkingHelper.getCrossRefNodeAsString(node, true);
89 var created = tryCreateVariableForArgument(variableOrNodeExpr, variableName, qualifiedNameConverter,
90 scope);
91 if (created) {
92 break;
93 }
94 }
95 }
96
97 protected boolean tryCreateVariableForArgument(VariableOrNodeExpr variableOrNodeExpr, String variableName,
98 IQualifiedNameConverter qualifiedNameConverter, IScope scope) {
99 if (!NamingUtil.isValidId(variableName)) {
100 return false;
101 }
102 QualifiedName qualifiedName;
103 try {
104 qualifiedName = qualifiedNameConverter.toQualifiedName(variableName);
105 } catch (IllegalArgumentException e) {
106 return false;
107 }
108 if (scope.getSingleElement(qualifiedName) != null) {
109 return false;
110 }
111 if (NamingUtil.isSingletonVariableName(variableName)) {
112 createSingletonVariable(variableOrNodeExpr, variableName);
113 return true;
114 }
115 if (!knownVariables.contains(variableName)) {
116 createVariable(variableName);
117 return true;
118 }
119 return false;
120 }
121
122 protected void createVariable(String variableName) {
123 knownVariables.add(variableName);
124 ImplicitVariable variable = createNamedVariable(variableName);
125 quantifier.getImplicitVariables().add(variable);
126 }
127
128 protected void createSingletonVariable(VariableOrNodeExpr variableOrNodeExpr, String variableName) {
129 ImplicitVariable variable = createNamedVariable(variableName);
130 variableOrNodeExpr.setSingletonVariable(variable);
131 }
132
133 protected ImplicitVariable createNamedVariable(String variableName) {
134 var variable = ProblemFactory.eINSTANCE.createImplicitVariable();
135 variable.setName(variableName);
136 return variable;
137 }
138}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemXmiResourceFactory.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemXmiResourceFactory.java
deleted file mode 100644
index 68aa6016..00000000
--- a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemXmiResourceFactory.java
+++ /dev/null
@@ -1,16 +0,0 @@
1package tools.refinery.language.resource;
2
3import org.eclipse.emf.common.util.URI;
4import org.eclipse.emf.ecore.resource.Resource;
5import org.eclipse.xtext.resource.IResourceFactory;
6
7import tools.refinery.language.model.problem.util.ProblemResourceFactoryImpl;
8
9public class ProblemXmiResourceFactory implements IResourceFactory {
10 private Resource.Factory problemResourceFactory = new ProblemResourceFactoryImpl();
11
12 @Override
13 public Resource createResource(URI uri) {
14 return problemResourceFactory.createResource(uri);
15 }
16}
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 567c3c26..c2045aea 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
@@ -3,34 +3,23 @@
3 */ 3 */
4package tools.refinery.language.scoping; 4package tools.refinery.language.scoping;
5 5
6import java.util.ArrayList; 6import com.google.inject.Inject;
7import java.util.List;
8
9import org.eclipse.emf.ecore.EObject; 7import org.eclipse.emf.ecore.EObject;
10import org.eclipse.emf.ecore.EReference; 8import org.eclipse.emf.ecore.EReference;
11import org.eclipse.xtext.EcoreUtil2; 9import org.eclipse.xtext.EcoreUtil2;
12import org.eclipse.xtext.scoping.IScope; 10import org.eclipse.xtext.scoping.IScope;
13import org.eclipse.xtext.scoping.Scopes; 11import org.eclipse.xtext.scoping.Scopes;
14 12import tools.refinery.language.model.problem.*;
15import com.google.inject.Inject;
16
17import tools.refinery.language.model.problem.ClassDeclaration;
18import tools.refinery.language.model.problem.Consequent;
19import tools.refinery.language.model.problem.ExistentialQuantifier;
20import tools.refinery.language.model.problem.NewAction;
21import tools.refinery.language.model.problem.ParametricDefinition;
22import tools.refinery.language.model.problem.Problem;
23import tools.refinery.language.model.problem.ProblemPackage;
24import tools.refinery.language.model.problem.ReferenceDeclaration;
25import tools.refinery.language.model.problem.Variable;
26import tools.refinery.language.model.problem.VariableOrNodeArgument;
27import tools.refinery.language.utils.ProblemDesugarer; 13import tools.refinery.language.utils.ProblemDesugarer;
28 14
15import java.util.ArrayList;
16import java.util.List;
17
29/** 18/**
30 * This class contains custom scoping description. 19 * This class contains custom scoping description.
31 * 20 * <p>
32 * See 21 * See
33 * https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#scoping 22 * <a href="https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#scoping">...</a>
34 * on how and when to use it. 23 * on how and when to use it.
35 */ 24 */
36public class ProblemScopeProvider extends AbstractProblemScopeProvider { 25public class ProblemScopeProvider extends AbstractProblemScopeProvider {
@@ -44,7 +33,7 @@ public class ProblemScopeProvider extends AbstractProblemScopeProvider {
44 || reference == ProblemPackage.Literals.NODE_VALUE_ASSERTION__NODE) { 33 || reference == ProblemPackage.Literals.NODE_VALUE_ASSERTION__NODE) {
45 return getNodesScope(context, scope); 34 return getNodesScope(context, scope);
46 } 35 }
47 if (reference == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE 36 if (reference == ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE
48 || reference == ProblemPackage.Literals.NEW_ACTION__PARENT 37 || reference == ProblemPackage.Literals.NEW_ACTION__PARENT
49 || reference == ProblemPackage.Literals.DELETE_ACTION__VARIABLE_OR_NODE) { 38 || reference == ProblemPackage.Literals.DELETE_ACTION__VARIABLE_OR_NODE) {
50 return getVariableScope(context, scope); 39 return getVariableScope(context, scope);
@@ -80,8 +69,8 @@ public class ProblemScopeProvider extends AbstractProblemScopeProvider {
80 } 69 }
81 70
82 protected void addSingletonVariableToScope(EObject context, List<Variable> variables) { 71 protected void addSingletonVariableToScope(EObject context, List<Variable> variables) {
83 if (context instanceof VariableOrNodeArgument argument) { 72 if (context instanceof VariableOrNodeExpr expr) {
84 Variable singletonVariable = argument.getSingletonVariable(); 73 Variable singletonVariable = expr.getSingletonVariable();
85 if (singletonVariable != null) { 74 if (singletonVariable != null) {
86 variables.add(singletonVariable); 75 variables.add(singletonVariable);
87 } 76 }
@@ -91,6 +80,8 @@ public class ProblemScopeProvider extends AbstractProblemScopeProvider {
91 protected void addExistentiallyQualifiedVariableToScope(EObject currentContext, List<Variable> variables) { 80 protected void addExistentiallyQualifiedVariableToScope(EObject currentContext, List<Variable> variables) {
92 if (currentContext instanceof ExistentialQuantifier quantifier) { 81 if (currentContext instanceof ExistentialQuantifier quantifier) {
93 variables.addAll(quantifier.getImplicitVariables()); 82 variables.addAll(quantifier.getImplicitVariables());
83 } else if (currentContext instanceof Match match) {
84 variables.addAll(match.getCondition().getImplicitVariables());
94 } else if (currentContext instanceof Consequent consequent) { 85 } else if (currentContext instanceof Consequent consequent) {
95 for (var literal : consequent.getActions()) { 86 for (var literal : consequent.getActions()) {
96 if (literal instanceof NewAction newAction && newAction.getVariable() != null) { 87 if (literal instanceof NewAction newAction && newAction.getVariable() != null) {
@@ -106,10 +97,9 @@ public class ProblemScopeProvider extends AbstractProblemScopeProvider {
106 return delegateScope; 97 return delegateScope;
107 } 98 }
108 var relation = referenceDeclaration.getReferenceType(); 99 var relation = referenceDeclaration.getReferenceType();
109 if (!(relation instanceof ClassDeclaration)) { 100 if (!(relation instanceof ClassDeclaration classDeclaration)) {
110 return delegateScope; 101 return delegateScope;
111 } 102 }
112 var classDeclaration = (ClassDeclaration) relation;
113 var referenceDeclarations = desugarer.getAllReferenceDeclarations(classDeclaration); 103 var referenceDeclarations = desugarer.getAllReferenceDeclarations(classDeclaration);
114 return Scopes.scopeFor(referenceDeclarations, delegateScope); 104 return Scopes.scopeFor(referenceDeclarations, delegateScope);
115 } 105 }
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 a7acd747..1e5164d3 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
@@ -44,7 +44,7 @@ public final class ProblemUtil {
44 } 44 }
45 45
46 public static boolean isSingletonVariable(Variable variable) { 46 public static boolean isSingletonVariable(Variable variable) {
47 return variable.eContainingFeature() == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__SINGLETON_VARIABLE; 47 return variable.eContainingFeature() == ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__SINGLETON_VARIABLE;
48 } 48 }
49 49
50 public static boolean isImplicitVariable(Variable variable) { 50 public static boolean isImplicitVariable(Variable variable) {
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 a0e78e1d..659d882c 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
@@ -8,19 +8,15 @@ import org.eclipse.xtext.validation.Check;
8 8
9import com.google.inject.Inject; 9import com.google.inject.Inject;
10 10
11import tools.refinery.language.model.problem.Node; 11import tools.refinery.language.model.problem.*;
12import tools.refinery.language.model.problem.Problem;
13import tools.refinery.language.model.problem.ProblemPackage;
14import tools.refinery.language.model.problem.Variable;
15import tools.refinery.language.model.problem.VariableOrNodeArgument;
16import tools.refinery.language.resource.ReferenceCounter; 12import tools.refinery.language.resource.ReferenceCounter;
17import tools.refinery.language.utils.ProblemUtil; 13import tools.refinery.language.utils.ProblemUtil;
18 14
19/** 15/**
20 * This class contains custom validation rules. 16 * This class contains custom validation rules.
21 * 17 * <p>
22 * See 18 * See
23 * https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation 19 * <a href="https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation">...</a>
24 */ 20 */
25public class ProblemValidator extends AbstractProblemValidator { 21public class ProblemValidator extends AbstractProblemValidator {
26 private static final String ISSUE_PREFIX = "tools.refinery.language.validation.ProblemValidator."; 22 private static final String ISSUE_PREFIX = "tools.refinery.language.validation.ProblemValidator.";
@@ -33,29 +29,29 @@ public class ProblemValidator extends AbstractProblemValidator {
33 private ReferenceCounter referenceCounter; 29 private ReferenceCounter referenceCounter;
34 30
35 @Check 31 @Check
36 public void checkUniqueVariable(VariableOrNodeArgument argument) { 32 public void checkUniqueVariable(VariableOrNodeExpr expr) {
37 var variableOrNode = argument.getVariableOrNode(); 33 var variableOrNode = expr.getVariableOrNode();
38 if (variableOrNode instanceof Variable variable && ProblemUtil.isImplicitVariable(variable) 34 if (variableOrNode instanceof Variable variable && ProblemUtil.isImplicitVariable(variable)
39 && !ProblemUtil.isSingletonVariable(variable)) { 35 && !ProblemUtil.isSingletonVariable(variable)) {
40 var problem = EcoreUtil2.getContainerOfType(variable, Problem.class); 36 var problem = EcoreUtil2.getContainerOfType(variable, Problem.class);
41 if (problem != null && referenceCounter.countReferences(problem, variable) <= 1) { 37 if (problem != null && referenceCounter.countReferences(problem, variable) <= 1) {
42 var name = variable.getName(); 38 var name = variable.getName();
43 var message = "Variable '%s' has only a single reference. Add another reference or mark is as a singleton variable: '_%s'" 39 var message = ("Variable '%s' has only a single reference. " +
44 .formatted(name, name); 40 "Add another reference or mark is as a singleton variable: '_%s'").formatted(name, name);
45 warning(message, argument, ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE, 41 warning(message, expr, ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE,
46 INSIGNIFICANT_INDEX, SINGLETON_VARIABLE_ISSUE); 42 INSIGNIFICANT_INDEX, SINGLETON_VARIABLE_ISSUE);
47 } 43 }
48 } 44 }
49 } 45 }
50 46
51 @Check 47 @Check
52 public void checkNonUniqueNode(VariableOrNodeArgument argument) { 48 public void checkNonUniqueNode(VariableOrNodeExpr expr) {
53 var variableOrNode = argument.getVariableOrNode(); 49 var variableOrNode = expr.getVariableOrNode();
54 if (variableOrNode instanceof Node node && !ProblemUtil.isIndividualNode(node)) { 50 if (variableOrNode instanceof Node node && !ProblemUtil.isIndividualNode(node)) {
55 var name = node.getName(); 51 var name = node.getName();
56 var message = "Only individual nodes can be referenced in predicates. Mark '%s' as individual with the declaration 'indiv %s.'" 52 var message = ("Only individual nodes can be referenced in predicates. " +
57 .formatted(name, name); 53 "Mark '%s' as individual with the declaration 'indiv %s.'").formatted(name, name);
58 error(message, argument, ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE, 54 error(message, expr, ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE,
59 INSIGNIFICANT_INDEX, NON_INDIVIDUAL_NODE_ISSUE); 55 INSIGNIFICANT_INDEX, NON_INDIVIDUAL_NODE_ISSUE);
60 } 56 }
61 } 57 }