Implement argument passing
This commit is contained in:
parent
5cb0e18a87
commit
9293faea09
11
example.elna
11
example.elna
@ -103,6 +103,14 @@ begin
|
||||
writei("Test not false")
|
||||
end;
|
||||
|
||||
proc test_param(d: Int, e: Int);
|
||||
begin
|
||||
writei("");
|
||||
writei("Test param");
|
||||
writei(d);
|
||||
writei(e)
|
||||
end;
|
||||
|
||||
begin
|
||||
test_primitive();
|
||||
test_string();
|
||||
@ -111,5 +119,6 @@ begin
|
||||
test_record();
|
||||
test_const();
|
||||
test_if();
|
||||
test_not()
|
||||
test_not();
|
||||
test_param(8, 7)
|
||||
end.
|
||||
|
@ -87,10 +87,17 @@ namespace gcc
|
||||
}
|
||||
else if (symbol)
|
||||
{
|
||||
tree fndecl_type = build_function_type_list(integer_type_node, NULL_TREE);
|
||||
tree fndecl_type = build_function_type(void_type_node, TYPE_ARG_TYPES(symbol->payload));
|
||||
tree printf_fn = build1(ADDR_EXPR, build_pointer_type(fndecl_type), symbol->payload);
|
||||
|
||||
tree stmt = build_call_nary(integer_type_node, printf_fn, 0);
|
||||
std::vector<tree> arguments(statement->arguments().size());
|
||||
for (std::size_t i = 0; i < statement->arguments().size(); ++i)
|
||||
{
|
||||
statement->arguments().at(i)->accept(this);
|
||||
arguments[i] = this->current_expression;
|
||||
}
|
||||
tree stmt = build_call_array_loc(get_location(&statement->position()),
|
||||
void_type_node, printf_fn, arguments.size(), arguments.data());
|
||||
|
||||
append_to_statement_list(stmt, &this->current_statements);
|
||||
this->current_expression = NULL_TREE;
|
||||
@ -148,14 +155,14 @@ namespace gcc
|
||||
|
||||
void generic_visitor::visit(source::procedure_definition *definition)
|
||||
{
|
||||
tree *parameter_types = reinterpret_cast<tree *>(xmalloc(definition->parameters().size() * sizeof(tree)));
|
||||
std::vector<tree> parameter_types(definition->parameters().size());
|
||||
|
||||
for (std::size_t i = 0; i < definition->parameters().size(); ++i)
|
||||
{
|
||||
parameter_types[i] = build_type(definition->parameters().at(i)->type());
|
||||
}
|
||||
tree declaration_type = build_function_type_array(void_type_node,
|
||||
definition->parameters().size(), parameter_types);
|
||||
definition->parameters().size(), parameter_types.data());
|
||||
this->main_fndecl = build_fn_decl(definition->identifier().c_str(), declaration_type);
|
||||
|
||||
this->symbol_map->enter(definition->identifier(), source::make_info(this->main_fndecl));
|
||||
@ -166,6 +173,21 @@ namespace gcc
|
||||
DECL_RESULT(this->main_fndecl) = resdecl;
|
||||
|
||||
enter_scope();
|
||||
|
||||
gcc::tree_chain argument_chain;
|
||||
for (std::size_t i = 0; i < definition->parameters().size(); ++i)
|
||||
{
|
||||
auto parameter = definition->parameters().at(i);
|
||||
|
||||
tree declaration_tree = build_decl(get_location(¶meter->position()), PARM_DECL,
|
||||
get_identifier(parameter->identifier().c_str()), parameter_types[i]);
|
||||
DECL_CONTEXT(declaration_tree) = this->main_fndecl;
|
||||
DECL_ARG_TYPE(declaration_tree) = parameter_types[i];
|
||||
|
||||
this->symbol_map->enter(parameter->identifier(), source::make_info(declaration_tree));
|
||||
argument_chain.append(declaration_tree);
|
||||
}
|
||||
DECL_ARGUMENTS(this->main_fndecl) = argument_chain.head();
|
||||
definition->body().accept(this);
|
||||
|
||||
tree_symbol_mapping mapping = leave_scope();
|
||||
@ -180,8 +202,6 @@ namespace gcc
|
||||
gimplify_function_tree(this->main_fndecl);
|
||||
|
||||
cgraph_node::finalize_function(this->main_fndecl, true);
|
||||
|
||||
free(parameter_types);
|
||||
}
|
||||
|
||||
void generic_visitor::enter_scope()
|
||||
|
100
include/elna/source/semantic.h
Normal file
100
include/elna/source/semantic.h
Normal file
@ -0,0 +1,100 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
// obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include "elna/source/ast.h"
|
||||
#include "elna/source/symbol_table.h"
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace source
|
||||
{
|
||||
class name_analysis_visitor final : public empty_visitor
|
||||
{
|
||||
std::shared_ptr<symbol_table> table;
|
||||
const char *filename;
|
||||
std::list<std::unique_ptr<error>> m_errors;
|
||||
const std::size_t pointer_size;
|
||||
|
||||
std::shared_ptr<const type> convert_declaration_type(const type_expression& ast_type) const;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param table Symbol table.
|
||||
* \param path Source filename.
|
||||
* \param target_pointer_size Pointer size on the target platform.
|
||||
*/
|
||||
name_analysis_visitor(std::shared_ptr<symbol_table> table, const char *filename,
|
||||
const std::size_t target_pointer_size);
|
||||
|
||||
/**
|
||||
* \return Collected errors.
|
||||
*/
|
||||
const std::list<std::unique_ptr<error>>& errors() const noexcept;
|
||||
|
||||
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
|
||||
{
|
||||
std::shared_ptr<symbol_table> table;
|
||||
const char *filename;
|
||||
const std::size_t pointer_size;
|
||||
std::list<std::unique_ptr<error>> m_errors;
|
||||
|
||||
public:
|
||||
/**
|
||||
* \param table Symbol table.
|
||||
* \param path Source filename.
|
||||
* \param target_pointer_size Pointer size on the target platform.
|
||||
*/
|
||||
type_analysis_visitor(std::shared_ptr<symbol_table> table, const char *filename,
|
||||
const std::size_t target_pointer_size);
|
||||
|
||||
/**
|
||||
* \return Collected errors.
|
||||
*/
|
||||
const std::list<std::unique_ptr<error>>& errors() const noexcept;
|
||||
|
||||
void visit(program *program) override;
|
||||
void visit(procedure_definition *procedure) override;
|
||||
void visit(integer_literal *literal) override;
|
||||
void visit(boolean_literal *literal) override;
|
||||
void visit(variable_expression *expression) override;
|
||||
void visit(unary_expression *expression) override;
|
||||
void visit(binary_expression *expression) override;
|
||||
void visit(call_statement *statement) override;
|
||||
void visit(constant_definition *definition) override;
|
||||
void visit(while_statement *statement) override;
|
||||
void visit(if_statement *statement) override;
|
||||
void visit(assign_statement *statement) override;
|
||||
};
|
||||
}
|
||||
}
|
198
include/elna/source/symbol_table.h
Normal file
198
include/elna/source/symbol_table.h
Normal file
@ -0,0 +1,198 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
// obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace source
|
||||
{
|
||||
class symbol_table;
|
||||
class type_info;
|
||||
class typed_info;
|
||||
class constant_info;
|
||||
class variable_info;
|
||||
class parameter_info;
|
||||
class intrinsic_info;
|
||||
class procedure_info;
|
||||
|
||||
/**
|
||||
* Generic language entity information.
|
||||
*/
|
||||
class info
|
||||
{
|
||||
public:
|
||||
virtual ~info() = 0;
|
||||
|
||||
virtual type_info *is_type_info() noexcept;
|
||||
virtual typed_info *is_typed_info() noexcept;
|
||||
virtual constant_info *is_constant_info() noexcept;
|
||||
virtual variable_info *is_variable_info() noexcept;
|
||||
virtual parameter_info *is_parameter_info() noexcept;
|
||||
virtual intrinsic_info *is_intrinsic_info() noexcept;
|
||||
virtual procedure_info *is_procedure_info() noexcept;
|
||||
|
||||
protected:
|
||||
info();
|
||||
};
|
||||
|
||||
/**
|
||||
* Type information.
|
||||
*/
|
||||
class type_info final : public info
|
||||
{
|
||||
std::shared_ptr<const class type> m_type;
|
||||
|
||||
public:
|
||||
explicit type_info(std::shared_ptr<class type> type);
|
||||
~type_info() override;
|
||||
|
||||
virtual type_info *is_type_info() noexcept override;
|
||||
std::shared_ptr<const class type> type() const noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Information for a typed symbol.
|
||||
*/
|
||||
class typed_info : public info
|
||||
{
|
||||
std::shared_ptr<const class type> m_type;
|
||||
|
||||
protected:
|
||||
typed_info(std::shared_ptr<const class type> type);
|
||||
|
||||
public:
|
||||
~typed_info() override;
|
||||
|
||||
virtual typed_info *is_typed_info() noexcept override;
|
||||
std::shared_ptr<const class type> type() const noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constant information.
|
||||
*/
|
||||
class constant_info final : public typed_info
|
||||
{
|
||||
std::int32_t m_value;
|
||||
|
||||
public:
|
||||
constant_info(std::shared_ptr<const class type> type, const std::int32_t value);
|
||||
|
||||
virtual constant_info *is_constant_info() noexcept override;
|
||||
std::int32_t value() const noexcept;
|
||||
};
|
||||
|
||||
/**
|
||||
* Variable information.
|
||||
*/
|
||||
class variable_info final : public typed_info
|
||||
{
|
||||
public:
|
||||
std::ptrdiff_t offset{ 0 };
|
||||
|
||||
explicit variable_info(std::shared_ptr<const class type> type);
|
||||
|
||||
virtual variable_info *is_variable_info() noexcept override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Procedure parameter information.
|
||||
*/
|
||||
class parameter_info final : public typed_info
|
||||
{
|
||||
public:
|
||||
std::ptrdiff_t offset{ 0 };
|
||||
|
||||
explicit parameter_info(std::shared_ptr<const class type> type);
|
||||
|
||||
virtual parameter_info *is_parameter_info() noexcept override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Intrinsic and external procedure information.
|
||||
*/
|
||||
class intrinsic_info : public info
|
||||
{
|
||||
std::shared_ptr<const class procedure_type> m_type;
|
||||
|
||||
public:
|
||||
explicit intrinsic_info(const class procedure_type& type);
|
||||
~intrinsic_info() override;
|
||||
|
||||
std::shared_ptr<const class procedure_type> type() const noexcept;
|
||||
std::size_t parameter_stack_size() const noexcept;
|
||||
virtual intrinsic_info *is_intrinsic_info() noexcept override;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 };
|
||||
|
||||
procedure_info(const class procedure_type& type, std::shared_ptr<symbol_table> outer_scope);
|
||||
~procedure_info() override;
|
||||
|
||||
std::shared_ptr<symbol_table> scope();
|
||||
std::size_t stack_size() const noexcept;
|
||||
virtual procedure_info *is_procedure_info() noexcept override;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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();
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a symbol table with predefined symbols.
|
||||
*
|
||||
* \return A symbol table with predefined symbols.
|
||||
*/
|
||||
std::shared_ptr<source::symbol_table> add_builtin_symbols();}
|
||||
}
|
325
source/semantic.cc
Normal file
325
source/semantic.cc
Normal file
@ -0,0 +1,325 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
// obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#include "elna/source/semantic.h"
|
||||
#include "elna/source/result.h"
|
||||
#include <cstdlib>
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace source
|
||||
{
|
||||
name_analysis_visitor::name_analysis_visitor(std::shared_ptr<symbol_table> table,
|
||||
const char *filename, const std::size_t target_pointer_size)
|
||||
: table(table), filename(filename), pointer_size(target_pointer_size)
|
||||
{
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(constant_definition *definition)
|
||||
{
|
||||
auto constant_type = std::make_shared<const class primitive_type>(int_type);
|
||||
this->table->enter(definition->identifier(),
|
||||
std::make_shared<constant_info>(constant_type, definition->body().number()));
|
||||
}
|
||||
|
||||
std::shared_ptr<const type> name_analysis_visitor::convert_declaration_type(const type_expression& ast_type) const
|
||||
{
|
||||
auto variable_type = table->lookup(ast_type.base())->is_type_info()->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 *declarationx)
|
||||
{
|
||||
std::shared_ptr<const type> declaration_type = convert_declaration_type(declarationx->type());
|
||||
|
||||
this->table->enter(declarationx->identifier(),
|
||||
std::make_shared<variable_info>(declaration_type));
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(program *program)
|
||||
{
|
||||
class procedure_type main_type{ std::vector<std::shared_ptr<const class type>>(), this->pointer_size };
|
||||
this->table->enter("_start", std::make_shared<procedure_info>(main_type, this->table));
|
||||
empty_visitor::visit(program);
|
||||
}
|
||||
|
||||
void name_analysis_visitor::visit(procedure_definition *procedure)
|
||||
{
|
||||
std::vector<std::shared_ptr<const type>> arguments;
|
||||
|
||||
for (auto& parameter : procedure->parameters())
|
||||
{
|
||||
auto declaration_type = convert_declaration_type(parameter->type());
|
||||
arguments.push_back(declaration_type);
|
||||
}
|
||||
procedure_type definition_type{ std::move(arguments), this->pointer_size };
|
||||
auto info = std::make_shared<procedure_info>(definition_type, this->table);
|
||||
|
||||
this->table->enter(procedure->identifier(), info);
|
||||
this->table = info->scope();
|
||||
|
||||
for (std::size_t i = 0; i < procedure->parameters().size(); ++i)
|
||||
{
|
||||
this->table->enter(procedure->parameters().at(i)->identifier(),
|
||||
std::make_shared<parameter_info>(definition_type.arguments.at(i)));
|
||||
}
|
||||
procedure->body().accept(this);
|
||||
|
||||
this->table = info->scope()->scope();
|
||||
}
|
||||
|
||||
const std::list<std::unique_ptr<error>>& name_analysis_visitor::errors() const noexcept
|
||||
{
|
||||
return m_errors;
|
||||
}
|
||||
|
||||
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 = declaration_info->is_variable_info())
|
||||
{
|
||||
this->local_offset -= sizeof(std::int32_t);
|
||||
variable->offset = this->local_offset;
|
||||
}
|
||||
else if (auto parameter = declaration_info->is_parameter_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);
|
||||
table->lookup("_start")->is_procedure_info()->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 = this->table->lookup(procedure->identifier())->is_procedure_info();
|
||||
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 = this->table->lookup(statement->name())->is_intrinsic_info();
|
||||
|
||||
this->argument_offset = std::max(static_cast<std::size_t>(this->argument_offset),
|
||||
call_info->parameter_stack_size());
|
||||
}
|
||||
|
||||
type_analysis_visitor::type_analysis_visitor(std::shared_ptr<symbol_table> table,
|
||||
const char *filename, const std::size_t target_pointer_size)
|
||||
: table(table), filename(filename), pointer_size(target_pointer_size)
|
||||
{
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(program *program)
|
||||
{
|
||||
for (auto& definition : program->definitions())
|
||||
{
|
||||
definition->accept(this);
|
||||
}
|
||||
program->body().accept(this);
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(procedure_definition *procedure)
|
||||
{
|
||||
auto info = this->table->lookup(procedure->identifier())->is_procedure_info();
|
||||
this->table = info->scope();
|
||||
|
||||
procedure->body().accept(this);
|
||||
|
||||
this->table = info->scope()->scope();
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(integer_literal *literal)
|
||||
{
|
||||
literal->data_type = table->lookup("Int")->is_type_info()->type();
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(boolean_literal *literal)
|
||||
{
|
||||
literal->data_type = table->lookup("Boolean")->is_type_info()->type();
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(variable_expression *expression)
|
||||
{
|
||||
expression->data_type = table->lookup(expression->name())->is_typed_info()->type();
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(unary_expression *expression)
|
||||
{
|
||||
empty_visitor::visit(expression);
|
||||
|
||||
switch (expression->operation())
|
||||
{
|
||||
case unary_operator::reference:
|
||||
expression->data_type = std::make_shared<const pointer_type>(expression->operand().data_type,
|
||||
this->pointer_size);
|
||||
break;
|
||||
case unary_operator::dereference:
|
||||
auto operand_type = expression->operand().data_type;
|
||||
|
||||
if (operand_type->is_pointer_type() != nullptr)
|
||||
{
|
||||
expression->data_type = operand_type;
|
||||
}
|
||||
else if (operand_type != nullptr)
|
||||
{
|
||||
auto new_error = std::make_unique<type_mismatch>(operand_type,
|
||||
type_mismatch::operation::dereference, this->filename, expression->position());
|
||||
m_errors.push_back(std::move(new_error));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(binary_expression *expression)
|
||||
{
|
||||
empty_visitor::visit(expression);
|
||||
|
||||
switch (expression->operation())
|
||||
{
|
||||
case binary_operator::sum:
|
||||
case binary_operator::subtraction:
|
||||
case binary_operator::multiplication:
|
||||
case binary_operator::division:
|
||||
case binary_operator::less:
|
||||
case binary_operator::greater:
|
||||
case binary_operator::less_equal:
|
||||
case binary_operator::greater_equal:
|
||||
if (expression->lhs().data_type != nullptr && expression->rhs().data_type != nullptr)
|
||||
{
|
||||
std::unique_ptr<type_mismatch> new_error;
|
||||
|
||||
if (*expression->lhs().data_type != int_type)
|
||||
{
|
||||
new_error = std::make_unique<type_mismatch>(expression->lhs().data_type,
|
||||
type_mismatch::operation::arithmetic, this->filename, expression->lhs().position());
|
||||
}
|
||||
if (*expression->rhs().data_type != int_type)
|
||||
{
|
||||
new_error = std::make_unique<type_mismatch>(expression->rhs().data_type,
|
||||
type_mismatch::operation::arithmetic, this->filename, expression->rhs().position());
|
||||
}
|
||||
if (new_error != nullptr)
|
||||
{
|
||||
m_errors.push_back(std::move(new_error));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case binary_operator::equals:
|
||||
case binary_operator::not_equals:
|
||||
if (expression->lhs().data_type != nullptr && expression->rhs().data_type != nullptr)
|
||||
{
|
||||
if (expression->lhs().data_type != expression->rhs().data_type)
|
||||
{
|
||||
auto new_error = std::make_unique<type_mismatch>(expression->rhs().data_type,
|
||||
type_mismatch::operation::comparison, this->filename, expression->rhs().position());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(call_statement *statement)
|
||||
{
|
||||
auto call_info = this->table->lookup(statement->name())->is_intrinsic_info();
|
||||
|
||||
std::size_t i{ 0 };
|
||||
for (const auto& argument : statement->arguments())
|
||||
{
|
||||
argument->accept(this);
|
||||
|
||||
if (argument->data_type != nullptr && i < call_info->type()->arguments.size()
|
||||
&& call_info->type()->arguments[i] != argument->data_type)
|
||||
{
|
||||
auto new_error = std::make_unique<type_mismatch>(argument->data_type,
|
||||
type_mismatch::operation::argument, this->filename, argument->position());
|
||||
m_errors.push_back(std::move(new_error));
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(constant_definition *definition)
|
||||
{
|
||||
definition->body().accept(this);
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(while_statement *statement)
|
||||
{
|
||||
statement->prerequisite().accept(this);
|
||||
auto condition_type = statement->prerequisite().data_type;
|
||||
|
||||
if (condition_type != nullptr && *condition_type != boolean_type)
|
||||
{
|
||||
auto new_error = std::make_unique<type_mismatch>(condition_type,
|
||||
type_mismatch::operation::condition, this->filename, statement->prerequisite().position());
|
||||
m_errors.push_back(std::move(new_error));
|
||||
}
|
||||
statement->body().accept(this);
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(if_statement *statement)
|
||||
{
|
||||
statement->prerequisite().accept(this);
|
||||
auto condition_type = statement->prerequisite().data_type;
|
||||
|
||||
if (condition_type != nullptr && *condition_type != boolean_type)
|
||||
{
|
||||
auto new_error = std::make_unique<type_mismatch>(condition_type,
|
||||
type_mismatch::operation::condition, this->filename, statement->prerequisite().position());
|
||||
m_errors.push_back(std::move(new_error));
|
||||
}
|
||||
statement->body().accept(this);
|
||||
}
|
||||
|
||||
void type_analysis_visitor::visit(assign_statement *statement)
|
||||
{
|
||||
statement->rvalue().accept(this);
|
||||
auto lvalue_info = this->table->lookup(statement->lvalue())->is_typed_info();
|
||||
|
||||
if (statement->rvalue().data_type != nullptr && lvalue_info->type() == statement->rvalue().data_type)
|
||||
{
|
||||
auto new_error = std::make_unique<type_mismatch>(statement->rvalue().data_type,
|
||||
type_mismatch::operation::assignment, this->filename, statement->position());
|
||||
m_errors.push_back(std::move(new_error));
|
||||
}
|
||||
}
|
||||
|
||||
const std::list<std::unique_ptr<error>>& type_analysis_visitor::errors() const noexcept
|
||||
{
|
||||
return m_errors;
|
||||
}
|
||||
}
|
||||
}
|
233
source/symbol_table.cc
Normal file
233
source/symbol_table.cc
Normal file
@ -0,0 +1,233 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License
|
||||
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
// obtain one at http://mozilla.org/MPL/2.0/.
|
||||
#include "elna/source/types.h"
|
||||
#include "elna/source/symbol_table.h"
|
||||
|
||||
namespace elna
|
||||
{
|
||||
namespace source
|
||||
{
|
||||
symbol_table::symbol_table(std::shared_ptr<symbol_table> scope)
|
||||
: outer_scope(scope)
|
||||
{
|
||||
}
|
||||
|
||||
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({ name, entry });
|
||||
}
|
||||
|
||||
std::shared_ptr<symbol_table> symbol_table::scope()
|
||||
{
|
||||
return this->outer_scope;
|
||||
}
|
||||
|
||||
info::~info()
|
||||
{
|
||||
}
|
||||
|
||||
type_info *info::is_type_info() noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
typed_info *info::is_typed_info() noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
constant_info *info::is_constant_info() noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
variable_info *info::is_variable_info() noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
parameter_info *info::is_parameter_info() noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
intrinsic_info *info::is_intrinsic_info() noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
procedure_info *info::is_procedure_info() noexcept
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
info::info()
|
||||
{
|
||||
}
|
||||
|
||||
type_info::type_info(std::shared_ptr<class type> type)
|
||||
: info(), m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
type_info::~type_info()
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<const class type> type_info::type() const noexcept
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
type_info *type_info::is_type_info() noexcept
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
typed_info::typed_info(std::shared_ptr<const class type> type)
|
||||
: m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
typed_info::~typed_info()
|
||||
{
|
||||
}
|
||||
|
||||
typed_info *typed_info::is_typed_info() noexcept
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
std::shared_ptr<const class type> typed_info::type() const noexcept
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
constant_info::constant_info(const std::shared_ptr<const class type> type, const std::int32_t value)
|
||||
: typed_info(type), m_value(value)
|
||||
{
|
||||
}
|
||||
|
||||
constant_info *constant_info::is_constant_info() noexcept
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
std::int32_t constant_info::value() const noexcept
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
variable_info::variable_info(std::shared_ptr<const class type> type)
|
||||
: typed_info(type)
|
||||
{
|
||||
}
|
||||
|
||||
variable_info *variable_info::is_variable_info() noexcept
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
parameter_info::parameter_info(std::shared_ptr<const class type> type)
|
||||
: typed_info(type)
|
||||
{
|
||||
}
|
||||
|
||||
parameter_info *parameter_info::is_parameter_info() noexcept
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
intrinsic_info::intrinsic_info(const class procedure_type& type)
|
||||
: m_type(std::make_shared<procedure_type>(type))
|
||||
{
|
||||
}
|
||||
|
||||
intrinsic_info::~intrinsic_info()
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<const class procedure_type> intrinsic_info::type() const noexcept
|
||||
{
|
||||
return m_type;
|
||||
}
|
||||
|
||||
std::size_t intrinsic_info::parameter_stack_size() const noexcept
|
||||
{
|
||||
return type()->arguments.size() * sizeof(std::int32_t);
|
||||
}
|
||||
|
||||
intrinsic_info *intrinsic_info::is_intrinsic_info() noexcept
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
procedure_info::procedure_info(const class procedure_type& type, std::shared_ptr<symbol_table> outer_scope)
|
||||
: intrinsic_info(type), 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;
|
||||
}
|
||||
|
||||
procedure_info *procedure_info::is_procedure_info() noexcept
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
constexpr std::size_t pointer_size = 4;
|
||||
|
||||
std::shared_ptr<source::symbol_table> add_builtin_symbols()
|
||||
{
|
||||
source::symbol_table result;
|
||||
std::vector<std::shared_ptr<const source::type>> intrinsic_arguments;
|
||||
|
||||
auto prim = boolean_type;
|
||||
auto boolean_info = std::make_shared<type_info>(std::make_shared<primitive_type>(boolean_type));
|
||||
auto int_info = std::make_shared<source::type_info>(std::make_shared<primitive_type>(int_type));
|
||||
result.enter("Boolean", boolean_info);
|
||||
result.enter("Int", int_info);
|
||||
|
||||
intrinsic_arguments.push_back(int_info->type());
|
||||
auto writei = std::make_shared<source::intrinsic_info>(
|
||||
source::procedure_type{ intrinsic_arguments, pointer_size });
|
||||
result.enter("writei", writei);
|
||||
intrinsic_arguments.clear();
|
||||
|
||||
intrinsic_arguments.push_back(boolean_info->type());
|
||||
auto writeb = std::make_shared<source::intrinsic_info>(
|
||||
source::procedure_type{ intrinsic_arguments, pointer_size });
|
||||
result.enter("writeb", writeb);
|
||||
intrinsic_arguments.clear();
|
||||
|
||||
return std::make_shared<source::symbol_table>(std::move(result));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user