313 lines
7.1 KiB
C++
313 lines
7.1 KiB
C++
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <variant>
|
|
#include <list>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <type_traits>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
namespace elna::source
|
|
{
|
|
/**
|
|
* Position in the source text.
|
|
*/
|
|
struct position
|
|
{
|
|
/// Line.
|
|
std::size_t line = 1;
|
|
|
|
/// Column.
|
|
std::size_t column = 1;
|
|
};
|
|
|
|
/**
|
|
* A compilation error consists of an error message and position.
|
|
*/
|
|
class error
|
|
{
|
|
position m_position;
|
|
|
|
protected:
|
|
/**
|
|
* \param position Error position in the source text.
|
|
*/
|
|
error(const position position);
|
|
|
|
public:
|
|
/// Error text.
|
|
virtual std::string what() const = 0;
|
|
|
|
/// Error line in the source text.
|
|
std::size_t line() const noexcept;
|
|
|
|
/// Error column in the source text.
|
|
std::size_t column() const noexcept;
|
|
};
|
|
|
|
template<typename T>
|
|
struct result
|
|
{
|
|
using E = std::list<std::unique_ptr<source::error>>;
|
|
|
|
template<typename... Args, typename = std::enable_if<std::is_constructible_v<T, Args...>>>
|
|
explicit result(std::in_place_t, Args&&... arguments)
|
|
: payload(std::in_place_type<T>, std::forward<Args>(arguments)...)
|
|
{
|
|
}
|
|
|
|
explicit result(T&& result)
|
|
: payload(std::move(result))
|
|
{
|
|
}
|
|
|
|
template<typename U, typename = std::enable_if<std::is_base_of_v<error, U>>>
|
|
explicit result(U&& first)
|
|
: payload(E())
|
|
{
|
|
errors().emplace_back(std::make_unique<U>(first));
|
|
}
|
|
|
|
explicit result(E&& errors)
|
|
: payload(std::move(errors))
|
|
{
|
|
}
|
|
|
|
bool has_errors() const noexcept
|
|
{
|
|
return std::holds_alternative<E>(payload);
|
|
}
|
|
|
|
bool is_success() const noexcept
|
|
{
|
|
return std::holds_alternative<T>(payload);
|
|
}
|
|
|
|
T& success()
|
|
{
|
|
return std::get<T>(payload);
|
|
}
|
|
|
|
E& errors()
|
|
{
|
|
return std::get<E>(payload);
|
|
}
|
|
|
|
private:
|
|
std::variant<T, E> payload;
|
|
};
|
|
|
|
class name_collision final : public error
|
|
{
|
|
position previous;
|
|
std::string name;
|
|
|
|
public:
|
|
name_collision(const std::string& name, const position current, const position previous);
|
|
|
|
std::string what() const override;
|
|
};
|
|
|
|
class symbol_table;
|
|
|
|
/**
|
|
* Type representation.
|
|
*/
|
|
struct type
|
|
{
|
|
const std::size_t byte_size;
|
|
|
|
protected:
|
|
explicit type(const std::size_t byte_size);
|
|
};
|
|
|
|
/**
|
|
* Built-in type representation.
|
|
*/
|
|
struct primitive_type : public type
|
|
{
|
|
const std::string type_name;
|
|
|
|
primitive_type(const std::string& type_name, const std::size_t byte_size);
|
|
};
|
|
|
|
inline const primitive_type boolean_type{ "Boolean", 1 };
|
|
inline const primitive_type int_type{ "Int", 4 };
|
|
|
|
/**
|
|
* Generic language entity information.
|
|
*/
|
|
class info
|
|
{
|
|
public:
|
|
virtual ~info() = 0;
|
|
|
|
protected:
|
|
info();
|
|
};
|
|
|
|
/**
|
|
* Type information.
|
|
*/
|
|
class type_info final : public info
|
|
{
|
|
class type m_type;
|
|
|
|
public:
|
|
explicit type_info(const class type& type);
|
|
~type_info() override;
|
|
const class type& type() const noexcept;
|
|
};
|
|
|
|
/**
|
|
* Constant information.
|
|
*/
|
|
class constant_info final : public info
|
|
{
|
|
std::int32_t m_value;
|
|
|
|
public:
|
|
constant_info(const std::int32_t value);
|
|
~constant_info() override;
|
|
std::int32_t value() const noexcept;
|
|
};
|
|
|
|
/**
|
|
* Variable information.
|
|
*/
|
|
class variable_info final : public info
|
|
{
|
|
class type m_type;
|
|
|
|
public:
|
|
std::ptrdiff_t offset{ 0 };
|
|
|
|
explicit variable_info(const class type& type);
|
|
~variable_info() override;
|
|
const class type& type() const noexcept;
|
|
};
|
|
|
|
/**
|
|
* Procedure parameter information.
|
|
*/
|
|
class parameter_info final : public info
|
|
{
|
|
class type m_type;
|
|
|
|
public:
|
|
std::ptrdiff_t offset{ 0 };
|
|
|
|
explicit parameter_info(const class type& type);
|
|
~parameter_info() override;
|
|
const class type& type() const noexcept;
|
|
};
|
|
|
|
/**
|
|
* Intrinsic and external procedure information.
|
|
*/
|
|
class intrinsic_info : public info
|
|
{
|
|
public:
|
|
std::vector<parameter_info> parameter_infos;
|
|
|
|
~intrinsic_info() override;
|
|
std::size_t parameter_stack_size() const noexcept;
|
|
};
|
|
|
|
/**
|
|
* Procedure information.
|
|
*/
|
|
class procedure_info final : public intrinsic_info
|
|
{
|
|
std::shared_ptr<symbol_table> local_table;
|
|
|
|
public:
|
|
std::size_t local_stack_size{ 0 };
|
|
std::size_t argument_stack_size{ 0 };
|
|
|
|
explicit procedure_info(std::shared_ptr<symbol_table> outer_scope);
|
|
~procedure_info() override;
|
|
|
|
std::shared_ptr<symbol_table> scope();
|
|
std::size_t stack_size() const noexcept;
|
|
};
|
|
|
|
/**
|
|
* Symbol table.
|
|
*/
|
|
class symbol_table
|
|
{
|
|
std::unordered_map<std::string, std::shared_ptr<info>> entries;
|
|
std::shared_ptr<symbol_table> outer_scope;
|
|
|
|
public:
|
|
/**
|
|
* Constructs a new symbol with an optional outer scope.
|
|
*
|
|
* \param scope Outer scope.
|
|
*/
|
|
explicit symbol_table(std::shared_ptr<symbol_table> scope = nullptr);
|
|
|
|
/**
|
|
* Looks for symbol in the table by name. Returns nullptr if the symbol
|
|
* can not be found.
|
|
*
|
|
* \param name Symbol name.
|
|
* \return Symbol from the table if found.
|
|
*/
|
|
std::shared_ptr<info> lookup(const std::string& name);
|
|
|
|
/**
|
|
* Registers new symbol.
|
|
*
|
|
* \param name Symbol name.
|
|
* \param entry Symbol information.
|
|
*/
|
|
void enter(const std::string& name, std::shared_ptr<info> entry);
|
|
|
|
/**
|
|
* Returns the outer scope or nullptr if the this is the global scope.
|
|
*
|
|
* \return Outer scope.
|
|
*/
|
|
std::shared_ptr<symbol_table> scope();
|
|
};
|
|
|
|
struct writer
|
|
{
|
|
/**
|
|
* Writes data to the table and saves it under the given label.
|
|
*
|
|
* \param label Symbol name.
|
|
* \param data Data to be written.
|
|
* \param size Data size.
|
|
*
|
|
* \return New size of the table.
|
|
*/
|
|
virtual std::size_t sink(const std::string& label, const std::byte *data, std::size_t size) = 0;
|
|
|
|
/**
|
|
* Writes data and returns a label under that the data can be accessed.
|
|
*
|
|
* \param data Data to be written.
|
|
* \param size Data size.
|
|
*
|
|
* \return Label for the symbol.
|
|
*/
|
|
virtual std::string_view sink(const std::byte *data, std::size_t size) = 0;
|
|
|
|
/**
|
|
* Creates an external symbol.
|
|
*/
|
|
virtual void sink(const std::string& label) = 0;
|
|
|
|
/**
|
|
* \return Actual size of the text section.
|
|
*/
|
|
virtual std::size_t size() const = 0;
|
|
};
|
|
}
|