diff --git a/src/main/java/de/tlc4b/analysis/MachineContext.java b/src/main/java/de/tlc4b/analysis/MachineContext.java index ff194588996457bf2a9878714cba8103dd67b1bc..ae6f85e10e87716b1af3cfedb36b7207b2fb0f25 100644 --- a/src/main/java/de/tlc4b/analysis/MachineContext.java +++ b/src/main/java/de/tlc4b/analysis/MachineContext.java @@ -635,6 +635,31 @@ public class MachineContext extends DepthFirstAdapter { } node.getPredicate().apply(this); node.getSubstitution().apply(this); + contextTable.remove(contextTable.size() - 1); + } + + @Override + public void caseALetExpressionExpression(ALetExpressionExpression node) { + contextTable.add(new LinkedHashMap<>()); + List<PExpression> copy = new ArrayList<>(node.getIdentifiers()); + for (PExpression e : copy) { + putLocalVariableIntoCurrentScope((AIdentifierExpression) e); + } + node.getAssignment().apply(this); + node.getExpr().apply(this); + contextTable.remove(contextTable.size() - 1); + } + + @Override + public void caseALetPredicatePredicate(ALetPredicatePredicate node) { + contextTable.add(new LinkedHashMap<>()); + List<PExpression> copy = new ArrayList<>(node.getIdentifiers()); + for (PExpression e : copy) { + putLocalVariableIntoCurrentScope((AIdentifierExpression) e); + } + node.getAssignment().apply(this); + node.getPred().apply(this); + contextTable.remove(contextTable.size() - 1); } @Override @@ -646,6 +671,7 @@ public class MachineContext extends DepthFirstAdapter { } node.getWhere().apply(this); node.getThen().apply(this); + contextTable.remove(contextTable.size() - 1); } @Override diff --git a/src/main/java/de/tlc4b/analysis/Renamer.java b/src/main/java/de/tlc4b/analysis/Renamer.java index d98a9ffe40983a34cb856a37234bb8579d71315a..dd9b2d4ed3e17596b7087a3f428b9bd834d4d5ed 100644 --- a/src/main/java/de/tlc4b/analysis/Renamer.java +++ b/src/main/java/de/tlc4b/analysis/Renamer.java @@ -19,6 +19,8 @@ import de.be4.classicalb.core.parser.node.AGeneralProductExpression; import de.be4.classicalb.core.parser.node.AGeneralSumExpression; import de.be4.classicalb.core.parser.node.AIdentifierExpression; import de.be4.classicalb.core.parser.node.ALambdaExpression; +import de.be4.classicalb.core.parser.node.ALetExpressionExpression; +import de.be4.classicalb.core.parser.node.ALetPredicatePredicate; import de.be4.classicalb.core.parser.node.ALetSubstitution; import de.be4.classicalb.core.parser.node.AOperation; import de.be4.classicalb.core.parser.node.APredicateDefinitionDefinition; @@ -350,6 +352,34 @@ public class Renamer extends DepthFirstAdapter { removeLastContext(); } + @Override + public void caseALetExpressionExpression(ALetExpressionExpression node) { + List<PExpression> list = new ArrayList<>(node.getIdentifiers()); + evalBoundedVariables(node, list); + + List<PExpression> copy = new ArrayList<>(node.getIdentifiers()); + for (PExpression e : copy) { + e.apply(this); + } + node.getAssignment().apply(this); + node.getExpr().apply(this); + removeLastContext(); + } + + @Override + public void caseALetPredicatePredicate(ALetPredicatePredicate node) { + List<PExpression> list = new ArrayList<>(node.getIdentifiers()); + evalBoundedVariables(node, list); + + List<PExpression> copy = new ArrayList<>(node.getIdentifiers()); + for (PExpression e : copy) { + e.apply(this); + } + node.getAssignment().apply(this); + node.getPred().apply(this); + removeLastContext(); + } + @Override public void caseAVarSubstitution(AVarSubstitution node) { List<PExpression> list = new ArrayList<>(node.getIdentifiers()); diff --git a/src/main/java/de/tlc4b/analysis/Typechecker.java b/src/main/java/de/tlc4b/analysis/Typechecker.java index 55e34f707b6f8ae63d3d45fb9fd816a389ec50a6..4503bc3bc169f2cd16573d4318d8afd0549a9329 100644 --- a/src/main/java/de/tlc4b/analysis/Typechecker.java +++ b/src/main/java/de/tlc4b/analysis/Typechecker.java @@ -605,6 +605,38 @@ public class Typechecker extends DepthFirstAdapter implements ITypechecker { node.getSubstitution().apply(this); } + @Override + public void caseALetExpressionExpression(ALetExpressionExpression node) { + List<PExpression> copy = new ArrayList<>(node.getIdentifiers()); + for (PExpression e : copy) { + AIdentifierExpression v = (AIdentifierExpression) e; + setType(v, new UntypedType()); + } + + setType(node.getAssignment(), BoolType.getInstance()); + node.getAssignment().apply(this); + + setType(node.getExpr(), new UntypedType()); + node.getExpr().apply(this); + unify(getType(node), getType(node.getExpr()), node); + } + + @Override + public void caseALetPredicatePredicate(ALetPredicatePredicate node) { + List<PExpression> copy = new ArrayList<>(node.getIdentifiers()); + for (PExpression e : copy) { + AIdentifierExpression v = (AIdentifierExpression) e; + setType(v, new UntypedType()); + } + + setType(node.getAssignment(), BoolType.getInstance()); + node.getAssignment().apply(this); + + setType(node.getPred(), BoolType.getInstance()); + node.getPred().apply(this); + unify(getType(node), getType(node.getPred()), node); + } + /**************************************************************************** * Arithmetic operators * ****************************************************************************/ diff --git a/src/main/java/de/tlc4b/analysis/UnsupportedConstructsFinder.java b/src/main/java/de/tlc4b/analysis/UnsupportedConstructsFinder.java index 15cca6529a0b4d8d6a6edb9a4993c29a5248a998..55459d4b0447badd56a280ebf3a5a53a00d112e8 100644 --- a/src/main/java/de/tlc4b/analysis/UnsupportedConstructsFinder.java +++ b/src/main/java/de/tlc4b/analysis/UnsupportedConstructsFinder.java @@ -37,9 +37,6 @@ public class UnsupportedConstructsFinder extends DepthFirstAdapter { // should have been rewritten in parser add(AIfPredicatePredicate.class); add(AIfElsifPredicatePredicate.class); - - add(ALetExpressionExpression.class); - add(ALetPredicatePredicate.class); } private static void add(Class<? extends Node> clazz) { diff --git a/src/main/java/de/tlc4b/analysis/typerestriction/TypeRestrictor.java b/src/main/java/de/tlc4b/analysis/typerestriction/TypeRestrictor.java index 2d2a7d6d3852312a1a6691e4034d366513b9b58b..9cc265d4e2c88c24a04cf22d336ffb137b3c5b6d 100644 --- a/src/main/java/de/tlc4b/analysis/typerestriction/TypeRestrictor.java +++ b/src/main/java/de/tlc4b/analysis/typerestriction/TypeRestrictor.java @@ -25,6 +25,8 @@ import de.be4.classicalb.core.parser.node.AImplicationPredicate; import de.be4.classicalb.core.parser.node.AInitialisationMachineClause; import de.be4.classicalb.core.parser.node.AIntersectionExpression; import de.be4.classicalb.core.parser.node.ALambdaExpression; +import de.be4.classicalb.core.parser.node.ALetExpressionExpression; +import de.be4.classicalb.core.parser.node.ALetPredicatePredicate; import de.be4.classicalb.core.parser.node.ALetSubstitution; import de.be4.classicalb.core.parser.node.AMemberPredicate; import de.be4.classicalb.core.parser.node.ANotMemberPredicate; @@ -456,6 +458,22 @@ public class TypeRestrictor extends DepthFirstAdapter { createRestrictedTypeofLocalVariables(new HashSet<>(node.getIdentifiers()), false); } + @Override + public void inALetExpressionExpression(ALetExpressionExpression node) { + super.inALetExpressionExpression(node); + // no type restriction, will use "LET var == value IN expr" construct + } + + @Override + public void inALetPredicatePredicate(ALetPredicatePredicate node) { + HashSet<Node> list = new HashSet<>(); + List<PExpression> copy = new ArrayList<>(node.getIdentifiers()); + list.addAll(copy); + list.addAll(getExpectedIdentifier(node)); + analysePredicate(node.getAssignment(), list, new HashSet<>()); + createRestrictedTypeofLocalVariables(new HashSet<>(node.getIdentifiers()), false); + } + private Hashtable<Node, Node> variablesHashTable; public void inABecomesSuchSubstitution(ABecomesSuchSubstitution node) { diff --git a/src/main/java/de/tlc4b/prettyprint/TLAPrinter.java b/src/main/java/de/tlc4b/prettyprint/TLAPrinter.java index aa0c57c918bff4b949a8bc77eafcd0babf7679ab..9474ba326984a31d5db08e5b5c7860ebe205ee3f 100644 --- a/src/main/java/de/tlc4b/prettyprint/TLAPrinter.java +++ b/src/main/java/de/tlc4b/prettyprint/TLAPrinter.java @@ -2,10 +2,12 @@ package de.tlc4b.prettyprint; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Map; import de.be4.classicalb.core.parser.analysis.DepthFirstAdapter; import de.be4.classicalb.core.parser.node.*; @@ -28,6 +30,7 @@ import de.tlc4b.btypes.PairType; import de.tlc4b.btypes.SetType; import de.tlc4b.btypes.StructType; import de.tlc4b.btypes.UntypedType; +import de.tlc4b.exceptions.TranslationException; import de.tlc4b.ltl.LTLFormulaVisitor; import de.tlc4b.tla.ConfigFile; import de.tlc4b.tla.TLADefinition; @@ -997,20 +1000,18 @@ public class TLAPrinter extends DepthFirstAdapter { @Override public void caseALetSubstitution(ALetSubstitution node) { inALetSubstitution(node); + moduleStringAppend("\\E "); List<PExpression> copy = new ArrayList<>(node.getIdentifiers()); - if (!copy.isEmpty()) { - moduleStringAppend("\\E "); - for (int i = 0; i < copy.size(); i++) { - PExpression e = copy.get(i); - e.apply(this); - moduleStringAppend(" \\in "); - typeRestrictor.getRestrictedNode(e).apply(this); - if (i < copy.size() - 1) { - moduleStringAppend(", "); - } + for (int i = 0; i < copy.size(); i++) { + PExpression e = copy.get(i); + e.apply(this); + moduleStringAppend(" \\in "); + typeRestrictor.getRestrictedNode(e).apply(this); + if (i < copy.size() - 1) { + moduleStringAppend(", "); } - moduleStringAppend(" : "); } + moduleStringAppend(" : "); if (typeRestrictor.isARemovedNode(node.getPredicate())) { moduleStringAppend("TRUE"); @@ -1025,6 +1026,87 @@ public class TLAPrinter extends DepthFirstAdapter { outALetSubstitution(node); } + @Override + public void caseALetPredicatePredicate(ALetPredicatePredicate node) { + inALetPredicatePredicate(node); + moduleStringAppend("\\E "); + List<PExpression> copy = new ArrayList<>(node.getIdentifiers()); + for (int i = 0; i < copy.size(); i++) { + PExpression e = copy.get(i); + e.apply(this); + moduleStringAppend(" \\in "); + typeRestrictor.getRestrictedNode(e).apply(this); + if (i < copy.size() - 1) { + moduleStringAppend(", "); + } + } + moduleStringAppend(" : "); + + if (typeRestrictor.isARemovedNode(node.getAssignment())) { + moduleStringAppend("TRUE"); + } else { + node.getAssignment().apply(this); + } + + moduleStringAppend(" /\\ "); + node.getPred().apply(this); + + outALetPredicatePredicate(node); + } + + private static Map<String, PExpression> getValuesFromEqualPredicates(PPredicate p, Map<String, PExpression> values) { + if (p instanceof AEqualPredicate) { + AEqualPredicate eq = (AEqualPredicate) p; + PExpression left = eq.getLeft(); + if (left instanceof AIdentifierExpression) { + String s = Utils.getAIdentifierAsString((AIdentifierExpression) left); + if (values.containsKey(s)) { + throw new TranslationException("invalid predicate in LET expr: " + p); + } + values.put(s, eq.getRight()); + } else { + throw new TranslationException("invalid predicate in LET expr: " + p); + } + } else if (p instanceof AConjunctPredicate) { + AConjunctPredicate conj = (AConjunctPredicate) p; + getValuesFromEqualPredicates(conj.getLeft(), values); + getValuesFromEqualPredicates(conj.getRight(), values); + } else { + throw new TranslationException("invalid predicate in LET expr: " + p); + } + + return values; + } + + @Override + public void caseALetExpressionExpression(ALetExpressionExpression node) { + inALetExpressionExpression(node); + moduleStringAppend("LET "); + Map<String, PExpression> values = getValuesFromEqualPredicates(node.getAssignment(), new HashMap<>()); + List<PExpression> copy = new ArrayList<>(node.getIdentifiers()); + for (int i = 0; i < copy.size(); i++) { + PExpression e = copy.get(i); + e.apply(this); + moduleStringAppend(" == "); + + String identifier = Utils.getAIdentifierAsString((AIdentifierExpression) e); + PExpression value = values.get(identifier); + if (value == null) { + throw new TranslationException("no equals predicate for identifier " + identifier + " in LET expr"); + } + value.apply(this); + + if (i < copy.size() - 1) { + moduleStringAppend(", "); + } + } + + moduleStringAppend(" IN "); + node.getExpr().apply(this); + + outALetExpressionExpression(node); + } + @Override public void caseAOperation(AOperation node) { String name = renamer.getNameOfRef(node); diff --git a/src/test/java/de/tlc4b/analysis/UnsupportedConstructsTest.java b/src/test/java/de/tlc4b/analysis/UnsupportedConstructsTest.java index dd99257b7a4ffc1ff7c8aa8635ddb3b668aceff6..d1720cac8a80f5f7be01bb5eeeecaabd32f058f4 100644 --- a/src/test/java/de/tlc4b/analysis/UnsupportedConstructsTest.java +++ b/src/test/java/de/tlc4b/analysis/UnsupportedConstructsTest.java @@ -25,16 +25,4 @@ public class UnsupportedConstructsTest { final String machine = "MACHINE M FREETYPES F = F1, F2(INTEGER) END"; translate(machine); } - - @Test(expected = NotSupportedException.class) - public void testLetExpr() throws Exception { - final String machine = "MACHINE M VARIABLES x INVARIANT x : INTEGER INITIALISATION x := (LET foo BE foo=42 IN foo END) END"; - translate(machine); - } - - @Test(expected = NotSupportedException.class) - public void testLetPred() throws Exception { - final String machine = "MACHINE M VARIABLES x INVARIANT x : INTEGER INITIALISATION x : (LET foo BE foo=42 IN x=foo END) END"; - translate(machine); - } } diff --git a/src/test/java/de/tlc4b/prettyprint/OperationsTest.java b/src/test/java/de/tlc4b/prettyprint/OperationsTest.java index 1741ed12ab89d41915f1e5f45f3c31da107b9f36..bad0b01ad8bb5ece9b2bc415909504f91a6db3f4 100644 --- a/src/test/java/de/tlc4b/prettyprint/OperationsTest.java +++ b/src/test/java/de/tlc4b/prettyprint/OperationsTest.java @@ -426,5 +426,46 @@ public class OperationsTest { + "===="; compare(expected, machine); } - + + @Test + public void testOperationWithLetExpr() throws Exception { + String machine = "MACHINE test\n" + + "VARIABLES x\n" + + "INVARIANT x : 1..10\n" + + "INITIALISATION x := 1\n" + + "OPERATIONS\n" + + "inc = x := LET y BE y = x+1 IN y+1 END\n" + + "END"; + + String expected = "---- MODULE test ----\n" + + "EXTENDS Naturals\n" + + "VARIABLES x\n" + + "Invariant1 == x \\in 1..10\n" + + "Init == x = 1\n" + + "inc == x' = LET y == x+1 IN y+1\n" + + "Next == \\/ inc\n" + + "===="; + compare(expected, machine); + } + + @Test + public void testOperationWithLetPred() throws Exception { + String machine = "MACHINE test\n" + + "VARIABLES x\n" + + "INVARIANT x : 1..10\n" + + "INITIALISATION x := 1\n" + + "OPERATIONS\n" + + "inc = SELECT (LET y BE y = 1 IN x=y END) THEN x := x+1 END\n" + + "END"; + + String expected = "---- MODULE test ----\n" + + "EXTENDS Naturals\n" + + "VARIABLES x\n" + + "Invariant1 == x \\in 1..10\n" + + "Init == x = 1 \n" + + "inc == (\\E y \\in {1} : TRUE /\\ x = y /\\ x' = x + 1)\n" + + "Next == \\/ inc\n" + + "===="; + compare(expected, machine); + } }