diff --git a/include/anthem/Type.h b/include/anthem/Type.h new file mode 100644 index 0000000..61442ea --- /dev/null +++ b/include/anthem/Type.h @@ -0,0 +1,166 @@ +#ifndef __ANTHEM__TYPE_H +#define __ANTHEM__TYPE_H + +#include +#include + +namespace anthem +{ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Type +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +struct DefaultVariableDomainAccessor +{ + Domain operator()(const ast::Variable &variable) + { + return variable.declaration->domain; + } +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +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 diff --git a/include/anthem/Utils.h b/include/anthem/Utils.h index 58c9dcf..bad2c1a 100644 --- a/include/anthem/Utils.h +++ b/include/anthem/Utils.h @@ -42,6 +42,24 @@ enum class Domain //////////////////////////////////////////////////////////////////////////////////////////////////// +enum class SetSize +{ + Empty, + Unit, + Multi, + Unknown, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +struct Type +{ + Domain domain{Domain::Unknown}; + SetSize setSize{SetSize::Unknown}; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + } #endif