Impement array element assignment
This commit is contained in:
parent
ff4d0a9c3e
commit
fb4877c2ed
3 changed files with 159 additions and 98 deletions
|
|
@ -9,4 +9,6 @@ namespace pslang::interpreter
|
||||||
|
|
||||||
value eval(context & context, ast::expression_ptr const & expression);
|
value eval(context & context, ast::expression_ptr const & expression);
|
||||||
|
|
||||||
|
value * eval_ref(context & context, ast::expression_ptr const & expression);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,77 @@ namespace pslang::interpreter
|
||||||
out << "(unknown)";
|
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);
|
value eval_impl(context & context, ast::expression_ptr const & expression);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|
@ -474,77 +545,7 @@ namespace pslang::interpreter
|
||||||
|
|
||||||
if (auto avalue = std::get_if<array_value>(&array))
|
if (auto avalue = std::get_if<array_value>(&array))
|
||||||
{
|
{
|
||||||
std::optional<std::uint64_t> index_unsigned;
|
return *avalue->elements[get_array_index(index, avalue->elements.size())];
|
||||||
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];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
|
|
@ -558,6 +559,73 @@ namespace pslang::interpreter
|
||||||
return std::visit([&](auto const & expression){ return eval_impl(context, expression); }, *expression);
|
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)
|
value eval(context & context, ast::expression_ptr const & expression)
|
||||||
|
|
@ -565,4 +633,9 @@ namespace pslang::interpreter
|
||||||
return eval_impl(context, expression);
|
return eval_impl(context, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value * eval_ref(context & context, ast::expression_ptr const & expression)
|
||||||
|
{
|
||||||
|
return eval_ref_impl(context, expression);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,37 +37,23 @@ namespace pslang::interpreter
|
||||||
|
|
||||||
void exec_impl(context & context, ast::assignment const & assignment)
|
void exec_impl(context & context, ast::assignment const & assignment)
|
||||||
{
|
{
|
||||||
std::string name;
|
auto new_value = eval(context, assignment.rhs);
|
||||||
if (auto identifier = std::get_if<ast::identifier>(assignment.lhs.get()))
|
auto new_type = type_of(new_value);
|
||||||
name = identifier->name;
|
|
||||||
else
|
|
||||||
throw std::runtime_error("Cannot assign a value to a non-identifier");
|
|
||||||
|
|
||||||
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())
|
std::ostringstream os;
|
||||||
{
|
os << "Cannot assign a value of type ";
|
||||||
if (jt->second.category != ast::value_category::_mutable)
|
type::print(os, new_type);
|
||||||
throw std::runtime_error("Cannot assign a value to a non-mutable variable");
|
os << " to a variable of type ";
|
||||||
|
type::print(os, existing_type);
|
||||||
auto new_value = eval(context, assignment.rhs);
|
throw std::runtime_error(os.str());
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
void exec_impl(context & context, ast::variable_declaration const & variable_declaration)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue