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_ref(context & context, ast::expression_ptr const & expression);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue