protected static ASTNode unwrap(ASTNode node) { ASTNode result = node; while (result instanceof TermNode && result.hasChildren()) { result = result.child(0); } return result; }
public ASTNode substituteCommonTerms(ASTNode tree, IExpressionContext context) { ASTNode copy = tree.createCopy(true); final Map<Integer, List<ASTNode>> result = new HashMap<>(); final INodeVisitor visitor = new INodeVisitor() { @Override public boolean visit(ASTNode node, int currentDepth) { final Integer hash = node.hashCode(); List<ASTNode> existing = result.get(hash); if (existing == null) { existing = new ArrayList<>(); result.put(hash, existing); } existing.add(node); return true; } }; tree.visitInOrder(visitor); outer: for (Map.Entry<Integer, List<ASTNode>> entry : result.entrySet()) { final int hash = entry.getKey(); if (entry.getValue().size() <= 1) { continue; } for (ASTNode n : entry.getValue()) { if (n instanceof IdentifierNode) { continue outer; } } // create new variable final ASTNode value = entry.getValue().get(0).createCopy(true); final Identifier identifier = context.createIdentifier(value); final boolean substituted = replaceMatchingTermsWithVariable(context, copy, hash, identifier); if (substituted) { debugPrintln( "SUBSTITUTE: " + value + " => " + identifier + " ( " + entry.getValue().size() + " times )"); } else { context.remove(identifier); } } return copy; }
public void testParsing() { String expr = " a or not (( b and c ) or d) = e "; ASTNode expression = new BooleanExpressionParser().parse(expr, false); System.out.println("PARSED: " + expression); PrintWriter writer = new PrintWriter(System.out, true); expression.print(writer); writer.flush(); writer.close(); assertEquals("a OR NOT ((b AND c) OR d) = e", expression.toString()); }
protected ASTNode internalReduce(ASTNode term, final IExpressionContext context) { final ASTNode result = term.createCopy(true); final MutatingNodeVisitor visitor = new MutatingNodeVisitor(context) { @Override public void visit(ASTNode node, IExpressionContext context, IIterationContext it) { if (node.hasParent()) { if (node instanceof IdentifierNode && !node.hasLiteralValue(context)) { return; } final ASTNode reduced = node.evaluate(context); debugPrintln("REDUCE: " + node + " evaluates to " + reduced); if (reduced != null && reduced != node && reduced != unwrap(node)) { debugPrintln("REDUCE: Replacing " + node + " -> " + reduced); node.replaceWith(reduced); it.astMutated(); it.stop(); } } } }; applyPostOrder(result, visitor); return result; }
public void moveToTopLevel(ASTNode nodeToMove) { final List<ASTNode> pathFromRoot = nodeToMove.getPathFromRoot(); final ASTNode root = pathFromRoot.get(0); if (root == nodeToMove || pathFromRoot.size() == 2 && pathFromRoot.get(0) instanceof TermNode) { return; // nothing to do, already at top-level } /* * Task: isolate d * * Initial term: (a or b) and (c or d) = true * * Step 1: align associated operator (OR) with outer expression so we can re-order the AST nodes * * => (a or b) and (not(c) and not(d) ) = true // de-morgan * * =>(a or b) and not(c) and not(d) = true * * => d or ( (a or b) and not(c) and not(d) ) = d or true * * => * => AND * + => OR * * d + ( ( a + b ) * not(c) * not(d) ) = d + true * * => d OR (a OR b) AND (d OR NOT (c)) AND (d OR NOT (d)) = d or true * * => */ /* // a or (b and c) = (a or b) and (a or c) */ }
public Set<Identifier> gatherIdentifiers(ASTNode node) { final Set<Identifier> result = new HashSet<Identifier>(); INodeVisitor visitor = new INodeVisitor() { @Override public boolean visit(ASTNode node, int currentDepth) { if (node instanceof IdentifierNode) { result.add(((IdentifierNode) node).getIdentifier()); } return true; } }; node.visitInOrder(visitor); return result; }
public Boolean isTrue(BooleanExpression expr, IExpressionContext context) { ASTNode lhs = expr.getLHS(); ASTNode rhs = expr.getRHS(); ASTNode value1 = lhs.evaluate(context); ASTNode value2 = rhs.evaluate(context); if (value1 != null && value2 != null) { return value1.isEquivalent(value2, context); } return null; }
public ASTNode substituteIdentifiers(ASTNode input, IExpressionContext context) { ASTNode result = input.createCopy(true); final MutatingNodeVisitor visitor = new MutatingNodeVisitor(context) { @Override protected void visit(ASTNode node, IExpressionContext context, IIterationContext it) { if (node instanceof IdentifierNode) { Identifier id = ((IdentifierNode) node).getIdentifier(); ASTNode value = context.tryLookup(id); if (value != null) { node.replaceWith(value); it.astMutated(); } } } }; applyInOrder(result, visitor); return result; }
public ASTNode expand(ASTNode term, IExpressionContext context, boolean deleteExpandedVars) { final ASTNode result = term.createCopy(true); final Set<Identifier> expandedIdentifiers = new HashSet<>(); boolean expanded = false; do { expanded = false; final MutatingNodeVisitor visitor = new MutatingNodeVisitor(context) { @Override public void visit(ASTNode node, IExpressionContext context, IIterationContext it) { final ASTNode unwrapped = unwrap(node); if (node.hasParent() && unwrapped instanceof IdentifierNode) { final ASTNode expanded = unwrapped.evaluate(context); if (expanded != null && expanded != unwrapped) { debugPrintln("EXPAND: Expanding " + node + " -> " + expanded); expandedIdentifiers.add(((IdentifierNode) unwrapped).getIdentifier()); node.replaceWith(expanded); it.astMutated(); } } } }; expanded = applyPostOrder(result, visitor); } while (expanded); if (deleteExpandedVars) { for (Identifier id : expandedIdentifiers) { context.remove(id); } } return result; }
protected static boolean applyPreOrder(ASTNode term, MutatingNodeVisitor visitor) { do { term.visitPreOrder(visitor); } while (visitor.isASTMutated()); return visitor.getMutationCount() > 0; }
protected ASTNode simplifyTerm(ASTNode term, final IExpressionContext context) { debugPrintln("Simplifying " + term.toString(true)); final Comparator<ASTNode> comp = new Comparator<ASTNode>() { @Override public int compare(ASTNode o1, ASTNode o2) { if (o1.isLeafNode() && o2.isLeafNode()) { return o2.toString().compareTo(o1.toString()); } else if (o1.isLeafNode()) { return 1; } else if (o2.isLeafNode()) { return -1; } return 0; } }; ASTNode result = term.createCopy(true); result = reduce(result, context); int loopCounter = 0; boolean simplified = result.sortChildrenAscending(comp); do { simplified = false; // Assoziativgesetz // (a and b) and c = a and (b and c) // (a or b) or c = a or (b or c) simplified |= applyLawOfAssociativity(context, result); // Idempotenzgesetze: => x and x = x // => x or x = x simplified |= applyLawOfIdemPotency(context, result); // double negation: => not( not a) = a simplified |= applyRuleOfDoubleNegation(context, result); // Neutralitätsgesetze => a and 1 = a // => a or 0 = a simplified |= applyLawOfIdentity(context, result); // Extremalgesetze => a and 0 =0 // => a or 1 =1 simplified |= applyLawOfExtrema(context, result); // Komplementärgesetze => a and not a = 0 // => a or not a = 1 simplified |= applyLawOfComplements(context, result); // Absorptionsgesetze => a or (a and b) = a // => a and(a or b) = a simplified |= applyLawOfAbsorption(context, result); // Distributionsgesetz // a and (b or c) = (a and b) or (a and c) // a or (b and c) = (a or b) and (a or c) simplified |= applyDistributiveLaw(context, result); // De Morgansche Gesetze => not(a and b) = not a or not b // => not(a or b) = not a and not b simplified = applyLawOfDeMorgan(context, result); // De Morgansche Gesetze => not a or not b = not(a and b) // => not a and not b = not(a or b) // simplified |= applyInverseLawOfDeMorgan(context,result); loopCounter++; } while (simplified || loopCounter < 2); // get rid of all variables we eliminated context.retainOnly(gatherIdentifiers(result)); return result; }
protected ASTNode maybeWrapInTerm(ASTNode node) { if (node.getNodeCount() == 1) { return node; } return new TermNode(node); }
private static boolean isNonTrivialTerm(ASTNode node) { return node instanceof TermNode && !node.child(0).isLeafNode(); }