Implemented explicit syntax tree representation for first-order formulas.

This commit is contained in:
Patrick Lühne 2017-03-15 16:00:00 +01:00
parent 7ff537a515
commit 9e6d135781
Signed by: patrick
GPG Key ID: 05F3611E97A70ABF
12 changed files with 1225 additions and 569 deletions

View File

@ -22,10 +22,20 @@ struct BinaryOperation
{ {
enum class Operator enum class Operator
{ {
Add, Plus,
Subtract Minus,
Multiplication,
Division,
Modulo
}; };
BinaryOperation(Operator operator_, Term &&left, Term &&right)
: operator_{operator_},
left{std::move(left)},
right{std::move(right)}
{
}
Operator operator_; Operator operator_;
Term left; Term left;
Term right; Term right;
@ -35,53 +45,173 @@ struct BinaryOperation
struct Boolean struct Boolean
{ {
Boolean(bool value)
: value{value}
{
}
bool value = false; bool value = false;
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
struct Equals struct Comparison
{ {
enum class Operator
{
GreaterThan,
LessThan,
LessEqual,
GreaterEqual,
NotEqual,
Equal
};
Comparison(Operator operator_, Term &&left, Term &&right)
: operator_{operator_},
left{std::move(left)},
right{std::move(right)}
{
}
Operator operator_;
Term left; Term left;
Term right; Term right;
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
struct Constant
{
Constant(std::string &&name)
: name{std::move(name)}
{
}
std::string name;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
struct Function struct Function
{ {
const char *name = nullptr; Function(std::string &&name, std::vector<Term> &&arguments)
: name{std::move(name)},
arguments{std::move(arguments)}
{
}
std::string name;
std::vector<Term> arguments; std::vector<Term> arguments;
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
struct In
{
In(Term &&element, Term &&set)
: element{std::move(element)},
set{std::move(set)}
{
}
Term element;
Term set;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
struct Integer struct Integer
{ {
Integer(int value)
: value{value}
{
}
int value; int value;
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
struct IntegerRange struct Interval
{ {
Integer from; Interval(Term &&from, Term &&to)
Integer to; : from{std::move(from)},
to{std::move(to)}
{
}
Term from;
Term to;
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
struct Predicate struct Predicate
{ {
const char *name = nullptr; Predicate(std::string &&name)
: name{std::move(name)}
{
}
Predicate(std::string &&name, std::vector<Term> &&arguments)
: name{std::move(name)},
arguments{std::move(arguments)}
{
}
std::string name;
std::vector<Term> arguments; std::vector<Term> arguments;
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
struct SpecialInteger
{
enum class Type
{
Infimum,
Supremum
};
SpecialInteger(Type type)
: type{type}
{
}
Type type;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
struct String
{
String(std::string &&text)
: text{std::move(text)}
{
}
std::string text;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
struct Variable struct Variable
{ {
const char *name = nullptr; enum class Type
{
UserDefined,
Reserved
};
Variable(std::string &&name, Type type)
: name{std::move(name)},
type{type}
{
}
std::string name;
Type type;
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
@ -90,6 +220,13 @@ struct Variable
struct And struct And
{ {
And() = default;
And(std::vector<Formula> &&arguments)
: arguments{std::move(arguments)}
{
}
std::vector<Formula> arguments; std::vector<Formula> arguments;
}; };
@ -97,6 +234,12 @@ struct And
struct Biconditional struct Biconditional
{ {
Biconditional(Formula &&left, Formula &&right)
: left{std::move(left)},
right{std::move(right)}
{
}
Formula left; Formula left;
Formula right; Formula right;
}; };
@ -105,22 +248,40 @@ struct Biconditional
struct Exists struct Exists
{ {
Exists(std::vector<VariablePointer> &&variables, Formula &&argument)
: variables{std::move(variables)},
argument{std::move(argument)}
{
}
std::vector<VariablePointer> variables; std::vector<VariablePointer> variables;
Formula formula; Formula argument;
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
struct ForAll struct ForAll
{ {
ForAll(std::vector<VariablePointer> &&variables, Formula &&argument)
: variables{std::move(variables)},
argument{std::move(argument)}
{
}
std::vector<VariablePointer> variables; std::vector<VariablePointer> variables;
Formula formula; Formula argument;
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
struct Implies struct Implies
{ {
Implies(Formula &&antecedent, Formula &&consequent)
: antecedent{std::move(antecedent)},
consequent{std::move(consequent)}
{
}
Formula antecedent; Formula antecedent;
Formula consequent; Formula consequent;
}; };
@ -129,6 +290,11 @@ struct Implies
struct Not struct Not
{ {
Not(Formula &&argument)
: argument{std::move(argument)}
{
}
Formula argument; Formula argument;
}; };
@ -136,12 +302,261 @@ struct Not
struct Or struct Or
{ {
Or() = default;
Or(std::vector<Formula> &&arguments)
: arguments{std::move(arguments)}
{
}
std::vector<Formula> arguments; std::vector<Formula> arguments;
}; };
////////////////////////////////////////////////////////////////////////////////////////////////////
// Deep Copying
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
BinaryOperation deepCopy(const BinaryOperation &other);
Boolean deepCopy(const Boolean &other);
Comparison deepCopy(const Comparison &other);
Constant deepCopy(const Constant &other);
Function deepCopy(const Function &other);
Integer deepCopy(const Integer &other);
Interval deepCopy(const Interval &other);
Predicate deepCopy(const Predicate &other);
SpecialInteger deepCopy(const SpecialInteger &other);
String deepCopy(const String &other);
Variable deepCopy(const Variable &other);
std::vector<VariablePointer> deepCopy(const std::vector<VariablePointer> &other);
And deepCopy(const And &other);
Biconditional deepCopy(const Biconditional &other);
Exists deepCopy(const Exists &other);
ForAll deepCopy(const ForAll &other);
Implies deepCopy(const Implies &other);
Not deepCopy(const Not &other);
Or deepCopy(const Or &other);
Formula deepCopy(const Formula &formula);
std::vector<Formula> deepCopy(const std::vector<Formula> &formulas);
Term deepCopy(const Term &term);
std::vector<Term> deepCopy(const std::vector<Term> &terms);
////////////////////////////////////////////////////////////////////////////////////////////////////
const auto deepCopyUniquePtr =
[](const auto &uniquePtr) -> typename std::decay<decltype(uniquePtr)>::type
{
using Type = typename std::decay<decltype(uniquePtr)>::type::element_type;
return std::make_unique<Type>(deepCopy(*uniquePtr));
};
////////////////////////////////////////////////////////////////////////////////////////////////////
const auto deepCopyUniquePtrVector =
[](const auto &uniquePtrVector) -> typename std::decay<decltype(uniquePtrVector)>::type
{
using Type = typename std::decay<decltype(uniquePtrVector)>::type::value_type;
std::vector<Type> result;
result.reserve(uniquePtrVector.size());
for (const auto &uniquePtr : uniquePtrVector)
result.emplace_back(deepCopyUniquePtr(uniquePtr));
return result;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
const auto deepCopyVariant =
[](const auto &variant) -> typename std::decay<decltype(variant)>::type
{
return variant.match([](const auto &x) -> typename std::decay<decltype(variant)>::type {return deepCopyUniquePtr(x);});
};
////////////////////////////////////////////////////////////////////////////////////////////////////
const auto deepCopyVariantVector =
[](const auto &variantVector) -> typename std::decay<decltype(variantVector)>::type
{
using Type = typename std::decay<decltype(variantVector)>::type::value_type;
std::vector<Type> result;
result.reserve(variantVector.size());
for (const auto &variant : variantVector)
result.emplace_back(deepCopyVariant(variant));
return result;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
BinaryOperation deepCopy(const BinaryOperation &other)
{
return BinaryOperation(other.operator_, deepCopy(other.left), deepCopy(other.right));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Boolean deepCopy(const Boolean &other)
{
return Boolean(other.value);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Comparison deepCopy(const Comparison &other)
{
return Comparison(other.operator_, deepCopy(other.left), deepCopy(other.right));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Constant deepCopy(const Constant &other)
{
return Constant(std::string(other.name));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Function deepCopy(const Function &other)
{
return Function(std::string(other.name), deepCopy(other.arguments));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Integer deepCopy(const Integer &other)
{
return Integer(other.value);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Interval deepCopy(const Interval &other)
{
return Interval(deepCopy(other.from), deepCopy(other.to));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Predicate deepCopy(const Predicate &other)
{
return Predicate(std::string(other.name), deepCopy(other.arguments));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
SpecialInteger deepCopy(const SpecialInteger &other)
{
return SpecialInteger(other.type);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
String deepCopy(const String &other)
{
return String(std::string(other.text));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Variable deepCopy(const Variable &other)
{
return Variable(std::string(other.name), other.type);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
std::vector<VariablePointer> deepCopy(const std::vector<VariablePointer> &other)
{
return deepCopyUniquePtrVector(other);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
And deepCopy(const And &other)
{
return And(deepCopy(other.arguments));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Biconditional deepCopy(const Biconditional &other)
{
return Biconditional(deepCopy(other.left), deepCopy(other.right));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Exists deepCopy(const Exists &other)
{
return Exists(deepCopy(other.variables), deepCopy(other.argument));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ForAll deepCopy(const ForAll &other)
{
return ForAll(deepCopy(other.variables), deepCopy(other.argument));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
In deepCopy(const In &other)
{
return In(deepCopy(other.element), deepCopy(other.set));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Implies deepCopy(const Implies &other)
{
return Implies(deepCopy(other.antecedent), deepCopy(other.consequent));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Not deepCopy(const Not &other)
{
return Not(deepCopy(other.argument));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Or deepCopy(const Or &other)
{
return Or(deepCopy(other.arguments));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Formula deepCopy(const Formula &formula)
{
return deepCopyVariant(formula);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
std::vector<Formula> deepCopy(const std::vector<Formula> &formulas)
{
return deepCopyVariantVector(formulas);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Term deepCopy(const Term &term)
{
return deepCopyVariant(term);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
std::vector<Term> deepCopy(const std::vector<Term> &terms)
{
return deepCopyVariantVector(terms);
}
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -24,60 +24,55 @@ struct And;
struct BinaryOperation; struct BinaryOperation;
struct Biconditional; struct Biconditional;
struct Boolean; struct Boolean;
struct Comparison;
struct Constant; struct Constant;
struct Equals;
struct Exists; struct Exists;
struct ForAll; struct ForAll;
struct Function; struct Function;
struct Implies; struct Implies;
struct In;
struct Integer; struct Integer;
struct IntegerRange; struct Interval;
struct Not; struct Not;
struct Or; struct Or;
struct Predicate; struct Predicate;
struct SpecialInteger;
struct String;
struct Variable; struct Variable;
using AndPointer = std::unique_ptr<And>; using AndPointer = std::unique_ptr<And>;
using BinaryOperationPointer = std::unique_ptr<BinaryOperation>; using BinaryOperationPointer = std::unique_ptr<BinaryOperation>;
using BiconditionalPointer = std::unique_ptr<Biconditional>; using BiconditionalPointer = std::unique_ptr<Biconditional>;
using BooleanPointer = std::unique_ptr<Boolean>; using BooleanPointer = std::unique_ptr<Boolean>;
using ComparisonPointer = std::unique_ptr<Comparison>;
using ConstantPointer = std::unique_ptr<Constant>; using ConstantPointer = std::unique_ptr<Constant>;
using EqualsPointer = std::unique_ptr<Equals>;
using ExistsPointer = std::unique_ptr<Exists>; using ExistsPointer = std::unique_ptr<Exists>;
using ForAllPointer = std::unique_ptr<ForAll>; using ForAllPointer = std::unique_ptr<ForAll>;
using FunctionPointer = std::unique_ptr<Function>; using FunctionPointer = std::unique_ptr<Function>;
using ImpliesPointer = std::unique_ptr<Implies>; using ImpliesPointer = std::unique_ptr<Implies>;
using InPointer = std::unique_ptr<In>;
using IntegerPointer = std::unique_ptr<Integer>; using IntegerPointer = std::unique_ptr<Integer>;
using IntegerRangePointer = std::unique_ptr<IntegerRange>; using IntervalPointer = std::unique_ptr<Interval>;
using NotPointer = std::unique_ptr<Not>; using NotPointer = std::unique_ptr<Not>;
using OrPointer = std::unique_ptr<Or>; using OrPointer = std::unique_ptr<Or>;
using PredicatePointer = std::unique_ptr<Predicate>; using PredicatePointer = std::unique_ptr<Predicate>;
using SpecialIntegerPointer = std::unique_ptr<SpecialInteger>;
using StringPointer = std::unique_ptr<String>;
using VariablePointer = std::unique_ptr<Variable>; using VariablePointer = std::unique_ptr<Variable>;
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
// Variants // Variants
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
using TermT = mapbox::util::variant<
BinaryOperationPointer,
ConstantPointer,
IntegerPointer,
FunctionPointer,
VariablePointer>;
class Term : public TermT
{
using TermT::TermT;
};
using FormulaT = mapbox::util::variant< using FormulaT = mapbox::util::variant<
AndPointer, AndPointer,
BiconditionalPointer, BiconditionalPointer,
BooleanPointer, BooleanPointer,
EqualsPointer, ComparisonPointer,
ExistsPointer, ExistsPointer,
ForAllPointer, ForAllPointer,
ImpliesPointer, ImpliesPointer,
InPointer,
NotPointer, NotPointer,
OrPointer, OrPointer,
PredicatePointer>; PredicatePointer>;
@ -87,7 +82,21 @@ class Formula : public FormulaT
using FormulaT::FormulaT; using FormulaT::FormulaT;
}; };
using Formulas = std::vector<Formula>; using TermT = mapbox::util::variant<
BinaryOperationPointer,
BooleanPointer,
ConstantPointer,
FunctionPointer,
IntegerPointer,
IntervalPointer,
SpecialIntegerPointer,
StringPointer,
VariablePointer>;
class Term : public TermT
{
using TermT::TermT;
};
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -3,9 +3,9 @@
#include <algorithm> #include <algorithm>
#include <anthem/Context.h> #include <anthem/AST.h>
#include <anthem/Term.h>
#include <anthem/Utils.h> #include <anthem/Utils.h>
#include <anthem/output/ClingoOutput.h>
#include <anthem/output/Formatting.h> #include <anthem/output/Formatting.h>
namespace anthem namespace anthem
@ -17,204 +17,199 @@ namespace anthem
// //
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
struct TermPrintVisitor ast::Comparison::Operator translate(Clingo::AST::ComparisonOperator comparisonOperator)
{ {
void visit(const Clingo::Symbol &, const Clingo::AST::Literal &, const Clingo::AST::Term &term, Context &context) switch (comparisonOperator)
{
case Clingo::AST::ComparisonOperator::GreaterThan:
return ast::Comparison::Operator::GreaterThan;
case Clingo::AST::ComparisonOperator::LessThan:
return ast::Comparison::Operator::LessThan;
case Clingo::AST::ComparisonOperator::LessEqual:
return ast::Comparison::Operator::LessEqual;
case Clingo::AST::ComparisonOperator::GreaterEqual:
return ast::Comparison::Operator::GreaterEqual;
case Clingo::AST::ComparisonOperator::NotEqual:
return ast::Comparison::Operator::NotEqual;
case Clingo::AST::ComparisonOperator::Equal:
return ast::Comparison::Operator::Equal;
}
return ast::Comparison::Operator::NotEqual;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ast::VariablePointer makeAuxiliaryBodyVariable(const int i)
{
auto variableName = std::string(AuxiliaryBodyVariablePrefix) + std::to_string(i);
return std::make_unique<ast::Variable>(std::move(variableName), ast::Variable::Type::Reserved);
};
////////////////////////////////////////////////////////////////////////////////////////////////////
struct BodyTermTranslateVisitor
{
std::experimental::optional<ast::Formula> visit(const Clingo::Symbol &, const Clingo::AST::Literal &, const Clingo::AST::Term &term, Context &context)
{ {
throwErrorAtLocation(term.location, "“symbol” terms currently unsupported in this context", context); throwErrorAtLocation(term.location, "“symbol” terms currently unsupported in this context", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::Variable &, const Clingo::AST::Literal &, const Clingo::AST::Term &term, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Variable &, const Clingo::AST::Literal &, const Clingo::AST::Term &term, Context &context)
{ {
throwErrorAtLocation(term.location, "“variable” terms currently unsupported in this context", context); throwErrorAtLocation(term.location, "“variable” terms currently unsupported in this context", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::UnaryOperation &, const Clingo::AST::Literal &, const Clingo::AST::Term &term, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::UnaryOperation &, const Clingo::AST::Literal &, const Clingo::AST::Term &term, Context &context)
{ {
throwErrorAtLocation(term.location, "“unary operation” terms currently unsupported in this context", context); throwErrorAtLocation(term.location, "“unary operation” terms currently unsupported in this context", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::BinaryOperation &, const Clingo::AST::Literal &, const Clingo::AST::Term &term, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::BinaryOperation &, const Clingo::AST::Literal &, const Clingo::AST::Term &term, Context &context)
{ {
throwErrorAtLocation(term.location, "“binary operation” terms currently unsupported in this context", context); throwErrorAtLocation(term.location, "“binary operation” terms currently unsupported in this context", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::Interval &, const Clingo::AST::Literal &, const Clingo::AST::Term &term, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Interval &, const Clingo::AST::Literal &, const Clingo::AST::Term &term, Context &context)
{ {
throwErrorAtLocation(term.location, "“interval” terms currently unsupported in this context", context); throwErrorAtLocation(term.location, "“interval” terms currently unsupported in this context", context);
return std::experimental::nullopt;
} }
// TODO: check correctness // TODO: refactor
void visit(const Clingo::AST::Function &function, const Clingo::AST::Literal &literal, const Clingo::AST::Term &, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Function &function, const Clingo::AST::Literal &literal, const Clingo::AST::Term &, Context &context)
{ {
if (literal.sign == Clingo::AST::Sign::DoubleNegation) if (literal.sign == Clingo::AST::Sign::DoubleNegation)
throwErrorAtLocation(literal.location, "double-negated literals currently unsupported", context); throwErrorAtLocation(literal.location, "double-negated literals currently unsupported", context);
auto &outputStream = context.logger.outputStream();
if (function.arguments.empty()) if (function.arguments.empty())
return std::make_unique<ast::Predicate>(std::string(function.name));
std::vector<ast::VariablePointer> variables;
variables.reserve(function.arguments.size());
for (size_t i = 0; i < function.arguments.size(); i++)
variables.emplace_back(makeAuxiliaryBodyVariable(context.auxiliaryBodyLiteralID + i));
auto conjunction = std::make_unique<ast::And>();
for (size_t i = 0; i < function.arguments.size(); i++)
{ {
outputStream << output::Function(function.name); const auto &argument = function.arguments[i];
return; conjunction->arguments.emplace_back(std::make_unique<ast::In>(makeAuxiliaryBodyVariable(context.auxiliaryBodyLiteralID + i), translate(argument, context)));
} }
outputStream << output::Keyword("exists") << " "; auto predicate = std::make_unique<ast::Predicate>(std::string(function.name));
predicate->arguments.reserve(function.arguments.size());
for (auto i = function.arguments.cbegin(); i != function.arguments.cend(); i++) for (size_t i = 0; i < function.arguments.size(); i++)
{ predicate->arguments.emplace_back(makeAuxiliaryBodyVariable(context.auxiliaryBodyLiteralID + i));
if (i != function.arguments.cbegin())
outputStream << ", ";
const auto variableName = std::string(AuxiliaryBodyVariablePrefix) + std::to_string(context.auxiliaryBodyLiteralID + i - function.arguments.cbegin()); if (literal.sign == Clingo::AST::Sign::None)
conjunction->arguments.emplace_back(std::move(predicate));
outputStream << output::Variable(variableName.c_str()); else
} conjunction->arguments.emplace_back(std::make_unique<ast::Not>(std::move(predicate)));
outputStream << " (";
for (auto i = function.arguments.cbegin(); i != function.arguments.cend(); i++)
{
const auto &argument = *i;
if (i != function.arguments.cbegin())
outputStream << " " << Clingo::AST::BinaryOperator::And << " ";
const auto variableName = std::string(AuxiliaryBodyVariablePrefix) + std::to_string(context.auxiliaryBodyLiteralID + i - function.arguments.cbegin());
outputStream
<< output::Variable(variableName.c_str())
<< " " << output::Keyword("in") << " "
<< argument;
}
outputStream
<< " " << Clingo::AST::BinaryOperator::And << " "
<< literal.sign
<< output::Function(function.name) << "(";
for (auto i = function.arguments.cbegin(); i != function.arguments.cend(); i++)
{
if (i != function.arguments.cbegin())
outputStream << ", ";
const auto variableName = std::string(AuxiliaryBodyVariablePrefix) + std::to_string(context.auxiliaryBodyLiteralID + i - function.arguments.cbegin());
outputStream << output::Variable(variableName.c_str());
}
outputStream << "))";
context.auxiliaryBodyLiteralID += function.arguments.size(); context.auxiliaryBodyLiteralID += function.arguments.size();
return std::make_unique<ast::Exists>(std::move(variables), std::move(conjunction));
} }
void visit(const Clingo::AST::Pool &, const Clingo::AST::Literal &, const Clingo::AST::Term &term, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Pool &, const Clingo::AST::Literal &, const Clingo::AST::Term &term, Context &context)
{ {
throwErrorAtLocation(term.location, "“pool” terms currently unsupported", context); throwErrorAtLocation(term.location, "“pool” terms currently unsupported", context);
return std::experimental::nullopt;
} }
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
struct LiteralPrintVisitor struct BodyLiteralTranslateVisitor
{ {
void visit(const Clingo::AST::Boolean &, const Clingo::AST::Literal &literal, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Boolean &, const Clingo::AST::Literal &literal, Context &context)
{ {
throwErrorAtLocation(literal.location, "“boolean” literals currently unsupported in this context", context); throwErrorAtLocation(literal.location, "“boolean” literals currently unsupported in this context", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::Term &term, const Clingo::AST::Literal &literal, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Term &term, const Clingo::AST::Literal &literal, Context &context)
{ {
term.data.accept(TermPrintVisitor(), literal, term, context); return term.data.accept(BodyTermTranslateVisitor(), literal, term, context);
return std::experimental::nullopt;
} }
// TODO: refactor // TODO: refactor
void visit(const Clingo::AST::Comparison &comparison, const Clingo::AST::Literal &literal, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Comparison &comparison, const Clingo::AST::Literal &literal, Context &context)
{ {
assert(literal.sign == Clingo::AST::Sign::None); assert(literal.sign == Clingo::AST::Sign::None);
const char *operatorName = ""; const auto operator_ = translate(comparison.comparison);
switch (comparison.comparison) std::vector<ast::VariablePointer> variables;
{ variables.reserve(2);
case Clingo::AST::ComparisonOperator::GreaterThan: variables.emplace_back(makeAuxiliaryBodyVariable(context.auxiliaryBodyLiteralID));
operatorName = ">"; variables.emplace_back(makeAuxiliaryBodyVariable(context.auxiliaryBodyLiteralID + 1));
break;
case Clingo::AST::ComparisonOperator::LessThan:
operatorName = "<";
break;
case Clingo::AST::ComparisonOperator::LessEqual:
operatorName = "<=";
break;
case Clingo::AST::ComparisonOperator::GreaterEqual:
operatorName = ">=";
break;
case Clingo::AST::ComparisonOperator::NotEqual:
operatorName = "!=";
break;
case Clingo::AST::ComparisonOperator::Equal:
operatorName = "=";
break;
}
const auto variableName1 = std::string(AuxiliaryBodyVariablePrefix) + std::to_string(context.auxiliaryBodyLiteralID); auto conjunction = std::make_unique<ast::And>();
const auto variableName2 = std::string(AuxiliaryBodyVariablePrefix) + std::to_string(context.auxiliaryBodyLiteralID + 1); conjunction->arguments.reserve(3);
conjunction->arguments.emplace_back(std::make_unique<ast::In>(makeAuxiliaryBodyVariable(context.auxiliaryBodyLiteralID), translate(comparison.left, context)));
context.logger.outputStream() conjunction->arguments.emplace_back(std::make_unique<ast::In>(makeAuxiliaryBodyVariable(context.auxiliaryBodyLiteralID + 1), translate(comparison.right, context)));
<< output::Keyword("exists") << " " << output::Variable(variableName1.c_str()) conjunction->arguments.emplace_back(std::make_unique<ast::Comparison>(operator_, makeAuxiliaryBodyVariable(context.auxiliaryBodyLiteralID), makeAuxiliaryBodyVariable(context.auxiliaryBodyLiteralID + 1)));
<< ", " << output::Variable(variableName2.c_str())
<< " ("
<< output::Variable(variableName1.c_str())
<< " " << output::Keyword("in") << " " << comparison.left
<< " " << Clingo::AST::BinaryOperator::And << " "
<< output::Variable(variableName2.c_str())
<< " " << output::Keyword("in") << " " << comparison.right
<< " " << Clingo::AST::BinaryOperator::And << " "
<< output::Variable(variableName1.c_str())
<< " " << output::Operator(operatorName) << " "
<< output::Variable(variableName2.c_str())
<< ")";
context.auxiliaryBodyLiteralID += 2; context.auxiliaryBodyLiteralID += 2;
return std::make_unique<ast::Exists>(std::move(variables), std::move(conjunction));
} }
void visit(const Clingo::AST::CSPLiteral &, const Clingo::AST::Literal &literal, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::CSPLiteral &, const Clingo::AST::Literal &literal, Context &context)
{ {
throwErrorAtLocation(literal.location, "CSP literals currently unsupported", context); throwErrorAtLocation(literal.location, "CSP literals currently unsupported", context);
return std::experimental::nullopt;
} }
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
struct BodyLiteralPrintVisitor struct BodyBodyLiteralTranslateVisitor
{ {
void visit(const Clingo::AST::Literal &literal, const Clingo::AST::BodyLiteral &, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Literal &literal, const Clingo::AST::BodyLiteral &, Context &context)
{ {
literal.data.accept(LiteralPrintVisitor(), literal, context); return literal.data.accept(BodyLiteralTranslateVisitor(), literal, context);
} }
void visit(const Clingo::AST::ConditionalLiteral &, const Clingo::AST::BodyLiteral &bodyLiteral, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::ConditionalLiteral &, const Clingo::AST::BodyLiteral &bodyLiteral, Context &context)
{ {
throwErrorAtLocation(bodyLiteral.location, "“conditional literal” body literals currently unsupported", context); throwErrorAtLocation(bodyLiteral.location, "“conditional literal” body literals currently unsupported", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::Aggregate &, const Clingo::AST::BodyLiteral &bodyLiteral, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Aggregate &, const Clingo::AST::BodyLiteral &bodyLiteral, Context &context)
{ {
throwErrorAtLocation(bodyLiteral.location, "“aggregate” body literals currently unsupported", context); throwErrorAtLocation(bodyLiteral.location, "“aggregate” body literals currently unsupported", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::BodyAggregate &, const Clingo::AST::BodyLiteral &bodyLiteral, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::BodyAggregate &, const Clingo::AST::BodyLiteral &bodyLiteral, Context &context)
{ {
throwErrorAtLocation(bodyLiteral.location, "“body aggregate” body literals currently unsupported", context); throwErrorAtLocation(bodyLiteral.location, "“body aggregate” body literals currently unsupported", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::TheoryAtom &, const Clingo::AST::BodyLiteral &bodyLiteral, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::TheoryAtom &, const Clingo::AST::BodyLiteral &bodyLiteral, Context &context)
{ {
throwErrorAtLocation(bodyLiteral.location, "“theory atom” body literals currently unsupported", context); throwErrorAtLocation(bodyLiteral.location, "“theory atom” body literals currently unsupported", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::Disjoint &, const Clingo::AST::BodyLiteral &bodyLiteral, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Disjoint &, const Clingo::AST::BodyLiteral &bodyLiteral, Context &context)
{ {
throwErrorAtLocation(bodyLiteral.location, "“disjoint” body literals currently unsupported", context); throwErrorAtLocation(bodyLiteral.location, "“disjoint” body literals currently unsupported", context);
return std::experimental::nullopt;
} }
}; };

View File

@ -3,8 +3,8 @@
#include <algorithm> #include <algorithm>
#include <anthem/AST.h>
#include <anthem/Utils.h> #include <anthem/Utils.h>
#include <anthem/output/ClingoOutput.h>
#include <anthem/output/Formatting.h> #include <anthem/output/Formatting.h>
namespace anthem namespace anthem
@ -139,155 +139,179 @@ struct HeadLiteralCollectFunctionTermsVisitor
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
struct TermPrintSubstitutedVisitor struct FunctionTermTranslateVisitor
{ {
void visit(const Clingo::Symbol &, const Clingo::AST::Term &term, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::Symbol &, const Clingo::AST::Term &term, Context &context)
{ {
throwErrorAtLocation(term.location, "“symbol” terms not allowed, function expected", context); throwErrorAtLocation(term.location, "“symbol” terms not allowed, function expected", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::Variable &, const Clingo::AST::Term &term, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Variable &, const Clingo::AST::Term &term, Context &context)
{ {
throwErrorAtLocation(term.location, "“variable” terms currently unsupported, function expected", context); throwErrorAtLocation(term.location, "“variable” terms currently unsupported, function expected", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::UnaryOperation &, const Clingo::AST::Term &term, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::UnaryOperation &, const Clingo::AST::Term &term, Context &context)
{ {
throwErrorAtLocation(term.location, "“unary operation” terms currently unsupported, function expected", context); throwErrorAtLocation(term.location, "“unary operation” terms currently unsupported, function expected", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::BinaryOperation &, const Clingo::AST::Term &term, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::BinaryOperation &, const Clingo::AST::Term &term, Context &context)
{ {
throwErrorAtLocation(term.location, "“binary operation” terms currently unsupported, function expected", context); throwErrorAtLocation(term.location, "“binary operation” terms currently unsupported, function expected", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::Interval &, const Clingo::AST::Term &term, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Interval &, const Clingo::AST::Term &term, Context &context)
{ {
throwErrorAtLocation(term.location, "“interval” terms currently unsupported, function expected", context); throwErrorAtLocation(term.location, "“interval” terms currently unsupported, function expected", context);
return std::experimental::nullopt;
} }
// TODO: check correctness // TODO: check correctness
void visit(const Clingo::AST::Function &function, const Clingo::AST::Term &term, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Function &function, const Clingo::AST::Term &term, Context &context)
{ {
if (function.external) if (function.external)
throwErrorAtLocation(term.location, "external functions currently unsupported", context); throwErrorAtLocation(term.location, "external functions currently unsupported", context);
auto &outputStream = context.logger.outputStream(); std::vector<ast::Term> arguments;
arguments.reserve(function.arguments.size());
outputStream << output::Function(function.name);
if (function.arguments.empty())
return;
outputStream << "(";
for (auto i = function.arguments.cbegin(); i != function.arguments.cend(); i++) for (auto i = function.arguments.cbegin(); i != function.arguments.cend(); i++)
{ {
if (i != function.arguments.cbegin())
outputStream << ", ";
const auto &argument = *i; const auto &argument = *i;
const auto matchingTerm = std::find(context.headTerms.cbegin(), context.headTerms.cend(), &argument); const auto matchingTerm = std::find(context.headTerms.cbegin(), context.headTerms.cend(), &argument);
assert(matchingTerm != context.headTerms.cend()); assert(matchingTerm != context.headTerms.cend());
const auto variableName = std::string(AuxiliaryHeadVariablePrefix) + std::to_string(matchingTerm - context.headTerms.cbegin() + 1); auto variableName = std::string(AuxiliaryHeadVariablePrefix) + std::to_string(matchingTerm - context.headTerms.cbegin() + 1);
arguments.emplace_back(std::make_unique<ast::Variable>(std::move(variableName), ast::Variable::Type::Reserved));
outputStream << output::Variable(variableName.c_str());
} }
outputStream << ")"; return std::make_unique<ast::Predicate>(function.name, std::move(arguments));
} }
void visit(const Clingo::AST::Pool &, const Clingo::AST::Term &term, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Pool &, const Clingo::AST::Term &term, Context &context)
{ {
throwErrorAtLocation(term.location, "“pool” terms currently unsupported, function expected", context); throwErrorAtLocation(term.location, "“pool” terms currently unsupported, function expected", context);
return std::experimental::nullopt;
} }
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
struct LiteralPrintSubstitutedVisitor struct LiteralTranslateVisitor
{ {
void visit(const Clingo::AST::Boolean &boolean, const Clingo::AST::Literal &, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Boolean &boolean, const Clingo::AST::Literal &, Context &)
{ {
context.logger.outputStream() << boolean; return std::make_unique<ast::Boolean>(boolean.value);
} }
void visit(const Clingo::AST::Term &term, const Clingo::AST::Literal &, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Term &term, const Clingo::AST::Literal &, Context &context)
{ {
term.data.accept(TermPrintSubstitutedVisitor(), term, context); return term.data.accept(FunctionTermTranslateVisitor(), term, context);
} }
void visit(const Clingo::AST::Comparison &, const Clingo::AST::Literal &literal, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Comparison &, const Clingo::AST::Literal &literal, Context &context)
{ {
throwErrorAtLocation(literal.location, "only disjunctions of literals allowed as head literals", context); throwErrorAtLocation(literal.location, "only disjunctions of literals allowed as head literals", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::CSPLiteral &, const Clingo::AST::Literal &literal, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::CSPLiteral &, const Clingo::AST::Literal &literal, Context &context)
{ {
throwErrorAtLocation(literal.location, "only disjunctions of literals allowed as head literals", context); throwErrorAtLocation(literal.location, "only disjunctions of literals allowed as head literals", context);
return std::experimental::nullopt;
} }
}; };
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
struct HeadLiteralPrintSubstitutedVisitor struct HeadLiteralTranslateToConsequentVisitor
{ {
void visit(const Clingo::AST::Literal &literal, const Clingo::AST::HeadLiteral &, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Literal &literal, const Clingo::AST::HeadLiteral &, Context &context)
{ {
if (literal.sign == Clingo::AST::Sign::DoubleNegation) if (literal.sign == Clingo::AST::Sign::DoubleNegation)
throwErrorAtLocation(literal.location, "double-negated literals currently unsupported", context); throwErrorAtLocation(literal.location, "double-negated head literals currently unsupported", context);
context.logger.outputStream() << literal.sign; auto translatedLiteral = literal.data.accept(LiteralTranslateVisitor(), literal, context);
literal.data.accept(LiteralPrintSubstitutedVisitor(), literal, context); if (literal.sign == Clingo::AST::Sign::None)
return translatedLiteral;
if (!translatedLiteral)
return std::experimental::nullopt;
return std::make_unique<ast::Not>(std::move(translatedLiteral.value()));
} }
void visit(const Clingo::AST::Disjunction &disjunction, const Clingo::AST::HeadLiteral &headLiteral, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Disjunction &disjunction, const Clingo::AST::HeadLiteral &headLiteral, Context &context)
{ {
for (auto i = disjunction.elements.cbegin(); i != disjunction.elements.cend(); i++) std::vector<ast::Formula> arguments;
{ arguments.reserve(disjunction.elements.size());
const auto &conditionalLiteral = *i;
for (const auto &conditionalLiteral : disjunction.elements)
{
if (!conditionalLiteral.condition.empty()) if (!conditionalLiteral.condition.empty())
throwErrorAtLocation(headLiteral.location, "conditional head literals currently unsupported", context); throwErrorAtLocation(headLiteral.location, "conditional head literals currently unsupported", context);
if (i != disjunction.elements.cbegin()) auto argument = visit(conditionalLiteral.literal, headLiteral, context);
context.logger.outputStream() << " " << Clingo::AST::BinaryOperator::Or << " ";
visit(conditionalLiteral.literal, headLiteral, context); if (!argument)
throwErrorAtLocation(headLiteral.location, "could not parse argument", context);
arguments.emplace_back(std::move(argument.value()));
} }
return std::make_unique<ast::Or>(std::move(arguments));
} }
void visit(const Clingo::AST::Aggregate &aggregate, const Clingo::AST::HeadLiteral &headLiteral, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Aggregate &aggregate, const Clingo::AST::HeadLiteral &headLiteral, Context &context)
{ {
if (aggregate.left_guard || aggregate.right_guard) if (aggregate.left_guard || aggregate.right_guard)
throwErrorAtLocation(headLiteral.location, "aggregates with left or right guards not allowed", context); throwErrorAtLocation(headLiteral.location, "aggregates with left or right guards not allowed", context);
for (auto i = aggregate.elements.cbegin(); i != aggregate.elements.cend(); i++) const auto translateConditionalLiteral =
[&](const auto &conditionalLiteral)
{
if (!conditionalLiteral.condition.empty())
throwErrorAtLocation(headLiteral.location, "conditional head literals currently unsupported", context);
return this->visit(conditionalLiteral.literal, headLiteral, context);
};
if (aggregate.elements.size() == 1)
return translateConditionalLiteral(aggregate.elements[0]);
std::vector<ast::Formula> arguments;
arguments.reserve(aggregate.elements.size());
for (const auto &conditionalLiteral : aggregate.elements)
{ {
const auto &conditionalLiteral = *i; auto argument = translateConditionalLiteral(conditionalLiteral);
if (!conditionalLiteral.condition.empty()) if (!argument)
throwErrorAtLocation(headLiteral.location, "conditional head literals currently unsupported", context); throwErrorAtLocation(headLiteral.location, "could not parse argument", context);
if (i != aggregate.elements.cbegin()) arguments.emplace_back(std::move(argument.value()));
context.logger.outputStream() << " " << Clingo::AST::BinaryOperator::Or << " ";
visit(conditionalLiteral.literal, headLiteral, context);
} }
return std::make_unique<ast::Or>(std::move(arguments));
} }
void visit(const Clingo::AST::HeadAggregate &, const Clingo::AST::HeadLiteral &headLiteral, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::HeadAggregate &, const Clingo::AST::HeadLiteral &headLiteral, Context &context)
{ {
throwErrorAtLocation(headLiteral.location, "“head aggregate” head literals currently unsupported", context); throwErrorAtLocation(headLiteral.location, "“head aggregate” head literals currently unsupported", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::TheoryAtom &, const Clingo::AST::HeadLiteral &headLiteral, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::TheoryAtom &, const Clingo::AST::HeadLiteral &headLiteral, Context &context)
{ {
throwErrorAtLocation(headLiteral.location, "“theory” head literals currently unsupported", context); throwErrorAtLocation(headLiteral.location, "“theory” head literals currently unsupported", context);
return std::experimental::nullopt;
} }
}; };

View File

@ -1,10 +1,11 @@
#ifndef __ANTHEM__STATEMENT_VISITOR_H #ifndef __ANTHEM__STATEMENT_VISITOR_H
#define __ANTHEM__STATEMENT_VISITOR_H #define __ANTHEM__STATEMENT_VISITOR_H
#include <anthem/AST.h>
#include <anthem/Body.h> #include <anthem/Body.h>
#include <anthem/Head.h> #include <anthem/Head.h>
#include <anthem/Term.h>
#include <anthem/Utils.h> #include <anthem/Utils.h>
#include <anthem/output/ClingoOutput.h>
namespace anthem namespace anthem
{ {
@ -17,37 +18,46 @@ namespace anthem
struct StatementVisitor struct StatementVisitor
{ {
void visit(const Clingo::AST::Program &program, const Clingo::AST::Statement &statement, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Program &program, const Clingo::AST::Statement &statement, Context &context)
{ {
// TODO: refactor // TODO: refactor
context.logger.log(output::Priority::Debug, (std::string("[program] ") + program.name).c_str()); context.logger.log(output::Priority::Debug, (std::string("[program] ") + program.name).c_str());
if (!program.parameters.empty()) if (!program.parameters.empty())
throwErrorAtLocation(statement.location, "program parameters currently unsupported", context); throwErrorAtLocation(statement.location, "program parameters currently unsupported", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::Rule &rule, const Clingo::AST::Statement &, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Rule &rule, const Clingo::AST::Statement &, Context &context)
{ {
context.reset(); context.reset();
auto &outputStream = context.logger.outputStream();
// Concatenate all head terms // Concatenate all head terms
rule.head.data.accept(HeadLiteralCollectFunctionTermsVisitor(), rule.head, context); rule.head.data.accept(HeadLiteralCollectFunctionTermsVisitor(), rule.head, context);
auto antecedent = std::make_unique<ast::And>();
// Compute consequent
auto consequent = rule.head.data.accept(HeadLiteralTranslateToConsequentVisitor(), rule.head, context);
if (!consequent)
{
context.logger.log(output::Priority::Error, "could not translate formula consequent");
return std::experimental::nullopt;
}
// Print auxiliary variables replacing the head atoms arguments // Print auxiliary variables replacing the head atoms arguments
for (auto i = context.headTerms.cbegin(); i != context.headTerms.cend(); i++) for (auto i = context.headTerms.cbegin(); i != context.headTerms.cend(); i++)
{ {
const auto &headTerm = **i; const auto &headTerm = **i;
if (i != context.headTerms.cbegin()) auto variableName = std::string(AuxiliaryHeadVariablePrefix) + std::to_string(i - context.headTerms.cbegin() + 1);
outputStream << " " << Clingo::AST::BinaryOperator::And << " "; auto element = std::make_unique<ast::Variable>(std::move(variableName), ast::Variable::Type::Reserved);
auto set = translate(headTerm, context);
auto in = std::make_unique<ast::In>(std::move(element), std::move(set));
const auto variableName = std::string(AuxiliaryHeadVariablePrefix) + std::to_string(i - context.headTerms.cbegin() + 1); antecedent->arguments.emplace_back(std::move(in));
outputStream
<< output::Variable(variableName.c_str())
<< " " << output::Keyword("in") << " " << headTerm;
} }
// Print translated body literals // Print translated body literals
@ -55,97 +65,94 @@ struct StatementVisitor
{ {
const auto &bodyLiteral = *i; const auto &bodyLiteral = *i;
if (!context.headTerms.empty() || i != rule.body.cbegin())
outputStream << " " << Clingo::AST::BinaryOperator::And << " ";
if (bodyLiteral.sign != Clingo::AST::Sign::None) if (bodyLiteral.sign != Clingo::AST::Sign::None)
throwErrorAtLocation(bodyLiteral.location, "only positive literals currently supported", context); throwErrorAtLocation(bodyLiteral.location, "only positive literals currently supported", context);
bodyLiteral.data.accept(BodyLiteralPrintVisitor(), bodyLiteral, context); auto argument = bodyLiteral.data.accept(BodyBodyLiteralTranslateVisitor(), bodyLiteral, context);
if (!argument)
throwErrorAtLocation(bodyLiteral.location, "could not translate body literal", context);
antecedent->arguments.emplace_back(std::move(argument.value()));
} }
// Handle choice rules // Handle choice rules
if (context.isChoiceRule) if (context.isChoiceRule)
{ antecedent->arguments.emplace_back(ast::deepCopy(consequent.value()));
const bool isFirstOutput = rule.body.empty() && context.headTerms.empty();
if (!isFirstOutput) // Use “true” as the consequent in case it is empty
outputStream << " " << Clingo::AST::BinaryOperator::And << " "; if (antecedent->arguments.empty())
return std::make_unique<ast::Implies>(std::make_unique<ast::Boolean>(true), std::move(consequent.value()));
else if (antecedent->arguments.size() == 1)
return std::make_unique<ast::Implies>(std::move(antecedent->arguments[0]), std::move(consequent.value()));
if (context.numberOfHeadLiterals > 1 && !isFirstOutput) return std::make_unique<ast::Implies>(std::move(antecedent), std::move(consequent.value()));
outputStream << "(";
rule.head.data.accept(HeadLiteralPrintSubstitutedVisitor(), rule.head, context);
if (context.numberOfHeadLiterals > 1 && !isFirstOutput)
outputStream << ")";
}
// Print “true” on the left side of the formula in case there is nothing else
if (rule.body.empty() && context.headTerms.empty() && !context.isChoiceRule)
outputStream << Clingo::AST::Boolean({true});
outputStream << " " << output::Operator("->") << " ";
// Print consequent of the implication
rule.head.data.accept(HeadLiteralPrintSubstitutedVisitor(), rule.head, context);
outputStream << std::endl;
} }
void visit(const Clingo::AST::Definition &, const Clingo::AST::Statement &statement, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Definition &, const Clingo::AST::Statement &statement, Context &context)
{ {
throwErrorAtLocation(statement.location, "“definition” statements currently unsupported", context); throwErrorAtLocation(statement.location, "“definition” statements currently unsupported", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::ShowSignature &, const Clingo::AST::Statement &statement, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::ShowSignature &, const Clingo::AST::Statement &statement, Context &context)
{ {
throwErrorAtLocation(statement.location, "“show signature” statements currently unsupported", context); throwErrorAtLocation(statement.location, "“show signature” statements currently unsupported", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::ShowTerm &, const Clingo::AST::Statement &statement, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::ShowTerm &, const Clingo::AST::Statement &statement, Context &context)
{ {
throwErrorAtLocation(statement.location, "“show term” statements currently unsupported", context); throwErrorAtLocation(statement.location, "“show term” statements currently unsupported", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::Minimize &, const Clingo::AST::Statement &statement, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Minimize &, const Clingo::AST::Statement &statement, Context &context)
{ {
throwErrorAtLocation(statement.location, "“minimize” statements currently unsupported", context); throwErrorAtLocation(statement.location, "“minimize” statements currently unsupported", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::Script &, const Clingo::AST::Statement &statement, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Script &, const Clingo::AST::Statement &statement, Context &context)
{ {
throwErrorAtLocation(statement.location, "“script” statements currently unsupported", context); throwErrorAtLocation(statement.location, "“script” statements currently unsupported", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::External &, const Clingo::AST::Statement &statement, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::External &, const Clingo::AST::Statement &statement, Context &context)
{ {
throwErrorAtLocation(statement.location, "“external” statements currently unsupported", context); throwErrorAtLocation(statement.location, "“external” statements currently unsupported", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::Edge &, const Clingo::AST::Statement &statement, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Edge &, const Clingo::AST::Statement &statement, Context &context)
{ {
throwErrorAtLocation(statement.location, "“edge” statements currently unsupported", context); throwErrorAtLocation(statement.location, "“edge” statements currently unsupported", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::Heuristic &, const Clingo::AST::Statement &statement, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::Heuristic &, const Clingo::AST::Statement &statement, Context &context)
{ {
throwErrorAtLocation(statement.location, "“heuristic” statements currently unsupported", context); throwErrorAtLocation(statement.location, "“heuristic” statements currently unsupported", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::ProjectAtom &, const Clingo::AST::Statement &statement, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::ProjectAtom &, const Clingo::AST::Statement &statement, Context &context)
{ {
throwErrorAtLocation(statement.location, "“project atom” statements currently unsupported", context); throwErrorAtLocation(statement.location, "“project atom” statements currently unsupported", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::ProjectSignature &, const Clingo::AST::Statement &statement, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::ProjectSignature &, const Clingo::AST::Statement &statement, Context &context)
{ {
throwErrorAtLocation(statement.location, "“project signature” statements currently unsupported", context); throwErrorAtLocation(statement.location, "“project signature” statements currently unsupported", context);
return std::experimental::nullopt;
} }
void visit(const Clingo::AST::TheoryDefinition &, const Clingo::AST::Statement &statement, Context &context) std::experimental::optional<ast::Formula> visit(const Clingo::AST::TheoryDefinition &, const Clingo::AST::Statement &statement, Context &context)
{ {
throwErrorAtLocation(statement.location, "“theory definition” statements currently unsupported", context); throwErrorAtLocation(statement.location, "“theory definition” statements currently unsupported", context);
return std::experimental::nullopt;
} }
}; };

132
include/anthem/Term.h Normal file
View File

@ -0,0 +1,132 @@
#ifndef __ANTHEM__TERM_H
#define __ANTHEM__TERM_H
#include <algorithm>
#include <anthem/AST.h>
#include <anthem/Utils.h>
#include <anthem/output/Formatting.h>
namespace anthem
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Term
//
////////////////////////////////////////////////////////////////////////////////////////////////////
ast::BinaryOperation::Operator translate(Clingo::AST::BinaryOperator binaryOperator, const Clingo::AST::Term &term, Context &context)
{
switch (binaryOperator)
{
case Clingo::AST::BinaryOperator::Plus:
return ast::BinaryOperation::Operator::Plus;
case Clingo::AST::BinaryOperator::Minus:
return ast::BinaryOperation::Operator::Minus;
case Clingo::AST::BinaryOperator::Multiplication:
return ast::BinaryOperation::Operator::Multiplication;
case Clingo::AST::BinaryOperator::Division:
return ast::BinaryOperation::Operator::Division;
case Clingo::AST::BinaryOperator::Modulo:
return ast::BinaryOperation::Operator::Modulo;
default:
throwErrorAtLocation(term.location, "“binary operation” terms currently unsupported", context);
}
return ast::BinaryOperation::Operator::Plus;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ast::Term translate(const Clingo::AST::Term &term, Context &context);
////////////////////////////////////////////////////////////////////////////////////////////////////
struct TermTranslateVisitor
{
std::experimental::optional<ast::Term> visit(const Clingo::Symbol &symbol, const Clingo::AST::Term &term, Context &context)
{
switch (symbol.type())
{
case Clingo::SymbolType::Number:
return std::make_unique<ast::Integer>(symbol.number());
case Clingo::SymbolType::Infimum:
return std::make_unique<ast::SpecialInteger>(ast::SpecialInteger::Type::Infimum);
case Clingo::SymbolType::Supremum:
return std::make_unique<ast::SpecialInteger>(ast::SpecialInteger::Type::Supremum);
case Clingo::SymbolType::String:
return std::make_unique<ast::String>(std::string(symbol.string()));
default:
throwErrorAtLocation(term.location, "only numeric “symbol” terms allowed", context);
}
return std::experimental::nullopt;
}
std::experimental::optional<ast::Term> visit(const Clingo::AST::Variable &variable, const Clingo::AST::Term &, Context &)
{
return std::make_unique<ast::Variable>(std::string(variable.name), ast::Variable::Type::UserDefined);
}
std::experimental::optional<ast::Term> visit(const Clingo::AST::UnaryOperation &, const Clingo::AST::Term &term, Context &context)
{
throwErrorAtLocation(term.location, "“unary operation” terms currently unsupported", context);
return std::experimental::nullopt;
}
std::experimental::optional<ast::Term> visit(const Clingo::AST::BinaryOperation &binaryOperation, const Clingo::AST::Term &term, Context &context)
{
const auto operator_ = translate(binaryOperation.binary_operator, term, context);
auto left = translate(binaryOperation.left, context);
auto right = translate(binaryOperation.right, context);
return std::make_unique<ast::BinaryOperation>(operator_, std::move(left), std::move(right));
}
std::experimental::optional<ast::Term> visit(const Clingo::AST::Interval &interval, const Clingo::AST::Term &, Context &context)
{
auto left = translate(interval.left, context);
auto right = translate(interval.right, context);
return std::make_unique<ast::Interval>(std::move(left), std::move(right));
}
std::experimental::optional<ast::Term> visit(const Clingo::AST::Function &function, const Clingo::AST::Term &term, Context &context)
{
if (function.external)
throwErrorAtLocation(term.location, "external functions currently unsupported", context);
std::vector<ast::Term> arguments;
arguments.reserve(function.arguments.size());
for (const auto &argument : function.arguments)
arguments.emplace_back(translate(argument, context));
return std::make_unique<ast::Function>(function.name, std::move(arguments));
}
std::experimental::optional<ast::Term> visit(const Clingo::AST::Pool &, const Clingo::AST::Term &term, Context &context)
{
throwErrorAtLocation(term.location, "“pool” terms currently unsupported", context);
return std::experimental::nullopt;
}
};
////////////////////////////////////////////////////////////////////////////////////////////////////
ast::Term translate(const Clingo::AST::Term &term, Context &context)
{
auto translatedTerm = term.data.accept(TermTranslateVisitor(), term, context);
if (!translatedTerm)
throwErrorAtLocation(term.location, "could not translate term", context);
return std::move(translatedTerm.value());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}
#endif

345
include/anthem/output/AST.h Normal file
View File

@ -0,0 +1,345 @@
#ifndef __ANTHEM__OUTPUT__AST_H
#define __ANTHEM__OUTPUT__AST_H
#include <cassert>
#include <anthem/AST.h>
#include <anthem/Utils.h>
#include <anthem/output/ColorStream.h>
#include <anthem/output/Formatting.h>
namespace anthem
{
namespace ast
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// AST
//
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, BinaryOperation::Operator operator_);
output::ColorStream &operator<<(output::ColorStream &stream, const BinaryOperation &binaryOperation);
output::ColorStream &operator<<(output::ColorStream &stream, const Boolean &boolean);
output::ColorStream &operator<<(output::ColorStream &stream, const Comparison &comparison);
output::ColorStream &operator<<(output::ColorStream &stream, const Constant &constant);
output::ColorStream &operator<<(output::ColorStream &stream, const Function &function);
output::ColorStream &operator<<(output::ColorStream &stream, const In &in);
output::ColorStream &operator<<(output::ColorStream &stream, const Integer &integer);
output::ColorStream &operator<<(output::ColorStream &stream, const Interval &interval);
output::ColorStream &operator<<(output::ColorStream &stream, const Predicate &predicate);
output::ColorStream &operator<<(output::ColorStream &stream, const SpecialInteger &specialInteger);
output::ColorStream &operator<<(output::ColorStream &stream, const String &string);
output::ColorStream &operator<<(output::ColorStream &stream, const Variable &variable);
output::ColorStream &operator<<(output::ColorStream &stream, const And &and_);
output::ColorStream &operator<<(output::ColorStream &stream, const Biconditional &biconditional);
output::ColorStream &operator<<(output::ColorStream &stream, const Exists &exists);
output::ColorStream &operator<<(output::ColorStream &stream, const ForAll &forAll);
output::ColorStream &operator<<(output::ColorStream &stream, const Implies &implies);
output::ColorStream &operator<<(output::ColorStream &stream, const Not &not_);
output::ColorStream &operator<<(output::ColorStream &stream, const Or &or_);
output::ColorStream &operator<<(output::ColorStream &stream, const Formula &formula);
output::ColorStream &operator<<(output::ColorStream &stream, const Term &term);
////////////////////////////////////////////////////////////////////////////////////////////////////
// Primitives
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, BinaryOperation::Operator operator_)
{
switch (operator_)
{
case BinaryOperation::Operator::Plus:
return (stream << output::Operator("+"));
case BinaryOperation::Operator::Minus:
return (stream << output::Operator("-"));
case BinaryOperation::Operator::Multiplication:
return (stream << output::Operator("*"));
case BinaryOperation::Operator::Division:
return (stream << output::Operator("/"));
case BinaryOperation::Operator::Modulo:
return (stream << output::Operator("%"));
}
return stream;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const BinaryOperation &binaryOperation)
{
return (stream << "(" << binaryOperation.left << " " << binaryOperation.operator_ << " " << binaryOperation.right << ")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const Boolean &boolean)
{
if (boolean.value)
return (stream << output::Boolean("#true"));
return (stream << output::Boolean("#false"));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, Comparison::Operator operator_)
{
switch (operator_)
{
case Comparison::Operator::GreaterThan:
return (stream << output::Operator(">"));
case Comparison::Operator::LessThan:
return (stream << output::Operator("<"));
case Comparison::Operator::LessEqual:
return (stream << output::Operator("<="));
case Comparison::Operator::GreaterEqual:
return (stream << output::Operator(">="));
case Comparison::Operator::NotEqual:
return (stream << output::Operator("!="));
case Comparison::Operator::Equal:
return (stream << output::Operator("="));
}
return stream;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const Comparison &comparison)
{
return (stream << comparison.left << " " << comparison.operator_ << " " << comparison.right);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const Constant &constant)
{
return (stream << constant.name);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const Function &function)
{
stream << function.name;
if (function.arguments.empty())
return stream;
stream << "(";
for (auto i = function.arguments.cbegin(); i != function.arguments.cend(); i++)
{
if (i != function.arguments.cbegin())
stream << ", ";
stream << (*i);
}
if (function.name.empty() && function.arguments.size() == 1)
stream << ",";
return (stream << ")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const In &in)
{
return (stream << in.element << " " << output::Keyword("in") << " " << in.set);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const Integer &integer)
{
return (stream << output::Number<int>(integer.value));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const Interval &interval)
{
return (stream << interval.from << ".." << interval.to);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const Predicate &predicate)
{
stream << predicate.name;
if (predicate.arguments.empty())
return stream;
stream << "(";
for (auto i = predicate.arguments.cbegin(); i != predicate.arguments.cend(); i++)
{
if (i != predicate.arguments.cbegin())
stream << ", ";
stream << (*i);
}
return (stream << ")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const SpecialInteger &specialInteger)
{
switch (specialInteger.type)
{
case SpecialInteger::Type::Infimum:
return (stream << output::Number<std::string>("#inf"));
case SpecialInteger::Type::Supremum:
return (stream << output::Number<std::string>("#sup"));
}
return stream;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const String &string)
{
return (stream << output::String(string.text.c_str()));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const Variable &variable)
{
assert(!variable.name.empty());
assert(variable.name[0] >= 65 && variable.name[0] <= 90);
if (variable.type == ast::Variable::Type::Reserved || !isReservedVariableName(variable.name.c_str()))
return (stream << output::Variable(variable.name.c_str()));
const auto variableName = std::string(UserVariablePrefix) + variable.name;
return (stream << output::Variable(variableName.c_str()));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Expressions
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const And &and_)
{
stream << "(";
for (auto i = and_.arguments.cbegin(); i != and_.arguments.cend(); i++)
{
if (i != and_.arguments.cbegin())
stream << " " << output::Keyword("and") << " ";
stream << (*i);
}
return (stream << ")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const Biconditional &biconditional)
{
return (stream << "(" << biconditional.left << " <-> " << biconditional.right << ")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const Exists &exists)
{
stream << output::Keyword("exists") << " ";
for (auto i = exists.variables.cbegin(); i != exists.variables.cend(); i++)
{
if (i != exists.variables.cbegin())
stream << ", ";
stream << (**i);
}
return (stream << " " << exists.argument);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const ForAll &forAll)
{
stream << output::Keyword("forall") << " ";
for (auto i = forAll.variables.cbegin(); i != forAll.variables.cend(); i++)
{
if (i != forAll.variables.cbegin())
stream << ", ";
stream << (**i);
}
return (stream << " " << forAll.argument);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const Implies &implies)
{
return (stream << "(" << implies.antecedent << " -> " << implies.consequent << ")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const Not &not_)
{
return (stream << output::Keyword("not ") << not_.argument);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const Or &or_)
{
stream << "(";
for (auto i = or_.arguments.cbegin(); i != or_.arguments.cend(); i++)
{
if (i != or_.arguments.cbegin())
stream << " " << output::Keyword("or") << " ";
stream << (*i);
}
return (stream << ")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Variants
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const Formula &formula)
{
formula.match([&](const auto &x){stream << *x;});
return stream;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
output::ColorStream &operator<<(output::ColorStream &stream, const Term &term)
{
term.match([&](const auto &x){stream << *x;});
return stream;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}
}
#endif

View File

@ -1,36 +0,0 @@
#ifndef __ANTHEM__OUTPUT__CLINGO_OUTPUT_H
#define __ANTHEM__OUTPUT__CLINGO_OUTPUT_H
#include <clingo.hh>
#include <anthem/output/ColorStream.h>
namespace anthem
{
namespace output
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// ClingoOutput
//
////////////////////////////////////////////////////////////////////////////////////////////////////
ColorStream &operator<<(ColorStream &stream, const Clingo::Symbol &symbol);
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::Sign &sign);
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::Boolean &boolean);
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::Variable &variable);
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::BinaryOperator &binaryOperator);
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::UnaryOperation &unaryOperation);
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::BinaryOperation &binaryOperation);
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::Interval &interval);
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::Function &function);
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::Pool &pool);
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::Term &term);
////////////////////////////////////////////////////////////////////////////////////////////////////
}
}
#endif

View File

@ -118,7 +118,7 @@ struct Keyword
inline ColorStream &operator<<(ColorStream &stream, const Keyword &keyword) inline ColorStream &operator<<(ColorStream &stream, const Keyword &keyword)
{ {
return (stream return (stream
<< Format({Color::Blue, FontWeight::Bold}) << Format({Color::Blue, FontWeight::Normal})
<< keyword.name << keyword.name
<< ResetFormat()); << ResetFormat());
} }

View File

@ -8,6 +8,7 @@
#include <anthem/Context.h> #include <anthem/Context.h>
#include <anthem/StatementVisitor.h> #include <anthem/StatementVisitor.h>
#include <anthem/output/AST.h>
namespace anthem namespace anthem
{ {
@ -40,7 +41,12 @@ void translate(const char *fileName, std::istream &stream, Context &context)
const auto translateStatement = const auto translateStatement =
[&context](const Clingo::AST::Statement &statement) [&context](const Clingo::AST::Statement &statement)
{ {
statement.data.accept(StatementVisitor(), statement, context); const auto formula = statement.data.accept(StatementVisitor(), statement, context);
if (!formula)
return;
context.logger.outputStream() << formula.value() << std::endl;
}; };
const auto logger = const auto logger =

View File

@ -1,257 +0,0 @@
#include <anthem/output/ClingoOutput.h>
#include <anthem/Utils.h>
#include <anthem/output/Formatting.h>
namespace anthem
{
namespace output
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// ClingoOutput
//
////////////////////////////////////////////////////////////////////////////////////////////////////
const auto printCollection = [](auto &stream, const auto &collection,
const auto &preToken, const auto &delimiter, const auto &postToken, bool printTokensIfEmpty)
{
if (collection.empty() && !printTokensIfEmpty)
return;
if (collection.empty() && printTokensIfEmpty)
{
stream << preToken << postToken;
return;
}
stream << preToken;
// TODO: use cbegin/cend (requires support by Clingo::SymbolSpan)
for (auto i = collection.begin(); i != collection.end(); i++)
{
if (i != collection.begin())
stream << delimiter;
stream << *i;
}
stream << postToken;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
ColorStream &operator<<(ColorStream &stream, const Clingo::Symbol &symbol)
{
switch (symbol.type())
{
case Clingo::SymbolType::Infimum:
return (stream << Keyword("#inf"));
case Clingo::SymbolType::Supremum:
return (stream << Keyword("#sup"));
case Clingo::SymbolType::Number:
return (stream << Number<decltype(symbol.number())>(symbol.number()));
case Clingo::SymbolType::String:
return (stream << String(symbol.string()));
case Clingo::SymbolType::Function:
{
const auto isNegative = symbol.is_negative();
assert(isNegative != symbol.is_positive());
const auto isUnaryTuple = (symbol.name()[0] == '\0' && symbol.arguments().size() == 1);
const auto printIfEmpty = (symbol.name()[0] == '\0' || !symbol.arguments().empty());
if (isNegative)
stream << Operator("-");
stream << Function(symbol.name());
const auto postToken = (isUnaryTuple ? ",)" : ")");
printCollection(stream, symbol.arguments(), "(", ", ", postToken, printIfEmpty);
return stream;
}
}
return stream;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::Sign &sign)
{
switch (sign)
{
case Clingo::AST::Sign::None:
return stream;
case Clingo::AST::Sign::Negation:
return (stream << Keyword("not") << " ");
case Clingo::AST::Sign::DoubleNegation:
return (stream << Keyword("not") << " " << Keyword("not") << " ");
}
return stream;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::Boolean &boolean)
{
if (boolean.value == true)
return (stream << Boolean("#true"));
return (stream << Boolean("#false"));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::Variable &variable)
{
if (!isReservedVariableName(variable.name))
return (stream << Variable(variable.name));
const auto variableName = std::string(UserVariablePrefix) + variable.name;
return (stream << Variable(variableName.c_str()));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::BinaryOperator &binaryOperator)
{
switch (binaryOperator)
{
case Clingo::AST::BinaryOperator::XOr:
return (stream << Keyword("xor"));
case Clingo::AST::BinaryOperator::Or:
return (stream << Keyword("or"));
case Clingo::AST::BinaryOperator::And:
return (stream << Keyword("and"));
case Clingo::AST::BinaryOperator::Plus:
return (stream << Operator("+"));
case Clingo::AST::BinaryOperator::Minus:
return (stream << Operator("-"));
case Clingo::AST::BinaryOperator::Multiplication:
return (stream << Operator("*"));
case Clingo::AST::BinaryOperator::Division:
return (stream << Operator("/"));
case Clingo::AST::BinaryOperator::Modulo:
return (stream << Operator("\\"));
}
return stream;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::UnaryOperation &unaryOperation)
{
return (stream
<< Keyword(Clingo::AST::left_hand_side(unaryOperation.unary_operator))
<< unaryOperation.argument
<< Keyword(Clingo::AST::right_hand_side(unaryOperation.unary_operator)));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::BinaryOperation &binaryOperation)
{
return (stream << "(" << binaryOperation.left
<< " " << binaryOperation.binary_operator
<< " " << binaryOperation.right << ")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::Interval &interval)
{
return (stream << "(" << interval.left << Operator("..") << interval.right << ")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::Function &function)
{
const auto isUnaryTuple = (function.name[0] == '\0' && function.arguments.size() == 1);
const auto printIfEmpty = (function.name[0] == '\0' || !function.arguments.empty());
if (function.external)
stream << Operator("@");
stream << output::Function(function.name);
const auto postToken = (isUnaryTuple ? ",)" : ")");
printCollection(stream, function.arguments, "(", ", ", postToken, printIfEmpty);
return stream;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::Pool &pool)
{
// Note: There is no representation for an empty pool
if (pool.arguments.empty())
return (stream << "(" << Number<int>(1) << "/" << Number<int>(0) << ")");
printCollection(stream, pool.arguments, "(", ";", ")", true);
return stream;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
struct TermOutputVisitor
{
void visit(const Clingo::Symbol &symbol, ColorStream &stream)
{
stream << symbol;
}
void visit(const Clingo::AST::Variable &variable, ColorStream &stream)
{
stream << variable;
}
void visit(const Clingo::AST::UnaryOperation &unaryOperation, ColorStream &stream)
{
stream << unaryOperation;
}
void visit(const Clingo::AST::BinaryOperation &binaryOperation, ColorStream &stream)
{
stream << binaryOperation;
}
void visit(const Clingo::AST::Interval &interval, ColorStream &stream)
{
stream << interval;
}
void visit(const Clingo::AST::Function &function, ColorStream &stream)
{
stream << function;
}
void visit(const Clingo::AST::Pool &pool, ColorStream &stream)
{
stream << pool;
}
};
////////////////////////////////////////////////////////////////////////////////////////////////////
ColorStream &operator<<(ColorStream &stream, const Clingo::AST::Term &term)
{
term.data.accept(TermOutputVisitor(), stream);
return stream;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}
}

View File

@ -21,7 +21,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "p(1..5)."; input << "p(1..5).";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in (1..5) -> p(V1)\n"); REQUIRE(output.str() == "(V1 in 1..5 -> p(V1))\n");
} }
SECTION("simple example 2") SECTION("simple example 2")
@ -29,7 +29,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "p(N) :- N = 1..5."; input << "p(N) :- N = 1..5.";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in N and exists X1, X2 (X1 in N and X2 in (1..5) and X1 = X2) -> p(V1)\n"); REQUIRE(output.str() == "((V1 in N and exists X1, X2 (X1 in N and X2 in 1..5 and X1 = X2)) -> p(V1))\n");
} }
SECTION("simple example 3") SECTION("simple example 3")
@ -37,7 +37,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "p(N + 1) :- q(N)."; input << "p(N + 1) :- q(N).";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in (N + 1) and exists X1 (X1 in N and q(X1)) -> p(V1)\n"); REQUIRE(output.str() == "((V1 in (N + 1) and exists X1 (X1 in N and q(X1))) -> p(V1))\n");
} }
SECTION("n-ary head") SECTION("n-ary head")
@ -45,7 +45,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "p(N, 1, 2) :- N = 1..5."; input << "p(N, 1, 2) :- N = 1..5.";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in N and V2 in 1 and V3 in 2 and exists X1, X2 (X1 in N and X2 in (1..5) and X1 = X2) -> p(V1, V2, V3)\n"); REQUIRE(output.str() == "((V1 in N and V2 in 1 and V3 in 2 and exists X1, X2 (X1 in N and X2 in 1..5 and X1 = X2)) -> p(V1, V2, V3))\n");
} }
SECTION("disjunctive head") SECTION("disjunctive head")
@ -54,7 +54,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "q(3, N); p(N, 1, 2) :- N = 1..5."; input << "q(3, N); p(N, 1, 2) :- N = 1..5.";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in N and V2 in 1 and V3 in 2 and V4 in 3 and V5 in N and exists X1, X2 (X1 in N and X2 in (1..5) and X1 = X2) -> p(V1, V2, V3) or q(V4, V5)\n"); REQUIRE(output.str() == "((V1 in N and V2 in 1 and V3 in 2 and V4 in 3 and V5 in N and exists X1, X2 (X1 in N and X2 in 1..5 and X1 = X2)) -> (p(V1, V2, V3) or q(V4, V5)))\n");
} }
SECTION("disjunctive head (alternative syntax)") SECTION("disjunctive head (alternative syntax)")
@ -63,7 +63,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "q(3, N), p(N, 1, 2) :- N = 1..5."; input << "q(3, N), p(N, 1, 2) :- N = 1..5.";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in N and V2 in 1 and V3 in 2 and V4 in 3 and V5 in N and exists X1, X2 (X1 in N and X2 in (1..5) and X1 = X2) -> p(V1, V2, V3) or q(V4, V5)\n"); REQUIRE(output.str() == "((V1 in N and V2 in 1 and V3 in 2 and V4 in 3 and V5 in N and exists X1, X2 (X1 in N and X2 in 1..5 and X1 = X2)) -> (p(V1, V2, V3) or q(V4, V5)))\n");
} }
SECTION("escaping conflicting variable names") SECTION("escaping conflicting variable names")
@ -71,7 +71,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "p(X1, V1) :- q(X1), q(V1)."; input << "p(X1, V1) :- q(X1), q(V1).";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in _X1 and V2 in _V1 and exists X1 (X1 in _X1 and q(X1)) and exists X2 (X2 in _V1 and q(X2)) -> p(V1, V2)\n"); REQUIRE(output.str() == "((V1 in _X1 and V2 in _V1 and exists X1 (X1 in _X1 and q(X1)) and exists X2 (X2 in _V1 and q(X2))) -> p(V1, V2))\n");
} }
SECTION("fact") SECTION("fact")
@ -79,7 +79,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "p(42)."; input << "p(42).";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in 42 -> p(V1)\n"); REQUIRE(output.str() == "(V1 in 42 -> p(V1))\n");
} }
SECTION("0-ary fact") SECTION("0-ary fact")
@ -87,7 +87,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "p."; input << "p.";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "#true -> p\n"); REQUIRE(output.str() == "(#true -> p)\n");
} }
SECTION("disjunctive fact (no arguments)") SECTION("disjunctive fact (no arguments)")
@ -95,7 +95,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "q; p."; input << "q; p.";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "#true -> p or q\n"); REQUIRE(output.str() == "(#true -> (p or q))\n");
} }
SECTION("disjunctive fact (arguments)") SECTION("disjunctive fact (arguments)")
@ -103,7 +103,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "q; p(42)."; input << "q; p(42).";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in 42 -> p(V1) or q\n"); REQUIRE(output.str() == "(V1 in 42 -> (p(V1) or q))\n");
} }
SECTION("integrity constraint (no arguments)") SECTION("integrity constraint (no arguments)")
@ -111,7 +111,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << ":- p, q."; input << ":- p, q.";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "p and q -> #false\n"); REQUIRE(output.str() == "((p and q) -> #false)\n");
} }
SECTION("contradiction") SECTION("contradiction")
@ -119,7 +119,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << ":-."; input << ":-.";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "#true -> #false\n"); REQUIRE(output.str() == "(#true -> #false)\n");
} }
SECTION("integrity constraint (arguments)") SECTION("integrity constraint (arguments)")
@ -127,7 +127,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << ":- p(42), q."; input << ":- p(42), q.";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "exists X1 (X1 in 42 and p(X1)) and q -> #false\n"); REQUIRE(output.str() == "((exists X1 (X1 in 42 and p(X1)) and q) -> #false)\n");
} }
SECTION("inf/sup") SECTION("inf/sup")
@ -135,7 +135,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "p(X, #inf) :- q(X, #sup)."; input << "p(X, #inf) :- q(X, #sup).";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in X and V2 in #inf and exists X1, X2 (X1 in X and X2 in #sup and q(X1, X2)) -> p(V1, V2)\n"); REQUIRE(output.str() == "((V1 in X and V2 in #inf and exists X1, X2 (X1 in X and X2 in #sup and q(X1, X2))) -> p(V1, V2))\n");
} }
SECTION("strings") SECTION("strings")
@ -143,7 +143,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "p(X, \"foo\") :- q(X, \"bar\")."; input << "p(X, \"foo\") :- q(X, \"bar\").";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in X and V2 in \"foo\" and exists X1, X2 (X1 in X and X2 in \"bar\" and q(X1, X2)) -> p(V1, V2)\n"); REQUIRE(output.str() == "((V1 in X and V2 in \"foo\" and exists X1, X2 (X1 in X and X2 in \"bar\" and q(X1, X2))) -> p(V1, V2))\n");
} }
SECTION("tuples") SECTION("tuples")
@ -151,7 +151,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "p(X, (1, 2, 3)) :- q(X, (4, 5))."; input << "p(X, (1, 2, 3)) :- q(X, (4, 5)).";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in X and V2 in (1, 2, 3) and exists X1, X2 (X1 in X and X2 in (4, 5) and q(X1, X2)) -> p(V1, V2)\n"); REQUIRE(output.str() == "((V1 in X and V2 in (1, 2, 3) and exists X1, X2 (X1 in X and X2 in (4, 5) and q(X1, X2))) -> p(V1, V2))\n");
} }
SECTION("1-ary tuples") SECTION("1-ary tuples")
@ -159,7 +159,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "p(X, (1,)) :- q(X, (2,))."; input << "p(X, (1,)) :- q(X, (2,)).";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in X and V2 in (1,) and exists X1, X2 (X1 in X and X2 in (2,) and q(X1, X2)) -> p(V1, V2)\n"); REQUIRE(output.str() == "((V1 in X and V2 in (1,) and exists X1, X2 (X1 in X and X2 in (2,) and q(X1, X2))) -> p(V1, V2))\n");
} }
SECTION("intervals") SECTION("intervals")
@ -167,7 +167,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "p(X, 1..10) :- q(X, 6..12)."; input << "p(X, 1..10) :- q(X, 6..12).";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in X and V2 in (1..10) and exists X1, X2 (X1 in X and X2 in (6..12) and q(X1, X2)) -> p(V1, V2)\n"); REQUIRE(output.str() == "((V1 in X and V2 in 1..10 and exists X1, X2 (X1 in X and X2 in 6..12 and q(X1, X2))) -> p(V1, V2))\n");
} }
SECTION("comparisons") SECTION("comparisons")
@ -175,7 +175,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "p(M, N, O, P) :- M < N, P != O."; input << "p(M, N, O, P) :- M < N, P != O.";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in M and V2 in N and V3 in O and V4 in P and exists X1, X2 (X1 in M and X2 in N and X1 < X2) and exists X3, X4 (X3 in P and X4 in O and X3 != X4) -> p(V1, V2, V3, V4)\n"); REQUIRE(output.str() == "((V1 in M and V2 in N and V3 in O and V4 in P and exists X1, X2 (X1 in M and X2 in N and X1 < X2) and exists X3, X4 (X3 in P and X4 in O and X3 != X4)) -> p(V1, V2, V3, V4))\n");
} }
SECTION("single negation") SECTION("single negation")
@ -183,7 +183,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "not p(X, 1) :- not q(X, 2)."; input << "not p(X, 1) :- not q(X, 2).";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in X and V2 in 1 and exists X1, X2 (X1 in X and X2 in 2 and not q(X1, X2)) -> not p(V1, V2)\n"); REQUIRE(output.str() == "((V1 in X and V2 in 1 and exists X1, X2 (X1 in X and X2 in 2 and not q(X1, X2))) -> not p(V1, V2))\n");
} }
SECTION("variable numbering") SECTION("variable numbering")
@ -192,9 +192,9 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "f; q(A1, A2); p(A3, r(A4)); g(g(A5)) :- g(A3), f, q(A4, A1), p(A2, A5)."; input << "f; q(A1, A2); p(A3, r(A4)); g(g(A5)) :- g(A3), f, q(A4, A1), p(A2, A5).";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in A1 and V2 in A2 and V3 in A3 and V4 in r(A4) and V5 in g(A5)" REQUIRE(output.str() == "((V1 in A1 and V2 in A2 and V3 in A3 and V4 in r(A4) and V5 in g(A5)"
" and exists X1 (X1 in A3 and g(X1)) and f and exists X2, X3 (X2 in A4 and X3 in A1 and q(X2, X3)) and exists X4, X5 (X4 in A2 and X5 in A5 and p(X4, X5))" " and exists X1 (X1 in A3 and g(X1)) and f and exists X2, X3 (X2 in A4 and X3 in A1 and q(X2, X3)) and exists X4, X5 (X4 in A2 and X5 in A5 and p(X4, X5)))"
" -> q(V1, V2) or p(V3, V4) or g(V5) or f\n"); " -> (q(V1, V2) or p(V3, V4) or g(V5) or f))\n");
} }
SECTION("nested functions") SECTION("nested functions")
@ -202,7 +202,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "p(q(s(t(X1))), u(X2)) :- u(v(w(X2)), z(X1))."; input << "p(q(s(t(X1))), u(X2)) :- u(v(w(X2)), z(X1)).";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in q(s(t(_X1))) and V2 in u(_X2) and exists X1, X2 (X1 in v(w(_X2)) and X2 in z(_X1) and u(X1, X2)) -> p(V1, V2)\n"); REQUIRE(output.str() == "((V1 in q(s(t(_X1))) and V2 in u(_X2) and exists X1, X2 (X1 in v(w(_X2)) and X2 in z(_X1) and u(X1, X2))) -> p(V1, V2))\n");
} }
SECTION("choice rule (simple)") SECTION("choice rule (simple)")
@ -210,7 +210,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "{p}."; input << "{p}.";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "p -> p\n"); REQUIRE(output.str() == "(p -> p)\n");
} }
SECTION("choice rule (two elements)") SECTION("choice rule (two elements)")
@ -218,7 +218,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "{p; q}."; input << "{p; q}.";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "p or q -> p or q\n"); REQUIRE(output.str() == "((p or q) -> (p or q))\n");
} }
SECTION("choice rule (n-ary elements)") SECTION("choice rule (n-ary elements)")
@ -226,7 +226,7 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "{p(1..3, N); q(2..4)}."; input << "{p(1..3, N); q(2..4)}.";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in (1..3) and V2 in N and V3 in (2..4) and (p(V1, V2) or q(V3)) -> p(V1, V2) or q(V3)\n"); REQUIRE(output.str() == "((V1 in 1..3 and V2 in N and V3 in 2..4 and (p(V1, V2) or q(V3))) -> (p(V1, V2) or q(V3)))\n");
} }
SECTION("choice rule with body") SECTION("choice rule with body")
@ -234,6 +234,22 @@ TEST_CASE("[translation] Rules are translated correctly", "[translation]")
input << "{p(M, N); q(P)} :- s(M, N, P)."; input << "{p(M, N); q(P)} :- s(M, N, P).";
anthem::translate("input", input, context); anthem::translate("input", input, context);
REQUIRE(output.str() == "V1 in M and V2 in N and V3 in P and exists X1, X2, X3 (X1 in M and X2 in N and X3 in P and s(X1, X2, X3)) and (p(V1, V2) or q(V3)) -> p(V1, V2) or q(V3)\n"); REQUIRE(output.str() == "((V1 in M and V2 in N and V3 in P and exists X1, X2, X3 (X1 in M and X2 in N and X3 in P and s(X1, X2, X3)) and (p(V1, V2) or q(V3))) -> (p(V1, V2) or q(V3)))\n");
}
SECTION("choice rule with negation")
{
input << "{not p(X, 1)} :- not q(X, 2).";
anthem::translate("input", input, context);
REQUIRE(output.str() == "((V1 in X and V2 in 1 and exists X1, X2 (X1 in X and X2 in 2 and not q(X1, X2)) and not p(V1, V2)) -> not p(V1, V2))\n");
}
SECTION("choice rule with negation (two elements)")
{
input << "{not p(X, 1); not s} :- not q(X, 2).";
anthem::translate("input", input, context);
REQUIRE(output.str() == "((V1 in X and V2 in 1 and exists X1, X2 (X1 in X and X2 in 2 and not q(X1, X2)) and (not p(V1, V2) or not s)) -> (not p(V1, V2) or not s))\n");
} }
} }