Support variable declaration initializer
This commit is contained in:
		| @@ -408,9 +408,10 @@ namespace elna::boot | ||||
|         return this; | ||||
|     } | ||||
|  | ||||
|     variable_declaration::variable_declaration(const struct position position, identifier_definition identifier, | ||||
|             std::shared_ptr<type_expression> variable_type) | ||||
|         : declaration(position, identifier), m_variable_type(variable_type) | ||||
|     variable_declaration::variable_declaration(const struct position position, | ||||
|             std::vector<identifier_definition>&& identifier, std::shared_ptr<type_expression> variable_type, | ||||
|             expression *body) | ||||
|         : node(position), m_variable_type(variable_type), identifiers(std::move(identifier)), body(body) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -53,7 +53,7 @@ namespace elna::boot | ||||
|  | ||||
|         if (!declaration_visitor.errors().empty()) | ||||
|         { | ||||
|             std::swap(outcome.errors(), parse_driver.errors()); | ||||
|             std::swap(outcome.errors(), declaration_visitor.errors()); | ||||
|         } | ||||
|         outcome.unresolved = declaration_visitor.unresolved; | ||||
|  | ||||
|   | ||||
| @@ -135,7 +135,8 @@ along with GCC; see the file COPYING3.  If not see | ||||
| %type <std::vector<elna::boot::switch_case>> switch_cases; | ||||
| %type <elna::boot::constant_declaration *> constant_declaration; | ||||
| %type <std::vector<elna::boot::constant_declaration *>> constant_part constant_declarations; | ||||
| %type <std::vector<elna::boot::variable_declaration *>> variable_declarations variable_part variable_declaration; | ||||
| %type <elna::boot::variable_declaration *> variable_declaration; | ||||
| %type <std::vector<elna::boot::variable_declaration *>> variable_declarations variable_part; | ||||
| %type <elna::boot::type_expression *> type_expression; | ||||
| %type <std::vector<elna::boot::type_expression *>> type_expressions; | ||||
| %type <elna::boot::traits_expression *> traits_expression; | ||||
| @@ -501,24 +502,23 @@ identifiers: | ||||
|             $$.emplace($$.cbegin(), std::move($1)); | ||||
|         } | ||||
|     | IDENTIFIER { $$.emplace_back(std::move($1)); } | ||||
| variable_declaration: identifier_definitions ":" type_expression ";" | ||||
| variable_declaration: | ||||
|     identifier_definitions ":" type_expression ";" | ||||
|         { | ||||
|             std::shared_ptr<boot::type_expression> shared_type{ $3 }; | ||||
|  | ||||
|             for (boot::identifier_definition& identifier : $1) | ||||
|             { | ||||
|                 boot::variable_declaration *declaration = new boot::variable_declaration( | ||||
|                         boot::make_position(@2), std::move(identifier), shared_type); | ||||
|                 $$.push_back(declaration); | ||||
|             } | ||||
|             $$ = new boot::variable_declaration( boot::make_position(@2), std::move($1), shared_type); | ||||
|         } | ||||
|     | identifier_definitions ":" type_expression ":=" expression ";" | ||||
|         { | ||||
|             std::shared_ptr<boot::type_expression> shared_type{ $3 }; | ||||
|             $$ = new boot::variable_declaration( boot::make_position(@2), std::move($1), shared_type, $5); | ||||
|         } | ||||
| variable_declarations: | ||||
|     /* no variable declarations */ {} | ||||
|     | variable_declaration variable_declarations | ||||
|         { | ||||
|             std::swap($$, $1); | ||||
|             $$.reserve($$.size() + $2.size()); | ||||
|             $$.insert(std::end($$), std::begin($2), std::end($2)); | ||||
|             std::swap($$, $2); | ||||
|             $$.insert(std::cbegin($$), $1); | ||||
|         } | ||||
| variable_part: | ||||
|     /* no variable declarations */ {} | ||||
| @@ -531,7 +531,7 @@ constant_declarations: | ||||
|     constant_declaration constant_declarations | ||||
|         { | ||||
|             std::swap($$, $2); | ||||
|             $$.insert($$.cbegin(), $1); | ||||
|             $$.insert(std::cbegin($$), $1); | ||||
|         } | ||||
|     | /* no constant definitions */ {} | ||||
| constant_part: | ||||
|   | ||||
| @@ -83,6 +83,16 @@ namespace elna::boot | ||||
|         return "Procedure '" + identifier + "' is expected to return, but does not have a return statement"; | ||||
|     } | ||||
|  | ||||
|     variable_initializer_error::variable_initializer_error(const char *path, const struct position position) | ||||
|         : error(path, position) | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     std::string variable_initializer_error::what() const | ||||
|     { | ||||
|         return "Only one variable can be initialized"; | ||||
|     } | ||||
|  | ||||
|     type_analysis_visitor::type_analysis_visitor(const char *path) | ||||
|         : error_container(path) | ||||
|     { | ||||
| @@ -289,7 +299,10 @@ namespace elna::boot | ||||
|     { | ||||
|         declaration->variable_type().accept(this); | ||||
|  | ||||
|         this->bag.enter(declaration->identifier.identifier, std::make_shared<variable_info>(this->current_type)); | ||||
|         for (const auto& variable_identifier : declaration->identifiers) | ||||
|         { | ||||
|             this->bag.enter(variable_identifier.identifier, std::make_shared<variable_info>(this->current_type)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void name_analysis_visitor::visit(constant_declaration *definition) | ||||
| @@ -584,5 +597,33 @@ namespace elna::boot | ||||
|                 add_error<already_declared_error>(type->identifier.identifier, this->input_file, type->position()); | ||||
|             } | ||||
|         } | ||||
|         for (variable_declaration *const variable : unit->variables) | ||||
|         { | ||||
|             variable->accept(this); | ||||
|         } | ||||
|         for (procedure_declaration *const procedure : unit->procedures) | ||||
|         { | ||||
|             procedure->accept(this); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void declaration_visitor::visit(variable_declaration *declaration) | ||||
|     { | ||||
|         if (declaration->body != nullptr && declaration->identifiers.size() > 1) | ||||
|         { | ||||
|             add_error<variable_initializer_error>(this->input_file, declaration->position()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void declaration_visitor::visit(procedure_declaration *definition) | ||||
|     { | ||||
|         if (!definition->body.has_value()) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|         for (boot::variable_declaration *const variable : definition->body.value().variables()) | ||||
|         { | ||||
|             variable->accept(this); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -745,40 +745,48 @@ namespace elna::gcc | ||||
|  | ||||
|     void generic_visitor::visit(boot::variable_declaration *declaration) | ||||
|     { | ||||
|         this->current_expression = get_inner_alias( | ||||
|                 this->bag.lookup(declaration->identifier.identifier)->is_variable()->symbol, | ||||
|                 this->symbols); | ||||
|         for (const auto& variable_identifier : declaration->identifiers) | ||||
|         { | ||||
|             this->current_expression = get_inner_alias( | ||||
|                     this->bag.lookup(variable_identifier.identifier)->is_variable()->symbol, | ||||
|                     this->symbols); | ||||
|  | ||||
|         location_t declaration_location = get_location(&declaration->position()); | ||||
|         tree declaration_tree = build_decl(declaration_location, VAR_DECL, | ||||
|                 get_identifier(declaration->identifier.identifier.c_str()), this->current_expression); | ||||
|         bool result = this->symbols->enter(declaration->identifier.identifier, declaration_tree); | ||||
|             location_t declaration_location = get_location(&declaration->position()); | ||||
|             tree declaration_tree = build_decl(declaration_location, VAR_DECL, | ||||
|                     get_identifier(variable_identifier.identifier.c_str()), this->current_expression); | ||||
|             bool result = this->symbols->enter(variable_identifier.identifier, declaration_tree); | ||||
|  | ||||
|         if (POINTER_TYPE_P(this->current_expression)) | ||||
|         { | ||||
|             DECL_INITIAL(declaration_tree) = elna_pointer_nil_node; | ||||
|         } | ||||
|         TREE_PUBLIC(declaration_tree) = declaration->identifier.exported; | ||||
|         this->current_expression = NULL_TREE; | ||||
|         if (!result) | ||||
|         { | ||||
|             error_at(declaration_location, "Variable '%s' already declared in this scope", | ||||
|                     declaration->identifier.identifier.c_str()); | ||||
|         } | ||||
|         else if (lang_hooks.decls.global_bindings_p()) | ||||
|         { | ||||
|             TREE_STATIC(declaration_tree) = 1; | ||||
|             varpool_node::get_create(declaration_tree); | ||||
|             varpool_node::finalize_decl(declaration_tree); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             DECL_CONTEXT(declaration_tree) = current_function_decl; | ||||
|             f_names = chainon(f_names, declaration_tree); | ||||
|             if (declaration->body != nullptr) | ||||
|             { | ||||
|                 declaration->body->accept(this); | ||||
|                 DECL_INITIAL(declaration_tree) = this->current_expression; | ||||
|             } | ||||
|             else if (POINTER_TYPE_P(this->current_expression)) | ||||
|             { | ||||
|                 DECL_INITIAL(declaration_tree) = elna_pointer_nil_node; | ||||
|             } | ||||
|             TREE_PUBLIC(declaration_tree) = variable_identifier.exported; | ||||
|             this->current_expression = NULL_TREE; | ||||
|             if (!result) | ||||
|             { | ||||
|                 error_at(declaration_location, "Variable '%s' already declared in this scope", | ||||
|                         variable_identifier.identifier.c_str()); | ||||
|             } | ||||
|             else if (lang_hooks.decls.global_bindings_p()) | ||||
|             { | ||||
|                 TREE_STATIC(declaration_tree) = 1; | ||||
|                 varpool_node::get_create(declaration_tree); | ||||
|                 varpool_node::finalize_decl(declaration_tree); | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 DECL_CONTEXT(declaration_tree) = current_function_decl; | ||||
|                 f_names = chainon(f_names, declaration_tree); | ||||
|  | ||||
|             auto declaration_statement = build1_loc(declaration_location, DECL_EXPR, | ||||
|                     void_type_node, declaration_tree); | ||||
|             append_statement(declaration_statement); | ||||
|                 auto declaration_statement = build1_loc(declaration_location, DECL_EXPR, | ||||
|                         void_type_node, declaration_tree); | ||||
|                 append_statement(declaration_statement); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -340,17 +340,20 @@ namespace elna::boot | ||||
|     /** | ||||
|      * Variable declaration. | ||||
|      */ | ||||
|     class variable_declaration : public declaration | ||||
|     class variable_declaration : public node | ||||
|     { | ||||
|         std::shared_ptr<type_expression> m_variable_type; | ||||
|  | ||||
|     public: | ||||
|         variable_declaration(const struct position position, identifier_definition identifier, | ||||
|                 std::shared_ptr<type_expression> variable_type); | ||||
|         variable_declaration(const struct position position, | ||||
|                 std::vector<identifier_definition>&& identifier, std::shared_ptr<type_expression> variable_type, | ||||
|                 expression *body = nullptr); | ||||
|  | ||||
|         void accept(parser_visitor *visitor) override; | ||||
|  | ||||
|         const std::vector<identifier_definition> identifiers; | ||||
|         type_expression& variable_type(); | ||||
|         expression *const body; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -79,6 +79,14 @@ namespace elna::boot | ||||
|         std::string what() const override; | ||||
|     }; | ||||
|  | ||||
|     class variable_initializer_error : public error | ||||
|     { | ||||
|     public: | ||||
|         variable_initializer_error(const char *path, const struct position position); | ||||
|  | ||||
|         std::string what() const override; | ||||
|     }; | ||||
|  | ||||
|     /** | ||||
|      * Checks types. | ||||
|      */ | ||||
| @@ -174,5 +182,7 @@ namespace elna::boot | ||||
|         void visit(program *program) override; | ||||
|         void visit(import_declaration *) override; | ||||
|         void visit(unit *unit) override; | ||||
|         void visit(variable_declaration *declaration) override; | ||||
|         void visit(procedure_declaration *definition) override; | ||||
|     }; | ||||
| } | ||||
|   | ||||
| @@ -183,44 +183,45 @@ proc lexer_escape(escape: Char, result: ^Char) -> Bool; | ||||
| var | ||||
|   successful: Bool; | ||||
| begin | ||||
|   if escape = 'n' then | ||||
|     result^ := '\n'; | ||||
|     successful := true | ||||
|   elsif escape = 'a' then | ||||
|     result^ := '\a'; | ||||
|     successful := true | ||||
|   elsif escape = 'b' then | ||||
|     result^ := '\b'; | ||||
|     successful := true | ||||
|   elsif escape = 't' then | ||||
|     result^ := '\t'; | ||||
|     successful := true | ||||
|   elsif escape = 'f' then | ||||
|     result^ := '\f'; | ||||
|     successful := true | ||||
|   elsif escape = 'r' then | ||||
|     result^ := '\r'; | ||||
|     successful := true | ||||
|   elsif escape = 'v' then | ||||
|     result^ := '\v'; | ||||
|     successful := true | ||||
|   elsif escape = '\\' then | ||||
|     result^ := '\\'; | ||||
|     successful := true | ||||
|   elsif escape = '\'' then | ||||
|     result^ := '\''; | ||||
|     successful := true | ||||
|   elsif escape = '"' then | ||||
|     result^ := '"'; | ||||
|     successful := true | ||||
|   elsif escape = '?' then | ||||
|     result^ := '\?'; | ||||
|     successful := true | ||||
|   elsif escape = '0' then | ||||
|     result^ := '\0'; | ||||
|     successful := true | ||||
|   else | ||||
|     successful := false | ||||
|   case escape of | ||||
|     'n': | ||||
|       result^ := '\n'; | ||||
|       successful := true | ||||
|     | 'a': | ||||
|       result^ := '\a'; | ||||
|       successful := true | ||||
|     | 'b': | ||||
|       result^ := '\b'; | ||||
|       successful := true | ||||
|     | 't': | ||||
|       result^ := '\t'; | ||||
|       successful := true | ||||
|     | 'f': | ||||
|       result^ := '\f'; | ||||
|       successful := true | ||||
|     | 'r': | ||||
|       result^ := '\r'; | ||||
|       successful := true | ||||
|     | 'v': | ||||
|       result^ := '\v'; | ||||
|       successful := true | ||||
|     | '\\': | ||||
|       result^ := '\\'; | ||||
|       successful := true | ||||
|     | '\'': | ||||
|       result^ := '\''; | ||||
|       successful := true | ||||
|     | '"': | ||||
|       result^ := '"'; | ||||
|       successful := true | ||||
|     | '?': | ||||
|       result^ := '\?'; | ||||
|       successful := true | ||||
|     | '0': | ||||
|       result^ := '\0'; | ||||
|       successful := true | ||||
|     else | ||||
|       successful := false | ||||
|   end; | ||||
|   return successful | ||||
| end; | ||||
| @@ -304,11 +305,9 @@ proc lexer_string(source_code: ^SourceCode, token_content: ^StringBuffer) -> Boo | ||||
| var | ||||
|   token_end, constructed_string: ^Char; | ||||
|   token_length: Word; | ||||
|   is_valid: Bool; | ||||
|   is_valid: Bool := true; | ||||
|   next_char: Char; | ||||
| begin | ||||
|   is_valid := true; | ||||
|  | ||||
|   while is_valid & ~source_code_empty(source_code) & source_code_head(source_code^) <> '"' do | ||||
|     is_valid := lexer_character(source_code, @next_char); | ||||
|  | ||||
| @@ -625,9 +624,8 @@ end; | ||||
| proc parse(tokens: ^Token, tokens_size: Word); | ||||
| var | ||||
|   current_token: ^Token; | ||||
|   i: Word; | ||||
|   i: Word := 0u; | ||||
| begin | ||||
|   i := 0u; | ||||
|   while i < tokens_size do | ||||
|     current_token := tokens + i; | ||||
|  | ||||
| @@ -787,11 +785,9 @@ end; | ||||
|  | ||||
| proc compile_in_stages(command_line: ^CommandLine, source_code: SourceCode) -> Int; | ||||
| var | ||||
|   return_code: Int; | ||||
|   return_code: Int := 0; | ||||
|   lexer: Tokenizer; | ||||
| begin | ||||
|   return_code := 0; | ||||
|  | ||||
|   if command_line^.lex or command_line^.parse then | ||||
|     lexer := lexer_text(source_code) | ||||
|   end; | ||||
| @@ -808,11 +804,9 @@ var | ||||
|   tokens_size: Word; | ||||
|   source_code: SourceCode; | ||||
|   command_line: ^CommandLine; | ||||
|   return_code: Int; | ||||
|   return_code: Int := 0; | ||||
|   source_file: ^SourceFile; | ||||
| begin | ||||
|   return_code := 0; | ||||
|  | ||||
|   command_line := parse_command_line(argc, argv); | ||||
|   if command_line = nil then | ||||
|     return_code := 2 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user