Interpreter clean-up (wip - identifier/function resolving is broken)
This commit is contained in:
parent
9fb3db2247
commit
0ba3e4324d
11 changed files with 224 additions and 278 deletions
|
|
@ -1,6 +1,7 @@
|
|||
#include <pslang/parser/parser.hpp>
|
||||
#include <pslang/parser/error.hpp>
|
||||
#include <pslang/interpreter/exec.hpp>
|
||||
#include <pslang/interpreter/error.hpp>
|
||||
#include <pslang/ast/statement.hpp>
|
||||
#include <pslang/ast/preprocess.hpp>
|
||||
#include <pslang/ast/print.hpp>
|
||||
|
|
@ -114,24 +115,36 @@ int main(int argc, char ** argv)
|
|||
ast::check_and_infer_types(ast);
|
||||
parsed.push_back(std::move(ast));
|
||||
}
|
||||
catch (pslang::ast::parse_error const & error)
|
||||
catch (ast::parse_error const & error)
|
||||
{
|
||||
std::cerr << "Parse error at " << error.location() << ":\n " << error.what() << std::endl;
|
||||
print_error_context(argv[arg], error.location());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
catch (pslang::ast::type_error const & error)
|
||||
catch (ast::type_error const & error)
|
||||
{
|
||||
std::cerr << "Type error at " << error.location() << ":\n " << error.what() << std::endl;
|
||||
print_error_context(argv[arg], error.location());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
catch (pslang::ast::invalid_ast_error const & error)
|
||||
catch (ast::invalid_ast_error const & error)
|
||||
{
|
||||
std::cerr << "Invalid AST at " << error.location() << ":\n " << error.what() << std::endl;
|
||||
print_error_context(argv[arg], error.location());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
catch (interpreter::internal_error const & error)
|
||||
{
|
||||
std::cerr << "Internal error at " << error.location() << ":\n " << error.what() << std::endl;
|
||||
print_error_context(argv[arg], error.location());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
catch (interpreter::runtime_error const & error)
|
||||
{
|
||||
std::cerr << "Runtime error at " << error.location() << ":\n " << error.what() << std::endl;
|
||||
print_error_context(argv[arg], error.location());
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (dump_ast)
|
||||
|
|
|
|||
|
|
@ -29,3 +29,19 @@ func fib(n : u32) -> u32:
|
|||
return fib(n - 1u) + fib(n - 2u)
|
||||
|
||||
let fib10 = fib(10u)
|
||||
|
||||
|
||||
func h() -> u32:
|
||||
return 0u
|
||||
|
||||
func f() -> u32:
|
||||
return h()
|
||||
|
||||
func g() -> u32:
|
||||
func h() -> u32:
|
||||
return 1u
|
||||
return f()
|
||||
|
||||
// Should equal 0u, but equals 1u due to an error in
|
||||
// how the interpreter resolves functions & variables
|
||||
let x = g()
|
||||
|
|
|
|||
|
|
@ -343,6 +343,7 @@ namespace pslang::ast
|
|||
put_indent(out, options);
|
||||
out << "body\n";
|
||||
child(*node.statements);
|
||||
--options.indent_level;
|
||||
}
|
||||
|
||||
void apply(return_statement const & node)
|
||||
|
|
|
|||
|
|
@ -346,7 +346,7 @@ namespace pslang::ast
|
|||
types::print(os, *ftype->arguments[i]);
|
||||
os << " but got type ";
|
||||
types::print(os, *arg_type);
|
||||
throw type_error(os.str(), node.location);
|
||||
throw type_error(os.str(), get_location(*node.arguments[i]));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace pslang::interpreter
|
|||
std::vector<field> fields;
|
||||
};
|
||||
|
||||
struct scope
|
||||
struct frame
|
||||
{
|
||||
std::unordered_map<std::string, variable_data> variables;
|
||||
std::unordered_map<std::string, struct_data> structs;
|
||||
|
|
@ -40,14 +40,14 @@ namespace pslang::interpreter
|
|||
return variables.count(name) > 0 || structs.count(name) > 0;
|
||||
}
|
||||
|
||||
bool is_function_scope = false;
|
||||
types::type_ptr expected_return_type = nullptr;
|
||||
value return_value = unit_value{};
|
||||
};
|
||||
|
||||
struct context
|
||||
{
|
||||
bool trace = false;
|
||||
std::vector<scope> scope_stack;
|
||||
std::vector<frame> frame_stack;
|
||||
};
|
||||
|
||||
context empty_context();
|
||||
|
|
|
|||
20
libs/interpreter/include/pslang/interpreter/error.hpp
Normal file
20
libs/interpreter/include/pslang/interpreter/error.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <pslang/ast/error.hpp>
|
||||
|
||||
namespace pslang::interpreter
|
||||
{
|
||||
|
||||
struct internal_error
|
||||
: ast::error
|
||||
{
|
||||
using error::error;
|
||||
};
|
||||
|
||||
struct runtime_error
|
||||
: ast::error
|
||||
{
|
||||
using error::error;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -9,10 +9,7 @@
|
|||
namespace pslang::interpreter
|
||||
{
|
||||
|
||||
types::type resolve_type(context & context, ast::type const & type);
|
||||
|
||||
value eval(context & context, ast::expression_ptr const & expression);
|
||||
|
||||
value * eval_ref(context & context, ast::expression_ptr const & expression);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ namespace pslang::interpreter
|
|||
|
||||
value apply(types::named_type const & named_type)
|
||||
{
|
||||
auto const & struct_data = context.scope_stack.at(named_type.level).structs.at(named_type.name);
|
||||
auto const & struct_data = context.frame_stack.at(named_type.level).structs.at(named_type.name);
|
||||
|
||||
struct_value result{.struct_type = std::make_unique<types::type>(named_type)};
|
||||
for (auto const & field : struct_data.fields)
|
||||
|
|
@ -55,13 +55,13 @@ namespace pslang::interpreter
|
|||
context empty_context()
|
||||
{
|
||||
context result;
|
||||
result.scope_stack.emplace_back();
|
||||
result.frame_stack.emplace_back();
|
||||
return result;
|
||||
}
|
||||
|
||||
void dump(std::ostream & out, context const & context)
|
||||
{
|
||||
for (auto const & scope : context.scope_stack)
|
||||
for (auto const & scope : context.frame_stack)
|
||||
{
|
||||
for (auto const & variable : scope.variables)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include <pslang/interpreter/eval.hpp>
|
||||
#include <pslang/interpreter/exec.hpp>
|
||||
#include <pslang/interpreter/value.hpp>
|
||||
#include <pslang/interpreter/error.hpp>
|
||||
#include <pslang/ast/expression.hpp>
|
||||
#include <pslang/types/print.hpp>
|
||||
|
||||
|
|
@ -13,107 +14,7 @@ namespace pslang::interpreter
|
|||
namespace
|
||||
{
|
||||
|
||||
types::type resolve_type_impl(context &, types::unit_type const & type)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
types::type resolve_type_impl(context &, types::primitive_type const & type)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
types::type resolve_type_impl(context & context, ast::array_type const & type)
|
||||
{
|
||||
return types::array_type{std::make_unique<types::type>(resolve_type(context, *type.element_type)), type.size};
|
||||
}
|
||||
|
||||
types::type resolve_type_impl(context & context, ast::function_type const & type)
|
||||
{
|
||||
types::function_type result;
|
||||
for (auto const & argument : type.arguments)
|
||||
result.arguments.push_back(std::make_unique<types::type>(resolve_type(context, *argument)));
|
||||
result.result = std::make_unique<types::type>(resolve_type(context, *type.result));
|
||||
return result;
|
||||
}
|
||||
|
||||
types::type resolve_type_impl(context & context, ast::type_identifier const & type)
|
||||
{
|
||||
return types::named_type{type.name, type.level};
|
||||
}
|
||||
|
||||
types::type resolve_type_impl(context & context, ast::type const & type)
|
||||
{
|
||||
return std::visit([&](auto const & type){ return resolve_type_impl(context, type); }, type);
|
||||
}
|
||||
|
||||
void print(std::ostream & out, ast::unary_operation_type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ast::unary_operation_type::negation:
|
||||
out << "-";
|
||||
return;
|
||||
case ast::unary_operation_type::logical_not:
|
||||
out << "!";
|
||||
return;
|
||||
}
|
||||
|
||||
out << "(unknown)";
|
||||
}
|
||||
|
||||
void print(std::ostream & out, ast::binary_operation_type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ast::binary_operation_type::addition:
|
||||
out << "+";
|
||||
return;
|
||||
case ast::binary_operation_type::subtraction:
|
||||
out << "-";
|
||||
return;
|
||||
case ast::binary_operation_type::multiplication:
|
||||
out << "*";
|
||||
return;
|
||||
case ast::binary_operation_type::division:
|
||||
out << "/";
|
||||
return;
|
||||
case ast::binary_operation_type::remainder:
|
||||
out << "%";
|
||||
return;
|
||||
case ast::binary_operation_type::logical_and:
|
||||
out << "&";
|
||||
return;
|
||||
case ast::binary_operation_type::logical_or:
|
||||
out << "|";
|
||||
return;
|
||||
case ast::binary_operation_type::logical_xor:
|
||||
out << "^";
|
||||
return;
|
||||
case ast::binary_operation_type::equals:
|
||||
out << "==";
|
||||
return;
|
||||
case ast::binary_operation_type::not_equals:
|
||||
out << "!=";
|
||||
return;
|
||||
case ast::binary_operation_type::less:
|
||||
out << "<";
|
||||
return;
|
||||
case ast::binary_operation_type::greater:
|
||||
out << ">";
|
||||
return;
|
||||
case ast::binary_operation_type::less_equals:
|
||||
out << "<=";
|
||||
return;
|
||||
case ast::binary_operation_type::greater_equals:
|
||||
out << ">=";
|
||||
return;
|
||||
}
|
||||
|
||||
out << "(unknown)";
|
||||
}
|
||||
|
||||
std::uint64_t get_array_index(value const & index, std::uint64_t size)
|
||||
std::uint64_t get_array_index(value const & index, std::uint64_t size, ast::location const & location)
|
||||
{
|
||||
std::optional<std::uint64_t> index_unsigned;
|
||||
std::optional<std::int64_t> index_signed;
|
||||
|
|
@ -159,7 +60,7 @@ namespace pslang::interpreter
|
|||
std::ostringstream os;
|
||||
os << "Cannot index into an array with an expression of type ";
|
||||
types::print(os, type_of(index));
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), location);
|
||||
}
|
||||
|
||||
if (index_unsigned)
|
||||
|
|
@ -168,7 +69,7 @@ namespace pslang::interpreter
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "Array index " << *index_unsigned << " out of bounds " << size;
|
||||
throw std::runtime_error(os.str());
|
||||
throw runtime_error(os.str(), location);
|
||||
}
|
||||
return *index_unsigned;
|
||||
}
|
||||
|
|
@ -178,7 +79,7 @@ namespace pslang::interpreter
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "Array index " << *index_signed << " out of bounds " << size;
|
||||
throw std::runtime_error(os.str());
|
||||
throw runtime_error(os.str(), location);
|
||||
}
|
||||
return *index_signed;
|
||||
}
|
||||
|
|
@ -199,28 +100,26 @@ namespace pslang::interpreter
|
|||
|
||||
value eval_impl(context & context, ast::identifier const & identifier)
|
||||
{
|
||||
for (auto it = context.scope_stack.rbegin(); it != context.scope_stack.rend(); ++it)
|
||||
{
|
||||
// NB: cannot use identifier.level here because lexical scope stack
|
||||
// almost always differs from execution frame stack
|
||||
for (auto it = context.frame_stack.rbegin(); it != context.frame_stack.rend(); ++it)
|
||||
if (auto jt = it->variables.find(identifier.name); jt != it->variables.end())
|
||||
return jt->second.value;
|
||||
}
|
||||
|
||||
throw std::runtime_error("Identifier \"" + identifier.name + "\" is not defined");
|
||||
throw internal_error("Identifier \"" + identifier.name + "\" is not defined", identifier.location);
|
||||
}
|
||||
|
||||
template <typename Value>
|
||||
value unary_operation_impl(ast::unary_operation_type type, Value const & value)
|
||||
value unary_operation_impl(ast::unary_operation_type type, Value const & value, ast::location const & location)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot apply unary operator \"";
|
||||
print(os, type);
|
||||
os << "\" to a value of type ";
|
||||
os << "Cannot apply " << type << " to a value of type ";
|
||||
types::print(os, type_of(value));
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), location);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
value unary_operation_impl(ast::unary_operation_type type, primitive_value_base<T> const & arg1)
|
||||
value unary_operation_impl(ast::unary_operation_type type, primitive_value_base<T> const & arg1, ast::location const & location)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
|
|
@ -243,22 +142,20 @@ namespace pslang::interpreter
|
|||
}
|
||||
|
||||
std::ostringstream os;
|
||||
os << "Cannot apply unary operator \"";
|
||||
print(os, type);
|
||||
os << "\" to a value of type ";
|
||||
os << "Cannot apply " << type << " to a value of type ";
|
||||
types::print(os, type_of(primitive_value(arg1)));
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), location);
|
||||
}
|
||||
|
||||
value unary_operation_impl(ast::unary_operation_type type, primitive_value const & arg1)
|
||||
value unary_operation_impl(ast::unary_operation_type type, primitive_value const & arg1, ast::location const & location)
|
||||
{
|
||||
return std::visit([&](auto const & value){ return unary_operation_impl(type, value); }, arg1);
|
||||
return std::visit([&](auto const & value){ return unary_operation_impl(type, value, location); }, arg1);
|
||||
}
|
||||
|
||||
value eval_impl(context & context, ast::unary_operation const & unary_operation)
|
||||
{
|
||||
auto arg1 = eval_impl(context, unary_operation.arg1);
|
||||
return std::visit([&](auto const & value){ return unary_operation_impl(unary_operation.type, value); }, arg1);
|
||||
return std::visit([&](auto const & value){ return unary_operation_impl(unary_operation.type, value, unary_operation.location); }, arg1);
|
||||
}
|
||||
|
||||
bool requires_same_argument_type(ast::binary_operation_type)
|
||||
|
|
@ -268,7 +165,7 @@ namespace pslang::interpreter
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
value binary_operation_impl_same_type(ast::binary_operation_type type, primitive_value_base<T> const & arg1, value const & arg2_generic)
|
||||
value binary_operation_impl_same_type(ast::binary_operation_type type, primitive_value_base<T> const & arg1, value const & arg2_generic, ast::location const & location)
|
||||
{
|
||||
primitive_value_base<T> const & arg2 = std::get<primitive_value_base<T>>(std::get<primitive_value>(arg2_generic));
|
||||
|
||||
|
|
@ -359,31 +256,27 @@ namespace pslang::interpreter
|
|||
}
|
||||
|
||||
std::ostringstream os;
|
||||
os << "Cannot apply binary operator \"";
|
||||
print(os, type);
|
||||
os << "\" to values of type ";
|
||||
os << "Cannot apply " << type << " to values of type ";
|
||||
types::print(os, type_of(primitive_value(arg1)));
|
||||
os << " and ";
|
||||
types::print(os, type_of(primitive_value(arg2)));
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), location);
|
||||
}
|
||||
|
||||
template <typename Value>
|
||||
value binary_operation_impl_same_type(ast::binary_operation_type type, Value const & arg1, value const & arg2)
|
||||
value binary_operation_impl_same_type(ast::binary_operation_type type, Value const & arg1, value const & arg2, ast::location const & location)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot apply binary operator \"";
|
||||
print(os, type);
|
||||
os << "\" to values of type ";
|
||||
os << "Cannot apply " << type << " to values of type ";
|
||||
types::print(os, type_of(arg1));
|
||||
os << " and ";
|
||||
types::print(os, type_of(arg2));
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), location);
|
||||
}
|
||||
|
||||
value binary_operation_impl_same_type(ast::binary_operation_type type, primitive_value const & arg1, value const & arg2)
|
||||
value binary_operation_impl_same_type(ast::binary_operation_type type, primitive_value const & arg1, value const & arg2, ast::location const & location)
|
||||
{
|
||||
return std::visit([&](auto const & value){ return binary_operation_impl_same_type(type, value, arg2); }, arg1);
|
||||
return std::visit([&](auto const & value){ return binary_operation_impl_same_type(type, value, arg2, location); }, arg1);
|
||||
}
|
||||
|
||||
value eval_impl(context & context, ast::binary_operation const & binary_operation)
|
||||
|
|
@ -399,45 +292,43 @@ namespace pslang::interpreter
|
|||
if (!types::equal(type1, type2))
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot apply binary operator \"";
|
||||
print(os, binary_operation.type);
|
||||
os << "\" to values of type ";
|
||||
os << "Cannot apply " << binary_operation.type << " to values of type ";
|
||||
types::print(os, type1);
|
||||
os << " and ";
|
||||
types::print(os, type2);
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), binary_operation.location);
|
||||
}
|
||||
|
||||
return std::visit([&](auto const & value){ return binary_operation_impl_same_type(binary_operation.type, value, arg2); }, arg1);
|
||||
return std::visit([&](auto const & value){ return binary_operation_impl_same_type(binary_operation.type, value, arg2, binary_operation.location); }, arg1);
|
||||
}
|
||||
|
||||
throw std::runtime_error("eval(binary_operation) for different argument types not implemented");
|
||||
throw internal_error("eval(binary_operation) for different argument types not implemented", binary_operation.location);
|
||||
}
|
||||
|
||||
value cast_impl(unit_value const & value, types::type const & type)
|
||||
value cast_impl(unit_value const & value, types::type const & type, ast::location const & location)
|
||||
{
|
||||
if (types::equal(type, types::unit_type{}))
|
||||
return value;
|
||||
|
||||
throw std::runtime_error("Cannot cast unit type to anything");
|
||||
throw internal_error("Cannot cast unit type to anything", location);
|
||||
}
|
||||
|
||||
value cast_impl(array_value const & value, types::type const & type)
|
||||
value cast_impl(array_value const & value, types::type const & type, ast::location const & location)
|
||||
{
|
||||
if (types::equal(type, type_of(value)))
|
||||
return value;
|
||||
|
||||
throw std::runtime_error("Cannot cast array type to anything");
|
||||
throw internal_error("Cannot cast array type to anything", location);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
value cast_impl(primitive_value_base<T> const & value, types::unit_type const &)
|
||||
value cast_impl(primitive_value_base<T> const & value, types::unit_type const &, ast::location const & location)
|
||||
{
|
||||
throw std::runtime_error("Cannot cast anything to unit type");
|
||||
throw internal_error("Cannot cast anything to unit type", location);
|
||||
}
|
||||
|
||||
template <typename T, typename H>
|
||||
value cast_impl(primitive_value_base<T> const & value, types::primitive_type_base<H> const & type)
|
||||
value cast_impl(primitive_value_base<T> const & value, types::primitive_type_base<H> const & type, ast::location const & location)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, H>)
|
||||
{
|
||||
|
|
@ -453,56 +344,56 @@ namespace pslang::interpreter
|
|||
types::print(os, type_of(primitive_value(value)));
|
||||
os << " to type ";
|
||||
types::print(os, types::primitive_type(type));
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), location);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
value cast_impl(primitive_value_base<T> const & value, types::primitive_type const & type)
|
||||
value cast_impl(primitive_value_base<T> const & value, types::primitive_type const & type, ast::location const & location)
|
||||
{
|
||||
return std::visit([&](auto const & type){ return cast_impl(value, type); }, type);
|
||||
return std::visit([&](auto const & type){ return cast_impl(value, type, location); }, type);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
value cast_impl(primitive_value_base<T> const &, types::array_type const &)
|
||||
value cast_impl(primitive_value_base<T> const &, types::array_type const &, ast::location const & location)
|
||||
{
|
||||
throw std::runtime_error("Cannot cast anything to array type");
|
||||
throw internal_error("Cannot cast anything to array type", location);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
value cast_impl(primitive_value_base<T> const &, types::function_type const &)
|
||||
value cast_impl(primitive_value_base<T> const &, types::function_type const &, ast::location const & location)
|
||||
{
|
||||
throw std::runtime_error("Cannot cast anything to function type");
|
||||
throw internal_error("Cannot cast anything to function type", location);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
value cast_impl(primitive_value_base<T> const & value, types::type const & type)
|
||||
value cast_impl(primitive_value_base<T> const & value, types::type const & type, ast::location const & location)
|
||||
{
|
||||
return std::visit([&](auto const & type){ return cast_impl(value, type); }, type);
|
||||
return std::visit([&](auto const & type){ return cast_impl(value, type, location); }, type);
|
||||
}
|
||||
|
||||
value cast_impl(primitive_value const & value, types::type const & type)
|
||||
value cast_impl(primitive_value const & value, types::type const & type, ast::location const & location)
|
||||
{
|
||||
return std::visit([&](auto const & value){ return cast_impl(value, type); }, value);
|
||||
return std::visit([&](auto const & value){ return cast_impl(value, type, location); }, value);
|
||||
}
|
||||
|
||||
value cast_impl(struct_value const & value, types::type const & type)
|
||||
value cast_impl(struct_value const & value, types::type const & type, ast::location const & location)
|
||||
{
|
||||
if (types::equal(type, types::unit_type{}))
|
||||
return value;
|
||||
|
||||
throw std::runtime_error("Cannot cast struct type to anything");
|
||||
throw internal_error("Cannot cast struct type to anything", location);
|
||||
}
|
||||
|
||||
value cast_impl(function_value const &, types::type const &)
|
||||
value cast_impl(function_value const &, types::type const &, ast::location const & location)
|
||||
{
|
||||
throw std::runtime_error("Cannot cast function type to anything");
|
||||
throw internal_error("Cannot cast function type to anything", location);
|
||||
}
|
||||
|
||||
value eval_impl(context & context, ast::cast_operation const & cast_operation)
|
||||
{
|
||||
auto arg = eval(context, cast_operation.expression);
|
||||
auto type = resolve_type(context, *cast_operation.type);
|
||||
return std::visit([&](auto const & value){ return cast_impl(value, type); }, arg);
|
||||
auto type = get_type(*cast_operation.type);
|
||||
return std::visit([&](auto const & value){ return cast_impl(value, *type, cast_operation.location); }, arg);
|
||||
}
|
||||
|
||||
value eval_impl(context & context, ast::function_call const & function_call)
|
||||
|
|
@ -517,7 +408,7 @@ namespace pslang::interpreter
|
|||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot call function: expected " << fvalue->arguments.size() << " arguments, got " << function_call.arguments.size();
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), function_call.location);
|
||||
}
|
||||
|
||||
std::vector<value> args;
|
||||
|
|
@ -534,33 +425,21 @@ namespace pslang::interpreter
|
|||
types::print(os, *fvalue->arguments[i].type);
|
||||
os << " but actual type is ";
|
||||
types::print(os, actual_type);
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), ast::get_location(*function_call.arguments[i]));
|
||||
}
|
||||
}
|
||||
|
||||
auto & function_scope = context.scope_stack.emplace_back();
|
||||
function_scope.is_function_scope = true;
|
||||
auto & function_scope = context.frame_stack.emplace_back();
|
||||
|
||||
for (std::size_t i = 0; i < args.size(); ++i)
|
||||
function_scope.variables[fvalue->arguments[i].name] = {.category = ast::value_category::constant, .value = std::move(args[i])};
|
||||
|
||||
auto expected_return_type = fvalue->return_type;
|
||||
function_scope.expected_return_type = fvalue->return_type;
|
||||
|
||||
exec(context, fvalue->statements);
|
||||
|
||||
auto actual_return_type = type_of(context.scope_stack.back().return_value);
|
||||
if (!types::equal(actual_return_type, *expected_return_type))
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Error returning from function: expected return type is ";
|
||||
types::print(os, *expected_return_type);
|
||||
os << " but actual type is ";
|
||||
types::print(os, actual_return_type);
|
||||
throw std::runtime_error(os.str());
|
||||
}
|
||||
|
||||
auto result = std::move(context.scope_stack.back().return_value);
|
||||
context.scope_stack.pop_back();
|
||||
auto result = std::move(context.frame_stack.back().return_value);
|
||||
context.frame_stack.pop_back();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -568,7 +447,7 @@ namespace pslang::interpreter
|
|||
os << "Cannot call ";
|
||||
print(os, lvalue);
|
||||
os << ": not a function";
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), function_call.location);
|
||||
}
|
||||
else if (function_call.type)
|
||||
{
|
||||
|
|
@ -582,16 +461,23 @@ namespace pslang::interpreter
|
|||
os << "Cannot create built-in type ";
|
||||
types::print(os, *type);
|
||||
os << ": expected 0 arguments, but got " << function_call.arguments.size();
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), function_call.location);
|
||||
}
|
||||
else if (auto named_type = std::get_if<types::named_type>(type.get()))
|
||||
{
|
||||
auto const & scope = context.scope_stack.at(named_type->level);
|
||||
auto const & scope = context.frame_stack.at(named_type->level);
|
||||
auto const & data = scope.structs.at(named_type->name);
|
||||
|
||||
if (function_call.arguments.empty())
|
||||
return zero_value(context, *type);
|
||||
|
||||
if (data.fields.size() != function_call.arguments.size())
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot create struct \"" << named_type->name << "\": expected " << data.fields.size() << " arguments, got " << function_call.arguments.size();
|
||||
throw internal_error(os.str(), function_call.location);
|
||||
}
|
||||
|
||||
std::vector<value> args;
|
||||
for (auto const & expression : function_call.arguments)
|
||||
args.push_back(eval(context, expression));
|
||||
|
|
@ -599,9 +485,7 @@ namespace pslang::interpreter
|
|||
std::unordered_map<std::string, value_ptr> fields;
|
||||
|
||||
for (std::size_t i = 0; i < args.size(); ++i)
|
||||
{
|
||||
fields[data.fields[i].name] = std::make_unique<value>(std::move(args[i]));
|
||||
}
|
||||
|
||||
return struct_value{
|
||||
.struct_type = type,
|
||||
|
|
@ -609,16 +493,16 @@ namespace pslang::interpreter
|
|||
};
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Unknown type in constructor");
|
||||
throw internal_error("Unknown type in constructor", function_call.location);
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Function call node has neither function nor type");
|
||||
throw internal_error("Function call node has neither function nor type", function_call.location);
|
||||
}
|
||||
|
||||
value eval_impl(context & context, ast::array const & array)
|
||||
{
|
||||
if (array.elements.empty())
|
||||
throw std::runtime_error("Internal error: array ast node cannot have zero elements");
|
||||
throw internal_error("Array node cannot have zero elements", array.location);
|
||||
|
||||
types::type_ptr element_type;
|
||||
std::vector<value_ptr> elements;
|
||||
|
|
@ -637,7 +521,7 @@ namespace pslang::interpreter
|
|||
types::print(os, *element_type);
|
||||
os << " but element #" << i << " type is ";
|
||||
types::print(os, new_type);
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), array.location);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -653,14 +537,12 @@ namespace pslang::interpreter
|
|||
auto index = eval(context, array_access.index);
|
||||
|
||||
if (auto avalue = std::get_if<array_value>(&array))
|
||||
{
|
||||
return *avalue->elements[get_array_index(index, avalue->elements.size())];
|
||||
}
|
||||
return *avalue->elements[get_array_index(index, avalue->elements.size(), array_access.location)];
|
||||
|
||||
std::ostringstream os;
|
||||
os << "Cannot index into a non-array of type ";
|
||||
types::print(os, type_of(array));
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), array_access.location);
|
||||
}
|
||||
|
||||
value eval_impl(context & context, ast::field_access const & field_access)
|
||||
|
|
@ -675,14 +557,14 @@ namespace pslang::interpreter
|
|||
os << "Struct ";
|
||||
types::print(os, type_of(object));
|
||||
os << " has no field named \"" << field_access.field_name << "\"";
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), field_access.location);
|
||||
}
|
||||
|
||||
std::ostringstream os;
|
||||
os << "Value of type ";
|
||||
types::print(os, type_of(object));
|
||||
os << " is not a struct";
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), field_access.location);
|
||||
}
|
||||
|
||||
value eval_impl(context & context, ast::expression_ptr const & expression)
|
||||
|
|
@ -690,50 +572,48 @@ namespace pslang::interpreter
|
|||
return std::visit([&](auto const & expression){ return eval_impl(context, expression); }, *expression);
|
||||
}
|
||||
|
||||
value * eval_ref_impl(context & context, ast::literal const &)
|
||||
value * eval_ref_impl(context & context, ast::literal const & literal)
|
||||
{
|
||||
throw std::runtime_error("Literal cannot be on the left-hand-side of assignment");
|
||||
throw internal_error("Literal cannot be on the left-hand-side of assignment", ast::get_location(literal));
|
||||
}
|
||||
|
||||
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");
|
||||
if (identifier.level >= context.frame_stack.size())
|
||||
throw internal_error("Bad identifier level", identifier.location);
|
||||
|
||||
return &jt->second.value;
|
||||
}
|
||||
}
|
||||
auto & scope = context.frame_stack[identifier.level];
|
||||
|
||||
throw std::runtime_error("Identifier \"" + identifier.name + "\" is not defined");
|
||||
auto it = scope.variables.find(identifier.name);
|
||||
if (it == scope.variables.end())
|
||||
throw internal_error("Identifier \"" + identifier.name + "\" is not defined", identifier.location);
|
||||
|
||||
return &it->second.value;
|
||||
}
|
||||
|
||||
value * eval_ref_impl(context & context, ast::unary_operation const &)
|
||||
value * eval_ref_impl(context & context, ast::unary_operation const & unary_operation)
|
||||
{
|
||||
throw std::runtime_error("Unary operation cannot be on the left-hand-side of assignment");
|
||||
throw internal_error("Unary operation cannot be on the left-hand-side of assignment", unary_operation.location);
|
||||
}
|
||||
|
||||
value * eval_ref_impl(context & context, ast::binary_operation const &)
|
||||
value * eval_ref_impl(context & context, ast::binary_operation const & binary_operation)
|
||||
{
|
||||
throw std::runtime_error("Binary operation cannot be on the left-hand-side of assignment");
|
||||
throw internal_error("Binary operation cannot be on the left-hand-side of assignment", binary_operation.location);
|
||||
}
|
||||
|
||||
value * eval_ref_impl(context & context, ast::cast_operation const &)
|
||||
value * eval_ref_impl(context & context, ast::cast_operation const & cast_operation)
|
||||
{
|
||||
throw std::runtime_error("Cast operation cannot be on the left-hand-side of assignment");
|
||||
throw internal_error("Cast operation cannot be on the left-hand-side of assignment", cast_operation.location);
|
||||
}
|
||||
|
||||
value * eval_ref_impl(context & context, ast::function_call const &)
|
||||
value * eval_ref_impl(context & context, ast::function_call const & function_call)
|
||||
{
|
||||
throw std::runtime_error("Function call cannot be on the left-hand-side of assignment");
|
||||
throw internal_error("Function call cannot be on the left-hand-side of assignment", function_call.location);
|
||||
}
|
||||
|
||||
value * eval_ref_impl(context & context, ast::array const &)
|
||||
value * eval_ref_impl(context & context, ast::array const & array)
|
||||
{
|
||||
throw std::runtime_error("Array cannot be on the left-hand-side of assignment");
|
||||
throw internal_error("Array cannot be on the left-hand-side of assignment", array.location);
|
||||
}
|
||||
|
||||
value * eval_ref_impl(context & context, ast::array_access const & array_access)
|
||||
|
|
@ -743,13 +623,13 @@ namespace pslang::interpreter
|
|||
|
||||
if (auto avalue = std::get_if<array_value>(array_ref))
|
||||
{
|
||||
return avalue->elements[get_array_index(index, avalue->elements.size())].get();
|
||||
return avalue->elements[get_array_index(index, avalue->elements.size(), ast::get_location(*array_access.index))].get();
|
||||
}
|
||||
|
||||
std::ostringstream os;
|
||||
os << "Cannot index into a non-array of type ";
|
||||
types::print(os, type_of(*array_ref));
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), array_access.location);
|
||||
}
|
||||
|
||||
value * eval_ref_impl(context & context, ast::field_access const & field_access)
|
||||
|
|
@ -764,14 +644,14 @@ namespace pslang::interpreter
|
|||
os << "Struct ";
|
||||
types::print(os, type_of(*object_ref));
|
||||
os << " has no field named \"" << field_access.field_name << "\"";
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), field_access.location);
|
||||
}
|
||||
|
||||
std::ostringstream os;
|
||||
os << "Value of type ";
|
||||
types::print(os, type_of(*object_ref));
|
||||
os << " is not a struct";
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), field_access.location);
|
||||
}
|
||||
|
||||
value * eval_ref_impl(context & context, ast::expression_ptr const & expression)
|
||||
|
|
@ -781,11 +661,6 @@ namespace pslang::interpreter
|
|||
|
||||
}
|
||||
|
||||
types::type resolve_type(context & context, ast::type const & type)
|
||||
{
|
||||
return resolve_type_impl(context, type);
|
||||
}
|
||||
|
||||
value eval(context & context, ast::expression_ptr const & expression)
|
||||
{
|
||||
return eval_impl(context, expression);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include <pslang/interpreter/exec.hpp>
|
||||
#include <pslang/interpreter/eval.hpp>
|
||||
#include <pslang/interpreter/error.hpp>
|
||||
#include <pslang/ast/statement.hpp>
|
||||
#include <pslang/types/print.hpp>
|
||||
|
||||
|
|
@ -23,7 +24,7 @@ namespace pslang::interpreter
|
|||
|
||||
~stack_pop_guard()
|
||||
{
|
||||
context_.scope_stack.pop_back();
|
||||
context_.frame_stack.pop_back();
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -50,7 +51,7 @@ namespace pslang::interpreter
|
|||
types::print(os, new_type);
|
||||
os << " to a variable of type ";
|
||||
types::print(os, existing_type);
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), assignment.location);
|
||||
}
|
||||
|
||||
*ref = std::move(new_value);
|
||||
|
|
@ -58,41 +59,41 @@ namespace pslang::interpreter
|
|||
|
||||
void exec_impl(context & context, ast::variable_declaration const & variable_declaration)
|
||||
{
|
||||
auto & scope = context.scope_stack.back();
|
||||
auto & scope = context.frame_stack.back();
|
||||
if (scope.contains(variable_declaration.name))
|
||||
throw std::runtime_error("Identifier \"" + variable_declaration.name + "\" is already defined in this scope");
|
||||
throw internal_error("Identifier \"" + variable_declaration.name + "\" is already defined in this scope", variable_declaration.location);
|
||||
|
||||
auto value = eval(context, variable_declaration.initializer);
|
||||
if (variable_declaration.type)
|
||||
{
|
||||
auto expected_type = resolve_type(context, *variable_declaration.type);
|
||||
auto expected_type = get_type(*variable_declaration.type);
|
||||
auto actual_type = type_of(value);
|
||||
if (!types::equal(expected_type, actual_type))
|
||||
if (!types::equal(*expected_type, actual_type))
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Cannot initialize a variable of type ";
|
||||
types::print(os, expected_type);
|
||||
types::print(os, *expected_type);
|
||||
os << " with an expression of type ";
|
||||
types::print(os, actual_type);
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), variable_declaration.location);
|
||||
}
|
||||
}
|
||||
context.scope_stack.back().variables[variable_declaration.name] = {.category = variable_declaration.category, .value = value};
|
||||
context.frame_stack.back().variables[variable_declaration.name] = {.category = variable_declaration.category, .value = value};
|
||||
}
|
||||
|
||||
void exec_impl(context & context, ast::if_block const &)
|
||||
void exec_impl(context & context, ast::if_block const & if_block)
|
||||
{
|
||||
throw std::runtime_error("Internal interpreter error: if blocks cannot be present in the final AST");
|
||||
throw internal_error("if blocks cannot be present in the final AST", if_block.location);
|
||||
}
|
||||
|
||||
void exec_impl(context & context, ast::else_block const &)
|
||||
void exec_impl(context & context, ast::else_block const & else_block)
|
||||
{
|
||||
throw std::runtime_error("Internal interpreter error: else blocks cannot be present in the final AST");
|
||||
throw internal_error("else blocks cannot be present in the final AST", else_block.location);
|
||||
}
|
||||
|
||||
void exec_impl(context & context, ast::else_if_block const &)
|
||||
void exec_impl(context & context, ast::else_if_block const & else_if_block)
|
||||
{
|
||||
throw std::runtime_error("Internal interpreter error: else if blocks cannot be present in the final AST");
|
||||
throw internal_error("else if blocks cannot be present in the final AST", else_if_block.location);
|
||||
}
|
||||
|
||||
void exec_impl(context & context, ast::if_chain const & if_chain)
|
||||
|
|
@ -111,7 +112,7 @@ namespace pslang::interpreter
|
|||
os << "Expected type bool, got type ";
|
||||
types::print(os, actual_type);
|
||||
os << " in if block condition";
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), get_location(*block.condition));
|
||||
}
|
||||
|
||||
do_exec = std::get<bool_value>(std::get<primitive_value>(value)).value;
|
||||
|
|
@ -120,7 +121,7 @@ namespace pslang::interpreter
|
|||
if (!do_exec)
|
||||
continue;
|
||||
|
||||
context.scope_stack.emplace_back();
|
||||
context.frame_stack.emplace_back();
|
||||
stack_pop_guard guard(context);
|
||||
exec(context, block.statements);
|
||||
break;
|
||||
|
|
@ -139,12 +140,12 @@ namespace pslang::interpreter
|
|||
os << "Expected type bool, got type ";
|
||||
types::print(os, actual_type);
|
||||
os << " in while block condition";
|
||||
throw std::runtime_error(os.str());
|
||||
throw internal_error(os.str(), get_location(*while_block.condition));
|
||||
}
|
||||
|
||||
if (std::get<bool_value>(std::get<primitive_value>(value)).value)
|
||||
{
|
||||
context.scope_stack.emplace_back();
|
||||
context.frame_stack.emplace_back();
|
||||
stack_pop_guard guard(context);
|
||||
exec(context, while_block.statements);
|
||||
}
|
||||
|
|
@ -155,55 +156,78 @@ namespace pslang::interpreter
|
|||
|
||||
void exec_impl(context & context, ast::function_definition const & function_definition)
|
||||
{
|
||||
auto & scope = context.scope_stack.back();
|
||||
if (scope.contains(function_definition.name))
|
||||
auto & frame = context.frame_stack.back();
|
||||
if (frame.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.arguments.push_back({.name = argument.name, .type = get_type(*argument.type)});
|
||||
value.return_type = get_type(*function_definition.return_type);
|
||||
value.statements = function_definition.statements;
|
||||
|
||||
scope.variables[function_definition.name] = {.category = ast::value_category::constant, .value = std::move(value)};
|
||||
frame.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)
|
||||
// NB: cannot use return_statement.level here because lexical scope stack
|
||||
// almost always differs from execution frame stack
|
||||
|
||||
// NB: cannot use iterators here because they might get invalidated during eval()
|
||||
|
||||
for (std::size_t n = context.frame_stack.size(); n --> 0;)
|
||||
{
|
||||
if (it->is_function_scope)
|
||||
{
|
||||
it->return_value = std::move(value);
|
||||
throw return_exception{};
|
||||
auto & frame = context.frame_stack[n];
|
||||
|
||||
if (!frame.expected_return_type)
|
||||
continue;
|
||||
}
|
||||
|
||||
auto value = return_statement.value ? eval(context, return_statement.value) : unit_value{};
|
||||
auto actual_type = type_of(value);
|
||||
|
||||
auto & frame = context.frame_stack[n];
|
||||
|
||||
if (!types::equal(*frame.expected_return_type, actual_type))
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Returning value of type ";
|
||||
types::print(os, actual_type);
|
||||
os << " from a function returning ";
|
||||
types::print(os, *frame.expected_return_type);
|
||||
throw internal_error(os.str(), return_statement.location);
|
||||
}
|
||||
|
||||
frame.return_value = std::move(value);
|
||||
throw return_exception{};
|
||||
}
|
||||
|
||||
throw std::runtime_error("Cannot return outside of a function");
|
||||
throw internal_error("Cannot return outside of function scope", return_statement.location);
|
||||
}
|
||||
|
||||
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");
|
||||
throw internal_error("Field definitions cannot be present in the final AST outside of struct definitions", field_definition.location);
|
||||
}
|
||||
|
||||
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");
|
||||
auto & frame = context.frame_stack.back();
|
||||
if (frame.contains(struct_definition.name))
|
||||
throw internal_error("Identifier \"" + struct_definition.name + "\" is already defined in this scope", struct_definition.location);
|
||||
|
||||
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)),
|
||||
.type = get_type(*field.type),
|
||||
});
|
||||
}
|
||||
scope.structs[struct_definition.name] = std::move(result);
|
||||
frame.structs[struct_definition.name] = std::move(result);
|
||||
}
|
||||
|
||||
void exec_impl(context & context, ast::statement_list_ptr const & statements)
|
||||
|
|
@ -216,7 +240,7 @@ namespace pslang::interpreter
|
|||
}
|
||||
catch (return_exception const &)
|
||||
{
|
||||
if (context.scope_stack.back().is_function_scope)
|
||||
if (context.frame_stack.back().expected_return_type)
|
||||
break;
|
||||
else
|
||||
throw;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
Future plans:
|
||||
* Clean up interpreter: don't repeat checks done in type checker, throw internal errors when appropriate
|
||||
* Fix interpreter identifier resolution for functions & variables
|
||||
* Pointers: pointer types, address-of operator (&), dereferencing, scope-based lifetime tracking in interpreter
|
||||
* Function overloading: separate functions from values (again) in interpreter, allow casting to specific function type to take function value
|
||||
* C FFI: `foreign func sin(x : f32) -> f32`
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue