Initial commit

This commit is contained in:
2022-06-05 15:16:04 +02:00
commit 9651c57760
90 changed files with 11887 additions and 0 deletions

325
source/lexer.cpp Normal file
View File

@ -0,0 +1,325 @@
#include "elna/source/lexer.hpp"
#include <cassert>
#include <variant>
namespace elna::source
{
using source_position = elna::source::position;
using source_error = elna::source::error;
token::value::value()
: nil(nullptr)
{
}
token::value::value(std::int32_t value)
: number(value)
{
}
token::value::value(const std::string& value)
: identifier(value)
{
}
token::value::~value()
{
}
token::token(const type of, const std::string& value, const source_position position)
: m_type(of), m_value(value), m_position(position)
{
}
token::token(const type of, std::int32_t number, const source_position position)
: m_type(of), m_value(number), m_position(position)
{
}
token::token(type of, value&& value, const elna::source::position position)
: m_type(of), m_position(position)
{
if (has_identifier())
{
new((void *) &m_value.identifier) std::string(std::move(value.identifier));
}
else if (is_numeric())
{
m_value.number = value.number;
}
else
{
m_value.nil = nullptr;
}
}
token::token(const type of, source_position position)
: m_type(of), m_position(position)
{
}
token::token(const token& that)
{
*this = that;
}
token::token(token&& that)
{
*this = std::move(that);
}
token::~token()
{
if (has_identifier())
{
m_value.identifier.~basic_string();
}
}
token& token::operator=(const token& that)
{
if (has_identifier())
{
m_value.identifier.~basic_string();
}
m_type = that.of();
m_position = that.position();
if (that.has_identifier())
{
new((void *) &m_value.identifier) std::string(that.identifier());
}
else if (that.is_numeric())
{
m_value.number = that.number();
}
else
{
m_value.nil = nullptr;
}
return *this;
}
token& token::operator=(token&& that)
{
if (has_identifier())
{
m_value.identifier.~basic_string();
}
m_type = that.of();
m_position = that.position();
if (that.has_identifier())
{
new((void *) &m_value.identifier) std::string(std::move(that.identifier()));
}
else if (that.is_numeric())
{
m_value.number = that.number();
}
else
{
m_value.nil = nullptr;
}
return *this;
}
token::type token::of() const noexcept
{
return m_type;
}
const std::string& token::identifier() const
{
if (!has_identifier())
{
throw std::bad_variant_access();
}
return m_value.identifier;
}
std::int32_t token::number() const
{
if (!is_numeric())
{
throw std::bad_variant_access();
}
return m_value.number;
}
const source_position& token::position() const noexcept
{
return m_position;
}
bool token::has_identifier() const noexcept
{
return of() == type::identifier
|| of() == type::term_operator
|| of() == type::factor_operator
|| of() == type::comparison_operator;
}
bool token::is_numeric() const noexcept
{
return of() == type::number
|| of() == type::boolean;
}
std::string token::to_string() const
{
switch (this->m_type)
{
case type::number:
return "«number»";
case type::boolean:
return "«boolean»";
case type::term_operator:
return "«term_operator»";
case type::let:
return "«const»";
case type::identifier:
return "«identifier»";
case type::equals:
return "«=»";
case type::var:
return "«var»";
case type::semicolon:
return "«;»";
case type::left_paren:
return "«(»";
case type::right_paren:
return "«)»";
case type::dot:
return "«)»";
case type::comma:
return "«,»";
case type::factor_operator:
return "«*»";
case type::eof:
return "«EOF»";
case type::begin:
return "«begin»";
case type::end:
return "«end»";
case type::assignment:
return "«:=»";
case type::colon:
return "«:»";
case type::when:
return "«if»";
case type::then:
return "«then»";
case type::loop:
return "«while»";
case type::_do:
return "«do»";
case type::procedure:
return "«proc»";
case type::comparison_operator:
return "«comparison_operator»";
};
assert(false);
}
unexpected_character::unexpected_character(const std::string& character, const std::filesystem::path& path,
const source::position position)
: error(path, position), character(character)
{
}
std::string unexpected_character::what() const
{
std::string ss{ "Unexpected character '" };
ss.insert(ss.cend(), character.cbegin(), character.cend());
ss.push_back('\'');
return ss;
}
unexpected_token::unexpected_token(const token& token, const std::filesystem::path& path)
: error(path, token.position()), m_token(token)
{
}
std::string unexpected_token::what() const
{
return "Unexpected token " + m_token.to_string();
}
lexer::lexer(std::vector<token>&& tokens, const position last_position, const std::filesystem::path& path)
: tokens(std::move(tokens)), iterator(this->tokens.cbegin()), eof(token(token::type::eof, last_position)),
source_file(path)
{
}
lexer& lexer::operator++()
{
++iterator;
return *this;
}
const token& lexer::operator*() const
{
return *iterator;
}
const token *lexer::operator->() const
{
return iterator.base();
}
const token& lexer::current() const noexcept
{
if (iterator == tokens.cend())
{
return this->eof;
}
return *iterator;
}
bool lexer::current(const token::type token_type) const noexcept
{
return current().of() == token_type;
}
void lexer::add_error(const token& expected)
{
m_errors.push_back(std::make_unique<unexpected_token>(expected, this->source_file));
}
std::optional<std::reference_wrapper<const token>> lexer::advance(const token::type token_type)
{
if (iterator != tokens.cend() && iterator->of() == token_type)
{
return std::make_optional<>(std::cref(*iterator++));
}
add_error(current());
return std::optional<std::reference_wrapper<const token>>();
}
const token& lexer::look_ahead() const
{
auto tmp = iterator;
++tmp;
if (iterator == tokens.cend() || tmp == tokens.cend())
{
return eof;
}
return *tmp;
}
bool lexer::look_ahead(const token::type token_type) const
{
return look_ahead().of() == token_type;
}
bool lexer::skip(const token::type token_type)
{
return advance(token_type).has_value();
}
const std::list<std::unique_ptr<error>>& lexer::errors() const noexcept
{
return m_errors;
}
}

267
source/optimizer.cpp Normal file
View File

@ -0,0 +1,267 @@
#include "elna/source/optimizer.hpp"
#include <cassert>
namespace elna::source
{
quadruple::quadruple(const quadruple_operator operation, std::shared_ptr<operand> operand1,
std::shared_ptr<operand> operand2, std::shared_ptr<operand> operand3)
: m_operation(operation), m_operand1(operand1), m_operand2(operand2), m_operand3(operand3)
{
}
quadruple_operator quadruple::operation() const noexcept
{
return m_operation;
}
std::shared_ptr<operand> quadruple::operand1()
{
return m_operand1;
}
std::shared_ptr<operand> quadruple::operand2()
{
return m_operand2;
}
std::shared_ptr<operand> quadruple::operand3()
{
return m_operand3;
}
intermediate_code::intermediate_code()
{
clear();
}
void intermediate_code::emplace_back(const quadruple_operator operation, std::shared_ptr<operand> operand1,
std::shared_ptr<operand> operand2, std::shared_ptr<operand> operand3)
{
this->instructions.emplace_back(operation, operand1, operand2, operand3);
}
void intermediate_code::clear()
{
this->instructions.clear();
std::uint32_t variable_counter = 1;
std::uint32_t label_counter = 0;
}
std::vector<quadruple>::iterator intermediate_code::begin()
{
return this->instructions.begin();
}
std::vector<quadruple>::iterator intermediate_code::end()
{
return this->instructions.end();
}
std::int32_t intermediate_code::variable_counter() const noexcept
{
return m_variable_counter;
}
std::int32_t intermediate_code::increment_variable() noexcept
{
return m_variable_counter++;
}
std::int32_t intermediate_code::label_counter() const noexcept
{
return m_label_counter;
}
std::int32_t intermediate_code::increment_label() noexcept
{
return m_label_counter++;
}
intermediate_code_generator::intermediate_code_generator(std::shared_ptr<symbol_table> table)
: table(table)
{
}
quadruple_operator intermediate_code_generator::convert(const binary_operator operation) const
{
switch (operation)
{
case binary_operator::sum:
return quadruple_operator::add;
case binary_operator::subtraction:
return quadruple_operator::sub;
case binary_operator::multiplication:
return quadruple_operator::mul;
case binary_operator::division:
return quadruple_operator::div;
case binary_operator::equals:
return quadruple_operator::eq;
case source::binary_operator::not_equals:
return quadruple_operator::neq;
case source::binary_operator::less:
return quadruple_operator::lt;
case source::binary_operator::greater_equal:
return quadruple_operator::ge;
case source::binary_operator::greater:
return quadruple_operator::gt;
case source::binary_operator::less_equal:
return quadruple_operator::le;
default:
assert(false);
}
}
quadruple_operator intermediate_code_generator::convert(const unary_operator operation) const
{
switch (operation)
{
case unary_operator::reference:
return quadruple_operator::ref;
case unary_operator::dereference:
return quadruple_operator::load;
default:
assert(false);
}
}
void intermediate_code_generator::visit(source::declaration *declaration)
{
}
void intermediate_code_generator::visit(source::constant_definition *definition)
{
}
void intermediate_code_generator::visit(procedure_definition *definition)
{
this->current.emplace_back(quadruple_operator::start);
definition->body().accept(this);
this->current.emplace_back(quadruple_operator::stop);
code[definition->identifier()] = std::move(this->current);
this->current.clear();
}
std::unordered_map<std::string, intermediate_code>::iterator intermediate_code_generator::begin()
{
return code.begin();
}
std::unordered_map<std::string, intermediate_code>::iterator intermediate_code_generator::end()
{
return code.end();
}
void intermediate_code_generator::visit(block *block)
{
block->body().accept(this);
}
void intermediate_code_generator::visit(program *program)
{
for (auto& definition : program->definitions())
{
definition->accept(this);
}
this->current.emplace_back(quadruple_operator::start);
program->body().accept(this);
this->current.emplace_back(quadruple_operator::stop);
code["main"] = std::move(this->current);
}
void intermediate_code_generator::visit(call_statement *statement)
{
for (auto& argument : statement->arguments())
{
argument->accept(this);
this->current.emplace_back(quadruple_operator::param, argument->place, nullptr, nullptr);
}
this->current.emplace_back(quadruple_operator::call,
std::make_shared<variable_operand>(statement->name()),
std::make_shared<integer_operand>(statement->arguments().size()));
}
void intermediate_code_generator::visit(assign_statement *statement)
{
statement->rvalue().accept(this);
this->current.emplace_back(quadruple_operator::assign, statement->rvalue().place, nullptr,
std::make_shared<variable_operand>(statement->lvalue()));
}
void intermediate_code_generator::visit(if_statement *statement)
{
statement->prerequisite().accept(this);
auto end_label = std::make_shared<label_operand>(this->current.increment_label());
this->current.emplace_back(quadruple_operator::beqz, statement->prerequisite().place, nullptr, end_label);
statement->body().accept(this);
this->current.emplace_back(quadruple_operator::label, nullptr, nullptr, end_label);
}
void intermediate_code_generator::visit(while_statement *statement)
{
auto condition_label = std::make_shared<label_operand>(this->current.increment_label());
this->current.emplace_back(quadruple_operator::label, nullptr, nullptr, condition_label);
statement->prerequisite().accept(this);
auto end_label = std::make_shared<label_operand>(this->current.increment_label());
this->current.emplace_back(quadruple_operator::beqz, statement->prerequisite().place, nullptr, end_label);
statement->body().accept(this);
this->current.emplace_back(quadruple_operator::j, nullptr, nullptr, condition_label);
this->current.emplace_back(quadruple_operator::label, nullptr, nullptr, end_label);
}
void intermediate_code_generator::visit(type_expression *type)
{
}
void intermediate_code_generator::visit(variable_expression *variable)
{
auto symbol = table->lookup(variable->name());
if (auto constant_symbol = std::dynamic_pointer_cast<source::constant_info>(symbol))
{
variable->place = std::make_shared<integer_operand>(constant_symbol->value());
}
else
{
variable->place = std::make_shared<variable_operand>(variable->name());
}
}
void intermediate_code_generator::visit(binary_expression *expression)
{
expression->lhs().accept(this);
auto left = expression->lhs().place;
expression->rhs().accept(this);
auto right = expression->rhs().place;
auto operation = convert(expression->operation());
auto new_place = std::make_shared<temporary_variable>(this->current.increment_variable());
this->current.emplace_back(operation, left, right, new_place);
expression->place = new_place;
}
void intermediate_code_generator::visit(unary_expression *expression)
{
expression->operand().accept(this);
auto operation = convert(expression->operation());
auto new_place = std::make_shared<temporary_variable>(this->current.increment_variable());
this->current.emplace_back(operation, expression->operand().place, nullptr, new_place);
expression->place = new_place;
}
void intermediate_code_generator::visit(integer_literal *number)
{
number->place = std::make_shared<integer_operand>(number->number());
}
void intermediate_code_generator::visit(boolean_literal *number)
{
number->place = std::make_shared<integer_operand>(number->boolean());
}
}

1063
source/parser.cpp Normal file

File diff suppressed because it is too large Load Diff

36
source/result.cpp Normal file
View File

@ -0,0 +1,36 @@
#include "elna/source/result.hpp"
#include "elna/source/types.hpp"
namespace elna::source
{
error::error(const std::filesystem::path& path, const position position)
: m_position(position), m_path(path)
{
}
std::size_t error::line() const noexcept
{
return this->m_position.line;
}
std::size_t error::column() const noexcept
{
return this->m_position.column;
}
const std::filesystem::path& error::path() const noexcept
{
return this->m_path;
}
name_collision::name_collision(const std::string& name, const std::filesystem::path& path,
const position current, const position previous)
: error(path, current), name(name), previous(previous)
{
}
std::string name_collision::what() const
{
return "Name '" + name + "' was already defined";
}
}

204
source/scanner.l Normal file
View File

@ -0,0 +1,204 @@
%{
#define YY_NO_UNISTD_H
#define YY_USER_ACTION token_position = elna::source::position{ line_no, column_no }; column_no += yyleng;
#include <fstream>
#include "elna/source/lexer.hpp"
elna::source::token::value yylval{};
elna::source::position token_position{};
static std::size_t column_no = 1;
static std::size_t line_no = 1;
%}
%option noyywrap
%option never-interactive
%%
\-\-.* {
/* Skip the comment */
}
[\ \t\r] {
/* Skip the whitespaces */
}
\n {
++line_no;
column_no = 1;
}
if {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::when);
}
then {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::then);
}
while {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::loop);
}
do {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::_do);
}
proc {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::procedure);
}
begin {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::begin);
}
end {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::end);
}
const {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::let);
}
var {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::var);
}
True {
yylval.number = 1;
return static_cast<int>(elna::source::token::type::boolean);
}
False {
yylval.number = 0;
return static_cast<int>(elna::source::token::type::boolean);
}
[A-Za-z_][A-Za-z0-9_]* {
new((void *) &yylval.identifier) std::string(yytext);
return static_cast<int>(elna::source::token::type::identifier);
}
[0-9]+ {
yylval.number = strtol(yytext, NULL, 10);
return static_cast<int>(elna::source::token::type::number);
}
\( {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::left_paren);
}
\) {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::right_paren);
}
\>= {
new((void *) &yylval.identifier) std::string(1, 'g');
return static_cast<int>(elna::source::token::type::comparison_operator);
}
\<= {
new((void *) &yylval.identifier) std::string(1, 'l');
return static_cast<int>(elna::source::token::type::comparison_operator);
}
(>|<) {
new((void *) &yylval.identifier) std::string(yytext);
return static_cast<int>(elna::source::token::type::comparison_operator);
}
\/= {
new((void *) &yylval.identifier) std::string(1, 'n');
return static_cast<int>(elna::source::token::type::comparison_operator);
}
= {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::equals);
}
; {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::semicolon);
}
\. {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::dot);
}
, {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::comma);
}
(\+|\-) {
new((void *) &yylval.identifier) std::string(yytext);
return static_cast<int>(elna::source::token::type::term_operator);
}
(\*|\/) {
new((void *) &yylval.identifier) std::string(yytext);
return static_cast<int>(elna::source::token::type::factor_operator);
}
:= {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::assignment);
}
: {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::colon);
}
\^ {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::hat);
}
@ {
yylval.nil = nullptr;
return static_cast<int>(elna::source::token::type::at);
}
. {
return -1;
}
%%
namespace elna::source
{
result<lexer> tokenize(const std::filesystem::path& path)
{
int yytoken;
std::vector<token> tokens;
yyin = fopen(path.c_str(), "rb");
if (yyin == nullptr)
{
throw std::ios_base::failure("File does not exist");
}
do
{
yytoken = yylex();
if (yytoken < 0)
{
return result<lexer>(unexpected_character{ std::string{ yytext[0] }, path, token_position });
}
tokens.emplace_back(static_cast<token::type>(yytoken), std::move(yylval), token_position);
}
while (yytoken != 0);
return result<lexer>(std::in_place, std::move(tokens), position{ line_no, column_no }, path);
}
}

118
source/semantic.cpp Normal file
View File

@ -0,0 +1,118 @@
#include "elna/source/semantic.hpp"
#include "elna/source/types.hpp"
#include <cstdlib>
namespace elna::source
{
name_analysis_visitor::name_analysis_visitor(std::shared_ptr<symbol_table> table)
: table(table)
{
}
void name_analysis_visitor::visit(constant_definition *definition)
{
this->table->enter(definition->identifier(),
std::make_shared<constant_info>(constant_info(definition->body().number())));
}
std::shared_ptr<const type> name_analysis_visitor::convert_declaration_type(const type_expression& ast_type) const
{
auto variable_type = std::dynamic_pointer_cast<type_info>(table->lookup(ast_type.base()))
->type();
std::shared_ptr<type> declaration_type;
if (ast_type.is_pointer())
{
return std::make_shared<pointer_type>(variable_type, 4);
}
else
{
return variable_type;
}
}
void name_analysis_visitor::visit(declaration *declaration)
{
std::shared_ptr<const type> declaration_type = convert_declaration_type(declaration->type());
this->table->enter(declaration->identifier(),
std::make_shared<variable_info>(declaration_type));
}
void name_analysis_visitor::visit(program *program)
{
this->table->enter("main", std::make_shared<procedure_info>(this->table));
empty_visitor::visit(program);
}
void name_analysis_visitor::visit(procedure_definition *procedure)
{
auto info = std::make_shared<procedure_info>(this->table);
this->table->enter(procedure->identifier(), info);
this->table = info->scope();
for (auto& parameter : procedure->parameters())
{
auto declaration_type = convert_declaration_type(parameter->type());
this->table->enter(parameter->identifier(),
std::make_shared<parameter_info>(declaration_type));
}
procedure->body().accept(this);
this->table = info->scope()->scope();
}
allocator_visitor::allocator_visitor(std::shared_ptr<symbol_table> table)
: table(table)
{
}
void allocator_visitor::visit(declaration *declaration)
{
auto declaration_info = this->table->lookup(declaration->identifier());
if (auto variable = std::dynamic_pointer_cast<variable_info>(declaration_info))
{
this->local_offset -= sizeof(std::int32_t);
variable->offset = this->local_offset;
}
else if (auto parameter = std::dynamic_pointer_cast<parameter_info>(declaration_info))
{
parameter->offset = this->argument_offset;
this->argument_offset += sizeof(std::int32_t);
}
}
void allocator_visitor::visit(program *program)
{
this->local_offset = 0;
this->argument_offset = 0;
empty_visitor::visit(program);
std::dynamic_pointer_cast<procedure_info>(table->lookup("main"))->local_stack_size =
std::abs(this->local_offset);
}
void allocator_visitor::visit(procedure_definition *procedure)
{
this->local_offset = 0;
this->argument_offset = 0;
auto info = std::dynamic_pointer_cast<procedure_info>(this->table->lookup(procedure->identifier()));
this->table = info->scope();
empty_visitor::visit(procedure);
this->table = info->scope()->scope();
info->local_stack_size = std::abs(this->local_offset);
info->argument_stack_size = this->argument_offset;
}
void allocator_visitor::visit(call_statement *statement)
{
auto call_info = std::dynamic_pointer_cast<intrinsic_info>(this->table->lookup(statement->name()));
this->argument_offset = std::max(static_cast<std::size_t>(this->argument_offset),
call_info->parameter_stack_size());
}
}

