Initial commit
This commit is contained in:
163
include/elna/backend/riscv.hpp
Normal file
163
include/elna/backend/riscv.hpp
Normal file
@ -0,0 +1,163 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "elna/source/optimizer.hpp"
|
||||
|
||||
namespace elna::riscv
|
||||
{
|
||||
enum class address_t
|
||||
{
|
||||
text,
|
||||
high20,
|
||||
lower12i
|
||||
};
|
||||
|
||||
struct reference
|
||||
{
|
||||
std::string name;
|
||||
std::size_t offset;
|
||||
address_t target;
|
||||
};
|
||||
|
||||
enum class x_register : std::uint8_t
|
||||
{
|
||||
zero = 0,
|
||||
ra = 1,
|
||||
sp = 2,
|
||||
gp = 3,
|
||||
tp = 4,
|
||||
t0 = 5,
|
||||
t1 = 6,
|
||||
t2 = 7,
|
||||
s0 = 8,
|
||||
s1 = 9,
|
||||
a0 = 10,
|
||||
a1 = 11,
|
||||
a2 = 12,
|
||||
a3 = 13,
|
||||
a4 = 14,
|
||||
a5 = 15,
|
||||
a6 = 16,
|
||||
a7 = 17,
|
||||
s2 = 18,
|
||||
s3 = 19,
|
||||
s4 = 20,
|
||||
s5 = 21,
|
||||
s6 = 22,
|
||||
s7 = 23,
|
||||
s8 = 24,
|
||||
s9 = 25,
|
||||
s10 = 26,
|
||||
s11 = 27,
|
||||
t3 = 28,
|
||||
t4 = 29,
|
||||
t5 = 30,
|
||||
t6 = 31,
|
||||
};
|
||||
|
||||
enum class funct3_t : std::uint8_t
|
||||
{
|
||||
addi = 0b000,
|
||||
slti = 0b001,
|
||||
sltiu = 0b011,
|
||||
andi = 0b111,
|
||||
ori = 0b110,
|
||||
xori = 0b100,
|
||||
slli = 0b000,
|
||||
srli = 0b101,
|
||||
srai = 0b101,
|
||||
add = 0b000,
|
||||
slt = 0b010,
|
||||
sltu = 0b011,
|
||||
_and = 0b111,
|
||||
_or = 0b110,
|
||||
_xor = 0b100,
|
||||
sll = 0b001,
|
||||
srl = 0b101,
|
||||
sub = 0b000,
|
||||
sra = 0b101,
|
||||
beq = 0b000,
|
||||
bne = 0b001,
|
||||
blt = 0b100,
|
||||
bltu = 0b110,
|
||||
bge = 0b101,
|
||||
bgeu = 0b111,
|
||||
fence = 0b000,
|
||||
fenceI = 0b001,
|
||||
csrrw = 0b001,
|
||||
csrrs = 0b010,
|
||||
csrrc = 0b011,
|
||||
csrrwi = 0b101,
|
||||
csrrsi = 0b110,
|
||||
csrrci = 0b111,
|
||||
priv = 0b000,
|
||||
sb = 0b000,
|
||||
sh = 0b001,
|
||||
sw = 0b010,
|
||||
lb = 0b000,
|
||||
lh = 0b001,
|
||||
lw = 0b010,
|
||||
lbu = 0b100,
|
||||
lhu = 0b101,
|
||||
jalr = 0b000,
|
||||
mul = 0b000,
|
||||
mulh = 0b001,
|
||||
mulhsu = 0b010,
|
||||
mulhu = 0b011,
|
||||
div = 0b100,
|
||||
divu = 0b101,
|
||||
rem = 0b110,
|
||||
remu = 0b111
|
||||
};
|
||||
|
||||
enum class funct12_t : std::uint8_t
|
||||
{
|
||||
ecall = 0b000000000000,
|
||||
ebreak = 0b000000000001,
|
||||
};
|
||||
|
||||
enum class funct7_t : std::uint8_t
|
||||
{
|
||||
none = 0,
|
||||
sub = 0b0100000,
|
||||
muldiv = 0b0000001
|
||||
};
|
||||
|
||||
enum class base_opcode : std::uint8_t
|
||||
{
|
||||
opImm = 0b0010011,
|
||||
lui = 0b0110111,
|
||||
auipc = 0b0010111,
|
||||
op = 0b0110011,
|
||||
jal = 0b1101111,
|
||||
jalr = 0b1100111,
|
||||
branch = 0b1100011,
|
||||
load = 0b0000011,
|
||||
store = 0b0100011,
|
||||
miscMem = 0b0001111,
|
||||
system = 0b1110011,
|
||||
};
|
||||
|
||||
struct instruction
|
||||
{
|
||||
instruction() = default; // NOP = addi x0, x0, 0.
|
||||
instruction(base_opcode opcode);
|
||||
|
||||
instruction& i(x_register rd, funct3_t funct3, x_register rs1, std::uint32_t immediate);
|
||||
instruction& s(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2);
|
||||
instruction& b(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2);
|
||||
instruction& r(x_register rd, funct3_t funct3, x_register rs1, x_register rs2,
|
||||
funct7_t funct7 = funct7_t::none);
|
||||
instruction& u(x_register rd, std::uint32_t imm);
|
||||
instruction& j(x_register rd, std::uint32_t imm);
|
||||
|
||||
const std::byte *cbegin() const;
|
||||
const std::byte *cend() const;
|
||||
|
||||
private:
|
||||
std::uint32_t representation{ 0 };
|
||||
};
|
||||
|
||||
std::vector<reference> generate(source::intermediate_code_generator generator,
|
||||
std::shared_ptr<source::symbol_table> table, std::shared_ptr<source::writer<std::byte>> writer);
|
||||
}
|
107
include/elna/backend/target.hpp
Normal file
107
include/elna/backend/target.hpp
Normal file
@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "elna/source/parser.hpp"
|
||||
#include "elna/source/optimizer.hpp"
|
||||
#include <filesystem>
|
||||
#include <elfio/elfio.hpp>
|
||||
|
||||
namespace elna::riscv
|
||||
{
|
||||
/**
|
||||
* Writer to a single label.
|
||||
*/
|
||||
struct elfio_section_writer
|
||||
{
|
||||
struct entry
|
||||
{
|
||||
std::string_view label;
|
||||
const std::byte *data{ nullptr };
|
||||
std::size_t size{ 0 };
|
||||
};
|
||||
|
||||
/**
|
||||
* An iterator over label and string pairs.
|
||||
*/
|
||||
struct iterator
|
||||
{
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = ptrdiff_t;
|
||||
using value_type = entry;
|
||||
using pointer = const value_type *;
|
||||
using reference = const value_type&;
|
||||
|
||||
reference operator*() const noexcept;
|
||||
pointer operator->() const noexcept;
|
||||
iterator& operator++();
|
||||
iterator& operator++(int);
|
||||
bool operator==(const iterator& that) const;
|
||||
bool operator!=(const iterator& that) const;
|
||||
|
||||
private:
|
||||
std::vector<std::string>::const_iterator labels;
|
||||
std::vector<std::size_t>::const_iterator sizes;
|
||||
value_type payload;
|
||||
|
||||
iterator(std::vector<std::string>::const_iterator labels, std::vector<std::size_t>::const_iterator sizes,
|
||||
const std::byte *data)
|
||||
: labels(labels), sizes(sizes)
|
||||
{
|
||||
if (data != nullptr)
|
||||
{
|
||||
payload = { *this->labels, data, *this->sizes};
|
||||
}
|
||||
}
|
||||
|
||||
iterator(std::vector<std::string>::const_iterator labels, std::vector<std::size_t>::const_iterator sizes)
|
||||
: labels(labels), sizes(sizes), payload{}
|
||||
{
|
||||
}
|
||||
|
||||
friend elfio_section_writer;
|
||||
};
|
||||
explicit elfio_section_writer(ELFIO::section *section);
|
||||
|
||||
void operator()(const std::string& label, const std::byte *data, std::size_t size);
|
||||
std::pair<std::string_view, bool> operator()(const std::byte *data, std::size_t size);
|
||||
|
||||
iterator begin() const;
|
||||
iterator end() const;
|
||||
|
||||
ELFIO::section *section() noexcept;
|
||||
|
||||
private:
|
||||
std::vector<std::string> labels;
|
||||
std::vector<std::size_t> sizes;
|
||||
ELFIO::section *m_section;
|
||||
};
|
||||
|
||||
class elfio_writer final : public source::writer<std::byte>
|
||||
{
|
||||
ELFIO::section *text;
|
||||
elfio_section_writer read_only;
|
||||
ELFIO::symbol_section_accessor symbol_accessor;
|
||||
ELFIO::string_section_accessor string_accessor;
|
||||
|
||||
public:
|
||||
elfio_writer(ELFIO::section *text, ELFIO::section *read_only, ELFIO::symbol_section_accessor symbol_accessor,
|
||||
ELFIO::string_section_accessor string_accessor);
|
||||
|
||||
std::size_t sink(const std::string& label, const std::byte *data, std::size_t size) override;
|
||||
std::string_view sink(const std::byte *data, std::size_t size) override;
|
||||
void sink(const std::string& label) override;
|
||||
std::size_t size() const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Searches the content by label and returns its index or -1 when the
|
||||
* label does not exist.
|
||||
*
|
||||
* \param symbol_accessor Object accessor.
|
||||
* \param needle Label name.
|
||||
* \return Data index.
|
||||
*/
|
||||
std::ptrdiff_t lookup(ELFIO::symbol_section_accessor symbol_accessor, const std::string& label);
|
||||
|
||||
void riscv32_elf(source::program *ast, source::intermediate_code_generator intermediate_code_generator,
|
||||
std::shared_ptr<source::symbol_table> table, const std::filesystem::path& out_file);
|
||||
}
|
37
include/elna/cli/cl.hpp
Normal file
37
include/elna/cli/cl.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include "elna/source/result.hpp"
|
||||
|
||||
namespace elna::cli
|
||||
{
|
||||
/**
|
||||
* Formats and prints the given error.
|
||||
*
|
||||
* \param compile_error The error to print.
|
||||
*/
|
||||
void print_error(const std::unique_ptr<source::error>& compile_error);
|
||||
|
||||
/**
|
||||
* Prints the given errors to the standard output.
|
||||
*
|
||||
* \param begin Pointer to the first error.
|
||||
* \param end Pointer pass the last error.
|
||||
*/
|
||||
template<typename I>
|
||||
void print_errors(I begin, I end)
|
||||
{
|
||||
std::for_each(begin, end, &print_error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles \a in_file and writes the generated code into \a out_file.
|
||||
*
|
||||
* \param in_file Input file.
|
||||
* \param out_file Output file.
|
||||
*
|
||||
* \return Exit status.
|
||||
*/
|
||||
int compile(const std::filesystem::path& in_file, const std::filesystem::path& out_file);
|
||||
}
|
212
include/elna/source/lexer.hpp
Normal file
212
include/elna/source/lexer.hpp
Normal file
@ -0,0 +1,212 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "elna/source/result.hpp"
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
/**
|
||||
* Union type representing a single token.
|
||||
*/
|
||||
struct token
|
||||
{
|
||||
/**
|
||||
* Token type.
|
||||
*/
|
||||
enum class type : std::uint16_t
|
||||
{
|
||||
dot,
|
||||
number,
|
||||
boolean,
|
||||
term_operator,
|
||||
let,
|
||||
identifier,
|
||||
equals,
|
||||
var,
|
||||
semicolon,
|
||||
left_paren,
|
||||
right_paren,
|
||||
comma,
|
||||
factor_operator,
|
||||
eof,
|
||||
begin,
|
||||
end,
|
||||
assignment,
|
||||
colon,
|
||||
when,
|
||||
then,
|
||||
loop,
|
||||
_do,
|
||||
procedure,
|
||||
comparison_operator,
|
||||
hat,
|
||||
at
|
||||
};
|
||||
|
||||
/**
|
||||
* Type of the token value.
|
||||
*/
|
||||
union value
|
||||
{
|
||||
value();
|
||||
value(std::int32_t value);
|
||||
value(const std::string& value);
|
||||
~value();
|
||||
|
||||
std::nullptr_t nil;
|
||||
std::int32_t number;
|
||||
std::string identifier;
|
||||
};
|
||||
|
||||
token(type of, elna::source::position position);
|
||||
token(type of, std::int32_t value, const elna::source::position position);
|
||||
token(type of, const std::string& value, const elna::source::position position);
|
||||
token(type of, value&& value, const elna::source::position position);
|
||||
token(const token& that);
|
||||
token(token&& that);
|
||||
~token();
|
||||
|
||||
token& operator=(const token& that);
|
||||
token& operator=(token&& that);
|
||||
|
||||
type of() const noexcept;
|
||||
const std::string& identifier() const;
|
||||
std::int32_t number() const;
|
||||
const elna::source::position& position() const noexcept;
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
private:
|
||||
type m_type;
|
||||
value m_value{};
|
||||
elna::source::position m_position;
|
||||
|
||||
bool has_identifier() const noexcept;
|
||||
bool is_numeric() const noexcept;
|
||||
};
|
||||
|
||||
class unexpected_character final : public error
|
||||
{
|
||||
std::string character;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param character Unexpected character.
|
||||
* \param path Source file name.
|
||||
* \param position Unexpected token position.
|
||||
*/
|
||||
unexpected_character(const std::string& character, const std::filesystem::path& path,
|
||||
const source::position position);
|
||||
|
||||
std::string what() const override;
|
||||
};
|
||||
|
||||
class unexpected_token final : public error
|
||||
{
|
||||
token m_token;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param token Unexpected token.
|
||||
* \param path Source file name.
|
||||
*/
|
||||
unexpected_token(const token& token, const std::filesystem::path& path);
|
||||
|
||||
std::string what() const override;
|
||||
};
|
||||
|
||||
struct lexer
|
||||
{
|
||||
lexer(std::vector<token>&& tokens, const position last_position, const std::filesystem::path& path);
|
||||
lexer(const lexer&) = delete;
|
||||
lexer(lexer&&) = default;
|
||||
|
||||
lexer& operator=(const lexer&) = delete;
|
||||
lexer& operator=(lexer&&) = default;
|
||||
|
||||
lexer& operator++();
|
||||
const token& operator*() const;
|
||||
const token *operator->() const;
|
||||
|
||||
/**
|
||||
* This function never fails and returns \ref token::type::eof at the
|
||||
* end of the token stream.
|
||||
*
|
||||
* \return Current token.
|
||||
*/
|
||||
const token& current() const noexcept;
|
||||
|
||||
/**
|
||||
* \param token_type Token type.
|
||||
* \return Whether the current token is \a token_type.
|
||||
*/
|
||||
bool current(const token::type token_type) const noexcept;
|
||||
|
||||
/**
|
||||
* Adds an \ref unexpected_token error to the error list.
|
||||
*
|
||||
* \param expected The token was expected.
|
||||
*/
|
||||
void add_error(const token& expected);
|
||||
|
||||
/**
|
||||
* Expects the current token to be \a token_type. In this case returns
|
||||
* this token and advances to the next token in the stream.
|
||||
*
|
||||
* \param token_type Expected token type.
|
||||
* \return Current token.
|
||||
*/
|
||||
std::optional<std::reference_wrapper<const token>> advance(const token::type token_type);
|
||||
|
||||
/**
|
||||
* Returns that follows the current token. If the current token is
|
||||
* \ref token::type::eof, returns it.
|
||||
*
|
||||
* \return The token that follows the current one.
|
||||
*/
|
||||
const token& look_ahead() const;
|
||||
|
||||
/**
|
||||
* Tells whether the token following the current one is \a token_type.
|
||||
*
|
||||
* \param token_type Token type.
|
||||
* \return Whether the next token is \a token_type.
|
||||
*/
|
||||
bool look_ahead(const token::type token_type) const;
|
||||
|
||||
/**
|
||||
* Skips one token if it is of type \a token_type. Adds an
|
||||
* \ref unexpected_token error otherwise.
|
||||
*
|
||||
* \param token_type The token type was expected.
|
||||
* \return Whether the current token was \a token_type.
|
||||
*/
|
||||
bool skip(const token::type token_type);
|
||||
|
||||
/**
|
||||
* Gets produced errors.
|
||||
*
|
||||
* \return Produced error list.
|
||||
*/
|
||||
const std::list<std::unique_ptr<error>>& errors() const noexcept;
|
||||
|
||||
private:
|
||||
std::vector<token> tokens;
|
||||
std::vector<token>::const_iterator iterator;
|
||||
std::list<std::unique_ptr<error>> m_errors;
|
||||
std::filesystem::path source_file;
|
||||
token eof;
|
||||
};
|
||||
|
||||
/**
|
||||
* Splits the source text into tokens.
|
||||
*
|
||||
* \param path Source file location.
|
||||
* \return Tokens or error.
|
||||
*/
|
||||
elna::source::result<lexer> tokenize(const std::filesystem::path& path);
|
||||
}
|
106
include/elna/source/optimizer.hpp
Normal file
106
include/elna/source/optimizer.hpp
Normal file
@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include "elna/source/parser.hpp"
|
||||
#include "elna/source/symbol_table.hpp"
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
enum class quadruple_operator
|
||||
{
|
||||
start,
|
||||
stop,
|
||||
add,
|
||||
sub,
|
||||
mul,
|
||||
div,
|
||||
eq,
|
||||
neq,
|
||||
lt,
|
||||
ge,
|
||||
gt,
|
||||
le,
|
||||
load,
|
||||
ref,
|
||||
beqz,
|
||||
j,
|
||||
label,
|
||||
assign,
|
||||
param,
|
||||
call
|
||||
};
|
||||
|
||||
/**
|
||||
* Single instruction representation.
|
||||
*/
|
||||
struct quadruple
|
||||
{
|
||||
quadruple(const quadruple_operator operation, std::shared_ptr<operand> operand1 = nullptr,
|
||||
std::shared_ptr<operand> operand2 = nullptr, std::shared_ptr<operand> operand3 = nullptr);
|
||||
|
||||
quadruple_operator operation() const noexcept;
|
||||
std::shared_ptr<operand> operand1();
|
||||
std::shared_ptr<operand> operand2();
|
||||
std::shared_ptr<operand> operand3();
|
||||
|
||||
private:
|
||||
quadruple_operator m_operation;
|
||||
std::shared_ptr<operand> m_operand1;
|
||||
std::shared_ptr<operand> m_operand2;
|
||||
std::shared_ptr<operand> m_operand3;
|
||||
};
|
||||
|
||||
class intermediate_code final
|
||||
{
|
||||
std::vector<quadruple> instructions;
|
||||
std::uint32_t m_variable_counter;
|
||||
std::uint32_t m_label_counter;
|
||||
|
||||
public:
|
||||
intermediate_code();
|
||||
|
||||
void emplace_back(const quadruple_operator operation, std::shared_ptr<operand> operand1 = nullptr,
|
||||
std::shared_ptr<operand> operand2 = nullptr, std::shared_ptr<operand> operand3 = nullptr);
|
||||
void clear();
|
||||
|
||||
std::vector<quadruple>::iterator begin();
|
||||
std::vector<quadruple>::iterator end();
|
||||
|
||||
std::int32_t variable_counter() const noexcept;
|
||||
std::int32_t increment_variable() noexcept;
|
||||
std::int32_t label_counter() const noexcept;
|
||||
std::int32_t increment_label() noexcept;
|
||||
};
|
||||
|
||||
class intermediate_code_generator final : public empty_visitor
|
||||
{
|
||||
std::unordered_map<std::string, intermediate_code> code;
|
||||
intermediate_code current;
|
||||
std::shared_ptr<symbol_table> table;
|
||||
|
||||
quadruple_operator convert(const binary_operator operation) const;
|
||||
quadruple_operator convert(const unary_operator operation) const;
|
||||
|
||||
public:
|
||||
intermediate_code_generator(std::shared_ptr<symbol_table> table);
|
||||
|
||||
std::unordered_map<std::string, intermediate_code>::iterator begin();
|
||||
std::unordered_map<std::string, intermediate_code>::iterator end();
|
||||
|
||||
void visit(declaration *declaration) override;
|
||||
void visit(constant_definition *definition) override;
|
||||
void visit(procedure_definition *definition) override;
|
||||
void visit(call_statement *statement) override;
|
||||
void visit(assign_statement *statement) override;
|
||||
void visit(if_statement *statement) override;
|
||||
void visit(while_statement *statement) override;
|
||||
void visit(block *block) override;
|
||||
void visit(program *program) override;
|
||||
void visit(type_expression *variable) override;
|
||||
void visit(variable_expression *variable) override;
|
||||
void visit(binary_expression *expression) override;
|
||||
void visit(unary_expression *expression) override;
|
||||
void visit(integer_literal *number) override;
|
||||
void visit(boolean_literal *number) override;
|
||||
};
|
||||
}
|
520
include/elna/source/parser.hpp
Normal file
520
include/elna/source/parser.hpp
Normal file
@ -0,0 +1,520 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/core/noncopyable.hpp>
|
||||
#include <memory>
|
||||
#include <elna/source/lexer.hpp>
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
enum class binary_operator
|
||||
{
|
||||
sum,
|
||||
subtraction,
|
||||
multiplication,
|
||||
division,
|
||||
equals,
|
||||
not_equals,
|
||||
less,
|
||||
greater,
|
||||
less_equal,
|
||||
greater_equal
|
||||
};
|
||||
|
||||
enum class unary_operator
|
||||
{
|
||||
reference,
|
||||
dereference
|
||||
};
|
||||
|
||||
class declaration;
|
||||
class constant_definition;
|
||||
class procedure_definition;
|
||||
class call_statement;
|
||||
class compound_statement;
|
||||
class assign_statement;
|
||||
class if_statement;
|
||||
class while_statement;
|
||||
class block;
|
||||
class program;
|
||||
class binary_expression;
|
||||
class unary_expression;
|
||||
class type_expression;
|
||||
class variable_expression;
|
||||
class integer_literal;
|
||||
class boolean_literal;
|
||||
|
||||
/**
|
||||
* Interface for AST visitors.
|
||||
*/
|
||||
struct parser_visitor
|
||||
{
|
||||
virtual void visit(declaration *) = 0;
|
||||
virtual void visit(constant_definition *) = 0;
|
||||
virtual void visit(procedure_definition *) = 0;
|
||||
virtual void visit(call_statement *) = 0;
|
||||
virtual void visit(compound_statement *) = 0;
|
||||
virtual void visit(assign_statement *) = 0;
|
||||
virtual void visit(if_statement *) = 0;
|
||||
virtual void visit(while_statement *) = 0;
|
||||
virtual void visit(block *) = 0;
|
||||
virtual void visit(program *) = 0;
|
||||
virtual void visit(binary_expression *) = 0;
|
||||
virtual void visit(unary_expression *) = 0;
|
||||
virtual void visit(type_expression *) = 0;
|
||||
virtual void visit(variable_expression *) = 0;
|
||||
virtual void visit(integer_literal *) = 0;
|
||||
virtual void visit(boolean_literal *) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A visitor which visits all nodes but does nothing.
|
||||
*/
|
||||
struct empty_visitor : parser_visitor
|
||||
{
|
||||
virtual void visit(declaration *declaration) override;
|
||||
virtual void visit(constant_definition *definition) override;
|
||||
virtual void visit(procedure_definition *definition) override;
|
||||
virtual void visit(call_statement *statement) override;
|
||||
virtual void visit(compound_statement *statement) override;
|
||||
virtual void visit(assign_statement *statement) override;
|
||||
virtual void visit(if_statement *) override;
|
||||
virtual void visit(while_statement *) override;
|
||||
virtual void visit(block *block) override;
|
||||
virtual void visit(program *program) override;
|
||||
virtual void visit(binary_expression *expression) override;
|
||||
virtual void visit(unary_expression *expression) override;
|
||||
virtual void visit(type_expression *variable) override;
|
||||
virtual void visit(variable_expression *variable) override;
|
||||
virtual void visit(integer_literal *number) override;
|
||||
virtual void visit(boolean_literal *boolean) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Operand representing a subexpression in the 3 address code.
|
||||
*/
|
||||
struct operand
|
||||
{
|
||||
public:
|
||||
virtual ~operand() noexcept = 0;
|
||||
};
|
||||
|
||||
struct integer_operand final : public operand
|
||||
{
|
||||
std::int32_t m_value;
|
||||
|
||||
public:
|
||||
explicit integer_operand(const std::int32_t value);
|
||||
|
||||
std::int32_t value() const noexcept;
|
||||
};
|
||||
|
||||
class variable_operand final : public operand
|
||||
{
|
||||
std::string m_name;
|
||||
|
||||
public:
|
||||
explicit variable_operand(const std::string& name);
|
||||
|
||||
const std::string& name() const noexcept;
|
||||
};
|
||||
|
||||
struct temporary_variable final : public operand
|
||||
{
|
||||
std::size_t m_counter;
|
||||
|
||||
public:
|
||||
explicit temporary_variable(const std::size_t counter);
|
||||
|
||||
std::size_t counter() const noexcept;
|
||||
};
|
||||
|
||||
struct label_operand final : public operand
|
||||
{
|
||||
std::size_t m_counter;
|
||||
|
||||
public:
|
||||
explicit label_operand(const std::size_t counter);
|
||||
|
||||
std::size_t counter() const noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* AST node.
|
||||
*/
|
||||
class node
|
||||
{
|
||||
const struct position source_position;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
*/
|
||||
explicit node(const position position);
|
||||
|
||||
public:
|
||||
virtual void accept(parser_visitor *) = 0;
|
||||
|
||||
/**
|
||||
* \return Node position in the source code.
|
||||
*/
|
||||
const struct position& position() const noexcept;
|
||||
};
|
||||
|
||||
class statement : public node
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
*/
|
||||
explicit statement(const struct position position);
|
||||
};
|
||||
|
||||
class expression : public node
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<operand> place;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
*/
|
||||
explicit expression(const struct position position);
|
||||
};
|
||||
|
||||
/**
|
||||
* Symbol definition.
|
||||
*/
|
||||
class definition : public node
|
||||
{
|
||||
std::string m_identifier;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructs a definition identified by some name.
|
||||
*
|
||||
* \param position Source code position.
|
||||
* \param identifier Definition name.
|
||||
*/
|
||||
definition(const struct position position, const std::string& identifier);
|
||||
|
||||
public:
|
||||
/**
|
||||
* \return Definition name.
|
||||
*/
|
||||
std::string& identifier() noexcept;
|
||||
};
|
||||
|
||||
class type_expression : public node
|
||||
{
|
||||
std::string m_base;
|
||||
bool m_pointer{ false };
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param name Type name.
|
||||
* \param is_pointer Whether it is a pointer type.
|
||||
*/
|
||||
type_expression(const struct position position, const std::string& name, const bool is_pointer = false);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
/**
|
||||
* \return Name of the base type.
|
||||
*/
|
||||
const std::string& base() const noexcept;
|
||||
|
||||
/**
|
||||
* \return Whether the type is a pointer.
|
||||
*/
|
||||
bool is_pointer() const noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Variable declaration.
|
||||
*/
|
||||
class declaration : public definition
|
||||
{
|
||||
std::unique_ptr<type_expression> m_type;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructs a declaration with a name and a type.
|
||||
*
|
||||
* \param position Source code position.
|
||||
* \param identifier Definition name.
|
||||
* \param type Declared type.
|
||||
*/
|
||||
declaration(const struct position position, const std::string& identifier,
|
||||
std::unique_ptr<type_expression>&& type);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
type_expression& type() noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constant definition.
|
||||
*/
|
||||
class constant_definition : public definition
|
||||
{
|
||||
std::unique_ptr<integer_literal> m_body;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param identifier Constant name.
|
||||
* \param body Constant value.
|
||||
*/
|
||||
constant_definition(const struct position position, const std::string& identifier,
|
||||
std::unique_ptr<integer_literal>&& body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
integer_literal& body();
|
||||
};
|
||||
|
||||
/**
|
||||
* Procedure definition.
|
||||
*/
|
||||
class procedure_definition : public definition
|
||||
{
|
||||
std::unique_ptr<block> m_body;
|
||||
std::vector<std::unique_ptr<declaration>> m_parameters;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param identifier Procedure name.
|
||||
* \param body Procedure body.
|
||||
*/
|
||||
procedure_definition(const struct position position, const std::string& identifier,
|
||||
std::unique_ptr<block>&& body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
block& body();
|
||||
std::vector<std::unique_ptr<declaration>>& parameters() noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Call statement.
|
||||
*/
|
||||
class call_statement : public statement
|
||||
{
|
||||
std::string m_name;
|
||||
std::vector<std::unique_ptr<expression>> m_arguments;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param name Callable's name.
|
||||
*/
|
||||
call_statement(const struct position position, const std::string& name);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
std::string& name() noexcept;
|
||||
std::vector<std::unique_ptr<expression>>& arguments() noexcept;
|
||||
};
|
||||
|
||||
class compound_statement : public statement
|
||||
{
|
||||
std::vector<std::unique_ptr<statement>> m_statements;
|
||||
|
||||
public:
|
||||
explicit compound_statement(const struct position position);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
std::vector<std::unique_ptr<statement>>& statements();
|
||||
};
|
||||
|
||||
class assign_statement : public statement
|
||||
{
|
||||
std::string m_lvalue;
|
||||
std::unique_ptr<expression> m_rvalue;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param lvalue Left-hand side.
|
||||
* \param rvalue Assigned expression.
|
||||
*/
|
||||
assign_statement(const struct position position, const std::string& lvalue,
|
||||
std::unique_ptr<expression>&& rvalue);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
std::string& lvalue() noexcept;
|
||||
expression& rvalue();
|
||||
};
|
||||
|
||||
/**
|
||||
* If-statement.
|
||||
*/
|
||||
class if_statement : public statement
|
||||
{
|
||||
std::unique_ptr<expression> m_prerequisite;
|
||||
std::unique_ptr<statement> m_body;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param prerequisite Condition.
|
||||
* \param body Statement executed if the condition is met.
|
||||
*/
|
||||
if_statement(const struct position position, std::unique_ptr<expression>&& prerequisite,
|
||||
std::unique_ptr<statement>&& body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
expression& prerequisite();
|
||||
statement& body();
|
||||
};
|
||||
|
||||
/**
|
||||
* While-statement.
|
||||
*/
|
||||
class while_statement : public statement
|
||||
{
|
||||
std::unique_ptr<expression> m_prerequisite;
|
||||
std::unique_ptr<statement> m_body;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param position Source code position.
|
||||
* \param prerequisite Condition.
|
||||
* \param body Statement executed while the condition is met.
|
||||
*/
|
||||
while_statement(const struct position position, std::unique_ptr<expression>&& prerequisite,
|
||||
std::unique_ptr<statement>&& body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
expression& prerequisite();
|
||||
statement& body();
|
||||
};
|
||||
|
||||
class block : public node
|
||||
{
|
||||
std::unique_ptr<statement> m_body;
|
||||
std::vector<std::unique_ptr<definition>> m_definitions;
|
||||
std::vector<std::unique_ptr<declaration>> m_declarations;
|
||||
|
||||
public:
|
||||
block(const struct position position, std::vector<std::unique_ptr<definition>>&& definitions,
|
||||
std::vector<std::unique_ptr<declaration>>&& declarations,
|
||||
std::unique_ptr<statement>&& body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
statement& body();
|
||||
std::vector<std::unique_ptr<definition>>& definitions() noexcept;
|
||||
std::vector<std::unique_ptr<declaration>>& declarations() noexcept;
|
||||
};
|
||||
|
||||
class program : public block
|
||||
{
|
||||
public:
|
||||
program(const struct position position, std::vector<std::unique_ptr<definition>>&& definitions,
|
||||
std::vector<std::unique_ptr<declaration>>&& declarations,
|
||||
std::unique_ptr<statement>&& body);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
};
|
||||
|
||||
class integer_literal : public expression
|
||||
{
|
||||
std::int32_t m_number;
|
||||
|
||||
public:
|
||||
integer_literal(const struct position position, const std::int32_t value);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
std::int32_t number() const noexcept;
|
||||
};
|
||||
|
||||
class boolean_literal : public expression
|
||||
{
|
||||
bool m_boolean;
|
||||
|
||||
public:
|
||||
boolean_literal(const struct position position, const bool value);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
bool boolean() const noexcept;
|
||||
};
|
||||
|
||||
class variable_expression : public expression
|
||||
{
|
||||
std::string m_name;
|
||||
|
||||
public:
|
||||
variable_expression(const struct position position, const std::string& name);
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
|
||||
const std::string& name() const noexcept;
|
||||
};
|
||||
|
||||
class binary_expression : public expression
|
||||
{
|
||||
std::unique_ptr<expression> m_lhs;
|
||||
std::unique_ptr<expression> m_rhs;
|
||||
binary_operator m_operator;
|
||||
|
||||
public:
|
||||
binary_expression(const struct position position, std::unique_ptr<expression>&& lhs,
|
||||
std::unique_ptr<expression>&& rhs, const unsigned char operation);
|
||||
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
expression& lhs();
|
||||
expression& rhs();
|
||||
binary_operator operation() const noexcept;
|
||||
};
|
||||
|
||||
class unary_expression : public expression
|
||||
{
|
||||
std::unique_ptr<expression> m_operand;
|
||||
unary_operator m_operator;
|
||||
|
||||
public:
|
||||
unary_expression(const struct position position, std::unique_ptr<expression>&& operand,
|
||||
const unsigned char operation);
|
||||
|
||||
virtual void accept(parser_visitor *visitor) override;
|
||||
expression& operand();
|
||||
unary_operator operation() const noexcept;
|
||||
};
|
||||
|
||||
class parser : boost::noncopyable
|
||||
{
|
||||
std::unique_ptr<expression> parse_unary_expression();
|
||||
std::unique_ptr<expression> parse_factor();
|
||||
std::unique_ptr<expression> parse_term();
|
||||
std::unique_ptr<expression> parse_expression();
|
||||
std::unique_ptr<expression> parse_condition();
|
||||
std::unique_ptr<constant_definition> parse_constant_definition();
|
||||
std::unique_ptr<procedure_definition> parse_procedure_definition();
|
||||
std::unique_ptr<type_expression> parse_type_expression();
|
||||
std::unique_ptr<declaration> parse_declaration();
|
||||
std::unique_ptr<statement> parse_statement();
|
||||
std::unique_ptr<call_statement> parse_call_statement();
|
||||
std::unique_ptr<compound_statement> parse_compound_statement();
|
||||
std::unique_ptr<assign_statement> parse_assign_statement();
|
||||
std::unique_ptr<if_statement> parse_if_statement();
|
||||
std::unique_ptr<while_statement> parse_while_statement();
|
||||
std::vector<std::unique_ptr<constant_definition>> parse_constant_definitions();
|
||||
std::vector<std::unique_ptr<procedure_definition>> parse_procedure_definitions();
|
||||
std::vector<std::unique_ptr<declaration>> parse_declarations();
|
||||
std::unique_ptr<block> parse_block();
|
||||
|
||||
lexer iterator;
|
||||
|
||||
public:
|
||||
parser(lexer&& tokens);
|
||||
|
||||
/**
|
||||
* Parses a source text.
|
||||
*
|
||||
* \return Parsed program or nothing if an error occurred.
|
||||
*/
|
||||
std::unique_ptr<program> parse();
|
||||
|
||||
/**
|
||||
* Gets produced errors.
|
||||
*
|
||||
* \return Produced error list.
|
||||
*/
|
||||
const std::list<std::unique_ptr<error>>& errors() const noexcept;
|
||||
};
|
||||
}
|
160
include/elna/source/result.hpp
Normal file
160
include/elna/source/result.hpp
Normal file
@ -0,0 +1,160 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <filesystem>
|
||||
#include <variant>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
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;
|
||||
std::filesystem::path m_path;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructs an error.
|
||||
*
|
||||
* \param path Source file name.
|
||||
* \param position Error position in the source text.
|
||||
*/
|
||||
error(const std::filesystem::path& path, 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;
|
||||
|
||||
/// Source file name.
|
||||
const std::filesystem::path& path() 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:
|
||||
/**
|
||||
* \param name Symbol name.
|
||||
* \param path Source file name.
|
||||
* \param current Current symbol position.
|
||||
* \param previous Position of the previously defined symbol.
|
||||
*/
|
||||
name_collision(const std::string& name, const std::filesystem::path& path,
|
||||
const position current, const position previous);
|
||||
|
||||
std::string what() const override;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
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 T *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 T *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;
|
||||
};
|
||||
}
|
48
include/elna/source/semantic.hpp
Normal file
48
include/elna/source/semantic.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "elna/source/parser.hpp"
|
||||
#include "elna/source/symbol_table.hpp"
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
class name_analysis_visitor final : public empty_visitor
|
||||
{
|
||||
std::shared_ptr<symbol_table> table = std::make_shared<symbol_table>();
|
||||
|
||||
std::shared_ptr<const type> convert_declaration_type(const type_expression& ast_type) const;
|
||||
|
||||
public:
|
||||
name_analysis_visitor(std::shared_ptr<symbol_table> table);
|
||||
|
||||
void visit(constant_definition *definition) override;
|
||||
void visit(declaration *declaration) override;
|
||||
void visit(program *program) override;
|
||||
void visit(procedure_definition *procedure) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visitor which allocates registers and stack space for variables and
|
||||
* parameters.
|
||||
*/
|
||||
class allocator_visitor final : public empty_visitor
|
||||
{
|
||||
std::ptrdiff_t local_offset;
|
||||
std::ptrdiff_t argument_offset;
|
||||
std::shared_ptr<symbol_table> table;
|
||||
|
||||
public:
|
||||
allocator_visitor(std::shared_ptr<symbol_table> table);
|
||||
|
||||
void visit(declaration *declaration) override;
|
||||
void visit(program *program) override;
|
||||
void visit(procedure_definition *procedure) override;
|
||||
void visit(call_statement *statement) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* This visitor performs the type checking.
|
||||
*/
|
||||
class type_analysis_visitor final : public empty_visitor
|
||||
{
|
||||
};
|
||||
}
|
151
include/elna/source/symbol_table.hpp
Normal file
151
include/elna/source/symbol_table.hpp
Normal file
@ -0,0 +1,151 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
class symbol_table;
|
||||
|
||||
/**
|
||||
* Generic language entity information.
|
||||
*/
|
||||
class info
|
||||
{
|
||||
public:
|
||||
virtual ~info() = 0;
|
||||
|
||||
protected:
|
||||
info();
|
||||
};
|
||||
|
||||
/**
|
||||
* Type information.
|
||||
*/
|
||||
class type_info final : public info
|
||||
{
|
||||
std::shared_ptr<class type> m_type;
|
||||
|
||||
public:
|
||||
explicit type_info(const class type& type);
|
||||
~type_info() override;
|
||||
std::shared_ptr<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
|
||||
{
|
||||
std::shared_ptr<const class type> m_type;
|
||||
|
||||
public:
|
||||
std::ptrdiff_t offset{ 0 };
|
||||
|
||||
explicit variable_info(std::shared_ptr<const class type> type);
|
||||
~variable_info() override;
|
||||
std::shared_ptr<const class type> type() noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Procedure parameter information.
|
||||
*/
|
||||
class parameter_info final : public info
|
||||
{
|
||||
std::shared_ptr<const class type> m_type;
|
||||
|
||||
public:
|
||||
std::ptrdiff_t offset{ 0 };
|
||||
|
||||
explicit parameter_info(std::shared_ptr<const class type> type);
|
||||
~parameter_info() override;
|
||||
std::shared_ptr<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();
|
||||
};
|
||||
}
|
41
include/elna/source/types.hpp
Normal file
41
include/elna/source/types.hpp
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace elna::source
|
||||
{
|
||||
/**
|
||||
* 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);
|
||||
};
|
||||
|
||||
/**
|
||||
* Typed pointer.
|
||||
*/
|
||||
struct pointer_type : public type
|
||||
{
|
||||
std::shared_ptr<const type> base_type;
|
||||
|
||||
pointer_type(std::shared_ptr<const type> base_type, const std::size_t byte_size);
|
||||
};
|
||||
|
||||
inline const primitive_type boolean_type{ "Boolean", 1 };
|
||||
inline const primitive_type int_type{ "Int", 4 };
|
||||
}
|
55
include/elna/tester.hpp
Normal file
55
include/elna/tester.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
|
||||
#define BOOST_PROCESS_USE_STD_FS
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/process.hpp>
|
||||
#include <boost/process/v2.hpp>
|
||||
|
||||
namespace elna
|
||||
{
|
||||
enum class test_status
|
||||
{
|
||||
successful,
|
||||
compile_failed,
|
||||
build_failed,
|
||||
expectation_failed,
|
||||
expectation_not_found
|
||||
};
|
||||
|
||||
struct test_result final
|
||||
{
|
||||
test_status status{ test_status::successful };
|
||||
int code{ 0 };
|
||||
std::string output;
|
||||
};
|
||||
|
||||
class test_results final
|
||||
{
|
||||
std::uint32_t m_total{ 0 };
|
||||
std::uint32_t m_passed{ 0 };
|
||||
|
||||
public:
|
||||
test_results() = default;
|
||||
|
||||
std::uint32_t total() const noexcept;
|
||||
std::uint32_t passed() const noexcept;
|
||||
std::uint32_t failed() const noexcept;
|
||||
|
||||
int exit_code() const noexcept;
|
||||
void add_exit_code(const test_status result) noexcept;
|
||||
};
|
||||
|
||||
boost::process::v2::process_stdio get_output_streams(const std::uint8_t stream_number,
|
||||
boost::asio::readable_pipe& read_pipe);
|
||||
test_result run_for_output(boost::asio::io_context& context, const std::uint8_t stream_number,
|
||||
const std::filesystem::path& binary, std::initializer_list<boost::string_view> arguments);
|
||||
std::string find_object(const std::vector<std::filesystem::path>& environment, const std::string& object);
|
||||
test_result build_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry);
|
||||
test_result run_test(boost::asio::io_context& context, const std::filesystem::directory_entry& test_entry);
|
||||
void print_result(const std::filesystem::directory_entry& test_entry, const test_result& result);
|
||||
}
|
Reference in New Issue
Block a user