|
#pragma once
|
|
#include <charconv>
|
|
#include <concepts>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <stack>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <variant>
|
|
#include <vector>
|
|
|
|
#include "molasses/lexer.h"
|
|
|
|
|
|
enum class architecture_t {
|
|
x86_64_linux,
|
|
x86_linux
|
|
};
|
|
|
|
#if defined(__x86_64__) && __linux__
|
|
constexpr architecture_t architecture = architecture_t::x86_64_linux;
|
|
constexpr size_t architecture_ptr_size = 8;
|
|
#elif defined(__i386) || defined(__i386__) || defined(i386) && __linux__
|
|
constexpr architecture_t architecture = architecture_t::x86_linux;
|
|
constexpr size_t architecture_ptr_size = 4;
|
|
#endif
|
|
|
|
namespace molasses {
|
|
namespace details {
|
|
template<typename ...RestT>
|
|
std::string concatenate_builder_impl(std::stringstream& stream)
|
|
requires (sizeof...(RestT) == 0)
|
|
{
|
|
return stream.str();
|
|
}
|
|
template<typename FirstT, typename ...RestT>
|
|
std::string concatenate_builder_impl(std::stringstream& stream, FirstT first, RestT... rest) {
|
|
stream << first;
|
|
return concatenate_builder_impl(stream, rest...);
|
|
}
|
|
template<typename ...RestT>
|
|
std::string concatenate_builder(RestT... rest) {
|
|
std::stringstream stream{};
|
|
return concatenate_builder_impl(stream, rest...);
|
|
}
|
|
}
|
|
|
|
struct type {
|
|
[[nodiscard]] virtual std::string name() const = 0;
|
|
[[nodiscard]] virtual size_t byte_size() const = 0;
|
|
};
|
|
|
|
inline auto operator<=>(const type& lhs, const type& rhs) {
|
|
return lhs.name() <=> rhs.name();
|
|
}
|
|
|
|
struct primitive_type : public type {
|
|
std::string _name;
|
|
size_t _byte_size;
|
|
|
|
primitive_type(std::string name, size_t byte_size)
|
|
: _name(std::forward<std::string>(name))
|
|
, _byte_size(byte_size)
|
|
{}
|
|
|
|
[[nodiscard]] std::string name() const final {
|
|
return _name;
|
|
}
|
|
[[nodiscard]] size_t byte_size() const final {
|
|
return _byte_size;
|
|
};
|
|
};
|
|
|
|
struct parser_context;
|
|
|
|
struct ptr_type{
|
|
void* ptr;
|
|
std::shared_ptr<type> pointed;
|
|
};
|
|
|
|
using stack_element = std::variant<int32_t, int64_t, ptr_type>;
|
|
|
|
using interpreter_stack = std::stack<stack_element>;
|
|
|
|
struct generate_context;
|
|
|
|
struct operation {
|
|
[[nodiscard]] virtual std::string name() const = 0;
|
|
[[nodiscard]] virtual std::vector<std::string> argument_types() const = 0;
|
|
[[nodiscard]] virtual std::vector<std::string> return_types() const = 0;
|
|
[[nodiscard]] virtual std::vector<std::string> generate(const parser_context&, const lexed_output& lexer_data) const = 0;
|
|
[[nodiscard]] virtual std::vector<std::string> emit(const parser_context&) const = 0;
|
|
virtual void execute(const generate_context&, interpreter_stack&) const = 0;
|
|
};
|
|
|
|
struct primitive_operation : public operation {
|
|
std::string _name;
|
|
std::vector<std::string> _args;
|
|
std::vector<std::string> _rets;
|
|
std::vector<std::string> _instructions;
|
|
std::function<void(const generate_context&, interpreter_stack&)> _executable;
|
|
|
|
primitive_operation(std::string name, std::vector<std::string> args, std::vector<std::string> rets, std::vector<std::string> body, std::function<void(const generate_context&,interpreter_stack&)> executable)
|
|
: _name(std::forward<std::string>(name))
|
|
, _args(std::forward<std::vector<std::string>>(args))
|
|
, _rets(std::forward<std::vector<std::string>>(rets))
|
|
, _instructions(std::forward<std::vector<std::string>>(body))
|
|
, _executable(std::move(executable))
|
|
{}
|
|
|
|
[[nodiscard]] std::string name() const final {
|
|
return _name;
|
|
}
|
|
[[nodiscard]] std::vector<std::string> argument_types() const final {
|
|
return _args;
|
|
}
|
|
[[nodiscard]] std::vector<std::string> return_types() const final {
|
|
return _rets;
|
|
}
|
|
[[nodiscard]] std::vector<std::string> generate(const parser_context&, const lexed_output& lexer_data) const final {
|
|
return {};
|
|
}
|
|
[[nodiscard]] std::vector<std::string> emit(const parser_context&) const final {
|
|
return _instructions;
|
|
}
|
|
|
|
void execute(const generate_context& ctx, interpreter_stack& stack) const override {
|
|
_executable(ctx, stack);
|
|
};
|
|
};
|
|
|
|
struct procedure_operation : public operation {
|
|
std::string _name;
|
|
std::vector<std::string> _args;
|
|
std::vector<std::string> _rets;
|
|
std::vector<symbol> _body;
|
|
|
|
procedure_operation(std::string name, std::vector<std::string> args, std::vector<std::string> rets, std::vector<symbol> body)
|
|
: _name(std::forward<std::string>(name))
|
|
, _args(std::forward<std::vector<std::string>>(args))
|
|
, _rets(std::forward<std::vector<std::string>>(rets))
|
|
, _body(std::forward<std::vector<symbol>>(body))
|
|
{}
|
|
|
|
[[nodiscard]] std::string name() const final {
|
|
return _name;
|
|
}
|
|
[[nodiscard]] std::vector<std::string> argument_types() const final {
|
|
return _args;
|
|
}
|
|
[[nodiscard]] std::vector<std::string> return_types() const final {
|
|
return _rets;
|
|
}
|
|
[[nodiscard]] std::vector<std::string> generate(const parser_context&, const lexed_output& lexer_data) const final;
|
|
[[nodiscard]] std::vector<std::string> emit(const parser_context&) const final;
|
|
void execute(const generate_context&, interpreter_stack&) const final;
|
|
};
|
|
|
|
inline auto operator<=>(const operation& lhs, const operation& rhs) {
|
|
return lhs.name() <=> rhs.name();
|
|
}
|
|
|
|
struct parser_error : public std::runtime_error {
|
|
explicit parser_error(const std::string& str) : std::runtime_error(str) {}
|
|
};
|
|
|
|
struct type_input_error : public parser_error {
|
|
type_input_error() : parser_error("Bad type provided") {}
|
|
// TODO: Better error message
|
|
};
|
|
struct value_missing_error : public parser_error {
|
|
value_missing_error() : parser_error("Expected value, none provided") {}
|
|
// TODO: Better error message
|
|
};
|
|
struct procedure_stack_error : public parser_error {
|
|
procedure_stack_error() : parser_error("Expected the stack to look like the return stack upon completion\n") {}
|
|
// TODO: Better error message
|
|
};
|
|
struct unexpected_token_error : public parser_error {
|
|
unexpected_token_error(const symbol& sym, const std::string& found, const std::string& expected)
|
|
: parser_error (
|
|
details::concatenate_builder(
|
|
"Unexpected token encountered\n",
|
|
"\tAt ", sym.file_name,":",sym.line,":",sym.column,"\n",
|
|
"\tExpected ", expected, "\n",
|
|
"\tFound ", found, "\n"
|
|
)
|
|
) {}
|
|
};
|
|
struct expecting_token_error : public parser_error {
|
|
expecting_token_error(const std::string& expected, const std::string& context)
|
|
: parser_error(
|
|
details::concatenate_builder(
|
|
"An expected token has not been encountered before the end of the input\n",
|
|
"\tExpected ", expected,"\n",
|
|
"\t", context,"\n"
|
|
)
|
|
)
|
|
{}
|
|
// TODO: Better error message
|
|
};
|
|
struct unknown_token_error : public parser_error {
|
|
explicit unknown_token_error(const symbol& sym) : parser_error(details::concatenate_builder("An unknown token has been encountered\n", "\tAt ", sym.file_name,":",sym.line,":",sym.column,"\n")) {}
|
|
// TODO: Better error message
|
|
};
|
|
struct type_expected_with_modifier_error : public parser_error {
|
|
type_expected_with_modifier_error() : parser_error("A type is expected before a modifier") {}
|
|
// TODO: Better error message
|
|
};
|
|
|
|
std::vector<std::string> operator>>(std::vector<std::string> current_stack, const operation& next_op);
|
|
|
|
std::optional<int32_t> try_parse_int32(const std::string& str);
|
|
std::optional<int64_t> try_parse_int64(const std::string& str);
|
|
|
|
struct parser_context {
|
|
std::vector<std::shared_ptr<type>> types;
|
|
std::vector<std::shared_ptr<operation>> operations;
|
|
std::vector<std::shared_ptr<procedure_operation>> procedures;
|
|
|
|
[[nodiscard]] std::shared_ptr<type> lookup_type(const std::string&) const;
|
|
[[nodiscard]] std::shared_ptr<operation> lookup_operation(const std::string&) const;
|
|
};
|
|
|
|
struct generate_context {
|
|
lexed_output lexer;
|
|
parser_context parser;
|
|
std::vector<std::shared_ptr<procedure_operation>> procedures;
|
|
};
|
|
|
|
using success_t = bool;
|
|
|
|
std::tuple<interpreter_stack, generate_context, std::optional<std::shared_ptr<std::runtime_error>>> interpret(interpreter_stack, generate_context, std::string );
|
|
|
|
generate_context parse(parser_context, const lexed_output&);
|
|
std::vector<std::string> generate(const generate_context&);
|
|
|
|
parser_context register_integers(parser_context);
|
|
|
|
template<architecture_t Arch = architecture>
|
|
parser_context register_i32_operations(parser_context);
|
|
|
|
bool type_check(const parser_context&, const lexed_output&, const std::vector<symbol>&, std::vector<std::string> execution_input, const std::vector<std::string>& execution_output);
|
|
}
|
|
|