pslang/libs/interpreter/source/interpreter.cpp

221 lines
6.2 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
{
struct return_exception
{};
struct stack_pop_guard
{
stack_pop_guard(context & context)
: context_(context)
{}
~stack_pop_guard()
{
context_.scope_stack.pop_back();
}
private:
context & context_;
};
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());
}
}
context.scope_stack.back().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)
{
bool do_execute = true;
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());
}
do_execute = std::get<bool_value>(std::get<primitive_value>(value)).value;
}
if (!do_execute)
continue;
context.scope_stack.emplace_back();
stack_pop_guard guard(context);
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)
{
context.scope_stack.emplace_back();
stack_pop_guard guard(context);
execute(context, while_block.statements);
}
else
break;
}
}
void execute_impl(context & context, ast::function_definition const & function_definition)
{
auto & scope = context.scope_stack.back();
if (scope.functions.count(function_definition.name) > 0)
throw std::runtime_error("Function \"" + function_definition.name + "\" is already defined");
auto & function = scope.functions[function_definition.name];
function.arguments = function_definition.arguments;
function.return_type = function_definition.return_type;
function.statements = function_definition.statements;
}
void execute_impl(context & context, ast::return_statement const & return_statement)
{
auto value = eval(context, return_statement.value);
for (auto it = context.scope_stack.rbegin(); it != context.scope_stack.rend(); ++it)
{
if (it->is_function_scope)
{
it->return_value = std::move(value);
throw return_exception{};
}
}
throw std::runtime_error("Cannot return outside of a function");
}
void execute_impl(context & context, ast::statement_list_ptr const & statements)
{
for (auto const & statement : statements->statements)
{
try
{
std::visit([&](auto const & statement){ execute_impl(context, statement); }, *statement);
}
catch (return_exception const &)
{
if (context.scope_stack.back().is_function_scope)
break;
else
throw;
}
}
}
}
void execute(context & context, ast::statement_list_ptr const & statements)
{
execute_impl(context, statements);
}
}