diff --git a/CHANGELOG.md b/CHANGELOG.md index 8945930..19cd2f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Features * more and advanced simplification rules -* adds support for exponentiation operator +* adds support for exponentiation (power) and modulus (absolute value) * new examples: prime numbers, permutation generator, and graph coloring (extended) ## 0.1.7 (2018-04-08) diff --git a/include/anthem/AST.h b/include/anthem/AST.h index f5c70fe..587819d 100644 --- a/include/anthem/AST.h +++ b/include/anthem/AST.h @@ -293,6 +293,30 @@ struct String //////////////////////////////////////////////////////////////////////////////////////////////////// +struct UnaryOperation +{ + enum class Operator + { + Absolute + }; + + explicit UnaryOperation(Operator operator_, Term &&argument) + : operator_{operator_}, + argument{std::move(argument)} + { + } + + UnaryOperation(const UnaryOperation &other) = delete; + UnaryOperation &operator=(const UnaryOperation &other) = delete; + UnaryOperation(UnaryOperation &&other) noexcept = default; + UnaryOperation &operator=(UnaryOperation &&other) noexcept = default; + + Operator operator_; + Term argument; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + struct Variable { explicit Variable(VariableDeclaration *declaration) diff --git a/include/anthem/ASTForward.h b/include/anthem/ASTForward.h index d679222..b70f2c2 100644 --- a/include/anthem/ASTForward.h +++ b/include/anthem/ASTForward.h @@ -37,6 +37,7 @@ struct Or; struct Predicate; struct SpecialInteger; struct String; +struct UnaryOperation; struct Variable; struct VariableDeclaration; using VariableDeclarationPointer = std::unique_ptr; @@ -68,6 +69,7 @@ using Term = Clingo::Variant< Interval, SpecialInteger, String, + UnaryOperation, Variable>; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/anthem/ASTVisitors.h b/include/anthem/ASTVisitors.h index 18d075a..5f63691 100644 --- a/include/anthem/ASTVisitors.h +++ b/include/anthem/ASTVisitors.h @@ -165,6 +165,14 @@ struct RecursiveTermVisitor return T::accept(string, term, std::forward(arguments)...); } + template + ReturnType visit(UnaryOperation &unaryOperation, Term &term, Arguments &&... arguments) + { + unaryOperation.argument.accept(*this, unaryOperation.argument, std::forward(arguments)...); + + return T::accept(unaryOperation, term, std::forward(arguments)...); + } + template ReturnType visit(Variable &variable, Term &term, Arguments &&... arguments) { diff --git a/include/anthem/Equality.h b/include/anthem/Equality.h index 158cf34..06eb834 100644 --- a/include/anthem/Equality.h +++ b/include/anthem/Equality.h @@ -382,6 +382,19 @@ struct TermEqualityVisitor : Tristate::False; } + Tristate visit(const UnaryOperation &unaryOperation, const Term &otherTerm) + { + if (!otherTerm.is()) + return Tristate::Unknown; + + const auto &otherUnaryOperation = otherTerm.get(); + + if (unaryOperation.operator_ != otherUnaryOperation.operator_) + return Tristate::Unknown; + + return equal(unaryOperation.argument, otherUnaryOperation.argument); + } + Tristate visit(const Variable &variable, const Term &otherTerm) { if (!otherTerm.is()) diff --git a/include/anthem/Term.h b/include/anthem/Term.h index 01373f8..5c2cbd7 100644 --- a/include/anthem/Term.h +++ b/include/anthem/Term.h @@ -48,6 +48,23 @@ ast::BinaryOperation::Operator translate(Clingo::AST::BinaryOperator binaryOpera //////////////////////////////////////////////////////////////////////////////////////////////////// +ast::UnaryOperation::Operator translate(Clingo::AST::UnaryOperator unaryOperator, const Clingo::AST::Term &term) +{ + switch (unaryOperator) + { + case Clingo::AST::UnaryOperator::Absolute: + return ast::UnaryOperation::Operator::Absolute; + case Clingo::AST::UnaryOperator::Minus: + throw TranslationException(term.location, "binary operation “minus” currently unsupported"); + case Clingo::AST::UnaryOperator::Negation: + throw TranslationException(term.location, "binary operation “negation” currently unsupported"); + } + + throw TranslationException(term.location, "unknown unary operation"); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + ast::Term translate(const Clingo::AST::Term &term, RuleContext &ruleContext, const ast::VariableStack &variableStack); //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -106,12 +123,6 @@ struct TermTranslateVisitor return ast::Term::make(ruleContext.freeVariables.back().get()); } - std::optional visit(const Clingo::AST::UnaryOperation &, const Clingo::AST::Term &term, RuleContext &, const ast::VariableStack &) - { - throw TranslationException(term.location, "“unary operation” terms currently unsupported"); - return std::nullopt; - } - std::optional visit(const Clingo::AST::BinaryOperation &binaryOperation, const Clingo::AST::Term &term, RuleContext &ruleContext, const ast::VariableStack &variableStack) { const auto operator_ = translate(binaryOperation.binary_operator, term); @@ -121,6 +132,14 @@ struct TermTranslateVisitor return ast::Term::make(operator_, std::move(left), std::move(right)); } + std::optional visit(const Clingo::AST::UnaryOperation &unaryOperation, const Clingo::AST::Term &term, RuleContext &ruleContext, const ast::VariableStack &variableStack) + { + const auto operator_ = translate(unaryOperation.unary_operator, term); + auto argument = translate(unaryOperation.argument, ruleContext, variableStack); + + return ast::Term::make(operator_, std::move(argument)); + } + std::optional visit(const Clingo::AST::Interval &interval, const Clingo::AST::Term &, RuleContext &ruleContext, const ast::VariableStack &variableStack) { auto left = translate(interval.left, ruleContext, variableStack); diff --git a/include/anthem/output/AST.h b/include/anthem/output/AST.h index 8f7ea04..440993c 100644 --- a/include/anthem/output/AST.h +++ b/include/anthem/output/AST.h @@ -53,6 +53,7 @@ output::ColorStream &print(output::ColorStream &stream, const Interval &interval output::ColorStream &print(output::ColorStream &stream, const Predicate &predicate, PrintContext &printContext); output::ColorStream &print(output::ColorStream &stream, const SpecialInteger &specialInteger, PrintContext &printContext); output::ColorStream &print(output::ColorStream &stream, const String &string, PrintContext &printContext); +output::ColorStream &print(output::ColorStream &stream, const UnaryOperation &unaryOperation, PrintContext &printContext); output::ColorStream &print(output::ColorStream &stream, const Variable &variable, PrintContext &printContext); output::ColorStream &print(output::ColorStream &stream, const VariableDeclaration &variableDeclaration, PrintContext &printContext); @@ -282,6 +283,29 @@ inline output::ColorStream &print(output::ColorStream &stream, const String &str //////////////////////////////////////////////////////////////////////////////////////////////////// +inline output::ColorStream &print(output::ColorStream &stream, const UnaryOperation &unaryOperation, PrintContext &printContext) +{ + switch (unaryOperation.operator_) + { + case UnaryOperation::Operator::Absolute: + stream << "|"; + break; + } + + print(stream, unaryOperation.argument, printContext); + + switch (unaryOperation.operator_) + { + case UnaryOperation::Operator::Absolute: + stream << "|"; + break; + } + + return stream; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + inline output::ColorStream &print(output::ColorStream &stream, const Variable &variable, PrintContext &printContext) { assert(variable.declaration != nullptr); diff --git a/src/anthem/ASTCopy.cpp b/src/anthem/ASTCopy.cpp index d161873..5f71b43 100644 --- a/src/anthem/ASTCopy.cpp +++ b/src/anthem/ASTCopy.cpp @@ -159,6 +159,13 @@ String prepareCopy(const String &other) //////////////////////////////////////////////////////////////////////////////////////////////////// +UnaryOperation prepareCopy(const UnaryOperation &other) +{ + return UnaryOperation(other.operator_, prepareCopy(other.argument)); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// + Variable prepareCopy(const Variable &other) { return Variable(other.declaration); @@ -320,6 +327,12 @@ struct FixDanglingVariablesInTermVisitor { } + template + void visit(UnaryOperation &unaryOperation, Arguments &&... arguments) + { + unaryOperation.argument.accept(*this, std::forward(arguments)...); + } + void visit(Variable &variable, ScopedFormula &scopedFormula, VariableStack &variableStack, std::map &replacements) { diff --git a/src/anthem/ASTUtils.cpp b/src/anthem/ASTUtils.cpp index a4d62c2..e25da41 100644 --- a/src/anthem/ASTUtils.cpp +++ b/src/anthem/ASTUtils.cpp @@ -178,6 +178,11 @@ struct CollectFreeVariablesVisitor { } + void visit(UnaryOperation &unaryOperation, VariableStack &variableStack, std::vector &freeVariables) + { + unaryOperation.argument.accept(*this, variableStack, freeVariables); + } + void visit(Variable &variable, VariableStack &variableStack, std::vector &freeVariables) { if (variableStack.contains(*variable.declaration)) diff --git a/tests/TestCompletion.cpp b/tests/TestCompletion.cpp index f675df4..d35d153 100644 --- a/tests/TestCompletion.cpp +++ b/tests/TestCompletion.cpp @@ -184,4 +184,12 @@ TEST_CASE("[completion] Rules are completed", "[completion]") CHECK(output.str() == "forall V1, V2 not color(V1, V2)\nforall U1, U2, U3 (not color(U1, U2) or not color(U1, U3) or U2 = U3)\n"); } + + SECTION("absolute value operation") + { + input << "adj(X, Y) :- X = 1..n, Y = 1..n, |X - Y| = 1."; + anthem::translate("input", input, context); + + CHECK(output.str() == "forall V1, V2 (adj(V1, V2) <-> (V1 in 1..n and V2 in 1..n and |(V1 - V2)| = 1))\n"); + } }