Impement array element assignment

This commit is contained in:
Nikita Lisitsa 2025-12-17 16:07:39 +03:00
parent ff4d0a9c3e
commit fb4877c2ed
3 changed files with 159 additions and 98 deletions

View file

@ -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);
}

View file

@ -79,6 +79,77 @@ namespace pslang::interpreter
out << "(unknown)";
}
std::uint64_t get_array_index(value const & index, std::uint64_t size)
{
std::optional<std::uint64_t> index_unsigned;
std::optional<std::int64_t> index_signed;
if (auto pvalue = std::get_if<primitive_value>(&index))
{
if (auto i8 = std::get_if<i8_value>(pvalue))
{
index_signed = i8->value;
}
else if (auto u8 = std::get_if<u8_value>(pvalue))
{
index_unsigned = u8->value;
}
else if (auto i16 = std::get_if<i16_value>(pvalue))
{
index_signed = i16->value;
}
else if (auto u16 = std::get_if<u16_value>(pvalue))
{
index_unsigned = u16->value;
}
else if (auto i32 = std::get_if<i32_value>(pvalue))
{
index_signed = i32->value;
}
else if (auto u32 = std::get_if<u32_value>(pvalue))
{
index_unsigned = u32->value;
}
else if (auto i64 = std::get_if<i64_value>(pvalue))
{
index_signed = i64->value;
}
else if (auto u64 = std::get_if<u64_value>(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 <typename T>
@ -474,77 +545,7 @@ namespace pslang::interpreter
if (auto avalue = std::get_if<array_value>(&array))
{
std::optional<std::uint64_t> index_unsigned;
std::optional<std::int64_t> index_signed;
if (auto pvalue = std::get_if<primitive_value>(&index))
{
if (auto i8 = std::get_if<i8_value>(pvalue))
{
index_signed = i8->value;
}
else if (auto u8 = std::get_if<u8_value>(pvalue))
{
index_unsigned = u8->value;
}
else if (auto i16 = std::get_if<i16_value>(pvalue))
{
index_signed = i16->value;
}
else if (auto u16 = std::get_if<u16_value>(pvalue))
{
index_unsigned = u16->value;
}
else if (auto i32 = std::get_if<i32_value>(pvalue))
{
index_signed = i32->value;
}
else if (auto u32 = std::get_if<u32_value>(pvalue))
{
index_unsigned = u32->value;
}
else if (auto i64 = std::get_if<i64_value>(pvalue))
{
index_signed = i64->value;
}
else if (auto u64 = std::get_if<u64_value>(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_value>(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);
}
}

View file

@ -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<ast::identifier>(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)