158 lines
4.6 KiB
C++
158 lines
4.6 KiB
C++
#include <pslang/interpreter/interpreter.hpp>
|
|
#include <pslang/interpreter/eval.hpp>
|
|
#include <pslang/ast/statement.hpp>
|
|
#include <pslang/type/print.hpp>
|
|
|
|
#include <stdexcept>
|
|
#include <sstream>
|
|
|
|
namespace pslang::interpreter
|
|
{
|
|
|
|
namespace
|
|
{
|
|
|
|
void execute_impl(context & context, ast::expression_ptr const & expression)
|
|
{
|
|
eval(context, expression);
|
|
}
|
|
|
|
void execute_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");
|
|
|
|
for (auto it = context.scope_stack.rbegin(); it != context.scope_stack.rend(); ++it)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
throw std::runtime_error("Identifier \"" + name + "\" is not defined");
|
|
}
|
|
|
|
void execute_impl(context & context, ast::variable_declaration const & variable_declaration)
|
|
{
|
|
auto & scope = context.scope_stack.back();
|
|
if (scope.variables.count(variable_declaration.name) > 0)
|
|
throw std::runtime_error("Error: variable \"" + variable_declaration.name + "\" is already declared");
|
|
|
|
auto value = eval(context, variable_declaration.initializer);
|
|
if (variable_declaration.type)
|
|
{
|
|
auto actual_type = type_of(value);
|
|
if (!type::equal(*variable_declaration.type, actual_type))
|
|
{
|
|
std::ostringstream os;
|
|
os << "Cannot initialize a variable of type ";
|
|
type::print(os, *variable_declaration.type);
|
|
os << " with an expression of type ";
|
|
type::print(os, actual_type);
|
|
throw std::runtime_error(os.str());
|
|
}
|
|
}
|
|
scope.variables[variable_declaration.name] = {.category = variable_declaration.category, .value = value};
|
|
}
|
|
|
|
void execute_impl(context & context, ast::if_block const &)
|
|
{
|
|
throw std::runtime_error("Internal interpreter error: if blocks cannot be present in the final AST");
|
|
}
|
|
|
|
void execute_impl(context & context, ast::else_block const &)
|
|
{
|
|
throw std::runtime_error("Internal interpreter error: else blocks cannot be present in the final AST");
|
|
}
|
|
|
|
void execute_impl(context & context, ast::else_if_block const &)
|
|
{
|
|
throw std::runtime_error("Internal interpreter error: else if blocks cannot be present in the final AST");
|
|
}
|
|
|
|
void execute_impl(context & context, ast::if_chain const & if_chain)
|
|
{
|
|
for (auto const & block : if_chain.blocks)
|
|
{
|
|
if (block.condition)
|
|
{
|
|
auto value = eval(context, block.condition);
|
|
auto actual_type = type_of(value);
|
|
if (!type::equal(actual_type, type::primitive_type{type::bool_type{}}))
|
|
{
|
|
std::ostringstream os;
|
|
os << "Expected type bool, got type ";
|
|
type::print(os, actual_type);
|
|
os << " in if block condition";
|
|
throw std::runtime_error(os.str());
|
|
}
|
|
|
|
if (std::get<bool_value>(std::get<primitive_value>(value)).value)
|
|
{
|
|
execute(context, block.statements);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void execute_impl(context & context, ast::while_block const & while_block)
|
|
{
|
|
while (true)
|
|
{
|
|
auto value = eval(context, while_block.condition);
|
|
auto actual_type = type_of(value);
|
|
if (!type::equal(actual_type, type::primitive_type{type::bool_type{}}))
|
|
{
|
|
std::ostringstream os;
|
|
os << "Expected type bool, got type ";
|
|
type::print(os, actual_type);
|
|
os << " in while block condition";
|
|
throw std::runtime_error(os.str());
|
|
}
|
|
|
|
if (std::get<bool_value>(std::get<primitive_value>(value)).value)
|
|
{
|
|
execute(context, while_block.statements);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
void execute_impl(context & context, ast::statement_list_ptr const & statements)
|
|
{
|
|
for (auto const & statement : statements->statements)
|
|
{
|
|
std::visit([&](auto const & statement){ execute_impl(context, statement); }, *statement);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void execute(context & context, ast::statement_list_ptr const & statements)
|
|
{
|
|
execute_impl(context, statements);
|
|
}
|
|
|
|
}
|