Implement argument passing
This commit is contained in:
		
							
								
								
									
										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)); | ||||
|     } | ||||
| } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user