diff options
author | Kristóf Marussy <kristof@marussy.com> | 2024-01-31 02:00:09 +0100 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2024-01-31 18:45:13 +0100 |
commit | c63126d2f1ce5f571c316b37e00fb43d2da7c7d3 (patch) | |
tree | 16e9dd04624565f7c9ccedd17749a9f264e89cb0 /subprojects/language-semantics/src | |
parent | fix(build): avoid cyclic dependency (diff) | |
download | refinery-c63126d2f1ce5f571c316b37e00fb43d2da7c7d3.tar.gz refinery-c63126d2f1ce5f571c316b37e00fb43d2da7c7d3.tar.zst refinery-c63126d2f1ce5f571c316b37e00fb43d2da7c7d3.zip |
refactor(language): module and node declarations
* New default file extension: .refinery (.problem is also supported).
* Add module keyword for self-contained modules.
* Rename indiv declarations to atom declaration.
* Add node and multi declarations for explicitly declared nodes and
multi-objects, respectively.
Diffstat (limited to 'subprojects/language-semantics/src')
3 files changed, 116 insertions, 54 deletions
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java index 38bf5a61..b462fd70 100644 --- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java +++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java | |||
@@ -169,9 +169,9 @@ public class ModelInitializer { | |||
169 | 169 | ||
170 | private void collectNodes() { | 170 | private void collectNodes() { |
171 | for (var statement : problem.getStatements()) { | 171 | for (var statement : problem.getStatements()) { |
172 | if (statement instanceof IndividualDeclaration individualDeclaration) { | 172 | if (statement instanceof NodeDeclaration nodeDeclaration) { |
173 | for (var individual : individualDeclaration.getNodes()) { | 173 | for (var node : nodeDeclaration.getNodes()) { |
174 | collectNode(individual); | 174 | collectNode(node); |
175 | } | 175 | } |
176 | } else if (statement instanceof ClassDeclaration classDeclaration) { | 176 | } else if (statement instanceof ClassDeclaration classDeclaration) { |
177 | var newNode = classDeclaration.getNewNode(); | 177 | var newNode = classDeclaration.getNewNode(); |
@@ -355,10 +355,8 @@ public class ModelInitializer { | |||
355 | collectClassDeclarationAssertions(classDeclaration); | 355 | collectClassDeclarationAssertions(classDeclaration); |
356 | } else if (statement instanceof EnumDeclaration enumDeclaration) { | 356 | } else if (statement instanceof EnumDeclaration enumDeclaration) { |
357 | collectEnumAssertions(enumDeclaration); | 357 | collectEnumAssertions(enumDeclaration); |
358 | } else if (statement instanceof IndividualDeclaration individualDeclaration) { | 358 | } else if (statement instanceof NodeDeclaration nodeDeclaration) { |
359 | for (var individual : individualDeclaration.getNodes()) { | 359 | collectNodeDeclarationAssertions(nodeDeclaration); |
360 | collectIndividualAssertions(individual); | ||
361 | } | ||
362 | } else if (statement instanceof Assertion assertion) { | 360 | } else if (statement instanceof Assertion assertion) { |
363 | collectAssertion(assertion); | 361 | collectAssertion(assertion); |
364 | } | 362 | } |
@@ -379,7 +377,7 @@ public class ModelInitializer { | |||
379 | private void collectEnumAssertions(EnumDeclaration enumDeclaration) { | 377 | private void collectEnumAssertions(EnumDeclaration enumDeclaration) { |
380 | var overlay = MutableSeed.of(1, null); | 378 | var overlay = MutableSeed.of(1, null); |
381 | for (var literal : enumDeclaration.getLiterals()) { | 379 | for (var literal : enumDeclaration.getLiterals()) { |
382 | collectIndividualAssertions(literal); | 380 | collectCardinalityAssertions(literal, TruthValue.TRUE); |
383 | var nodeId = getNodeId(literal); | 381 | var nodeId = getNodeId(literal); |
384 | overlay.mergeValue(Tuple.of(nodeId), TruthValue.TRUE); | 382 | overlay.mergeValue(Tuple.of(nodeId), TruthValue.TRUE); |
385 | } | 383 | } |
@@ -387,9 +385,25 @@ public class ModelInitializer { | |||
387 | info.assertions().overwriteValues(overlay); | 385 | info.assertions().overwriteValues(overlay); |
388 | } | 386 | } |
389 | 387 | ||
390 | private void collectIndividualAssertions(Node node) { | 388 | private void collectNodeDeclarationAssertions(NodeDeclaration nodeDeclaration) { |
389 | var kind = nodeDeclaration.getKind(); | ||
390 | TruthValue value; | ||
391 | switch (kind) { | ||
392 | case ATOM -> value = TruthValue.TRUE; | ||
393 | case MULTI -> value = TruthValue.UNKNOWN; | ||
394 | case NODE -> { | ||
395 | return; | ||
396 | } | ||
397 | default -> throw new IllegalArgumentException("Unknown node kind: " + kind); | ||
398 | } | ||
399 | for (var node : nodeDeclaration.getNodes()) { | ||
400 | collectCardinalityAssertions(node, value); | ||
401 | } | ||
402 | } | ||
403 | |||
404 | private void collectCardinalityAssertions(Node node, TruthValue value) { | ||
391 | var nodeId = getNodeId(node); | 405 | var nodeId = getNodeId(node); |
392 | collectCardinalityAssertions(nodeId, TruthValue.TRUE); | 406 | collectCardinalityAssertions(nodeId, value); |
393 | } | 407 | } |
394 | 408 | ||
395 | private void collectCardinalityAssertions(int nodeId, TruthValue value) { | 409 | private void collectCardinalityAssertions(int nodeId, TruthValue value) { |
@@ -587,47 +601,51 @@ public class ModelInitializer { | |||
587 | 601 | ||
588 | private void toLiterals(Expr expr, Map<tools.refinery.language.model.problem.Variable, Variable> localScope, | 602 | private void toLiterals(Expr expr, Map<tools.refinery.language.model.problem.Variable, Variable> localScope, |
589 | List<Literal> literals) { | 603 | List<Literal> literals) { |
590 | if (expr instanceof LogicConstant logicConstant) { | 604 | switch (expr) { |
591 | switch (logicConstant.getLogicValue()) { | 605 | case LogicConstant logicConstant -> { |
592 | case TRUE -> literals.add(BooleanLiteral.TRUE); | 606 | switch (logicConstant.getLogicValue()) { |
593 | case FALSE -> literals.add(BooleanLiteral.FALSE); | 607 | case TRUE -> literals.add(BooleanLiteral.TRUE); |
594 | default -> throw new TracedException(logicConstant, "Unsupported literal"); | 608 | case FALSE -> literals.add(BooleanLiteral.FALSE); |
595 | } | 609 | default -> throw new TracedException(logicConstant, "Unsupported literal"); |
596 | } else if (expr instanceof Atom atom) { | 610 | } |
597 | var target = getPartialRelation(atom.getRelation()); | 611 | } |
598 | var polarity = atom.isTransitiveClosure() ? CallPolarity.TRANSITIVE : CallPolarity.POSITIVE; | 612 | case Atom atom -> { |
599 | var argumentList = toArgumentList(atom.getArguments(), localScope, literals); | 613 | var target = getPartialRelation(atom.getRelation()); |
600 | literals.add(target.call(polarity, argumentList)); | 614 | var polarity = atom.isTransitiveClosure() ? CallPolarity.TRANSITIVE : CallPolarity.POSITIVE; |
601 | } else if (expr instanceof NegationExpr negationExpr) { | 615 | var argumentList = toArgumentList(atom.getArguments(), localScope, literals); |
602 | var body = negationExpr.getBody(); | 616 | literals.add(target.call(polarity, argumentList)); |
603 | if (!(body instanceof Atom atom)) { | 617 | } |
604 | throw new TracedException(body, "Cannot negate literal"); | 618 | case NegationExpr negationExpr -> { |
605 | } | 619 | var body = negationExpr.getBody(); |
606 | var target = getPartialRelation(atom.getRelation()); | 620 | if (!(body instanceof Atom atom)) { |
607 | Constraint constraint; | 621 | throw new TracedException(body, "Cannot negate literal"); |
608 | if (atom.isTransitiveClosure()) { | 622 | } |
609 | constraint = Query.of(target.name() + "#transitive", (builder, p1, p2) -> builder.clause( | 623 | var target = getPartialRelation(atom.getRelation()); |
610 | target.callTransitive(p1, p2) | 624 | Constraint constraint; |
611 | )).getDnf(); | 625 | if (atom.isTransitiveClosure()) { |
612 | } else { | 626 | constraint = Query.of(target.name() + "#transitive", (builder, p1, p2) -> builder.clause( |
613 | constraint = target; | 627 | target.callTransitive(p1, p2) |
614 | } | 628 | )).getDnf(); |
615 | var negatedScope = extendScope(localScope, negationExpr.getImplicitVariables()); | 629 | } else { |
616 | var argumentList = toArgumentList(atom.getArguments(), negatedScope, literals); | 630 | constraint = target; |
617 | literals.add(constraint.call(CallPolarity.NEGATIVE, argumentList)); | 631 | } |
618 | } else if (expr instanceof ComparisonExpr comparisonExpr) { | 632 | var negatedScope = extendScope(localScope, negationExpr.getImplicitVariables()); |
619 | var argumentList = toArgumentList(List.of(comparisonExpr.getLeft(), comparisonExpr.getRight()), | 633 | var argumentList = toArgumentList(atom.getArguments(), negatedScope, literals); |
620 | localScope, literals); | 634 | literals.add(constraint.call(CallPolarity.NEGATIVE, argumentList)); |
621 | boolean positive = switch (comparisonExpr.getOp()) { | 635 | } |
622 | case EQ -> true; | 636 | case ComparisonExpr comparisonExpr -> { |
623 | case NOT_EQ -> false; | 637 | var argumentList = toArgumentList(List.of(comparisonExpr.getLeft(), comparisonExpr.getRight()), |
624 | default -> throw new TracedException( | 638 | localScope, literals); |
625 | comparisonExpr, "Unsupported operator"); | 639 | boolean positive = switch (comparisonExpr.getOp()) { |
626 | }; | 640 | case EQ -> true; |
627 | literals.add(new EquivalenceLiteral(positive, argumentList.get(0), argumentList.get(1))); | 641 | case NOT_EQ -> false; |
628 | } else { | 642 | default -> throw new TracedException( |
629 | throw new TracedException(expr, "Unsupported literal"); | 643 | comparisonExpr, "Unsupported operator"); |
630 | } | 644 | }; |
645 | literals.add(new EquivalenceLiteral(positive, argumentList.get(0), argumentList.get(1))); | ||
646 | } | ||
647 | default -> throw new TracedException(expr, "Unsupported literal"); | ||
648 | } | ||
631 | } | 649 | } |
632 | 650 | ||
633 | private List<Variable> toArgumentList( | 651 | private List<Variable> toArgumentList( |
diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java index 09ba34fc..537d94ca 100644 --- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java +++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java | |||
@@ -197,9 +197,10 @@ public class SolutionSerializer { | |||
197 | var originalNode = pair.getOne(); | 197 | var originalNode = pair.getOne(); |
198 | int nodeId = pair.getTwo(); | 198 | int nodeId = pair.getTwo(); |
199 | var newNode = findNode(originalNode); | 199 | var newNode = findNode(originalNode); |
200 | // Since all implicit nodes that do not exist has already been remove in serializeSolution, | 200 | // Since all implicit nodes that do not exist has already been removed in serializeSolution, |
201 | // we only need to add !exists assertions to ::new nodes (nodes marked as an individual must always exist). | 201 | // we only need to add !exists assertions to ::new nodes and explicitly declared nodes that do not exist. |
202 | if (ProblemUtil.isNewNode(originalNode)) { | 202 | if (ProblemUtil.isMultiNode(originalNode) || |
203 | (ProblemUtil.isDeclaredNode(originalNode) && !isExistingNode(nodeId))) { | ||
203 | sortedNewNodes.put(nodeId, newNode); | 204 | sortedNewNodes.put(nodeId, newNode); |
204 | } else { | 205 | } else { |
205 | nodes.put(nodeId, newNode); | 206 | nodes.put(nodeId, newNode); |
diff --git a/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/SolutionSerializerTest.java b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/SolutionSerializerTest.java index 8ef449b0..b682a7d6 100644 --- a/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/SolutionSerializerTest.java +++ b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/SolutionSerializerTest.java | |||
@@ -183,6 +183,49 @@ class SolutionSerializerTest { | |||
183 | !exists(Foo::new). | 183 | !exists(Foo::new). |
184 | Foo(a). | 184 | Foo(a). |
185 | default !ref(*, *). | 185 | default !ref(*, *). |
186 | """), Arguments.of(""" | ||
187 | atom a. | ||
188 | class Foo. | ||
189 | """, """ | ||
190 | Foo(a). | ||
191 | scope Foo += 0. | ||
192 | """, """ | ||
193 | !exists(Foo::new). | ||
194 | Foo(a). | ||
195 | """), Arguments.of(""" | ||
196 | multi a. | ||
197 | class Foo. | ||
198 | """, """ | ||
199 | Foo(a). | ||
200 | !exists(Foo::new). | ||
201 | scope Foo = 2. | ||
202 | """, """ | ||
203 | !exists(a). | ||
204 | !exists(Foo::new). | ||
205 | Foo(foo1). | ||
206 | Foo(foo2). | ||
207 | """), Arguments.of(""" | ||
208 | node a. | ||
209 | class Foo. | ||
210 | """, """ | ||
211 | Foo(a). | ||
212 | ?exists(a). | ||
213 | scope Foo = 2, Foo += 1. | ||
214 | """, """ | ||
215 | !exists(Foo::new). | ||
216 | Foo(a). | ||
217 | Foo(foo1). | ||
218 | """), Arguments.of(""" | ||
219 | node a. | ||
220 | class Foo. | ||
221 | """, """ | ||
222 | Foo(a). | ||
223 | ?exists(a). | ||
224 | scope Foo = 1, Foo += 1. | ||
225 | """, """ | ||
226 | !exists(a). | ||
227 | !exists(Foo::new). | ||
228 | Foo(foo1). | ||
186 | """)); | 229 | """)); |
187 | } | 230 | } |
188 | } | 231 | } |