#include #include #include #include #include #include namespace pslang::interpreter { namespace { void print(std::ostream & out, ast::unary_operation_type type) { switch (type) { case ast::unary_operation_type::negation: out << "-"; return; case ast::unary_operation_type::logical_not: out << "!"; return; } out << "(unknown)"; } void print(std::ostream & out, ast::binary_operation_type type) { switch (type) { case ast::binary_operation_type::addition: out << "+"; return; case ast::binary_operation_type::subtraction: out << "-"; return; case ast::binary_operation_type::multiplication: out << "*"; return; case ast::binary_operation_type::division: out << "/"; return; case ast::binary_operation_type::remainder: out << "%"; return; case ast::binary_operation_type::logical_and: out << "&"; return; case ast::binary_operation_type::logical_or: out << "|"; return; case ast::binary_operation_type::logical_xor: out << "^"; return; case ast::binary_operation_type::equals: out << "=="; return; case ast::binary_operation_type::not_equals: out << "!="; return; case ast::binary_operation_type::less: out << "<"; return; case ast::binary_operation_type::greater: out << ">"; return; case ast::binary_operation_type::less_equals: out << "<="; return; case ast::binary_operation_type::greater_equals: out << ">="; return; } out << "(unknown)"; } value eval_impl(context & context, ast::expression_ptr const & expression); template value eval_impl(context & context, ast::numeric_literal_base const & literal) { return primitive_value(primitive_value_base{literal.value});; } value eval_impl(context & context, ast::literal const & literal) { return std::visit([&](auto const & expression){ return eval_impl(context, expression); }, literal); } value eval_impl(context & context, ast::identifier const & identifier) { for (auto it = context.scope_stack.rbegin(); it != context.scope_stack.rend(); ++it) { if (auto jt = it->variables.find(identifier.name); jt != it->variables.end()) return jt->second.value; } throw std::runtime_error("Identifier \"" + identifier.name + "\" is not defined"); } template value unary_operation_impl(ast::unary_operation_type type, primitive_value_base const & arg1) { switch (type) { case ast::unary_operation_type::negation: if constexpr ((std::is_integral_v || std::is_floating_point_v) && !std::is_same_v) { return primitive_value(primitive_value_base{static_cast(-arg1.value)}); } break; case ast::unary_operation_type::logical_not: if constexpr (std::is_same_v) { return primitive_value(primitive_value_base{static_cast(!arg1.value)}); } else if constexpr (std::is_integral_v) { return primitive_value(primitive_value_base{static_cast(~arg1.value)}); } break; } std::ostringstream os; os << "Cannot apply unary operator \""; print(os, type); os << "\" to a value of type "; type::print(os, type_of(primitive_value(arg1))); throw std::runtime_error(os.str()); } value unary_operation_impl(ast::unary_operation_type type, primitive_value const & arg1) { return std::visit([&](auto const & value){ return unary_operation_impl(type, value); }, arg1); } value eval_impl(context & context, ast::unary_operation const & unary_operation) { auto arg1 = eval_impl(context, unary_operation.arg1); return std::visit([&](auto const & value){ return unary_operation_impl(unary_operation.type, value); }, arg1); } bool requires_same_argument_type(ast::binary_operation_type) { // TODO: shift operators should return false return true; } template value binary_operation_impl_same_type(ast::binary_operation_type type, primitive_value_base const & arg1, value const & arg2_generic) { primitive_value_base const & arg2 = std::get>(std::get(arg2_generic)); switch (type) { case ast::binary_operation_type::addition: if constexpr (!std::is_same_v) { return primitive_value(primitive_value_base{static_cast(arg1.value + arg2.value)}); } break; case ast::binary_operation_type::subtraction: if constexpr (!std::is_same_v) { return primitive_value(primitive_value_base{static_cast(arg1.value - arg2.value)}); } break; case ast::binary_operation_type::multiplication: if constexpr (!std::is_same_v) { return primitive_value(primitive_value_base{static_cast(arg1.value * arg2.value)}); } break; case ast::binary_operation_type::division: if constexpr (!std::is_same_v) { if constexpr (std::is_integral_v) { if (arg2.value == static_cast(0)) throw std::runtime_error("Division by zero"); } return primitive_value(primitive_value_base{static_cast(arg1.value / arg2.value)}); } break; case ast::binary_operation_type::remainder: if constexpr (!std::is_same_v && std::is_integral_v) { if constexpr (std::is_integral_v) { if (arg2.value == static_cast(0)) throw std::runtime_error("Division by zero"); } return primitive_value(primitive_value_base{static_cast(arg1.value % arg2.value)}); } break; case ast::binary_operation_type::logical_and: if constexpr (std::is_same_v) { return primitive_value(primitive_value_base{static_cast(arg1.value && arg2.value)}); } else if constexpr (std::is_integral_v) { return primitive_value(primitive_value_base{static_cast(arg1.value & arg2.value)}); } break; case ast::binary_operation_type::logical_or: if constexpr (std::is_same_v) { return primitive_value(primitive_value_base{static_cast(arg1.value || arg2.value)}); } else if constexpr (std::is_integral_v) { return primitive_value(primitive_value_base{static_cast(arg1.value | arg2.value)}); } break; case ast::binary_operation_type::logical_xor: if constexpr (std::is_same_v) { return primitive_value(primitive_value_base{static_cast(arg1.value ^ arg2.value)}); } else if constexpr (std::is_integral_v) { return primitive_value(primitive_value_base{static_cast(arg1.value ^ arg2.value)}); } break; case ast::binary_operation_type::equals: return primitive_value(primitive_value_base{arg1.value == arg2.value}); case ast::binary_operation_type::not_equals: return primitive_value(primitive_value_base{arg1.value != arg2.value}); case ast::binary_operation_type::less: return primitive_value(primitive_value_base{arg1.value < arg2.value}); case ast::binary_operation_type::greater: return primitive_value(primitive_value_base{arg1.value > arg2.value}); case ast::binary_operation_type::less_equals: return primitive_value(primitive_value_base{arg1.value <= arg2.value}); case ast::binary_operation_type::greater_equals: return primitive_value(primitive_value_base{arg1.value >= arg2.value}); } std::ostringstream os; os << "Cannot apply binary operator \""; print(os, type); os << "\" to values of type "; type::print(os, type_of(primitive_value(arg1))); os << " and "; type::print(os, type_of(primitive_value(arg2))); throw std::runtime_error(os.str()); } value binary_operation_impl_same_type(ast::binary_operation_type type, primitive_value const & arg1, value const & arg2) { return std::visit([&](auto const & value){ return binary_operation_impl_same_type(type, value, arg2); }, arg1); } value eval_impl(context & context, ast::binary_operation const & binary_operation) { auto arg1 = eval_impl(context, binary_operation.arg1); auto arg2 = eval_impl(context, binary_operation.arg2); if (requires_same_argument_type(binary_operation.type)) { auto type1 = type_of(arg1); auto type2 = type_of(arg2); if (!type::equal(type1, type2)) { std::ostringstream os; os << "Cannot apply binary operator \""; print(os, binary_operation.type); os << "\" to values of type "; type::print(os, type1); os << " and "; type::print(os, type2); throw std::runtime_error(os.str()); } return std::visit([&](auto const & value){ return binary_operation_impl_same_type(binary_operation.type, value, arg2); }, arg1); } throw std::runtime_error("eval(binary_operation) for different argument types not implemented"); } template value cast_impl(primitive_value_base const & value, type::primitive_type_base const & type) { if constexpr (std::is_same_v) { return primitive_value(value); } else if constexpr (!std::is_same_v && !std::is_same_v) { return primitive_value(primitive_value_base{static_cast(value.value)}); } std::ostringstream os; os << "Cannot cast value of type "; type::print(os, type_of(primitive_value(value))); os << " to type "; type::print(os, type::primitive_type(type)); throw std::runtime_error(os.str()); } template value cast_impl(primitive_value_base const & value, type::primitive_type const & type) { return std::visit([&](auto const & type){ return cast_impl(value, type); }, type); } template value cast_impl(primitive_value_base const & value, type::type const & type) { return std::visit([&](auto const & type){ return cast_impl(value, type); }, type); } value cast_impl(primitive_value const & value, type::type const & type) { return std::visit([&](auto const & value){ return cast_impl(value, type); }, value); } value eval_impl(context & context, ast::cast_operation const & cast_operation) { auto arg = eval(context, cast_operation.expression); return std::visit([&](auto const & value){ return cast_impl(value, *cast_operation.type); }, arg); } value eval_impl(context & context, ast::expression_ptr const & expression) { return std::visit([&](auto const & expression){ return eval_impl(context, expression); }, *expression); } } value eval(context & context, ast::expression_ptr const & expression) { return eval_impl(context, expression); } }