#ifndef __ANTHEM__TYPE_H #define __ANTHEM__TYPE_H #include #include #include namespace anthem { //////////////////////////////////////////////////////////////////////////////////////////////////// // // Type // //////////////////////////////////////////////////////////////////////////////////////////////////// template Type type(const ast::Term &term, Arguments &&... arguments); //////////////////////////////////////////////////////////////////////////////////////////////////// template struct TermTypeVisitor { template static Type visit(const ast::BinaryOperation &binaryOperation, Arguments &&... arguments) { const auto leftType = type(binaryOperation.left, std::forward(arguments)...); const auto rightType = type(binaryOperation.right, std::forward(arguments)...); // Binary operations on empty sets return an empty set (also with division) if (leftType.setSize == SetSize::Empty || rightType.setSize == SetSize::Empty) return {Domain::Unknown, SetSize::Empty}; // Binary operations on nonintegers return an empty set (also with division) if (leftType.domain == Domain::Noninteger || rightType.domain == Domain::Noninteger) return {Domain::Unknown, SetSize::Empty}; // Binary operations on unknown types return an unknown set if (leftType.domain == Domain::Unknown || rightType.domain == Domain::Unknown) return {Domain::Unknown, SetSize::Unknown}; // Divisions return an unknown set if (binaryOperation.operator_ == ast::BinaryOperation::Operator::Division) return {Domain::Integer, SetSize::Unknown}; // Binary operations on integer sets of unknown size return an integer set of unknown size if (leftType.setSize == SetSize::Unknown || rightType.setSize == SetSize::Unknown) return {Domain::Integer, SetSize::Unknown}; // Binary operations on integer sets with multiple elements return an integer set with multiple elements if (leftType.setSize == SetSize::Multi || rightType.setSize == SetSize::Multi) return {Domain::Integer, SetSize::Multi}; // Binary operations on plain integers return a plain integer return {Domain::Integer, SetSize::Unit}; } template static Type visit(const ast::Boolean &, Arguments &&...) { return {Domain::Noninteger, SetSize::Unit}; } template static Type visit(const ast::Function &function, Arguments &&...) { // TODO: check that functions cannot return sets return {function.declaration->domain, SetSize::Unit}; } template static Type visit(const ast::Integer &, Arguments &&...) { return {Domain::Integer, SetSize::Unit}; } template static Type visit(const ast::Interval &interval, Arguments &&... arguments) { const auto fromType = type(interval.from, std::forward(arguments)...); const auto toType = type(interval.to, std::forward(arguments)...); // Intervals with empty sets return an empty set if (fromType.setSize == SetSize::Empty || toType.setSize == SetSize::Empty) return {Domain::Unknown, SetSize::Empty}; // Intervals with nonintegers return an empty set if (fromType.domain == Domain::Noninteger || toType.domain == Domain::Noninteger) return {Domain::Unknown, SetSize::Empty}; // Intervals with unknown types return an unknown set if (fromType.domain == Domain::Unknown || toType.domain == Domain::Unknown) return {Domain::Unknown, SetSize::Unknown}; // Intervals with integers generally return integer sets // TODO: handle 1-element intervals such as 1..1 and empty intervals such as 2..1 return {Domain::Integer, SetSize::Unknown}; } template static Type visit(const ast::SpecialInteger &, Arguments &&...) { return {Domain::Noninteger, SetSize::Unit}; } template static Type visit(const ast::String &, Arguments &&...) { return {Domain::Noninteger, SetSize::Unit}; } template static Type visit(const ast::UnaryOperation &unaryOperation, Arguments &&... arguments) { assert(unaryOperation.operator_ == ast::UnaryOperation::Operator::Absolute); const auto argumentType = type(unaryOperation.argument, std::forward(arguments)...); // Absolute value of an empty set returns an empty set if (argumentType.setSize == SetSize::Empty) return {Domain::Unknown, SetSize::Empty}; // Absolute value of nonintegers returns an empty set if (argumentType.domain == Domain::Noninteger) return {Domain::Unknown, SetSize::Empty}; // Absolute value of integers returns the same type if (argumentType.domain == Domain::Integer) return argumentType; return {Domain::Unknown, SetSize::Unknown}; } template static Type visit(const ast::Variable &variable, Arguments &&... arguments) { const auto domain = VariableDomainAccessor()(variable, std::forward(arguments)...); return {domain, SetSize::Unit}; } }; //////////////////////////////////////////////////////////////////////////////////////////////////// template Type type(const ast::Term &term, Arguments &&... arguments) { return term.accept(TermTypeVisitor(), std::forward(arguments)...); } //////////////////////////////////////////////////////////////////////////////////////////////////// } #endif