diff --git a/libs/interpreter/include/pslang/interpreter/eval.hpp b/libs/interpreter/include/pslang/interpreter/eval.hpp index be5b43a..2342c72 100644 --- a/libs/interpreter/include/pslang/interpreter/eval.hpp +++ b/libs/interpreter/include/pslang/interpreter/eval.hpp @@ -9,4 +9,6 @@ namespace pslang::interpreter value eval(context & context, ast::expression_ptr const & expression); + value * eval_ref(context & context, ast::expression_ptr const & expression); + } diff --git a/libs/interpreter/source/eval.cpp b/libs/interpreter/source/eval.cpp index d7ffd6d..d937310 100644 --- a/libs/interpreter/source/eval.cpp +++ b/libs/interpreter/source/eval.cpp @@ -79,6 +79,77 @@ namespace pslang::interpreter out << "(unknown)"; } + std::uint64_t get_array_index(value const & index, std::uint64_t size) + { + std::optional index_unsigned; + std::optional index_signed; + + if (auto pvalue = std::get_if(&index)) + { + if (auto i8 = std::get_if(pvalue)) + { + index_signed = i8->value; + } + else if (auto u8 = std::get_if(pvalue)) + { + index_unsigned = u8->value; + } + else if (auto i16 = std::get_if(pvalue)) + { + index_signed = i16->value; + } + else if (auto u16 = std::get_if(pvalue)) + { + index_unsigned = u16->value; + } + else if (auto i32 = std::get_if(pvalue)) + { + index_signed = i32->value; + } + else if (auto u32 = std::get_if(pvalue)) + { + index_unsigned = u32->value; + } + else if (auto i64 = std::get_if(pvalue)) + { + index_signed = i64->value; + } + else if (auto u64 = std::get_if(pvalue)) + { + index_unsigned = u64->value; + } + } + + if (!index_signed && !index_unsigned) + { + std::ostringstream os; + os << "Cannot index into an array with an expression of type "; + type::print(os, type_of(index)); + throw std::runtime_error(os.str()); + } + + if (index_unsigned) + { + if (*index_unsigned >= size) + { + std::ostringstream os; + os << "Array index " << *index_unsigned << " out of bounds " << size; + throw std::runtime_error(os.str()); + } + return *index_unsigned; + } + else // if (index_signed) + { + if (*index_signed < 0 || *index_signed >= size) + { + std::ostringstream os; + os << "Array index " << *index_signed << " out of bounds " << size; + throw std::runtime_error(os.str()); + } + return *index_signed; + } + } + value eval_impl(context & context, ast::expression_ptr const & expression); template @@ -474,77 +545,7 @@ namespace pslang::interpreter if (auto avalue = std::get_if(&array)) { - std::optional index_unsigned; - std::optional index_signed; - - if (auto pvalue = std::get_if(&index)) - { - if (auto i8 = std::get_if(pvalue)) - { - index_signed = i8->value; - } - else if (auto u8 = std::get_if(pvalue)) - { - index_unsigned = u8->value; - } - else if (auto i16 = std::get_if(pvalue)) - { - index_signed = i16->value; - } - else if (auto u16 = std::get_if(pvalue)) - { - index_unsigned = u16->value; - } - else if (auto i32 = std::get_if(pvalue)) - { - index_signed = i32->value; - } - else if (auto u32 = std::get_if(pvalue)) - { - index_unsigned = u32->value; - } - else if (auto i64 = std::get_if(pvalue)) - { - index_signed = i64->value; - } - else if (auto u64 = std::get_if(pvalue)) - { - index_unsigned = u64->value; - } - } - - if (!index_signed && !index_unsigned) - { - std::ostringstream os; - os << "Cannot index into an array with an expression of type "; - type::print(os, type_of(index)); - throw std::runtime_error(os.str()); - } - - std::uint64_t final_index; - - if (index_unsigned) - { - if (*index_unsigned >= avalue->elements.size()) - { - std::ostringstream os; - os << "Array index " << *index_unsigned << " out of bounds " << avalue->elements.size(); - throw std::runtime_error(os.str()); - } - final_index = *index_unsigned; - } - else // if (index_signed) - { - if (*index_signed < 0 || *index_signed >= avalue->elements.size()) - { - std::ostringstream os; - os << "Array index " << *index_signed << " out of bounds " << avalue->elements.size(); - throw std::runtime_error(os.str()); - } - final_index = *index_signed; - } - - return *avalue->elements[final_index]; + return *avalue->elements[get_array_index(index, avalue->elements.size())]; } std::ostringstream os; @@ -558,6 +559,73 @@ namespace pslang::interpreter return std::visit([&](auto const & expression){ return eval_impl(context, expression); }, *expression); } + value * eval_ref_impl(context & context, ast::literal const &) + { + throw std::runtime_error("Literal cannot be on the left-hand-side of assignment"); + } + + value * eval_ref_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()) + { + if (jt->second.category != ast::value_category::_mutable) + throw std::runtime_error("Cannot assign a value to a non-mutable variable"); + + return &jt->second.value; + } + } + + throw std::runtime_error("Identifier \"" + identifier.name + "\" is not defined"); + } + + value * eval_ref_impl(context & context, ast::unary_operation const &) + { + throw std::runtime_error("Unary operation cannot be on the left-hand-side of assignment"); + } + + value * eval_ref_impl(context & context, ast::binary_operation const &) + { + throw std::runtime_error("Binary operation cannot be on the left-hand-side of assignment"); + } + + value * eval_ref_impl(context & context, ast::cast_operation const &) + { + throw std::runtime_error("Cast operation cannot be on the left-hand-side of assignment"); + } + + value * eval_ref_impl(context & context, ast::function_call const &) + { + throw std::runtime_error("Function call cannot be on the left-hand-side of assignment"); + } + + value * eval_ref_impl(context & context, ast::array const &) + { + throw std::runtime_error("Array cannot be on the left-hand-side of assignment"); + } + + value * eval_ref_impl(context & context, ast::array_access const & array_access) + { + auto index = eval(context, array_access.index); + auto array_ref = eval_ref(context, array_access.array); + + if (auto avalue = std::get_if(array_ref)) + { + return avalue->elements[get_array_index(index, avalue->elements.size())].get(); + } + + std::ostringstream os; + os << "Cannot index into a non-array of type "; + type::print(os, type_of(*array_ref)); + throw std::runtime_error(os.str()); + } + + value * eval_ref_impl(context & context, ast::expression_ptr const & expression) + { + return std::visit([&](auto const & expression){ return eval_ref_impl(context, expression); }, *expression); + } + } value eval(context & context, ast::expression_ptr const & expression) @@ -565,4 +633,9 @@ namespace pslang::interpreter return eval_impl(context, expression); } + value * eval_ref(context & context, ast::expression_ptr const & expression) + { + return eval_ref_impl(context, expression); + } + } diff --git a/libs/interpreter/source/exec.cpp b/libs/interpreter/source/exec.cpp index 786b545..1348703 100644 --- a/libs/interpreter/source/exec.cpp +++ b/libs/interpreter/source/exec.cpp @@ -37,37 +37,23 @@ namespace pslang::interpreter void exec_impl(context & context, ast::assignment const & assignment) { - std::string name; - if (auto identifier = std::get_if(assignment.lhs.get())) - name = identifier->name; - else - throw std::runtime_error("Cannot assign a value to a non-identifier"); + auto new_value = eval(context, assignment.rhs); + auto new_type = type_of(new_value); - for (auto it = context.scope_stack.rbegin(); it != context.scope_stack.rend(); ++it) + auto ref = eval_ref(context, assignment.lhs); + + auto existing_type = type_of(*ref); + if (!type::equal(existing_type, new_type)) { - if (auto jt = it->variables.find(name); jt != it->variables.end()) - { - if (jt->second.category != ast::value_category::_mutable) - throw std::runtime_error("Cannot assign a value to a non-mutable variable"); - - auto new_value = eval(context, assignment.rhs); - auto new_type = type_of(new_value); - auto existing_type = type_of(jt->second.value); - if (!type::equal(existing_type, new_type)) - { - std::ostringstream os; - os << "Cannot assign a value of type "; - type::print(os, new_type); - os << " to a variable of type "; - type::print(os, existing_type); - throw std::runtime_error(os.str()); - } - jt->second.value = std::move(new_value);; - return; - } + std::ostringstream os; + os << "Cannot assign a value of type "; + type::print(os, new_type); + os << " to a variable of type "; + type::print(os, existing_type); + throw std::runtime_error(os.str()); } - throw std::runtime_error("Identifier \"" + name + "\" is not defined"); + *ref = std::move(new_value); } void exec_impl(context & context, ast::variable_declaration const & variable_declaration)