234 lines
6.7 KiB
C++
234 lines
6.7 KiB
C++
#include <pslang/interpreter/exec.hpp>
|
|
#include <pslang/interpreter/eval.hpp>
|
|
#include <pslang/ast/statement.hpp>
|
|
#include <pslang/types/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 exec_impl(context & context, ast::expression_ptr const & expression)
|
|
{
|
|
eval(context, expression);
|
|
}
|
|
|
|
void exec_impl(context & context, ast::assignment const & assignment)
|
|
{
|
|
auto new_value = eval(context, assignment.rhs);
|
|
auto new_type = type_of(new_value);
|
|
|
|
auto ref = eval_ref(context, assignment.lhs);
|
|
|
|
auto existing_type = type_of(*ref);
|
|
if (!types::equal(existing_type, new_type))
|
|
{
|
|
std::ostringstream os;
|
|
os << "Cannot assign a value of type ";
|
|
types::print(os, new_type);
|
|
os << " to a variable of type ";
|
|
types::print(os, existing_type);
|
|
throw std::runtime_error(os.str());
|
|
}
|
|
|
|
*ref = std::move(new_value);
|
|
}
|
|
|
|
void exec_impl(context & context, ast::variable_declaration const & variable_declaration)
|
|
{
|
|
auto & scope = context.scope_stack.back();
|
|
if (scope.contains(variable_declaration.name))
|
|
throw std::runtime_error("Identifier \"" + variable_declaration.name + "\" is already defined in this scope");
|
|
|
|
auto value = eval(context, variable_declaration.initializer);
|
|
if (variable_declaration.type)
|
|
{
|
|
auto expected_type = resolve_type(context, *variable_declaration.type);
|
|
auto actual_type = type_of(value);
|
|
if (!types::equal(expected_type, actual_type))
|
|
{
|
|
std::ostringstream os;
|
|
os << "Cannot initialize a variable of type ";
|
|
types::print(os, expected_type);
|
|
os << " with an expression of type ";
|
|
types::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 exec_impl(context & context, ast::if_block const &)
|
|
{
|
|
throw std::runtime_error("Internal interpreter error: if blocks cannot be present in the final AST");
|
|
}
|
|
|
|
void exec_impl(context & context, ast::else_block const &)
|
|
{
|
|
throw std::runtime_error("Internal interpreter error: else blocks cannot be present in the final AST");
|
|
}
|
|
|
|
void exec_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 exec_impl(context & context, ast::if_chain const & if_chain)
|
|
{
|
|
for (auto const & block : if_chain.blocks)
|
|
{
|
|
bool do_exec = true;
|
|
|
|
if (block.condition)
|
|
{
|
|
auto value = eval(context, block.condition);
|
|
auto actual_type = type_of(value);
|
|
if (!types::equal(actual_type, types::primitive_type{types::bool_type{}}))
|
|
{
|
|
std::ostringstream os;
|
|
os << "Expected type bool, got type ";
|
|
types::print(os, actual_type);
|
|
os << " in if block condition";
|
|
throw std::runtime_error(os.str());
|
|
}
|
|
|
|
do_exec = std::get<bool_value>(std::get<primitive_value>(value)).value;
|
|
}
|
|
|
|
if (!do_exec)
|
|
continue;
|
|
|
|
context.scope_stack.emplace_back();
|
|
stack_pop_guard guard(context);
|
|
exec(context, block.statements);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void exec_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 (!types::equal(actual_type, types::primitive_type{types::bool_type{}}))
|
|
{
|
|
std::ostringstream os;
|
|
os << "Expected type bool, got type ";
|
|
types::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);
|
|
exec(context, while_block.statements);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
void exec_impl(context & context, ast::function_definition const & function_definition)
|
|
{
|
|
auto & scope = context.scope_stack.back();
|
|
if (scope.contains(function_definition.name))
|
|
throw std::runtime_error("Identifier \"" + function_definition.name + "\" is already defined in this scope");
|
|
|
|
function_value value;
|
|
|
|
for (auto const & argument : function_definition.arguments)
|
|
value.arguments.push_back({.name = argument.name, .type = std::make_unique<types::type>(resolve_type(context, *argument.type))});
|
|
value.return_type = std::make_unique<types::type>(resolve_type(context, *function_definition.return_type));
|
|
value.statements = function_definition.statements;
|
|
|
|
scope.variables[function_definition.name] = {.category = ast::value_category::constant, .value = std::move(value)};
|
|
}
|
|
|
|
void exec_impl(context & context, ast::return_statement const & return_statement)
|
|
{
|
|
auto value = return_statement.value ? eval(context, return_statement.value) : unit_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 exec_impl(context & context, ast::field_definition const & field_definition)
|
|
{
|
|
throw std::runtime_error("Internal interpreter error: field definitions cannot be present in the final AST outside of struct definitions");
|
|
}
|
|
|
|
void exec_impl(context & context, ast::struct_definition const & struct_definition)
|
|
{
|
|
auto & scope = context.scope_stack.back();
|
|
if (scope.contains(struct_definition.name))
|
|
throw std::runtime_error("Identifier \"" + struct_definition.name + "\" is already defined in this scope");
|
|
|
|
struct_data result;
|
|
for (auto const & field : struct_definition.fields)
|
|
{
|
|
result.fields.push_back({
|
|
.name = field.name,
|
|
.type = std::make_unique<types::type>(resolve_type(context, *field.type)),
|
|
});
|
|
}
|
|
scope.structs[struct_definition.name] = std::move(result);
|
|
}
|
|
|
|
void exec_impl(context & context, ast::statement_list_ptr const & statements)
|
|
{
|
|
for (auto const & statement : statements->statements)
|
|
{
|
|
try
|
|
{
|
|
std::visit([&](auto const & statement){ exec_impl(context, statement); }, *statement);
|
|
}
|
|
catch (return_exception const &)
|
|
{
|
|
if (context.scope_stack.back().is_function_scope)
|
|
break;
|
|
else
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void exec(context & context, ast::statement_list_ptr const & statements)
|
|
{
|
|
exec_impl(context, statements);
|
|
}
|
|
|
|
}
|