diff options
31 files changed, 119 insertions, 34 deletions
diff --git a/boot/ast.cc b/boot/ast.cc index 69b15d3..456797a 100644 --- a/boot/ast.cc +++ b/boot/ast.cc @@ -1681,8 +1681,9 @@ namespace elna::boot delete this->alternative; } - import_declaration::import_declaration(const source_position position, std::vector<std::string>&& segments) - : node(position), segments(std::move(segments)) + import_declaration::import_declaration(const source_position position, std::vector<std::string>&& segments, + const bool exported) + : node(position), segments(std::move(segments)), exported(exported) { } diff --git a/boot/dependency.cc b/boot/dependency.cc index 5053a36..31eccc9 100644 --- a/boot/dependency.cc +++ b/boot/dependency.cc @@ -90,7 +90,7 @@ namespace elna::boot } for (const auto& import : imports) { - const std::vector<import_collision> collisions = result.value.add_import(import.symbols); + const std::vector<import_collision> collisions = result.value.add_import(import.symbols, import.reexport); for (const import_collision& collision : collisions) { diff --git a/boot/parser.yy b/boot/parser.yy index cfa63d5..65d3dba 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -160,7 +160,8 @@ along with GCC; see the file COPYING3. If not see %type <std::vector<elna::boot::identifier_definition>> identifier_definitions; %type <std::unique_ptr<elna::boot::attribute>> attribute; %type <std::vector<elna::boot::attribute>> attributes; -%type <std::vector<std::string>> import_declaration; +%type <std::pair<std::vector<std::string>, bool>> import_declaration; +%type <std::vector<std::string>> import_path; %type <std::vector<elna::boot::identifier>> required_identifiers optional_identifiers generic_parameters; %type <std::unique_ptr<elna::boot::enumeration_type_expression::member_declaration>> enumeration_member; %type <std::vector<elna::boot::enumeration_type_expression::member_declaration>> enumeration_members; @@ -736,22 +737,31 @@ variable_declarations: $$ = $2; $$.insert(std::cbegin($$), $1); } -import_declaration: - IDENTIFIER "." import_declaration +import_path: + IDENTIFIER "." import_path { $$ = $3; $$.emplace($$.cbegin(), $1); } | IDENTIFIER { $$.emplace_back($1); } +import_declaration: + import_path "*" { $$ = { $1, true }; } + | import_path { $$ = { $1, false }; } import_declarations: import_declaration "," import_declarations { + auto [segments, exported] = $1; + $$ = $3; - $$.emplace($$.cbegin(), new boot::import_declaration(boot::make_position(@$), $1)); + $$.emplace($$.cbegin(), + new boot::import_declaration(boot::make_position(@$), std::move(segments), exported)); } | import_declaration { - $$.emplace_back(new boot::import_declaration(boot::make_position(@$), $1)); + auto [segments, exported] = $1; + + $$.emplace_back( + new boot::import_declaration(boot::make_position(@$), std::move(segments), exported)); } type_declaration: identifier_definition generic_parameters "=" type_expression diff --git a/boot/symbol.cc b/boot/symbol.cc index 375cbfd..2805839 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -522,7 +522,8 @@ namespace elna::boot return this->unresolved.insert({ symbol_name, std::move(forward_declaration) }).second; } - std::vector<import_collision> symbol_bag::add_import(const std::shared_ptr<symbol_table>& symbols) + std::vector<import_collision> symbol_bag::add_import(const std::shared_ptr<symbol_table>& symbols, + const bool reexport) { std::vector<import_collision> collisions; @@ -538,7 +539,10 @@ namespace elna::boot } } this->imports.push_back(symbols); - + if (reexport) + { + this->reexports.push_back(symbols); + } return collisions; } @@ -553,6 +557,15 @@ namespace elna::boot if (!m_exported) { m_exported = std::make_shared<symbol_table>(symbols->scope()); + // The same symbol can reach a module along several re-export + // paths; it is the same info then, so entering it twice is a no-op. + for (const auto& reexported : this->reexports) + { + for (const auto& [name, info] : *reexported) + { + m_exported->enter(name, info); + } + } for (const auto& [name, info] : *symbols) { if (info->exported) diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h index 6edeb25..06e5d41 100644 --- a/include/elna/boot/ast.h +++ b/include/elna/boot/ast.h @@ -1027,13 +1027,19 @@ namespace elna::boot /** * Import statement. + * + * An asterisk after the module path marks a re-export: the symbols the + * imported module exports become visible to the modules importing this + * one as well. */ class import_declaration : public node { public: const std::vector<std::string> segments; + const bool exported; - import_declaration(const source_position position, std::vector<std::string>&& segments); + import_declaration(const source_position position, std::vector<std::string>&& segments, + const bool exported = false); void accept(parser_visitor *visitor) override; }; diff --git a/include/elna/boot/dependency.h b/include/elna/boot/dependency.h index 7bd3de9..f52bfb1 100644 --- a/include/elna/boot/dependency.h +++ b/include/elna/boot/dependency.h @@ -156,6 +156,8 @@ namespace elna::boot { source_position position; std::shared_ptr<symbol_table> symbols; + /// Whether the importing module re-exports the symbols. + bool reexport = false; }; /** @@ -302,7 +304,8 @@ namespace elna::boot } imports.push_back({ .position = sub_tree->position(), - .symbols = this->cache.find(module_path)->second.exported_symbols() + .symbols = this->cache.find(module_path)->second.exported_symbols(), + .reexport = sub_tree->exported }); } result.append(key, std::move(circular)); diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h index 07bcf80..bdac269 100644 --- a/include/elna/boot/symbol.h +++ b/include/elna/boot/symbol.h @@ -558,6 +558,8 @@ namespace elna::boot { std::shared_ptr<symbol_table> symbols; std::vector<std::shared_ptr<symbol_table>> imports; + // The subset of imports that is re-exported by this module. + std::vector<std::shared_ptr<symbol_table>> reexports; forward_table unresolved; std::shared_ptr<info> lookup_import(const std::string& name) const; @@ -643,11 +645,14 @@ namespace elna::boot * earlier import keeps its names. * * \param symbols Exported symbol table of another module. + * \param reexport Whether the imported symbols are exported by this + * module as well, so they are visible to its importers. * * \return Names an earlier import already brought into the scope, * sorted by name. */ - std::vector<import_collision> add_import(const std::shared_ptr<symbol_table>& symbols); + std::vector<import_collision> add_import(const std::shared_ptr<symbol_table>& symbols, + const bool reexport = false); /** * Tells whether the current scope is the module global scope. @@ -661,7 +666,7 @@ namespace elna::boot /** * The symbol table containing the exported declarations of the - * module, computed lazily. Must be called while the current scope is + * module and the symbols of the modules it re-exports, computed lazily. Must be called while the current scope is * still the module scope. * * \return Exported symbols of the module. diff --git a/rakelib/gcc.rake b/rakelib/gcc.rake index 6dce877..aeffd06 100644 --- a/rakelib/gcc.rake +++ b/rakelib/gcc.rake @@ -130,8 +130,9 @@ end def multi_module_extra_sources(test_target) sources = test_target.glob('**/*.elna') - .map { |extra| %("#{extra.relative_path_from(test_target)}") } - "(* { dg-additional-sources #{sources * ' '} } *)" + .map { |extra| extra.relative_path_from(test_target) } + .sort + "(* { dg-additional-sources \"#{sources * ' '}\" } *)" end def convert_multi_module_test(test_path, test_target, category) diff --git a/source/command_line_interface.elna b/source/command_line_interface.elna index 1318e67..4c3c1a6 100644 --- a/source/command_line_interface.elna +++ b/source/command_line_interface.elna @@ -5,7 +5,7 @@ Command line handling. *) module -import cstdlib, cstring, common +import sys.posix.cstdlib, sys.posix.cstring, common type CommandLine* = record diff --git a/source/common.elna b/source/common.elna index 65b0a65..93b4363 100644 --- a/source/common.elna +++ b/source/common.elna @@ -2,7 +2,7 @@ v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. *) module -import cstring, cstdio, cstdlib +import sys.posix.cstring, sys.posix.cstdio, sys.posix.cstdlib type ElnaLocation* = record @@ -86,4 +86,3 @@ begin return nil end. - diff --git a/source/diagnostic.elna b/source/diagnostic.elna index 771ce38..2356544 100644 --- a/source/diagnostic.elna +++ b/source/diagnostic.elna @@ -2,7 +2,7 @@ v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. *) module -import common, cstdio, cstdlib, lexer, ast +import common, sys.posix.cstdio, sys.posix.cstdlib, lexer, ast type ElnaErrorKind* = ( diff --git a/source/lexer.elna b/source/lexer.elna index 9ba593f..71e76a7 100644 --- a/source/lexer.elna +++ b/source/lexer.elna @@ -2,7 +2,7 @@ v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. *) module -import cstdio, common, cstdlib +import sys.posix.cstdio, common, sys.posix.cstdlib type ElnaLexerState* = ( @@ -762,8 +762,8 @@ begin cursor^.start := code_pointer; cursor^.finish := code_pointer; cursor^.token := nil; - cursor^.position.start_location := ElnaLocation{ line: 1u, column: 1u }; - cursor^.position.end_location := ElnaLocation{ line: 1u, column: 1u } + cursor^.position.start_location := { ElnaLocation; line: 1u, column: 1u }; + cursor^.position.end_location := { ElnaLocation; line: 1u, column: 1u } return (** diff --git a/source/main.elna b/source/main.elna index 6049362..3b78651 100644 --- a/source/main.elna +++ b/source/main.elna @@ -4,7 +4,7 @@ program(parameters) import - cstdio, cstdlib, cstring, common, command_line_interface, + sys.posix.cstdio, sys.posix.cstdlib, sys.posix.cstring, common, command_line_interface, lexer, ast, diagnostic, symbol, parser, name_analysis, type_check, tac, qbe proc read_file_to_buffer(filename: ^Word8) -> []const Word8 diff --git a/source/name_analysis.elna b/source/name_analysis.elna index 75d6dbd..cbb27c0 100644 --- a/source/name_analysis.elna +++ b/source/name_analysis.elna @@ -2,7 +2,7 @@ v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. *) module -import common, cstdlib, lexer, ast, symbol, diagnostic +import common, sys.posix.cstdlib, lexer, ast, symbol, diagnostic proc elna_name_type_expression*(parser_node: ^ElnaTreeTypeExpression; error_list: ^ElnaList) -> ^ElnaType var diff --git a/source/parser.elna b/source/parser.elna index 30f79f8..3912b65 100644 --- a/source/parser.elna +++ b/source/parser.elna @@ -2,7 +2,7 @@ v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. *) module -import common, cstdlib, lexer, ast, diagnostic +import common, sys.posix.cstdlib, lexer, ast, diagnostic proc elna_parser_expect*(cursor: ^ElnaLexerCursor; expected: ElnaLexerKind; error_list: ^ElnaList) -> ^ElnaLexerToken var @@ -1129,7 +1129,7 @@ var begin result := malloc#[ElnaTreeModuleDeclaration](#size(ElnaTreeModuleDeclaration)); result^.kind := ElnaTreeKind.module_declaration; - result^.position.start_location := ElnaLocation{ line: 1u, column: 1u }; + result^.position.start_location := { ElnaLocation; line: 1u, column: 1u }; result^.position.end_location := result^.position.start_location; result^.types := elna_parser_type_part(cursor, error_list); diff --git a/source/qbe.elna b/source/qbe.elna index 0a5a45b..47eac88 100644 --- a/source/qbe.elna +++ b/source/qbe.elna @@ -2,7 +2,7 @@ v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. *) module -import common, cstdlib, cstdio, cstring, lexer, ast, symbol, tac +import common, sys.posix.cstdlib, sys.posix.cstdio, sys.posix.cstring, lexer, ast, symbol, tac var qbe_unique_id: Word := 0u diff --git a/source/symbol.elna b/source/symbol.elna index 531f58c..a07a9f3 100644 --- a/source/symbol.elna +++ b/source/symbol.elna @@ -2,7 +2,7 @@ v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. *) module -import cstdlib, common, ast +import sys.posix.cstdlib, common, ast type ElnaSymbolInfoKind* = (type_info, temporary_info, procedure_info) diff --git a/source/cctype.elna b/source/sys/posix/cctype.elna index 19c3491..19c3491 100644 --- a/source/cctype.elna +++ b/source/sys/posix/cctype.elna diff --git a/source/cstdio.elna b/source/sys/posix/cstdio.elna index 928c7e1..928c7e1 100644 --- a/source/cstdio.elna +++ b/source/sys/posix/cstdio.elna diff --git a/source/cstdlib.elna b/source/sys/posix/cstdlib.elna index 10a4d80..10a4d80 100644 --- a/source/cstdlib.elna +++ b/source/sys/posix/cstdlib.elna diff --git a/source/cstring.elna b/source/sys/posix/cstring.elna index 3973573..3973573 100644 --- a/source/cstring.elna +++ b/source/sys/posix/cstring.elna diff --git a/source/tac.elna b/source/tac.elna index 7b05ab2..3d65e42 100644 --- a/source/tac.elna +++ b/source/tac.elna @@ -2,7 +2,7 @@ v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/. *) module -import common, cstdlib, cstdio, cstring, lexer, ast, symbol, diagnostic +import common, sys.posix.cstdlib, sys.posix.cstdio, sys.posix.cstring, lexer, ast, symbol, diagnostic type ElnaTacOperator* = ( diff --git a/testsuite/fail_compilation/transitive_import/helper.elna b/testsuite/fail_compilation/transitive_import/helper.elna new file mode 100644 index 0000000..c3c0bfd --- /dev/null +++ b/testsuite/fail_compilation/transitive_import/helper.elna @@ -0,0 +1,6 @@ +module + +proc multiply*(x, y: Int) -> Int +return x * y + +end. diff --git a/testsuite/fail_compilation/transitive_import/middle.elna b/testsuite/fail_compilation/transitive_import/middle.elna new file mode 100644 index 0000000..0aab392 --- /dev/null +++ b/testsuite/fail_compilation/transitive_import/middle.elna @@ -0,0 +1,5 @@ +module + +import helper + +end. diff --git a/testsuite/fail_compilation/transitive_import/sut.elna b/testsuite/fail_compilation/transitive_import/sut.elna new file mode 100644 index 0000000..5423dbd --- /dev/null +++ b/testsuite/fail_compilation/transitive_import/sut.elna @@ -0,0 +1,8 @@ +module + +import middle + +proc call() -> Int +return multiply(3, 4) (* @Error Symbol 'multiply' not declared *) + +end. diff --git a/testsuite/runnable/reexport/helper.elna b/testsuite/runnable/reexport/helper.elna new file mode 100644 index 0000000..c3c0bfd --- /dev/null +++ b/testsuite/runnable/reexport/helper.elna @@ -0,0 +1,6 @@ +module + +proc multiply*(x, y: Int) -> Int +return x * y + +end. diff --git a/testsuite/runnable/reexport/left.elna b/testsuite/runnable/reexport/left.elna new file mode 100644 index 0000000..f149065 --- /dev/null +++ b/testsuite/runnable/reexport/left.elna @@ -0,0 +1,5 @@ +module + +import helper* + +end. diff --git a/testsuite/runnable/reexport/middle.elna b/testsuite/runnable/reexport/middle.elna new file mode 100644 index 0000000..0e593db --- /dev/null +++ b/testsuite/runnable/reexport/middle.elna @@ -0,0 +1,5 @@ +module + +import left*, right* + +end. diff --git a/testsuite/runnable/reexport/right.elna b/testsuite/runnable/reexport/right.elna new file mode 100644 index 0000000..f149065 --- /dev/null +++ b/testsuite/runnable/reexport/right.elna @@ -0,0 +1,5 @@ +module + +import helper* + +end. diff --git a/testsuite/runnable/reexport/sut.elna b/testsuite/runnable/reexport/sut.elna new file mode 100644 index 0000000..8ddb567 --- /dev/null +++ b/testsuite/runnable/reexport/sut.elna @@ -0,0 +1,6 @@ +program() +import middle + +begin + assert(multiply(3, 4) = 12) +end. diff --git a/tools/tree-sitter/grammar.js b/tools/tree-sitter/grammar.js index bfb4c26..b854e82 100644 --- a/tools/tree-sitter/grammar.js +++ b/tools/tree-sitter/grammar.js @@ -92,6 +92,7 @@ module.exports = grammar({ import_path: $ => seq( $.identifier, repeat(seq('.', $.identifier)), + optional(field('exported', '*')), ), type_section: $ => seq( @@ -485,12 +486,12 @@ module.exports = grammar({ ')', ), - record_constructor: $ => prec(PREC.CALL, seq( - field('type', choice($.identifier, $.generic_type)), + record_constructor: $ => seq( '{', - optional(commaSep1($.field_initializer)), + field('type', $._type_expression), + optional(seq(';', commaSep1($.field_initializer))), '}', - )), + ), field_initializer: $ => seq( field('name', $.identifier), |
