Support variable declaration initializer
This commit is contained in:
		@@ -41,5 +41,12 @@ and Mac OS. In the latter case GCC is patched with the patches used by Homebrew
 | 
			
		||||
rake boot
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
`gcc` binary is used by default, but a different gcc version can be specified
 | 
			
		||||
by passing `CC` and `CXX` environment variables to rake, e.g.:
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
rake CC=gcc-15 CXX=g++-15 boot
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
See `rake -T` for more tasks. The GCC source is under `build/tools`. The
 | 
			
		||||
installation path is `build/host/install`.
 | 
			
		||||
 
 | 
			
		||||
@@ -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,57 @@ 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);
 | 
			
		||||
                if (is_assignable_from(TREE_TYPE(declaration_tree), this->current_expression))
 | 
			
		||||
                {
 | 
			
		||||
                    DECL_INITIAL(declaration_tree) = this->current_expression;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    error_at(declaration_location, "Cannot initialize variable of type '%s' with a value of type '%s'",
 | 
			
		||||
                            print_type(TREE_TYPE(declaration_tree)).c_str(),
 | 
			
		||||
                            print_type(TREE_TYPE(this->current_expression)).c_str());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            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;
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,7 @@ def gcc_verbose(gcc_binary)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def find_build_target
 | 
			
		||||
  gcc_verbose('gcc')
 | 
			
		||||
  gcc_verbose(ENV.fetch 'CC', 'gcc')
 | 
			
		||||
    .lines
 | 
			
		||||
    .find { |line| line.start_with? 'Target: ' }
 | 
			
		||||
    .split(' ')
 | 
			
		||||
 
 | 
			
		||||
@@ -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 := 0;
 | 
			
		||||
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