Initial commit
This commit is contained in:
325
source/lexer.cpp
Normal file
325
source/lexer.cpp
Normal 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
267
source/optimizer.cpp
Normal 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
1063
source/parser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
36
source/result.cpp
Normal file
36
source/result.cpp
Normal 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
204
source/scanner.l
Normal 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
118
source/semantic.cpp
Normal 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
142
source/symbol_table.cpp
Normal 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
19
source/types.cpp
Normal 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)
|
||||
{
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user