Move type definitions to the program node
This commit is contained in:
		
							
								
								
									
										24
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								README
									
									
									
									
									
								
							| @@ -1,15 +1,26 @@ | ||||
| # Elna programming language | ||||
|  | ||||
| Elna compiles simple mathematical operations to machine code. | ||||
| The compiled program returns the result of the operation. | ||||
| Elna is a simple, imperative, low-level programming language. | ||||
|  | ||||
| It is intendet to accompany other languages in the areas, where a high-level | ||||
| language doesn't fit well. It is also supposed to be an intermediate | ||||
| representation for a such high-level hypothetical programming language. | ||||
|  | ||||
| ## File extension | ||||
|  | ||||
| .elna | ||||
|  | ||||
| ## Current implementation | ||||
|  | ||||
| This repository contains a GCC frontend for Elna. After finishing the frontend | ||||
| I'm planning to rewrite the compiler in Elna itself with its own backend and | ||||
| a hand-written parser. So GCC gives a way to have a simple bootstrap compiler | ||||
| and a possbility to compile Elna programs for different platforms. | ||||
|  | ||||
| ## Grammar PL/0 | ||||
|  | ||||
| program = block "." ; | ||||
| program = [ "type" type_definitions ";" ] | ||||
|     block "." ; | ||||
|  | ||||
| block = [ "const" ident "=" number {"," ident "=" number} ";"] | ||||
|         [ "var" ident {"," ident} ";"] | ||||
| @@ -30,6 +41,13 @@ term = factor {("*"|"/") factor}; | ||||
|  | ||||
| factor = ident | number | "(" expression ")"; | ||||
|  | ||||
| type_expression = "array" number "of" type_expression | ||||
|                 | "^" type_expression | ||||
|                 | "record" field_list "end" | ||||
|                 | ident | ||||
|  | ||||
| field_list = ident ":" type_expression { ";" field_list } | ||||
|  | ||||
| ## Operations | ||||
|  | ||||
| "!" - Write a line. | ||||
|   | ||||
							
								
								
									
										22
									
								
								example.elna
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								example.elna
									
									
									
									
									
								
							| @@ -15,7 +15,7 @@ begin | ||||
| end; | ||||
|  | ||||
| proc test_array(); | ||||
| var a: T, x_3: Int; | ||||
| var a: T, x: Int; | ||||
| begin | ||||
|   a[0] := 2; | ||||
|   a[1] := 5; | ||||
| @@ -23,19 +23,19 @@ begin | ||||
|   writei(""); | ||||
|   writei("Test array:"); | ||||
|  | ||||
|   x_3 := 0; | ||||
|   while x_3 < 2 do | ||||
|   x := 0; | ||||
|   while x < 2 do | ||||
|   begin | ||||
|     writei(a[x_3]); | ||||
| 	x_3 := x_3 + 1 | ||||
|     writei(a[x]); | ||||
| 	x := x + 1 | ||||
|   end | ||||
| end; | ||||
|  | ||||
| proc test_pointer(); | ||||
| var x_2: Int, p: ^Int; | ||||
| var x: Int, p: ^Int; | ||||
| begin | ||||
|   x_2 := 5; | ||||
|   p := @x_2; | ||||
|   x := 5; | ||||
|   p := @x; | ||||
|  | ||||
|   writei(""); | ||||
|   writei("Test pointer:"); | ||||
| @@ -69,13 +69,13 @@ end; | ||||
|  | ||||
| proc test_const(); | ||||
| const t = 5; | ||||
| var x_1: Int; | ||||
| var x: Int; | ||||
| begin | ||||
|   x_1 := t; | ||||
|   x := t; | ||||
|  | ||||
|   writei(""); | ||||
|   writei("Test const:"); | ||||
|   writei(x_1) | ||||
|   writei(x) | ||||
| end; | ||||
|  | ||||
| proc test_if(); | ||||
|   | ||||
| @@ -30,7 +30,6 @@ elna_OBJS = \ | ||||
| 	elna/lexer.o \ | ||||
| 	elna/parser.o \ | ||||
| 	elna/result.o \ | ||||
| 	elna/types.o \ | ||||
| 	$(END) | ||||
|  | ||||
| elna1$(exeext): attribs.o $(elna_OBJS) $(BACKEND) $(LIBDEPS) | ||||
|   | ||||
| @@ -15,9 +15,14 @@ namespace elna | ||||
| { | ||||
| namespace gcc | ||||
| { | ||||
|     generic_visitor::generic_visitor() | ||||
|     { | ||||
|         this->symbol_map = std::make_shared<source::symbol_table<tree>>(); | ||||
|     } | ||||
|  | ||||
|     void generic_visitor::visit(source::call_statement *statement) | ||||
|     { | ||||
|         auto symbol = this->symbol_map.find(statement->name()); | ||||
|         auto symbol = this->symbol_map->lookup(statement->name()); | ||||
|  | ||||
|         if (statement->name() == "writei") | ||||
|         { | ||||
| @@ -80,10 +85,10 @@ namespace gcc | ||||
|             append_to_statement_list(stmt, &this->current_statements); | ||||
|             this->current_expression = NULL_TREE; | ||||
|         } | ||||
|         else if (symbol != this->symbol_map.end()) | ||||
|         else if (symbol) | ||||
|         { | ||||
|             tree fndecl_type = build_function_type_list(integer_type_node, NULL_TREE); | ||||
|             tree printf_fn = build1(ADDR_EXPR, build_pointer_type(fndecl_type), symbol->second); | ||||
|             tree printf_fn = build1(ADDR_EXPR, build_pointer_type(fndecl_type), symbol->payload); | ||||
|  | ||||
|             tree stmt = build_call_nary(integer_type_node, printf_fn, 0); | ||||
|  | ||||
| @@ -100,7 +105,7 @@ namespace gcc | ||||
|  | ||||
|     void generic_visitor::visit(source::program *program) | ||||
|     { | ||||
|         for (const auto& constant : program->definitions()) | ||||
|         for (const auto& constant : program->type_definitions) | ||||
|         { | ||||
|             constant->accept(this); | ||||
|         } | ||||
| @@ -153,7 +158,7 @@ namespace gcc | ||||
|                 definition->parameters().size(), parameter_types); | ||||
|         this->main_fndecl = build_fn_decl(definition->identifier().c_str(), declaration_type); | ||||
|  | ||||
|         this->symbol_map.insert({ definition->identifier(), this->main_fndecl }); | ||||
|         this->symbol_map->enter(definition->identifier(), source::make_info(this->main_fndecl)); | ||||
|  | ||||
|         tree resdecl = build_decl(UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, integer_type_node); | ||||
|         DECL_CONTEXT(resdecl) = this->main_fndecl; | ||||
| @@ -186,6 +191,7 @@ namespace gcc | ||||
|     { | ||||
|         this->current_statements = alloc_stmt_list(); | ||||
|         this->variable_chain = tree_chain(); | ||||
|         this->symbol_map = std::make_shared<source::symbol_table<tree>>(this->symbol_map); | ||||
|     } | ||||
|  | ||||
|     tree_symbol_mapping generic_visitor::leave_scope() | ||||
| @@ -194,6 +200,7 @@ namespace gcc | ||||
|                 NULL_TREE, NULL_TREE, NULL_TREE); | ||||
|         tree bind_expr = build3(BIND_EXPR, void_type_node, variable_chain.head(), | ||||
|                 this->current_statements, new_block); | ||||
|         this->symbol_map = this->symbol_map->scope(); | ||||
|  | ||||
|         return tree_symbol_mapping{ bind_expr, new_block }; | ||||
|     } | ||||
| @@ -344,9 +351,9 @@ namespace gcc | ||||
|         location_t definition_location = get_location(&definition->position()); | ||||
|         tree definition_tree = build_decl(definition_location, CONST_DECL, | ||||
|                 get_identifier(definition->identifier().c_str()), integer_type_node); | ||||
|         auto result = this->symbol_map.insert({ definition->identifier(), definition_tree }); | ||||
|         auto result = this->symbol_map->enter(definition->identifier(), source::make_info(definition_tree)); | ||||
|  | ||||
|         if (result.second) | ||||
|         if (result) | ||||
|         { | ||||
|             definition->body().accept(this); | ||||
|  | ||||
| @@ -378,9 +385,9 @@ namespace gcc | ||||
|         location_t definition_location = get_location(&definition->position()); | ||||
|         tree definition_tree = build_decl(definition_location, TYPE_DECL, | ||||
|                 get_identifier(definition->identifier().c_str()), tree_type); | ||||
|         auto result = this->symbol_map.insert({ definition->identifier(), definition_tree }); | ||||
|         auto result = this->symbol_map->enter(definition->identifier(), source::make_info(definition_tree)); | ||||
|  | ||||
|         if (result.second) | ||||
|         if (result) | ||||
|         { | ||||
|             DECL_CONTEXT(definition_tree) = this->main_fndecl; | ||||
|             variable_chain.append(definition_tree); | ||||
| @@ -421,11 +428,11 @@ namespace gcc | ||||
|             { | ||||
|                 return elna_string_type_node; | ||||
|             } | ||||
|             auto symbol = this->symbol_map.find(basic_type->base_name()); | ||||
|             auto symbol = this->symbol_map->lookup(basic_type->base_name()); | ||||
|  | ||||
|             if (symbol != this->symbol_map.end()) | ||||
|             if (symbol) | ||||
|             { | ||||
|                 return TREE_TYPE(symbol->second); | ||||
|                 return TREE_TYPE(symbol->payload); | ||||
|             } | ||||
|             error_at(get_location(&basic_type->position()), | ||||
|                     "type '%s' not declared", basic_type->base_name().c_str()); | ||||
| @@ -499,9 +506,9 @@ namespace gcc | ||||
|         auto declaration_location = get_location(&declaration->position()); | ||||
|         tree declaration_tree = build_decl(declaration_location, VAR_DECL, | ||||
|                 get_identifier(declaration->identifier().c_str()), declaration_type); | ||||
|         auto result = this->symbol_map.insert({ declaration->identifier(), declaration_tree }); | ||||
|         auto result = this->symbol_map->enter(declaration->identifier(), source::make_info(declaration_tree)); | ||||
|  | ||||
|         if (result.second) | ||||
|         if (result) | ||||
|         { | ||||
|             DECL_CONTEXT(declaration_tree) = this->main_fndecl; | ||||
|             variable_chain.append(declaration_tree); | ||||
| @@ -520,9 +527,9 @@ namespace gcc | ||||
|  | ||||
|     void generic_visitor::visit(source::variable_expression *expression) | ||||
|     { | ||||
|         auto symbol = this->symbol_map.find(expression->name()); | ||||
|         auto symbol = this->symbol_map->lookup(expression->name()); | ||||
|  | ||||
|         if (symbol == this->symbol_map.end()) | ||||
|         if (!symbol) | ||||
|         { | ||||
|             error_at(get_location(&expression->position()), | ||||
|                     "variable '%s' not declared in the current scope", | ||||
| @@ -530,7 +537,7 @@ namespace gcc | ||||
|             this->current_expression = error_mark_node; | ||||
|             return; | ||||
|         } | ||||
|         this->current_expression = symbol->second; | ||||
|         this->current_expression = symbol->payload; | ||||
|     } | ||||
|  | ||||
|     void generic_visitor::visit(source::array_access_expression *expression) | ||||
|   | ||||
| @@ -1,3 +1,6 @@ | ||||
| /* gcc-src/gcc/config/lang-specs.in */ | ||||
| {".elna",  "@elna", 0, 1, 0}, | ||||
| {"@elna",  "elna1 %i %(cc1_options) %{!fsyntax-only:%(invoke_as)}", 0, 1, 0}, | ||||
| /* gcc/gcc.cc */ | ||||
| {".elna",  "@elna", nullptr, 0, 0}, | ||||
| {"@elna", | ||||
|     "elna1 %{!Q:-quiet} \ | ||||
|     %i %{!fsyntax-only:%(invoke_as)}", | ||||
|     nullptr, 0, 0}, | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "elna/source/ast.h" | ||||
| #include "elna/source/symbol.h" | ||||
| #include "elna/gcc/elna-tree.h" | ||||
|  | ||||
| #include "config.h" | ||||
| @@ -20,7 +21,7 @@ namespace gcc | ||||
|     { | ||||
|         tree current_statements{ NULL_TREE }; | ||||
|         tree current_expression{ NULL_TREE }; | ||||
|         std::unordered_map<std::string, tree> symbol_map; | ||||
|         std::shared_ptr<source::symbol_table<tree>> symbol_map; | ||||
|         tree main_fndecl{ NULL_TREE }; | ||||
|         tree_chain variable_chain; | ||||
|  | ||||
| @@ -31,6 +32,8 @@ namespace gcc | ||||
|         tree_symbol_mapping leave_scope(); | ||||
|  | ||||
|     public: | ||||
|         generic_visitor(); | ||||
|  | ||||
|         void visit(source::program *program) override; | ||||
|         void visit(source::procedure_definition *definition) override; | ||||
|         void visit(source::call_statement *statement) override; | ||||
|   | ||||
| @@ -8,7 +8,6 @@ | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include "elna/source/result.h" | ||||
| #include "elna/source/types.h" | ||||
|  | ||||
| namespace elna | ||||
| { | ||||
| @@ -210,7 +209,6 @@ namespace source | ||||
|     { | ||||
|     public: | ||||
|         std::shared_ptr<operand> place; | ||||
|         std::shared_ptr<const type> data_type; | ||||
|  | ||||
|     protected: | ||||
|         /** | ||||
| @@ -596,19 +594,16 @@ namespace source | ||||
|  | ||||
|     class block : public node | ||||
|     { | ||||
|         std::vector<definition *> m_definitions; | ||||
|         statement *m_body; | ||||
|  | ||||
|     public: | ||||
|         std::vector<definition *> value_definitions; | ||||
|  | ||||
|         block(const struct position position, std::vector<definition *>&& definitions, | ||||
|                 std::vector<definition *>&& value_definitions, | ||||
|         block(const struct position position, std::vector<definition *>&& value_definitions, | ||||
|                 statement *body); | ||||
|         virtual void accept(parser_visitor *visitor) override; | ||||
|  | ||||
|         statement& body(); | ||||
|         std::vector<definition *>& definitions(); | ||||
|  | ||||
|         virtual ~block() override; | ||||
|     }; | ||||
| @@ -616,9 +611,13 @@ namespace source | ||||
|     class program : public block | ||||
|     { | ||||
|     public: | ||||
|         program(const struct position position, std::vector<definition *>&& definitions, | ||||
|         std::vector<definition *> type_definitions; | ||||
|  | ||||
|         program(const struct position position, std::vector<definition *>&& type_definitions, | ||||
|                 std::vector<definition *>&& value_definitions, statement *body); | ||||
|         virtual void accept(parser_visitor *visitor) override; | ||||
|  | ||||
|         virtual ~program() override; | ||||
|     }; | ||||
|  | ||||
|     template<typename T> | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <cstddef> | ||||
| #include "elna/source/types.h" | ||||
| #include <string> | ||||
|  | ||||
| namespace elna | ||||
| { | ||||
|   | ||||
							
								
								
									
										106
									
								
								include/elna/source/symbol.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								include/elna/source/symbol.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| // 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 | ||||
| { | ||||
|     /** | ||||
|      * Generic language entity information. | ||||
|      */ | ||||
|     template<typename T> | ||||
|     class info | ||||
|     { | ||||
|     public: | ||||
|         T payload; | ||||
|  | ||||
|         info(T payload) | ||||
|             : payload(payload) | ||||
|         { | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     template<typename T> | ||||
|     std::shared_ptr<info<T>> make_info(T payload) | ||||
|     { | ||||
|         return std::make_shared<info<T>>(info(payload)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Symbol table. | ||||
|      */ | ||||
|     template<typename T> | ||||
|     class symbol_table | ||||
|     { | ||||
|     public: | ||||
|         using symbol_ptr = std::shared_ptr<info<T>>; | ||||
|  | ||||
|     private: | ||||
|         std::unordered_map<std::string, symbol_ptr> 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) | ||||
|             : outer_scope(scope) | ||||
|         { | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * 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. | ||||
|          */ | ||||
|         symbol_ptr 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; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Registers new symbol. | ||||
|          * | ||||
|          * \param name Symbol name. | ||||
|          * \param entry Symbol information. | ||||
|          * | ||||
|          * \return Whether the insertion took place. | ||||
|          */ | ||||
|         bool enter(const std::string& name, symbol_ptr entry) | ||||
|         { | ||||
|             return entries.insert({ name, entry }).second; | ||||
|         } | ||||
|  | ||||
|         /** | ||||
|          * Returns the outer scope or nullptr if the this is the global scope. | ||||
|          * | ||||
|          * \return Outer scope. | ||||
|          */ | ||||
|         std::shared_ptr<symbol_table> scope() | ||||
|         { | ||||
|             return this->outer_scope; | ||||
|         } | ||||
|     }; | ||||
| } | ||||
| } | ||||
| @@ -65,10 +65,6 @@ namespace source | ||||
|  | ||||
|     void empty_visitor::visit(block *block) | ||||
|     { | ||||
|         for (const auto& constant : block->definitions()) | ||||
|         { | ||||
|             constant->accept(this); | ||||
|         } | ||||
|         for (const auto& definition : block->value_definitions) | ||||
|         { | ||||
|             definition->accept(this); | ||||
| @@ -78,6 +74,10 @@ namespace source | ||||
|  | ||||
|     void empty_visitor::visit(program *program) | ||||
|     { | ||||
|         for (auto definition : program->type_definitions) | ||||
|         { | ||||
|             definition->accept(this); | ||||
|         } | ||||
|         visit(reinterpret_cast<block *>(program)); | ||||
|     } | ||||
|  | ||||
| @@ -447,11 +447,9 @@ namespace source | ||||
|         delete m_body; | ||||
|     } | ||||
|  | ||||
|     block::block(const struct position position, std::vector<definition *>&& definitions, | ||||
|             std::vector<definition *>&& value_definitions, | ||||
|     block::block(const struct position position, std::vector<definition *>&& value_definitions, | ||||
|             statement *body) | ||||
|         : node(position), m_definitions(std::move(definitions)), | ||||
|         m_body(std::move(body)), value_definitions(std::move(value_definitions)) | ||||
|         : node(position), m_body(std::move(body)), value_definitions(std::move(value_definitions)) | ||||
|     { | ||||
|     } | ||||
|  | ||||
| @@ -460,11 +458,6 @@ namespace source | ||||
|         visitor->visit(this); | ||||
|     } | ||||
|  | ||||
|     std::vector<definition *>& block::definitions() | ||||
|     { | ||||
|         return m_definitions; | ||||
|     } | ||||
|  | ||||
|     statement& block::body() | ||||
|     { | ||||
|         return *m_body; | ||||
| @@ -472,10 +465,6 @@ namespace source | ||||
|  | ||||
|     block::~block() | ||||
|     { | ||||
|         for (auto definition : m_definitions) | ||||
|         { | ||||
|             delete definition; | ||||
|         } | ||||
|         for (auto definition : value_definitions) | ||||
|         { | ||||
|             delete definition; | ||||
| @@ -483,9 +472,11 @@ namespace source | ||||
|         delete m_body; | ||||
|     } | ||||
|  | ||||
|     program::program(const struct position position, std::vector<definition *>&& definitions, | ||||
|     program::program(const struct position position, | ||||
|             std::vector<definition *>&& type_definitions, | ||||
|             std::vector<definition *>&& value_definitions, statement *body) | ||||
|         : block(position, std::move(definitions), std::move(value_definitions), body) | ||||
|         : block(position, std::move(value_definitions), body), | ||||
|         type_definitions(std::move(type_definitions)) | ||||
|     { | ||||
|     } | ||||
|  | ||||
| @@ -494,6 +485,14 @@ namespace source | ||||
|         visitor->visit(this); | ||||
|     } | ||||
|  | ||||
|     program::~program() | ||||
|     { | ||||
|         for (auto definition : type_definitions) | ||||
|         { | ||||
|             delete definition; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     char_literal::char_literal(const struct position position, const unsigned char value) | ||||
|         : expression(position), m_character(value) | ||||
|     { | ||||
|   | ||||
| @@ -126,8 +126,10 @@ program: | ||||
|             { | ||||
|                 *value_definition++ = variable; | ||||
|             } | ||||
|             driver.tree = std::make_unique<elna::source::program>(elna::source::position{}, | ||||
|             auto tree = new elna::source::program(elna::source::position{}, | ||||
|                  std::move(definitions), std::move(value_definitions), std::move($5)); | ||||
|  | ||||
|             driver.tree.reset(tree); | ||||
|         } | ||||
| block: constant_part variable_part statement | ||||
|         { | ||||
| @@ -143,7 +145,7 @@ block: constant_part variable_part statement | ||||
|                 *definition++ = variable; | ||||
|             } | ||||
|             $$ = new elna::source::block(elna::source::position{}, | ||||
|                  {}, std::move(definitions), std::move($3)); | ||||
|                  std::move(definitions), std::move($3)); | ||||
|         }; | ||||
| procedure_definition: | ||||
|     PROCEDURE IDENTIFIER formal_parameter_list SEMICOLON block SEMICOLON | ||||
|   | ||||
		Reference in New Issue
	
	Block a user