142
source/symbol_table.cpp Normal file
View File

@ -0,0 +1,142 @@
#include "elna/source/types.hpp"
#include "elna/source/symbol_table.hpp"
namespace elna::source
{
symbol_table::symbol_table(std::shared_ptr<symbol_table> scope)
: outer_scope(scope)
{
if (scope == nullptr)
{
auto boolean_info = std::make_shared<type_info>(boolean_type);
auto int_info = std::make_shared<type_info>(int_type);
enter("Boolean", boolean_info);
enter("Int", int_info);
auto writei = std::make_shared<intrinsic_info>();
writei->parameter_infos.emplace_back(int_info->type());
enter("writei", writei);
auto writeb = std::make_shared<intrinsic_info>();
writeb->parameter_infos.emplace_back(boolean_info->type());
enter("writeb", writeb);
}
}
std::shared_ptr<info> symbol_table::lookup(const std::string& name)
{
auto entry = entries.find(name);
if (entry != entries.cend())
{
return entry->second;
}
if (this->outer_scope != nullptr)
{
return this->outer_scope->lookup(name);
}
return nullptr;
}
void symbol_table::enter(const std::string& name, std::shared_ptr<info> entry)
{
entries.insert_or_assign(name, entry);
}
std::shared_ptr<symbol_table> symbol_table::scope()
{
return this->outer_scope;
}
info::~info()
{
}
info::info()
{
}
type_info::type_info(const class type& type)
: info(), m_type(std::make_shared<class type>(type))
{
}
type_info::~type_info()
{
}
std::shared_ptr<const class type> type_info::type() const noexcept
{
return m_type;
}
constant_info::constant_info(const std::int32_t value)
: m_value(value)
{
}
constant_info::~constant_info()
{
}
std::int32_t constant_info::value() const noexcept
{
return m_value;
}
variable_info::variable_info(std::shared_ptr<const class type> type)
: m_type(type)
{
}
variable_info::~variable_info()
{
}
std::shared_ptr<const class type> variable_info::type() noexcept
{
return m_type;
}
parameter_info::parameter_info(std::shared_ptr<const class type> type)
: m_type(type)
{
}
parameter_info::~parameter_info()
{
}
std::shared_ptr<const class type> parameter_info::type() const noexcept
{
return m_type;
}
intrinsic_info::~intrinsic_info()
{
}
std::size_t intrinsic_info::parameter_stack_size() const noexcept
{
return this->parameter_infos.size() * sizeof(std::int32_t);
}
procedure_info::procedure_info(std::shared_ptr<symbol_table> outer_scope)
: local_table(std::make_shared<symbol_table>(outer_scope))
{
}
procedure_info::~procedure_info()
{
}
std::shared_ptr<symbol_table> procedure_info::scope()
{
return local_table;
}
std::size_t procedure_info::stack_size() const noexcept
{
return local_stack_size + argument_stack_size;
}
}

19
source/types.cpp Normal file
View File

@ -0,0 +1,19 @@
#include <elna/source/types.hpp>
namespace elna::source
{
type::type(const std::size_t byte_size)
: byte_size(byte_size)
{
}
primitive_type::primitive_type(const std::string& type_name, const std::size_t byte_size)
: type(byte_size), type_name(type_name)
{
}
pointer_type::pointer_type(std::shared_ptr<const type> base_type, const std::size_t byte_size)
: type(byte_size), base_type(base_type)
{
}
}