diff --git a/README b/README index b6e03eb..10ec738 100644 --- a/README +++ b/README @@ -1,37 +1,73 @@ # 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 -## Grammar PL/0 +## Current implementation -program = block "." ; +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. -block = [ "const" ident "=" number {"," ident "=" number} ";"] - [ "var" ident {"," ident} ";"] - { "procedure" ident ";" block ";" } statement ; +## Grammar -statement = [ ident ":=" expression | "call" ident - | "?" ident | "!" expression - | "begin" statement {";" statement } "end" - | "if" condition "then" statement - | "while" condition "do" statement ]; +digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"; + +integer = digit { digit }; +boolean = "true" | "false"; + +program = [ "type" type_definitions ";" ] + [ "const" ident "=" integer { "," ident "=" integer } ";"] + { procedure_definition } + [ "var" variable_declarations ";" ] + compound_statement "."; + +procedure_definition = "procedure" ident formal_parameter_list ";" block ";"; + +block = [ "const" ident "=" integer { "," ident "=" integer } ";" ] + [ "var" variable_declarations ";" ] + statement; + +statement = [ ident ":=" expression + | ident actual_parameter_list + | compound_statement + | "if" condition "then" statement + | "while" condition "do" statement ]; + +compound_statement = "begin" statement {";" statement } "end" ; condition = "odd" expression | - expression ("="|"#"|"<"|"<="|">"|">=") expression ; + expression ("="|"#"|"<"|"<="|">"|">=") expression; expression = [ "+"|"-"] term { ("+"|"-") term}; -term = factor {("*"|"/") factor}; +term = factor { ("*"|"/") factor }; -factor = ident | number | "(" expression ")"; +factor = ident | integer | "(" expression ")"; -## Operations +formal_parameter_list = "(" [ variable_declarations ] ")"; -"!" - Write a line. -"?" - Read user input. -"odd" - The only function, returns whether a number is odd. +actual_parameter_list = "(" [ expressions ] ")"; + +expressions = expression { "," expression }; + +variable_declarations = variable_declaration { ";" variable_declaration }; + +variable_declaration = ident ":" type_expression; + +type_expression = "array" integer "of" type_expression + | "^" type_expression + | "record" field_list "end" + | ident; + +field_list = field_declaration { ";" field_declaration }; + +field_declaration = ident ":" type_expression; diff --git a/example.elna b/example.elna index c07dead..d248d47 100644 --- a/example.elna +++ b/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(); diff --git a/gcc/Make-lang.in b/gcc/Make-lang.in index fdcebf1..e87da96 100644 --- a/gcc/Make-lang.in +++ b/gcc/Make-lang.in @@ -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) diff --git a/gcc/elna-generic.cc b/gcc/elna-generic.cc index 24c00db..cc7990d 100644 --- a/gcc/elna-generic.cc +++ b/gcc/elna-generic.cc @@ -15,9 +15,14 @@ namespace elna { namespace gcc { + generic_visitor::generic_visitor() + { + this->symbol_map = std::make_shared>(); + } + 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>(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) diff --git a/gcc/lang-specs.h b/gcc/lang-specs.h index 624fa37..2345566 100644 --- a/gcc/lang-specs.h +++ b/gcc/lang-specs.h @@ -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}, diff --git a/include/elna/gcc/elna-generic.h b/include/elna/gcc/elna-generic.h index 4b8c3c8..28a469d 100644 --- a/include/elna/gcc/elna-generic.h +++ b/include/elna/gcc/elna-generic.h @@ -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 symbol_map; + std::shared_ptr> 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; diff --git a/include/elna/source/ast.h b/include/elna/source/ast.h index e090df9..cbd7ede 100644 --- a/include/elna/source/ast.h +++ b/include/elna/source/ast.h @@ -8,7 +8,6 @@ #include #include #include "elna/source/result.h" -#include "elna/source/types.h" namespace elna { @@ -210,7 +209,6 @@ namespace source { public: std::shared_ptr place; - std::shared_ptr data_type; protected: /** @@ -596,19 +594,16 @@ namespace source class block : public node { - std::vector m_definitions; statement *m_body; public: std::vector value_definitions; - block(const struct position position, std::vector&& definitions, - std::vector&& value_definitions, + block(const struct position position, std::vector&& value_definitions, statement *body); virtual void accept(parser_visitor *visitor) override; statement& body(); - std::vector& definitions(); virtual ~block() override; }; @@ -616,9 +611,13 @@ namespace source class program : public block { public: - program(const struct position position, std::vector&& definitions, + std::vector type_definitions; + + program(const struct position position, std::vector&& type_definitions, std::vector&& value_definitions, statement *body); virtual void accept(parser_visitor *visitor) override; + + virtual ~program() override; }; template diff --git a/include/elna/source/result.h b/include/elna/source/result.h index ed49fb1..94789f4 100644 --- a/include/elna/source/result.h +++ b/include/elna/source/result.h @@ -4,7 +4,7 @@ #pragma once #include -#include "elna/source/types.h" +#include namespace elna { diff --git a/include/elna/source/symbol.h b/include/elna/source/symbol.h new file mode 100644 index 0000000..d05310f --- /dev/null +++ b/include/elna/source/symbol.h @@ -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 +#include +#include +#include + +namespace elna +{ +namespace source +{ + /** + * Generic language entity information. + */ + template + class info + { + public: + T payload; + + info(T payload) + : payload(payload) + { + } + }; + + template + std::shared_ptr> make_info(T payload) + { + return std::make_shared>(info(payload)); + } + + /** + * Symbol table. + */ + template + class symbol_table + { + public: + using symbol_ptr = std::shared_ptr>; + + private: + std::unordered_map entries; + std::shared_ptr outer_scope; + + public: + /** + * Constructs a new symbol with an optional outer scope. + * + * \param scope Outer scope. + */ + explicit symbol_table(std::shared_ptr 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 scope() + { + return this->outer_scope; + } + }; +} +} diff --git a/source/ast.cc b/source/ast.cc index 1f2cb35..50fca7e 100644 --- a/source/ast.cc +++ b/source/ast.cc @@ -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(program)); } @@ -447,11 +447,9 @@ namespace source delete m_body; } - block::block(const struct position position, std::vector&& definitions, - std::vector&& value_definitions, + block::block(const struct position position, std::vector&& 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& 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&& definitions, + program::program(const struct position position, + std::vector&& type_definitions, std::vector&& 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) { diff --git a/source/parser.yy b/source/parser.yy index 0276bb7..60de2dc 100644 --- a/source/parser.yy +++ b/source/parser.yy @@ -126,8 +126,10 @@ program: { *value_definition++ = variable; } - driver.tree = std::make_unique(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 @@ -285,7 +287,7 @@ expressions: } | expression { $$.emplace_back(std::move($1)); } designator_expression: - designator_expression LEFT_SQUARE expression RIGHT_SQUARE + designator_expression LEFT_SQUARE expression RIGHT_SQUARE { $$ = new elna::source::array_access_expression(elna::source::make_position(@1), $1, $3); }