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