aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language/src/main/java
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-12 17:48:47 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2021-12-12 17:48:47 +0100
commitfc7e9312d00e60171ed77c477ed91231d3dbfff9 (patch)
treecc185dd088b5fa6e9357aab3c9062a70626d1953 /subprojects/language/src/main/java
parentbuild: refactor java-application conventions (diff)
downloadrefinery-fc7e9312d00e60171ed77c477ed91231d3dbfff9.tar.gz
refinery-fc7e9312d00e60171ed77c477ed91231d3dbfff9.tar.zst
refinery-fc7e9312d00e60171ed77c477ed91231d3dbfff9.zip
build: move modules into subproject directory
Diffstat (limited to 'subprojects/language/src/main/java')
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/GenerateProblem.mwe268
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/Problem.xtext205
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java84
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/ProblemStandaloneSetup.java44
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/ProblemXmiRuntimeModule.java35
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/conversion/ProblemValueConverterService.java19
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/conversion/UpperBoundValueConverter.java35
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java183
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java25
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java15
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java194
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java88
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java163
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java33
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java103
-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/resource/ReferenceCounter.java51
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java18
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java42
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java104
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java62
21 files changed, 1587 insertions, 0 deletions
diff --git a/subprojects/language/src/main/java/tools/refinery/language/GenerateProblem.mwe2 b/subprojects/language/src/main/java/tools/refinery/language/GenerateProblem.mwe2
new file mode 100644
index 00000000..21ff456e
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/GenerateProblem.mwe2
@@ -0,0 +1,68 @@
1module tools.refinery.language.GenerateProblem
2
3import org.eclipse.xtext.xtext.generator.*
4import org.eclipse.xtext.xtext.generator.model.project.*
5
6var rootPath = '..'
7
8Workflow {
9 component = XtextGenerator {
10 configuration = {
11 project = StandardProjectConfig {
12 baseName = 'language'
13 rootPath = rootPath
14 runtimeTest = {
15 enabled = true
16 srcGen = 'src/testFixtures/xtext-gen'
17 }
18 genericIde = {
19 name = 'language-ide'
20 }
21 web = {
22 enabled = true
23 name = 'language-web'
24 }
25 mavenLayout = true
26 }
27 code = {
28 encoding = 'UTF-8'
29 lineDelimiter = '\n'
30 fileHeader = '/*\n * generated by Xtext \${version}\n */'
31 preferXtendStubs = false
32 }
33 }
34
35 language = StandardLanguage {
36 name = 'tools.refinery.language.Problem'
37 fileExtensions = 'problem'
38 referencedResource = 'platform:/resource/tools.refinery.refinery-language-model/model/problem.genmodel'
39 serializer = {
40 generateStub = false
41 }
42 formatter = {
43 generateStub = true
44 }
45 validator = {
46 generateDeprecationValidation = true
47 }
48 generator = {
49 generateStub = false
50 }
51 junitSupport = {
52 generateStub = false
53 skipXbaseTestingPackage = true
54 junitVersion = '5'
55 }
56 webSupport = {
57 // We only generate the {@code AbstractProblemWebModule},
58 // because we write our own integration code for CodeMirror 6.
59 framework = 'codemirror'
60 generateHtmlExample = false
61 generateJettyLauncher = false
62 generateJsHighlighting = false
63 generateServlet = false
64 generateWebXml = false
65 }
66 }
67 }
68}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
new file mode 100644
index 00000000..c94d40ab
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext
@@ -0,0 +1,205 @@
1grammar tools.refinery.language.Problem with org.eclipse.xtext.common.Terminals
2
3import "http://www.eclipse.org/emf/2002/Ecore" as ecore
4import "https://refinery.tools/emf/2021/Problem"
5
6Problem:
7 ("problem" name=Identifier ".")?
8 statements+=Statement*;
9
10Statement:
11 ClassDeclaration | EnumDeclaration | PredicateDefinition | RuleDefinition | Assertion | NodeValueAssertion |
12 ScopeDeclaration |
13 IndividualDeclaration;
14
15ClassDeclaration:
16 abstract?="abstract"? "class"
17 name=Identifier
18 ("extends" superTypes+=[Relation|QualifiedName] ("," superTypes+=[Relation|QualifiedName])*)?
19 ("{" (referenceDeclarations+=ReferenceDeclaration ";"?)* "}" | ".");
20
21EnumDeclaration:
22 "enum"
23 name=Identifier
24 ("{" (literals+=EnumLiteral ("," literals+=EnumLiteral)* ("," | ";")?)? "}" | ".");
25
26EnumLiteral returns Node:
27 name=Identifier;
28
29ReferenceDeclaration:
30 (containment?="contains" | "refers")?
31 referenceType=[Relation|QualifiedName]
32 ("[" multiplicity=Multiplicity "]")?
33 name=Identifier
34 ("opposite" opposite=[ReferenceDeclaration|QualifiedName])?;
35
36enum PredicateKind:
37 DIRECT="direct";
38
39PredicateDefinition:
40 (error?="error" "pred"? | kind=PredicateKind? "pred")
41 name=Identifier
42 "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")"
43 ("<->" bodies+=Conjunction (";" bodies+=Conjunction)*)?
44 ".";
45
46enum RuleKind:
47 DIRECT="direct";
48
49RuleDefinition:
50 kind=RuleKind "rule"
51 name=Identifier
52 "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")"
53 (":" bodies+=Conjunction (";" bodies+=Conjunction)*
54 "~>" action=Action)?
55 ".";
56
57Parameter:
58 parameterType=[Relation|QualifiedName]? name=Identifier;
59
60Conjunction:
61 literals+=Literal ("," literals+=Literal)*;
62
63Action:
64 actionLiterals+=ActionLiteral ("," actionLiterals+=ActionLiteral)*;
65
66Literal:
67 Atom | ValueLiteral | NegativeLiteral;
68
69ValueLiteral:
70 atom=Atom
71 (refinement?=":" | "=")
72 values+=LogicConstant ("|" values+=LogicConstant)*;
73
74NegativeLiteral:
75 "!" atom=Atom;
76
77ActionLiteral:
78 ValueActionLiteral | DeleteActionLiteral | NewActionLiteral;
79
80ValueActionLiteral:
81 atom=Atom
82 (refinement?=":" | "=")
83 value=LogicValue;
84
85DeleteActionLiteral:
86 "delete" variableOrNode=[VariableOrNode|QualifiedName];
87
88NewActionLiteral:
89 "new" variable=NewVariable;
90
91NewVariable:
92 name=Identifier;
93
94Atom:
95 relation=[Relation|QualifiedName]
96 transitiveClosure?="+"?
97 "(" (arguments+=Argument ("," arguments+=Argument)*)? ")";
98
99LogicConstant:
100 value=LogicValue;
101
102Argument:
103 VariableOrNodeArgument | ConstantArgument;
104
105VariableOrNodeArgument:
106 variableOrNode=[VariableOrNode|QualifiedName];
107
108ConstantArgument:
109 constant=Constant;
110
111Assertion:
112 default?="default"?
113 (value=ShortLogicValue?
114 relation=[Relation|QualifiedName]
115 "(" (arguments+=AssertionArgument ("," arguments+=AssertionArgument)*)? ")"
116 | relation=[Relation|QualifiedName]
117 "(" (arguments+=AssertionArgument ("," arguments+=AssertionArgument)*)? ")"
118 ":" value=LogicValue)
119 ".";
120
121AssertionArgument:
122 NodeAssertionArgument | WildcardAssertionArgument | ConstantAssertionArgument;
123
124NodeAssertionArgument:
125 node=[Node|QualifiedName];
126
127WildcardAssertionArgument:
128 {WildcardAssertionArgument} "*";
129
130ConstantAssertionArgument:
131 constant=Constant;
132
133enum LogicValue:
134 TRUE="true" | FALSE="false" | UNKNOWN="unknown" | ERROR="error";
135
136enum ShortLogicValue returns LogicValue:
137 FALSE="!" | UNKNOWN="?";
138
139NodeValueAssertion:
140 node=[Node|QualifiedName] ":" value=Constant ".";
141
142Constant:
143 RealConstant | IntConstant | StringConstant;
144
145IntConstant:
146 intValue=Integer;
147
148RealConstant:
149 realValue=Real;
150
151StringConstant:
152 stringValue=STRING;
153
154ScopeDeclaration:
155 "scope" typeScopes+=TypeScope ("," typeScopes+=TypeScope)* ".";
156
157TypeScope:
158 targetType=[ClassDeclaration|QualifiedName]
159 (increment?="+=" | "=")
160 multiplicity=DefiniteMultiplicity;
161
162Multiplicity:
163 UnboundedMultiplicity | DefiniteMultiplicity;
164
165DefiniteMultiplicity returns Multiplicity:
166 RangeMultiplicity | ExactMultiplicity;
167
168UnboundedMultiplicity:
169 {UnboundedMultiplicity};
170
171RangeMultiplicity:
172 lowerBound=INT ".." upperBound=UpperBound;
173
174ExactMultiplicity:
175 exactValue=INT;
176
177IndividualDeclaration:
178 "indiv" nodes+=EnumLiteral ("," nodes+=EnumLiteral)* ".";
179
180UpperBound returns ecore::EInt:
181 INT | "*";
182
183QualifiedName hidden():
184 Identifier ("::" Identifier)*;
185
186Identifier:
187 ID | "true" | "false" | "unknown" | "error" | "class" | "abstract" | "extends" | "enum" | "pred" |
188 "indiv" | "problem" | "new" | "delete" | "direct" | "rule";
189
190Integer returns ecore::EInt hidden():
191 "-"? INT;
192
193Real returns ecore::EDouble:
194 "-"? (EXPONENTIAL | INT "." (INT | EXPONENTIAL));
195
196@Override
197terminal ID:
198 ('a'..'z' | 'A'..'Z' | '_') ('a'..'z' | 'A'..'Z' | '_' | '0'..'9')*;
199
200terminal EXPONENTIAL:
201 INT ("e" | "E") ("+" | "-")? INT;
202
203@Override
204terminal SL_COMMENT:
205 ('%' | '//') !('\n' | '\r')* ('\r'? '\n')?;
diff --git a/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java b/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java
new file mode 100644
index 00000000..dd7731b4
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/ProblemRuntimeModule.java
@@ -0,0 +1,84 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language;
5
6import org.eclipse.xtext.conversion.IValueConverterService;
7import org.eclipse.xtext.naming.IQualifiedNameConverter;
8import org.eclipse.xtext.resource.DerivedStateAwareResource;
9import org.eclipse.xtext.resource.DerivedStateAwareResourceDescriptionManager;
10import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy;
11import org.eclipse.xtext.resource.IDerivedStateComputer;
12import org.eclipse.xtext.resource.ILocationInFileProvider;
13import org.eclipse.xtext.resource.IResourceDescription;
14import org.eclipse.xtext.resource.XtextResource;
15import org.eclipse.xtext.scoping.IGlobalScopeProvider;
16import org.eclipse.xtext.scoping.IScopeProvider;
17import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
18import org.eclipse.xtext.validation.IResourceValidator;
19import org.eclipse.xtext.xbase.annotations.validation.DerivedStateAwareResourceValidator;
20
21import com.google.inject.Binder;
22import com.google.inject.name.Names;
23
24import tools.refinery.language.conversion.ProblemValueConverterService;
25import tools.refinery.language.naming.ProblemQualifiedNameConverter;
26import tools.refinery.language.resource.ProblemDerivedStateComputer;
27import tools.refinery.language.resource.ProblemLocationInFileProvider;
28import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
29import tools.refinery.language.scoping.ProblemGlobalScopeProvider;
30import tools.refinery.language.scoping.ProblemLocalScopeProvider;
31
32/**
33 * Use this class to register components to be used at runtime / without the
34 * Equinox extension registry.
35 */
36public class ProblemRuntimeModule extends AbstractProblemRuntimeModule {
37 public Class<? extends IQualifiedNameConverter> bindIQualifiedNameConverter() {
38 return ProblemQualifiedNameConverter.class;
39 }
40
41 public Class<? extends IDefaultResourceDescriptionStrategy> bindIDefaultResourceDescriptionStrategy() {
42 return ProblemResourceDescriptionStrategy.class;
43 }
44
45 @Override
46 public Class<? extends IValueConverterService> bindIValueConverterService() {
47 return ProblemValueConverterService.class;
48 }
49
50 @Override
51 public Class<? extends IGlobalScopeProvider> bindIGlobalScopeProvider() {
52 return ProblemGlobalScopeProvider.class;
53 }
54
55 @Override
56 public void configureIScopeProviderDelegate(Binder binder) {
57 binder.bind(IScopeProvider.class).annotatedWith(Names.named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE))
58 .to(ProblemLocalScopeProvider.class);
59 }
60
61 @Override
62 public Class<? extends XtextResource> bindXtextResource() {
63 return DerivedStateAwareResource.class;
64 }
65
66 // Method name follows Xtext convention.
67 @SuppressWarnings("squid:S100")
68 public Class<? extends IResourceDescription.Manager> bindIResourceDescription$Manager() {
69 return DerivedStateAwareResourceDescriptionManager.class;
70 }
71
72 public Class<? extends IResourceValidator> bindIResourceValidator() {
73 return DerivedStateAwareResourceValidator.class;
74 }
75
76 public Class<? extends IDerivedStateComputer> bindIDerivedStateComputer() {
77 return ProblemDerivedStateComputer.class;
78 }
79
80 @Override
81 public Class<? extends ILocationInFileProvider> bindILocationInFileProvider() {
82 return ProblemLocationInFileProvider.class;
83 }
84}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/ProblemStandaloneSetup.java b/subprojects/language/src/main/java/tools/refinery/language/ProblemStandaloneSetup.java
new file mode 100644
index 00000000..d753a119
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/ProblemStandaloneSetup.java
@@ -0,0 +1,44 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language;
5
6import org.eclipse.emf.ecore.resource.Resource;
7import org.eclipse.xtext.resource.IResourceFactory;
8import org.eclipse.xtext.resource.IResourceServiceProvider;
9
10import com.google.inject.Guice;
11import com.google.inject.Injector;
12
13import tools.refinery.language.model.ProblemEMFSetup;
14
15/**
16 * Initialization support for running Xtext languages without Equinox extension
17 * registry.
18 */
19public class ProblemStandaloneSetup extends ProblemStandaloneSetupGenerated {
20
21 public static void doSetup() {
22 new ProblemStandaloneSetup().createInjectorAndDoEMFRegistration();
23 }
24
25 @Override
26 public Injector createInjectorAndDoEMFRegistration() {
27 ProblemEMFSetup.doEMFRegistration();
28 var xmiInjector = createXmiInjector();
29 registerXmiInjector(xmiInjector);
30 return super.createInjectorAndDoEMFRegistration();
31 }
32
33 protected Injector createXmiInjector() {
34 return Guice.createInjector(new ProblemXmiRuntimeModule());
35 }
36
37 protected void registerXmiInjector(Injector injector) {
38 IResourceFactory resourceFactory = injector.getInstance(IResourceFactory.class);
39 IResourceServiceProvider serviceProvider = injector.getInstance(IResourceServiceProvider.class);
40
41 Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put(ProblemEMFSetup.XMI_RESOURCE_EXTENSION, resourceFactory);
42 IResourceServiceProvider.Registry.INSTANCE.getExtensionToFactoryMap().put(ProblemEMFSetup.XMI_RESOURCE_EXTENSION, serviceProvider);
43 }
44}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/ProblemXmiRuntimeModule.java b/subprojects/language/src/main/java/tools/refinery/language/ProblemXmiRuntimeModule.java
new file mode 100644
index 00000000..03a33bee
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/ProblemXmiRuntimeModule.java
@@ -0,0 +1,35 @@
1package tools.refinery.language;
2
3import org.eclipse.xtext.naming.IQualifiedNameConverter;
4import org.eclipse.xtext.resource.IDefaultResourceDescriptionStrategy;
5import org.eclipse.xtext.resource.IResourceFactory;
6import org.eclipse.xtext.resource.generic.AbstractGenericResourceRuntimeModule;
7
8import tools.refinery.language.model.ProblemEMFSetup;
9import tools.refinery.language.naming.ProblemQualifiedNameConverter;
10import tools.refinery.language.resource.ProblemResourceDescriptionStrategy;
11import tools.refinery.language.resource.ProblemXmiResourceFactory;
12
13public class ProblemXmiRuntimeModule extends AbstractGenericResourceRuntimeModule {
14 @Override
15 protected String getLanguageName() {
16 return "tools.refinery.language.ProblemXmi";
17 }
18
19 @Override
20 protected String getFileExtensions() {
21 return ProblemEMFSetup.XMI_RESOURCE_EXTENSION;
22 }
23
24 public Class<? extends IResourceFactory> bindIResourceFactory() {
25 return ProblemXmiResourceFactory.class;
26 }
27
28 public Class<? extends IQualifiedNameConverter> bindIQualifiedNameConverter() {
29 return ProblemQualifiedNameConverter.class;
30 }
31
32 public Class<? extends IDefaultResourceDescriptionStrategy> bindIDefaultResourceDescriptionStrategy() {
33 return ProblemResourceDescriptionStrategy.class;
34 }
35}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/conversion/ProblemValueConverterService.java b/subprojects/language/src/main/java/tools/refinery/language/conversion/ProblemValueConverterService.java
new file mode 100644
index 00000000..508688ed
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/conversion/ProblemValueConverterService.java
@@ -0,0 +1,19 @@
1package tools.refinery.language.conversion;
2
3import org.eclipse.xtext.common.services.DefaultTerminalConverters;
4import org.eclipse.xtext.conversion.IValueConverter;
5import org.eclipse.xtext.conversion.ValueConverter;
6
7import com.google.inject.Inject;
8
9public class ProblemValueConverterService extends DefaultTerminalConverters {
10 @Inject
11 private UpperBoundValueConverter upperBoundValueConverter;
12
13 @ValueConverter(rule = "UpperBound")
14 // Method name follows Xtext convention.
15 @SuppressWarnings("squid:S100")
16 public IValueConverter<Integer> UpperBound() {
17 return upperBoundValueConverter;
18 }
19}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/conversion/UpperBoundValueConverter.java b/subprojects/language/src/main/java/tools/refinery/language/conversion/UpperBoundValueConverter.java
new file mode 100644
index 00000000..be0d15ad
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/conversion/UpperBoundValueConverter.java
@@ -0,0 +1,35 @@
1package tools.refinery.language.conversion;
2
3import org.eclipse.xtext.conversion.ValueConverterException;
4import org.eclipse.xtext.conversion.impl.AbstractValueConverter;
5import org.eclipse.xtext.conversion.impl.INTValueConverter;
6import org.eclipse.xtext.nodemodel.INode;
7
8import com.google.inject.Inject;
9import com.google.inject.Singleton;
10
11@Singleton
12public class UpperBoundValueConverter extends AbstractValueConverter<Integer> {
13 public static final String INFINITY = "*";
14
15 @Inject
16 private INTValueConverter intValueConverter;
17
18 @Override
19 public Integer toValue(String string, INode node) throws ValueConverterException {
20 if (INFINITY.equals(string)) {
21 return -1;
22 } else {
23 return intValueConverter.toValue(string, node);
24 }
25 }
26
27 @Override
28 public String toString(Integer value) throws ValueConverterException {
29 if (value < 0) {
30 return INFINITY;
31 } else {
32 return intValueConverter.toString(value);
33 }
34 }
35}
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
new file mode 100644
index 00000000..903347f7
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/formatting2/ProblemFormatter.java
@@ -0,0 +1,183 @@
1/*
2 * generated by Xtext 2.26.0.M2
3 */
4package tools.refinery.language.formatting2;
5
6import org.eclipse.emf.ecore.EObject;
7import org.eclipse.xtext.formatting2.AbstractJavaFormatter;
8import org.eclipse.xtext.formatting2.IFormattableDocument;
9import org.eclipse.xtext.formatting2.IHiddenRegionFormatter;
10import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionsFinder;
11import org.eclipse.xtext.formatting2.regionaccess.ISequentialRegion;
12import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
13
14import tools.refinery.language.model.problem.Assertion;
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
25public class ProblemFormatter extends AbstractJavaFormatter {
26
27 protected void format(Problem problem, IFormattableDocument doc) {
28 doc.prepend(problem, this::noSpace);
29 var region = regionFor(problem);
30 doc.append(region.keyword("problem"), this::oneSpace);
31 doc.prepend(region.keyword("."), this::noSpace);
32 appendNewLines(doc, region.keyword("."), this::twoNewLines);
33 for (var statement : problem.getStatements()) {
34 doc.format(statement);
35 }
36 }
37
38 protected void format(Assertion assertion, IFormattableDocument doc) {
39 surroundNewLines(doc, assertion, this::singleNewLine);
40 var region = regionFor(assertion);
41 doc.append(region.feature(ProblemPackage.Literals.ASSERTION__DEFAULT), this::oneSpace);
42 doc.append(region.feature(ProblemPackage.Literals.ASSERTION__VALUE), this::noSpace);
43 doc.append(region.feature(ProblemPackage.Literals.ASSERTION__RELATION), this::noSpace);
44 formatParenthesizedList(region, doc);
45 doc.prepend(region.keyword(":"), this::noSpace);
46 doc.append(region.keyword(":"), this::oneSpace);
47 doc.prepend(region.keyword("."), this::noSpace);
48 for (var argument : assertion.getArguments()) {
49 doc.format(argument);
50 }
51 }
52
53 protected void format(ClassDeclaration classDeclaration, IFormattableDocument doc) {
54 surroundNewLines(doc, classDeclaration, this::twoNewLines);
55 var region = regionFor(classDeclaration);
56 doc.append(region.feature(ProblemPackage.Literals.CLASS_DECLARATION__ABSTRACT), this::oneSpace);
57 doc.append(region.keyword("class"), this::oneSpace);
58 doc.surround(region.keyword("extends"), this::oneSpace);
59 formatList(region, ",", doc);
60 doc.prepend(region.keyword("{"), this::oneSpace);
61 doc.append(region.keyword("{"), it -> it.setNewLines(1, 1, 2));
62 doc.prepend(region.keyword("}"), it -> it.setNewLines(1, 1, 2));
63 doc.prepend(region.keyword("."), this::noSpace);
64 for (var referenceDeclaration : classDeclaration.getReferenceDeclarations()) {
65 doc.format(referenceDeclaration);
66 }
67 }
68
69 protected void format(PredicateDefinition predicateDefinition, IFormattableDocument doc) {
70 surroundNewLines(doc, predicateDefinition, this::twoNewLines);
71 var region = regionFor(predicateDefinition);
72 doc.append(region.feature(ProblemPackage.Literals.PREDICATE_DEFINITION__KIND), this::oneSpace);
73 doc.append(region.keyword("pred"), this::oneSpace);
74 doc.append(region.feature(ProblemPackage.Literals.NAMED_ELEMENT__NAME), this::noSpace);
75 formatParenthesizedList(region, doc);
76 doc.surround(region.keyword("<->"), this::oneSpace);
77 formatList(region, ";", doc);
78 doc.prepend(region.keyword("."), this::noSpace);
79 for (var parameter : predicateDefinition.getParameters()) {
80 doc.format(parameter);
81 }
82 for (var body : predicateDefinition.getBodies()) {
83 doc.format(body);
84 }
85 }
86
87 protected void format(Parameter parameter, IFormattableDocument doc) {
88 doc.append(regionFor(parameter).feature(ProblemPackage.Literals.PARAMETER__PARAMETER_TYPE), this::oneSpace);
89 }
90
91 protected void format(Conjunction conjunction, IFormattableDocument doc) {
92 var region = regionFor(conjunction);
93 formatList(region, ",", doc);
94 for (var literal : conjunction.getLiterals()) {
95 doc.format(literal);
96 }
97 }
98
99 protected void format(NegativeLiteral literal, IFormattableDocument doc) {
100 var region = regionFor(literal);
101 doc.append(region.keyword("!"), this::noSpace);
102 doc.format(literal.getAtom());
103 }
104
105 protected void format(Atom atom, IFormattableDocument doc) {
106 var region = regionFor(atom);
107 doc.append(region.feature(ProblemPackage.Literals.ATOM__RELATION), this::noSpace);
108 doc.append(region.feature(ProblemPackage.Literals.ATOM__TRANSITIVE_CLOSURE), this::noSpace);
109 formatParenthesizedList(region, doc);
110 for (var argument : atom.getArguments()) {
111 doc.format(argument);
112 }
113 }
114
115 protected void format(IndividualDeclaration individualDeclaration, IFormattableDocument doc) {
116 surroundNewLines(doc, individualDeclaration, this::singleNewLine);
117 var region = regionFor(individualDeclaration);
118 doc.append(region.keyword("indiv"), this::oneSpace);
119 formatList(region, ",", doc);
120 doc.prepend(region.keyword("."), this::noSpace);
121 }
122
123 protected void formatParenthesizedList(ISemanticRegionsFinder region, IFormattableDocument doc) {
124 doc.append(region.keyword("("), this::noSpace);
125 doc.prepend(region.keyword(")"), this::noSpace);
126 formatList(region, ",", doc);
127 }
128
129 protected void formatList(ISemanticRegionsFinder region, String separator, IFormattableDocument doc) {
130 for (var comma : region.keywords(separator)) {
131 doc.prepend(comma, this::noSpace);
132 doc.append(comma, this::oneSpace);
133 }
134 }
135
136 protected void singleNewLine(IHiddenRegionFormatter it) {
137 it.setNewLines(1, 1, 2);
138 }
139
140 protected void twoNewLines(IHiddenRegionFormatter it) {
141 it.highPriority();
142 it.setNewLines(2);
143 }
144
145 protected void surroundNewLines(IFormattableDocument doc, EObject eObject,
146 Procedure1<? super IHiddenRegionFormatter> init) {
147 var region = doc.getRequest().getTextRegionAccess().regionForEObject(eObject);
148 preprendNewLines(doc, region, init);
149 appendNewLines(doc, region, init);
150 }
151
152 protected void preprendNewLines(IFormattableDocument doc, ISequentialRegion region,
153 Procedure1<? super IHiddenRegionFormatter> init) {
154 if (region == null) {
155 return;
156 }
157 var previousHiddenRegion = region.getPreviousHiddenRegion();
158 if (previousHiddenRegion == null) {
159 return;
160 }
161 if (previousHiddenRegion.getPreviousSequentialRegion() == null) {
162 doc.set(previousHiddenRegion, it -> it.setNewLines(0));
163 } else {
164 doc.set(previousHiddenRegion, init);
165 }
166 }
167
168 protected void appendNewLines(IFormattableDocument doc, ISequentialRegion region,
169 Procedure1<? super IHiddenRegionFormatter> init) {
170 if (region == null) {
171 return;
172 }
173 var nextHiddenRegion = region.getNextHiddenRegion();
174 if (nextHiddenRegion == null) {
175 return;
176 }
177 if (nextHiddenRegion.getNextSequentialRegion() == null) {
178 doc.set(nextHiddenRegion, it -> it.setNewLines(1));
179 } else {
180 doc.set(nextHiddenRegion, init);
181 }
182 }
183}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java b/subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java
new file mode 100644
index 00000000..e959be74
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/naming/NamingUtil.java
@@ -0,0 +1,25 @@
1package tools.refinery.language.naming;
2
3import java.util.regex.Pattern;
4
5public final class NamingUtil {
6 private static final String SINGLETON_VARIABLE_PREFIX = "_";
7
8 private static final Pattern ID_REGEX = Pattern.compile("[_a-zA-Z][_0-9a-zA-Z]*");
9
10 private NamingUtil() {
11 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
12 }
13
14 public static boolean isNullOrEmpty(String name) {
15 return name == null || name.isEmpty();
16 }
17
18 public static boolean isSingletonVariableName(String name) {
19 return name != null && name.startsWith(SINGLETON_VARIABLE_PREFIX);
20 }
21
22 public static boolean isValidId(String name) {
23 return name != null && ID_REGEX.matcher(name).matches();
24 }
25}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java
new file mode 100644
index 00000000..5453906f
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/naming/ProblemQualifiedNameConverter.java
@@ -0,0 +1,15 @@
1package tools.refinery.language.naming;
2
3import org.eclipse.xtext.naming.IQualifiedNameConverter;
4
5import com.google.inject.Singleton;
6
7@Singleton
8public class ProblemQualifiedNameConverter extends IQualifiedNameConverter.DefaultImpl {
9 public static final String DELIMITER = "::";
10
11 @Override
12 public String getDelimiter() {
13 return DELIMITER;
14 }
15}
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
new file mode 100644
index 00000000..bb1226c4
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/DerivedVariableComputer.java
@@ -0,0 +1,194 @@
1package tools.refinery.language.resource;
2
3import java.util.HashSet;
4import java.util.List;
5import java.util.Set;
6
7import org.eclipse.xtext.linking.impl.LinkingHelper;
8import 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;
13import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
14
15import com.google.inject.Inject;
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.Conjunction;
22import tools.refinery.language.model.problem.ExistentialQuantifier;
23import tools.refinery.language.model.problem.ImplicitVariable;
24import tools.refinery.language.model.problem.Literal;
25import tools.refinery.language.model.problem.NegativeLiteral;
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.ValueLiteral;
33import tools.refinery.language.model.problem.VariableOrNodeArgument;
34import tools.refinery.language.naming.NamingUtil;
35
36@Singleton
37public class DerivedVariableComputer {
38 @Inject
39 private LinkingHelper linkingHelper;
40
41 @Inject
42 private IQualifiedNameConverter qualifiedNameConverter;
43
44 @Inject
45 @Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE)
46 private IScopeProvider scopeProvider;
47
48 public void installDerivedVariables(Problem problem, Set<String> nodeNames) {
49 for (Statement statement : problem.getStatements()) {
50 if (statement instanceof ParametricDefinition definition) {
51 installDerivedParametricDefinitionState(definition, nodeNames);
52 }
53 }
54 }
55
56 protected void installDerivedParametricDefinitionState(ParametricDefinition definition, Set<String> nodeNames) {
57 Set<String> knownVariables = new HashSet<>();
58 knownVariables.addAll(nodeNames);
59 for (Parameter parameter : definition.getParameters()) {
60 String name = parameter.getName();
61 if (name != null) {
62 knownVariables.add(name);
63 }
64 }
65 for (Conjunction body : definition.getBodies()) {
66 installDeriveConjunctionState(body, knownVariables);
67 }
68 }
69
70 protected void installDeriveConjunctionState(Conjunction conjunction, Set<String> knownVariables) {
71 Set<String> newVariables = new HashSet<>();
72 for (Literal literal : conjunction.getLiterals()) {
73 if (literal instanceof Atom atom) {
74 createSigletonVariablesAndCollectVariables(atom, knownVariables, newVariables);
75 } else
76 if (literal instanceof ValueLiteral valueLiteral) {
77 createSigletonVariablesAndCollectVariables(valueLiteral.getAtom(), knownVariables, newVariables);
78 }
79 }
80 createVariables(conjunction, newVariables);
81 newVariables.addAll(knownVariables);
82 for (Literal literal : conjunction.getLiterals()) {
83 if (literal instanceof NegativeLiteral negativeLiteral) {
84 installDeriveNegativeLiteralState(negativeLiteral, newVariables);
85 }
86 }
87 }
88
89 protected void installDeriveNegativeLiteralState(NegativeLiteral negativeLiteral, Set<String> knownVariables) {
90 Set<String> newVariables = new HashSet<>();
91 createSigletonVariablesAndCollectVariables(negativeLiteral.getAtom(), knownVariables, newVariables);
92 createVariables(negativeLiteral, newVariables);
93 }
94
95 protected void createSigletonVariablesAndCollectVariables(Atom atom, Set<String> knownVariables,
96 Set<String> newVariables) {
97 for (Argument argument : atom.getArguments()) {
98 if (argument instanceof VariableOrNodeArgument variableOrNodeArgument) {
99 IScope scope = scopeProvider.getScope(variableOrNodeArgument,
100 ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE);
101 List<INode> nodes = NodeModelUtils.findNodesForFeature(variableOrNodeArgument,
102 ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE);
103 for (INode node : nodes) {
104 var variableName = linkingHelper.getCrossRefNodeAsString(node, true);
105 var created = tryCreateVariableForArgument(variableOrNodeArgument, variableName, scope,
106 knownVariables, newVariables);
107 if (created) {
108 break;
109 }
110 }
111 }
112 }
113 }
114
115 protected boolean tryCreateVariableForArgument(VariableOrNodeArgument variableOrNodeArgument, String variableName,
116 IScope scope, Set<String> knownVariables, Set<String> newVariables) {
117 if (!NamingUtil.isValidId(variableName)) {
118 return false;
119 }
120 var qualifiedName = qualifiedNameConverter.toQualifiedName(variableName);
121 if (scope.getSingleElement(qualifiedName) != null) {
122 return false;
123 }
124 if (NamingUtil.isSingletonVariableName(variableName)) {
125 createSingletonVariable(variableOrNodeArgument, variableName);
126 return true;
127 }
128 if (!knownVariables.contains(variableName)) {
129 newVariables.add(variableName);
130 return true;
131 }
132 return false;
133 }
134
135 protected void createVariables(ExistentialQuantifier quantifier, Set<String> newVariables) {
136 for (String variableName : newVariables) {
137 createVariable(quantifier, variableName);
138 }
139 }
140
141 protected void createVariable(ExistentialQuantifier quantifier, String variableName) {
142 if (NamingUtil.isValidId(variableName)) {
143 ImplicitVariable variable = createNamedVariable(variableName);
144 quantifier.getImplicitVariables().add(variable);
145 }
146 }
147
148 protected void createSingletonVariable(VariableOrNodeArgument argument, String variableName) {
149 if (NamingUtil.isValidId(variableName)) {
150 ImplicitVariable variable = createNamedVariable(variableName);
151 argument.setSingletonVariable(variable);
152 }
153 }
154
155 protected ImplicitVariable createNamedVariable(String variableName) {
156 var variable = ProblemFactory.eINSTANCE.createImplicitVariable();
157 variable.setName(variableName);
158 return variable;
159 }
160
161 public void discardDerivedVariables(Problem problem) {
162 for (Statement statement : problem.getStatements()) {
163 if (statement instanceof ParametricDefinition parametricDefinition) {
164 discardParametricDefinitionState(parametricDefinition);
165 }
166 }
167 }
168
169 protected void discardParametricDefinitionState(ParametricDefinition definition) {
170 for (Conjunction body : definition.getBodies()) {
171 body.getImplicitVariables().clear();
172 for (Literal literal : body.getLiterals()) {
173 if (literal instanceof Atom atom) {
174 discardDerivedAtomState(atom);
175 }
176 if (literal instanceof NegativeLiteral negativeLiteral) {
177 negativeLiteral.getImplicitVariables().clear();
178 discardDerivedAtomState(negativeLiteral.getAtom());
179 }
180 }
181 }
182 }
183
184 protected void discardDerivedAtomState(Atom atom) {
185 if (atom == null) {
186 return;
187 }
188 for (Argument argument : atom.getArguments()) {
189 if (argument instanceof VariableOrNodeArgument variableOrNodeArgument) {
190 variableOrNodeArgument.setSingletonVariable(null);
191 }
192 }
193 }
194}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java b/subprojects/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java
new file mode 100644
index 00000000..99bf9b64
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/NodeNameCollector.java
@@ -0,0 +1,88 @@
1package tools.refinery.language.resource;
2
3import java.util.List;
4import java.util.Set;
5
6import org.eclipse.emf.ecore.EObject;
7import org.eclipse.emf.ecore.EStructuralFeature;
8import org.eclipse.xtext.linking.impl.LinkingHelper;
9import org.eclipse.xtext.naming.IQualifiedNameConverter;
10import org.eclipse.xtext.nodemodel.INode;
11import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
12import org.eclipse.xtext.scoping.IScope;
13import org.eclipse.xtext.scoping.IScopeProvider;
14import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
15
16import com.google.common.collect.ImmutableSet;
17import com.google.inject.Inject;
18import com.google.inject.name.Named;
19
20import tools.refinery.language.model.problem.Assertion;
21import tools.refinery.language.model.problem.AssertionArgument;
22import tools.refinery.language.model.problem.NodeAssertionArgument;
23import tools.refinery.language.model.problem.NodeValueAssertion;
24import tools.refinery.language.model.problem.Problem;
25import tools.refinery.language.model.problem.ProblemPackage;
26import tools.refinery.language.model.problem.Statement;
27import tools.refinery.language.naming.NamingUtil;
28
29public class NodeNameCollector {
30 @Inject
31 private LinkingHelper linkingHelper;
32
33 @Inject
34 private IQualifiedNameConverter qualifiedNameConverter;
35
36 @Inject
37 @Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE)
38 private IScopeProvider scopeProvider;
39
40 private final ImmutableSet.Builder<String> nodeNames = ImmutableSet.builder();
41
42 private IScope nodeScope;
43
44 public Set<String> getNodeNames() {
45 return nodeNames.build();
46 }
47
48 public void collectNodeNames(Problem problem) {
49 nodeScope = scopeProvider.getScope(problem, ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE);
50 for (Statement statement : problem.getStatements()) {
51 collectStatementNodeNames(statement);
52 }
53 }
54
55 protected void collectStatementNodeNames(Statement statement) {
56 if (statement instanceof Assertion assertion) {
57 collectAssertionNodeNames(assertion);
58 } else if (statement instanceof NodeValueAssertion nodeValueAssertion) {
59 collectNodeValueAssertionNodeNames(nodeValueAssertion);
60 }
61 }
62
63 protected void collectAssertionNodeNames(Assertion assertion) {
64 for (AssertionArgument argument : assertion.getArguments()) {
65 if (argument instanceof NodeAssertionArgument) {
66 collectNodeNames(argument, ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE);
67 }
68 }
69 }
70
71 protected void collectNodeValueAssertionNodeNames(NodeValueAssertion nodeValueAssertion) {
72 collectNodeNames(nodeValueAssertion, ProblemPackage.Literals.NODE_VALUE_ASSERTION__NODE);
73 }
74
75 private void collectNodeNames(EObject eObject, EStructuralFeature feature) {
76 List<INode> nodes = NodeModelUtils.findNodesForFeature(eObject, feature);
77 for (INode node : nodes) {
78 var nodeName = linkingHelper.getCrossRefNodeAsString(node, true);
79 if (!NamingUtil.isValidId(nodeName)) {
80 continue;
81 }
82 var qualifiedName = qualifiedNameConverter.toQualifiedName(nodeName);
83 if (nodeScope.getSingleElement(qualifiedName) == null) {
84 nodeNames.add(nodeName);
85 }
86 }
87 }
88} \ No newline at end of file
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java
new file mode 100644
index 00000000..275feca3
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemDerivedStateComputer.java
@@ -0,0 +1,163 @@
1package tools.refinery.language.resource;
2
3import java.util.Collection;
4import java.util.HashMap;
5import java.util.HashSet;
6import java.util.List;
7import java.util.Map;
8import java.util.Set;
9import java.util.function.Function;
10
11import org.eclipse.emf.common.notify.impl.AdapterImpl;
12import org.eclipse.emf.ecore.EObject;
13import org.eclipse.emf.ecore.resource.Resource;
14import org.eclipse.emf.ecore.util.EcoreUtil;
15import org.eclipse.xtext.Constants;
16import org.eclipse.xtext.resource.DerivedStateAwareResource;
17import org.eclipse.xtext.resource.IDerivedStateComputer;
18import org.eclipse.xtext.resource.XtextResource;
19import org.eclipse.xtext.scoping.IScopeProvider;
20import org.eclipse.xtext.scoping.impl.AbstractDeclarativeScopeProvider;
21
22import com.google.inject.Inject;
23import com.google.inject.Provider;
24import com.google.inject.Singleton;
25import com.google.inject.name.Named;
26
27import tools.refinery.language.model.problem.ClassDeclaration;
28import tools.refinery.language.model.problem.Node;
29import tools.refinery.language.model.problem.Problem;
30import tools.refinery.language.model.problem.ProblemFactory;
31import tools.refinery.language.model.problem.Statement;
32
33@Singleton
34public class ProblemDerivedStateComputer implements IDerivedStateComputer {
35 public static final String NEW_NODE = "new";
36
37 @Inject
38 @Named(Constants.LANGUAGE_NAME)
39 private String languageName;
40
41 @Inject
42 @Named(AbstractDeclarativeScopeProvider.NAMED_DELEGATE)
43 private IScopeProvider scopeProvider;
44
45 @Inject
46 private Provider<NodeNameCollector> nodeNameCollectorProvider;
47
48 @Inject
49 private DerivedVariableComputer derivedVariableComputer;
50
51 @Override
52 public void installDerivedState(DerivedStateAwareResource resource, boolean preLinkingPhase) {
53 var problem = getProblem(resource);
54 if (problem != null) {
55 var adapter = getOrInstallAdapter(resource);
56 installDerivedProblemState(problem, adapter, preLinkingPhase);
57 }
58 }
59
60 protected Problem getProblem(Resource resource) {
61 List<EObject> contents = resource.getContents();
62 if (contents.isEmpty()) {
63 return null;
64 }
65 EObject object = contents.get(0);
66 if (object instanceof Problem problem) {
67 return problem;
68 }
69 return null;
70 }
71
72 protected void installDerivedProblemState(Problem problem, Adapter adapter, boolean preLinkingPhase) {
73 installNewNodes(problem, adapter);
74 if (preLinkingPhase) {
75 return;
76 }
77 Set<String> nodeNames = installDerivedNodes(problem);
78 derivedVariableComputer.installDerivedVariables(problem, nodeNames);
79 }
80
81 protected void installNewNodes(Problem problem, Adapter adapter) {
82 for (Statement statement : problem.getStatements()) {
83 if (statement instanceof ClassDeclaration declaration && !declaration.isAbstract()
84 && declaration.getNewNode() == null) {
85 var newNode = adapter.createNodeIfAbsent(declaration, key -> createNode(NEW_NODE));
86 declaration.setNewNode(newNode);
87 }
88 }
89 }
90
91 protected Set<String> installDerivedNodes(Problem problem) {
92 var collector = nodeNameCollectorProvider.get();
93 collector.collectNodeNames(problem);
94 Set<String> nodeNames = collector.getNodeNames();
95 List<Node> grapNodes = problem.getNodes();
96 for (String nodeName : nodeNames) {
97 var graphNode = createNode(nodeName);
98 grapNodes.add(graphNode);
99 }
100 return nodeNames;
101 }
102
103 protected Node createNode(String name) {
104 var node = ProblemFactory.eINSTANCE.createNode();
105 node.setName(name);
106 return node;
107 }
108
109 @Override
110 public void discardDerivedState(DerivedStateAwareResource resource) {
111 var problem = getProblem(resource);
112 if (problem != null) {
113 var adapter = getOrInstallAdapter(resource);
114 discardDerivedProblemState(problem, adapter);
115 }
116 }
117
118 protected void discardDerivedProblemState(Problem problem, Adapter adapter) {
119 Set<ClassDeclaration> classDeclarations = new HashSet<>();
120 problem.getNodes().clear();
121 for (Statement statement : problem.getStatements()) {
122 if (statement instanceof ClassDeclaration classDeclaration) {
123 classDeclaration.setNewNode(null);
124 classDeclarations.add(classDeclaration);
125 }
126 }
127 adapter.retainAll(classDeclarations);
128 derivedVariableComputer.discardDerivedVariables(problem);
129 }
130
131 protected Adapter getOrInstallAdapter(Resource resource) {
132 if (!(resource instanceof XtextResource)) {
133 return new Adapter();
134 }
135 String resourceLanguageName = ((XtextResource) resource).getLanguageName();
136 if (!languageName.equals(resourceLanguageName)) {
137 return new Adapter();
138 }
139 var adapter = (Adapter) EcoreUtil.getAdapter(resource.eAdapters(), Adapter.class);
140 if (adapter == null) {
141 adapter = new Adapter();
142 resource.eAdapters().add(adapter);
143 }
144 return adapter;
145 }
146
147 protected static class Adapter extends AdapterImpl {
148 private Map<ClassDeclaration, Node> newNodes = new HashMap<>();
149
150 public Node createNodeIfAbsent(ClassDeclaration classDeclaration, Function<ClassDeclaration, Node> createNode) {
151 return newNodes.computeIfAbsent(classDeclaration, createNode);
152 }
153
154 public void retainAll(Collection<ClassDeclaration> classDeclarations) {
155 newNodes.keySet().retainAll(classDeclarations);
156 }
157
158 @Override
159 public boolean isAdapterForType(Object type) {
160 return Adapter.class == type;
161 }
162 }
163}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java
new file mode 100644
index 00000000..7aa75833
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemLocationInFileProvider.java
@@ -0,0 +1,33 @@
1package tools.refinery.language.resource;
2
3import org.eclipse.emf.ecore.EObject;
4import org.eclipse.xtext.resource.DefaultLocationInFileProvider;
5import org.eclipse.xtext.util.ITextRegion;
6
7import tools.refinery.language.model.ProblemUtil;
8import tools.refinery.language.model.problem.ImplicitVariable;
9import tools.refinery.language.model.problem.Node;
10
11public class ProblemLocationInFileProvider extends DefaultLocationInFileProvider {
12 @Override
13 protected ITextRegion doGetTextRegion(EObject obj, RegionDescription query) {
14 if (obj instanceof Node node) {
15 return getNodeTextRegion(node, query);
16 }
17 if (obj instanceof ImplicitVariable) {
18 return ITextRegion.EMPTY_REGION;
19 }
20 return super.doGetTextRegion(obj, query);
21 }
22
23 protected ITextRegion getNodeTextRegion(Node node, RegionDescription query) {
24 if (ProblemUtil.isIndividualNode(node)) {
25 return super.doGetTextRegion(node, query);
26 }
27 if (ProblemUtil.isNewNode(node)) {
28 EObject container = node.eContainer();
29 return doGetTextRegion(container, query);
30 }
31 return ITextRegion.EMPTY_REGION;
32 }
33}
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
new file mode 100644
index 00000000..f86ebd38
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemResourceDescriptionStrategy.java
@@ -0,0 +1,103 @@
1package tools.refinery.language.resource;
2
3import org.eclipse.emf.ecore.EObject;
4import org.eclipse.xtext.EcoreUtil2;
5import org.eclipse.xtext.naming.IQualifiedNameConverter;
6import org.eclipse.xtext.naming.QualifiedName;
7import org.eclipse.xtext.resource.EObjectDescription;
8import org.eclipse.xtext.resource.IEObjectDescription;
9import org.eclipse.xtext.resource.impl.DefaultResourceDescriptionStrategy;
10import org.eclipse.xtext.util.IAcceptor;
11
12import com.google.inject.Inject;
13import com.google.inject.Singleton;
14
15import tools.refinery.language.model.ProblemUtil;
16import tools.refinery.language.model.problem.NamedElement;
17import tools.refinery.language.model.problem.Node;
18import tools.refinery.language.model.problem.Problem;
19import tools.refinery.language.model.problem.Variable;
20import tools.refinery.language.naming.NamingUtil;
21
22@Singleton
23public class ProblemResourceDescriptionStrategy extends DefaultResourceDescriptionStrategy {
24 @Inject
25 private IQualifiedNameConverter qualifiedNameConverter;
26
27 @Override
28 public boolean createEObjectDescriptions(EObject eObject, IAcceptor<IEObjectDescription> acceptor) {
29 if (!shouldExport(eObject)) {
30 return false;
31 }
32 var qualifiedName = getNameAsQualifiedName(eObject);
33 if (qualifiedName == null) {
34 return true;
35 }
36 var problem = EcoreUtil2.getContainerOfType(eObject, Problem.class);
37 var problemQualifiedName = getNameAsQualifiedName(problem);
38 boolean nameExported;
39 if (shouldExportSimpleName(eObject)) {
40 acceptEObjectDescription(eObject, problemQualifiedName, qualifiedName, acceptor);
41 nameExported = true;
42 } else {
43 nameExported = false;
44 }
45 var parent = eObject.eContainer();
46 while (parent != null && parent != problem) {
47 var parentQualifiedName = getNameAsQualifiedName(parent);
48 if (parentQualifiedName == null) {
49 parent = parent.eContainer();
50 continue;
51 }
52 qualifiedName = parentQualifiedName.append(qualifiedName);
53 if (shouldExportSimpleName(parent)) {
54 acceptEObjectDescription(eObject, problemQualifiedName, qualifiedName, acceptor);
55 nameExported = true;
56 } else {
57 nameExported = false;
58 }
59 parent = parent.eContainer();
60 }
61 if (!nameExported) {
62 acceptEObjectDescription(eObject, problemQualifiedName, qualifiedName, acceptor);
63 }
64 return true;
65 }
66
67 protected QualifiedName getNameAsQualifiedName(EObject eObject) {
68 if (!(eObject instanceof NamedElement)) {
69 return null;
70 }
71 var namedElement = (NamedElement) eObject;
72 var name = namedElement.getName();
73 if (NamingUtil.isNullOrEmpty(name)) {
74 return null;
75 }
76 return qualifiedNameConverter.toQualifiedName(name);
77 }
78
79 protected boolean shouldExport(EObject eObject) {
80 if (eObject instanceof Variable) {
81 // Variables are always private to the containing predicate definition.
82 return false;
83 }
84 if (eObject instanceof Node node) {
85 // Only enum literals and new nodes are visible across problem files.
86 return ProblemUtil.isIndividualNode(node) || ProblemUtil.isNewNode(node);
87 }
88 return true;
89 }
90
91 protected boolean shouldExportSimpleName(EObject eObject) {
92 if (eObject instanceof Node node) {
93 return !ProblemUtil.isNewNode(node);
94 }
95 return true;
96 }
97
98 private void acceptEObjectDescription(EObject eObject, QualifiedName prefix, QualifiedName qualifiedName,
99 IAcceptor<IEObjectDescription> acceptor) {
100 var qualifiedNameWithPrefix = prefix == null ? qualifiedName : prefix.append(qualifiedName);
101 acceptor.accept(EObjectDescription.create(qualifiedNameWithPrefix, eObject));
102 }
103}
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
new file mode 100644
index 00000000..68aa6016
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ProblemXmiResourceFactory.java
@@ -0,0 +1,16 @@
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/resource/ReferenceCounter.java b/subprojects/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java
new file mode 100644
index 00000000..7525dfc6
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/resource/ReferenceCounter.java
@@ -0,0 +1,51 @@
1package tools.refinery.language.resource;
2
3import java.util.HashMap;
4import java.util.Map;
5
6import org.eclipse.emf.ecore.EObject;
7import org.eclipse.xtext.util.IResourceScopeCache;
8
9import com.google.inject.Inject;
10import com.google.inject.Singleton;
11
12import tools.refinery.language.model.problem.Problem;
13
14@Singleton
15public class ReferenceCounter {
16 @Inject
17 private IResourceScopeCache cache;
18
19 public int countReferences(Problem problem, EObject eObject) {
20 var count = getReferenceCounts(problem).get(eObject);
21 if (count == null) {
22 return 0;
23 }
24 return count;
25 }
26
27 protected Map<EObject, Integer> getReferenceCounts(Problem problem) {
28 var resource = problem.eResource();
29 if (resource == null) {
30 return doGetReferenceCounts(problem);
31 }
32 return cache.get(problem, resource, () -> doGetReferenceCounts(problem));
33 }
34
35 protected Map<EObject, Integer> doGetReferenceCounts(Problem problem) {
36 var map = new HashMap<EObject, Integer>();
37 countCrossReferences(problem, map);
38 var iterator = problem.eAllContents();
39 while (iterator.hasNext()) {
40 var eObject = iterator.next();
41 countCrossReferences(eObject, map);
42 }
43 return map;
44 }
45
46 protected void countCrossReferences(EObject eObject, Map<EObject, Integer> map) {
47 for (var referencedObject : eObject.eCrossReferences()) {
48 map.compute(referencedObject, (key, currentValue) -> currentValue == null ? 1 : currentValue + 1);
49 }
50 }
51}
diff --git a/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java
new file mode 100644
index 00000000..b582d16b
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemGlobalScopeProvider.java
@@ -0,0 +1,18 @@
1package tools.refinery.language.scoping;
2
3import java.util.LinkedHashSet;
4
5import org.eclipse.emf.common.util.URI;
6import org.eclipse.emf.ecore.resource.Resource;
7import org.eclipse.xtext.scoping.impl.ImportUriGlobalScopeProvider;
8
9import tools.refinery.language.model.ProblemUtil;
10
11public class ProblemGlobalScopeProvider extends ImportUriGlobalScopeProvider {
12 @Override
13 protected LinkedHashSet<URI> getImportedUris(Resource resource) {
14 LinkedHashSet<URI> importedUris = new LinkedHashSet<>();
15 importedUris.add(ProblemUtil.BUILTIN_LIBRARY_URI);
16 return importedUris;
17 }
18}
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
new file mode 100644
index 00000000..85797025
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemLocalScopeProvider.java
@@ -0,0 +1,42 @@
1package tools.refinery.language.scoping;
2
3import java.util.List;
4
5import org.eclipse.emf.ecore.EObject;
6import org.eclipse.emf.ecore.resource.Resource;
7import org.eclipse.xtext.naming.QualifiedName;
8import org.eclipse.xtext.resource.IResourceDescriptions;
9import org.eclipse.xtext.resource.IResourceDescriptionsProvider;
10import org.eclipse.xtext.resource.ISelectable;
11import org.eclipse.xtext.scoping.impl.ImportNormalizer;
12import org.eclipse.xtext.scoping.impl.ImportedNamespaceAwareLocalScopeProvider;
13
14import com.google.inject.Inject;
15
16import tools.refinery.language.model.ProblemUtil;
17
18public class ProblemLocalScopeProvider extends ImportedNamespaceAwareLocalScopeProvider {
19 private static final QualifiedName BUILTIN_LIBRARY_QUALIFIED_NAME = QualifiedName
20 .create(ProblemUtil.BUILTIN_LIBRARY_NAME);
21
22 @Inject
23 private IResourceDescriptionsProvider resourceDescriptionsProvider;
24
25 @Override
26 protected List<ImportNormalizer> getImplicitImports(boolean ignoreCase) {
27 return List.of(doCreateImportNormalizer(BUILTIN_LIBRARY_QUALIFIED_NAME, true, ignoreCase));
28 }
29
30 @Override
31 protected List<ImportNormalizer> getImportedNamespaceResolvers(EObject context, boolean ignoreCase) {
32 return List.of();
33 }
34
35 @Override
36 protected ISelectable internalGetAllDescriptions(Resource resource) {
37 // Force the use of ProblemResourceDescriptionStrategy to include all QualifiedNames of objects.
38 IResourceDescriptions resourceDescriptions = resourceDescriptionsProvider
39 .getResourceDescriptions(resource.getResourceSet());
40 return resourceDescriptions.getResourceDescription(resource.getURI());
41 }
42}
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
new file mode 100644
index 00000000..d31a5308
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/scoping/ProblemScopeProvider.java
@@ -0,0 +1,104 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language.scoping;
5
6import java.util.ArrayList;
7import java.util.List;
8
9import org.eclipse.emf.ecore.EObject;
10import org.eclipse.emf.ecore.EReference;
11import org.eclipse.xtext.EcoreUtil2;
12import org.eclipse.xtext.scoping.IScope;
13import org.eclipse.xtext.scoping.Scopes;
14
15import tools.refinery.language.model.ProblemUtil;
16import tools.refinery.language.model.problem.ClassDeclaration;
17import tools.refinery.language.model.problem.ExistentialQuantifier;
18import tools.refinery.language.model.problem.NewActionLiteral;
19import tools.refinery.language.model.problem.ParametricDefinition;
20import tools.refinery.language.model.problem.Action;
21import tools.refinery.language.model.problem.Problem;
22import tools.refinery.language.model.problem.ProblemPackage;
23import tools.refinery.language.model.problem.ReferenceDeclaration;
24import tools.refinery.language.model.problem.Variable;
25import tools.refinery.language.model.problem.VariableOrNodeArgument;
26
27/**
28 * This class contains custom scoping description.
29 *
30 * See
31 * https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#scoping
32 * on how and when to use it.
33 */
34public class ProblemScopeProvider extends AbstractProblemScopeProvider {
35
36 @Override
37 public IScope getScope(EObject context, EReference reference) {
38 var scope = super.getScope(context, reference);
39 if (reference == ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE
40 || reference == ProblemPackage.Literals.NODE_VALUE_ASSERTION__NODE) {
41 return getNodesScope(context, scope);
42 }
43 if (reference == ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE
44 || reference == ProblemPackage.Literals.DELETE_ACTION_LITERAL__VARIABLE_OR_NODE) {
45 return getVariableScope(context, scope);
46 }
47 if (reference == ProblemPackage.Literals.REFERENCE_DECLARATION__OPPOSITE) {
48 return getOppositeScope(context, scope);
49 }
50 return scope;
51 }
52
53 protected IScope getNodesScope(EObject context, IScope delegateScope) {
54 var problem = EcoreUtil2.getContainerOfType(context, Problem.class);
55 if (problem == null) {
56 return delegateScope;
57 }
58 return Scopes.scopeFor(problem.getNodes(), delegateScope);
59 }
60
61 protected IScope getVariableScope(EObject context, IScope delegateScope) {
62 List<Variable> variables = new ArrayList<>();
63 EObject currentContext = context;
64 if (context instanceof VariableOrNodeArgument argument) {
65 Variable singletonVariable = argument.getSingletonVariable();
66 if (singletonVariable != null) {
67 variables.add(singletonVariable);
68 }
69 }
70 while (currentContext != null && !(currentContext instanceof ParametricDefinition)) {
71 if (currentContext instanceof ExistentialQuantifier quantifier) {
72 variables.addAll(quantifier.getImplicitVariables());
73 } else
74 if(currentContext instanceof Action action) {
75 for (var literal : action.getActionLiterals()) {
76 if(literal instanceof NewActionLiteral newActionLiteral && newActionLiteral.getVariable() != null) {
77 variables.add(newActionLiteral.getVariable());
78 }
79 }
80 }
81 currentContext = currentContext.eContainer();
82 }
83 IScope parentScope = getNodesScope(context, delegateScope);
84 if (currentContext != null) {
85 ParametricDefinition definition = (ParametricDefinition) currentContext;
86 parentScope = Scopes.scopeFor(definition.getParameters(),parentScope);
87 }
88 return Scopes.scopeFor(variables,parentScope);
89 }
90
91 protected IScope getOppositeScope(EObject context, IScope delegateScope) {
92 var referenceDeclaration = EcoreUtil2.getContainerOfType(context, ReferenceDeclaration.class);
93 if (referenceDeclaration == null) {
94 return delegateScope;
95 }
96 var relation = referenceDeclaration.getReferenceType();
97 if (!(relation instanceof ClassDeclaration)) {
98 return delegateScope;
99 }
100 var classDeclaration = (ClassDeclaration) relation;
101 var referenceDeclarations = ProblemUtil.getAllReferenceDeclarations(classDeclaration);
102 return Scopes.scopeFor(referenceDeclarations, delegateScope);
103 }
104}
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
new file mode 100644
index 00000000..975fdca2
--- /dev/null
+++ b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java
@@ -0,0 +1,62 @@
1/*
2 * generated by Xtext 2.25.0
3 */
4package tools.refinery.language.validation;
5
6import org.eclipse.xtext.EcoreUtil2;
7import org.eclipse.xtext.validation.Check;
8
9import com.google.inject.Inject;
10
11import tools.refinery.language.model.ProblemUtil;
12import tools.refinery.language.model.problem.Node;
13import tools.refinery.language.model.problem.Problem;
14import tools.refinery.language.model.problem.ProblemPackage;
15import tools.refinery.language.model.problem.Variable;
16import tools.refinery.language.model.problem.VariableOrNodeArgument;
17import tools.refinery.language.resource.ReferenceCounter;
18
19/**
20 * This class contains custom validation rules.
21 *
22 * See
23 * https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation
24 */
25public class ProblemValidator extends AbstractProblemValidator {
26 private static final String ISSUE_PREFIX = "tools.refinery.language.validation.ProblemValidator.";
27
28 public static final String SINGLETON_VARIABLE_ISSUE = ISSUE_PREFIX + "SINGLETON_VARIABLE";
29
30 public static final String NON_INDIVIDUAL_NODE_ISSUE = ISSUE_PREFIX + "NON_INDIVIDUAL_NODE";
31
32 @Inject
33 private ReferenceCounter referenceCounter;
34
35 @Check
36 public void checkUniqueVariable(VariableOrNodeArgument argument) {
37 var variableOrNode = argument.getVariableOrNode();
38 if (variableOrNode instanceof Variable variable && ProblemUtil.isImplicitVariable(variable)
39 && !ProblemUtil.isSingletonVariable(variable)) {
40 var problem = EcoreUtil2.getContainerOfType(variable, Problem.class);
41 if (problem != null && referenceCounter.countReferences(problem, variable) <= 1) {
42 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'"
44 .formatted(name, name);
45 warning(message, argument, ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE,
46 INSIGNIFICANT_INDEX, SINGLETON_VARIABLE_ISSUE);
47 }
48 }
49 }
50
51 @Check
52 public void checkNonUniqueNode(VariableOrNodeArgument argument) {
53 var variableOrNode = argument.getVariableOrNode();
54 if (variableOrNode instanceof Node node && !ProblemUtil.isIndividualNode(node)) {
55 var name = node.getName();
56 var message = "Only individual nodes can be referenced in predicates. Mark '%s' as individual with the declaration 'indiv %s.'"
57 .formatted(name, name);
58 error(message, argument, ProblemPackage.Literals.VARIABLE_OR_NODE_ARGUMENT__VARIABLE_OR_NODE,
59 INSIGNIFICANT_INDEX, NON_INDIVIDUAL_NODE_ISSUE);
60 }
61 }
62}