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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -744,26 +744,42 @@ namespace elna::gcc
|
||||
}
|
||||
|
||||
void generic_visitor::visit(boot::variable_declaration *declaration)
|
||||
{
|
||||
for (const auto& variable_identifier : declaration->identifiers)
|
||||
{
|
||||
this->current_expression = get_inner_alias(
|
||||
this->bag.lookup(declaration->identifier.identifier)->is_variable()->symbol,
|
||||
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);
|
||||
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))
|
||||
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) = declaration->identifier.exported;
|
||||
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",
|
||||
declaration->identifier.identifier.c_str());
|
||||
variable_identifier.identifier.c_str());
|
||||
}
|
||||
else if (lang_hooks.decls.global_bindings_p())
|
||||
{
|
||||
@@ -781,6 +797,7 @@ namespace elna::gcc
|
||||
append_statement(declaration_statement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void generic_visitor::visit(boot::variable_expression *expression)
|
||||
{
|
||||
|
@@ -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,40 +183,41 @@ proc lexer_escape(escape: Char, result: ^Char) -> Bool;
|
||||
var
|
||||
successful: Bool;
|
||||
begin
|
||||
if escape = 'n' then
|
||||
case escape of
|
||||
'n':
|
||||
result^ := '\n';
|
||||
successful := true
|
||||
elsif escape = 'a' then
|
||||
| 'a':
|
||||
result^ := '\a';
|
||||
successful := true
|
||||
elsif escape = 'b' then
|
||||
| 'b':
|
||||
result^ := '\b';
|
||||
successful := true
|
||||
elsif escape = 't' then
|
||||
| 't':
|
||||
result^ := '\t';
|
||||
successful := true
|
||||
elsif escape = 'f' then
|
||||
| 'f':
|
||||
result^ := '\f';
|
||||
successful := true
|
||||
elsif escape = 'r' then
|
||||
| 'r':
|
||||
result^ := '\r';
|
||||
successful := true
|
||||
elsif escape = 'v' then
|
||||
| 'v':
|
||||
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
|
||||
| '0':
|
||||
result^ := '\0';
|
||||
successful := true
|
||||
else
|
||||
@@ -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