Support variable declaration initializer

This commit is contained in:
2025-08-19 22:58:39 +02:00
parent 569139d44a
commit 0c2a396320
10 changed files with 175 additions and 102 deletions

View File

@@ -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`.

View File

@@ -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)
{
}

View File

@@ -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;

View File

@@ -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:

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
};
/**

View File

@@ -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;
};
}

View File

@@ -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(' ')

View File

@@ -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