diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 8a81e70c..6cb7a95b 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -71,6 +71,7 @@ add_library(libpl ${LIBRARY_TYPE} source/pl/lib/std/hash.cpp source/pl/lib/std/random.cpp source/pl/core/resolvers.cpp + source/pl/core/parser_manager.cpp ) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") diff --git a/lib/include/pl/core/ast/ast_node_compound_statement.hpp b/lib/include/pl/core/ast/ast_node_compound_statement.hpp index b2f58bb7..ea594708 100644 --- a/lib/include/pl/core/ast/ast_node_compound_statement.hpp +++ b/lib/include/pl/core/ast/ast_node_compound_statement.hpp @@ -11,13 +11,14 @@ namespace pl::core::ast { public Attributable { public: explicit ASTNodeCompoundStatement(std::vector> &&statements, bool newScope = false); + explicit ASTNodeCompoundStatement(std::vector> &&statements, bool newScope = false); ASTNodeCompoundStatement(const ASTNodeCompoundStatement &other); [[nodiscard]] std::unique_ptr clone() const override { return std::unique_ptr(new ASTNodeCompoundStatement(*this)); } - [[nodiscard]] const std::vector>& getStatements() const { return this->m_statements; } + [[nodiscard]] const std::vector>& getStatements() const { return this->m_statements; } [[nodiscard]] std::unique_ptr evaluate(Evaluator *evaluator) const override; [[nodiscard]] std::vector> createPatterns(Evaluator *evaluator) const override; @@ -25,7 +26,7 @@ namespace pl::core::ast { void addAttribute(std::unique_ptr &&attribute) override; public: - std::vector> m_statements; + std::vector> m_statements; bool m_newScope = false; }; diff --git a/lib/include/pl/core/errors/result.hpp b/lib/include/pl/core/errors/result.hpp index bfa983e4..c02ca9c3 100644 --- a/lib/include/pl/core/errors/result.hpp +++ b/lib/include/pl/core/errors/result.hpp @@ -39,7 +39,7 @@ namespace pl::hlp { } [[nodiscard]] bool is_ok() const { - return ok.has_value(); + return !has_errs() && ok.has_value(); } [[nodiscard]] bool is_err() const { diff --git a/lib/include/pl/core/parser.hpp b/lib/include/pl/core/parser.hpp index 270e2089..c8559a38 100644 --- a/lib/include/pl/core/parser.hpp +++ b/lib/include/pl/core/parser.hpp @@ -57,7 +57,7 @@ namespace pl::core { std::map> m_types; std::vector m_matchedOptionals; - std::vector> m_currNamespace; + std::vector> m_currNamespace = { {} }; std::vector m_globalDocComments; i32 m_ignoreDocsCount = 0; diff --git a/lib/include/pl/core/parser_manager.hpp b/lib/include/pl/core/parser_manager.hpp index b1e01632..4f2954cb 100644 --- a/lib/include/pl/core/parser_manager.hpp +++ b/lib/include/pl/core/parser_manager.hpp @@ -6,6 +6,7 @@ #include #include +#include namespace pl::core { @@ -13,15 +14,27 @@ namespace pl::core { public: explicit ParserManager() = default; - CompileResult>> parse(const api::Source* source, const std::string &namespacePrefix = ""); + CompileResult>> parse(api::Source* source, const std::string &namespacePrefix = ""); - void setResolvers(Resolver* resolvers) { + void setResolver(Resolver* resolvers) { m_resolvers = resolvers; } + void setPatternLanguage(PatternLanguage* patternLanguage) { + m_patternLanguage = patternLanguage; + } + + [[nodiscard]] Resolver* getResolver() { + return m_resolvers; + } + private: std::map>> m_astCache; + Resolver* m_resolvers = nullptr; + PatternLanguage* m_patternLanguage = nullptr; + + std::set m_onceIncluded; }; } \ No newline at end of file diff --git a/lib/source/pl/core/ast/ast_node_compound_statement.cpp b/lib/source/pl/core/ast/ast_node_compound_statement.cpp index 84e974cd..b440eea4 100644 --- a/lib/source/pl/core/ast/ast_node_compound_statement.cpp +++ b/lib/source/pl/core/ast/ast_node_compound_statement.cpp @@ -5,7 +5,13 @@ namespace pl::core::ast { - ASTNodeCompoundStatement::ASTNodeCompoundStatement(std::vector> &&statements, bool newScope) : m_statements(std::move(statements)), m_newScope(newScope) { + ASTNodeCompoundStatement::ASTNodeCompoundStatement(std::vector> &&statements, bool newScope) : m_newScope(newScope) { + for (auto &statement : statements) { + this->m_statements.emplace_back(std::move(statement)); + } + } + + ASTNodeCompoundStatement::ASTNodeCompoundStatement(std::vector> &&statements, bool newScope) : m_statements(statements), m_newScope(newScope) { } ASTNodeCompoundStatement::ASTNodeCompoundStatement(const ASTNodeCompoundStatement &other) : ASTNode(other), Attributable(other) { diff --git a/lib/source/pl/core/evaluator.cpp b/lib/source/pl/core/evaluator.cpp index ced1eada..b669af79 100644 --- a/lib/source/pl/core/evaluator.cpp +++ b/lib/source/pl/core/evaluator.cpp @@ -726,7 +726,7 @@ namespace pl::core { this->m_scopes.pop_back(); } - std::vector unpackCompoundStatements(const std::vector> &nodes) { + std::vector unpackCompoundStatements(const std::vector> &nodes) { std::vector result; for (const auto &node : nodes) { diff --git a/lib/source/pl/core/parser.cpp b/lib/source/pl/core/parser.cpp index 11033469..72d5f10a 100644 --- a/lib/source/pl/core/parser.cpp +++ b/lib/source/pl/core/parser.cpp @@ -532,16 +532,29 @@ namespace pl::core { return create(getNamespacePrefixedNames(functionName).back(), std::move(params), std::move(body), parameterPack, std::move(defaultParameters)); } + // identifier_path := identifier *(dot identifier)? + // string_path := string + // + // path := identifier_path | string_path + // + // 'import' path (colon colon identifier)? ('as' identifier *(colon colon identifier)?)? std::unique_ptr Parser::parseImportStatement() { - // 'import' identifier ( colon colon identifier)* ('as' identifier)? semicolon - // parse consumed `import ` for us - auto name = getValue(-1).get(); + std::string path; + if(sequence(tkn::Literal::String)) { + path = std::get(getValue(-1)); + } else if(sequence(tkn::Literal::Identifier)) { + path = getValue(-1).get(); + + while (sequence(tkn::Separator::Dot, tkn::Literal::Identifier)) { + path += "/"; + path += getValue(-1).get(); + } - while (sequence(tkn::Operator::ScopeResolution, tkn::Literal::Identifier)) { - name += "/"; - name += getValue(-1).get(); + path += ".pat"; } + // TODO: struct import + std::string alias; if (sequence(tkn::Keyword::As)) { @@ -551,10 +564,21 @@ namespace pl::core { alias = getValue(-1).get(); } - if (!sequence(tkn::Separator::Semicolon)) - err::P0002.throwError(fmt::format("Expected ';' at end of import statement, got {}.", getFormattedToken(0)), {}, 1); + // resolve source + auto [resolvedSource, resolverErrors] = m_parserManager->getResolver()->resolve(path); + if(!resolverErrors.empty()) { + // handle all errors + err::P0003.throwError(resolverErrors[0], {}, 1); + } - return nullptr; + auto [parserAst, parserErrors] = m_parserManager->parse(resolvedSource.value(), alias); + if(!parserErrors.empty()) { + // handle all errors + err::P0003.throwError(parserErrors[0].getDescription(), {}, 1); + } + + auto compoundStatement = create(std::move(parserAst.value()), false); + return compoundStatement; } std::unique_ptr Parser::parseFunctionVariableDecl(bool constant) { @@ -1699,6 +1723,8 @@ namespace pl::core { statement = parseBitfield(); else if (sequence(tkn::Keyword::Function, tkn::Literal::Identifier)) statement = parseFunctionDefinition(); + else if (sequence(tkn::Keyword::Import)) + statement = parseImportStatement(); else if (sequence(tkn::Keyword::Namespace)) return parseNamespace(); else { @@ -1819,9 +1845,6 @@ namespace pl::core { this->m_matchedOptionals.clear(); this->m_processedDocComments.clear(); - this->m_currNamespace.clear(); - this->m_currNamespace.emplace_back(); - try { auto program = parseTillToken(tkn::Separator::EndOfProgram); diff --git a/lib/source/pl/core/parser_manager.cpp b/lib/source/pl/core/parser_manager.cpp new file mode 100644 index 00000000..3f444172 --- /dev/null +++ b/lib/source/pl/core/parser_manager.cpp @@ -0,0 +1,70 @@ +#include +#include +#include +#include +#include + +#include + +using namespace pl::core; + +CompileResult>> +ParserManager::parse(api::Source *source, const std::string &namespacePrefix) { + using result_t = CompileResult>>; + + if (this->m_astCache.contains(source->source)) { + return result_t::good(this->m_astCache[source->source]); + } + + if(m_onceIncluded.contains(source)) + return result_t::good({}); + + auto parser = Parser(); + + std::vector namespaces; + if (!namespacePrefix.empty()) { + namespaces = wolv::util::splitString(namespacePrefix, "::"); + } + + auto preprocessor = Preprocessor(); + + preprocessor.addPragmaHandler("namespace", [&](auto&, const std::string& value) { + if(namespacePrefix.empty()) + namespaces = wolv::util::splitString(value, "::"); + return true; + }); + + auto lexer = Lexer(); + auto validator = Validator(); + + auto [preprocessedCode, preprocessorErrors] = preprocessor.preprocess(this->m_patternLanguage, source); + if (!preprocessorErrors.empty()) { + return result_t::err(preprocessorErrors); + } + + source->content = preprocessedCode.value(); // update source object with preprocessed code + + if(preprocessor.shouldOnlyIncludeOnce()) + m_onceIncluded.insert(source); + + auto [tokens, lexerErrors] = lexer.lex(source); + + if (!lexerErrors.empty()) { + return result_t::err(lexerErrors); + } + + parser.addNamespace(namespaces); + parser.setParserManager(this); + + auto result = parser.parse(source->content, tokens.value()); + if (result.has_errs()) + return result; + + if (!validator.validate(source->content, *result.ok, true, true)) { + return result_t::err({}); + } + + this->m_astCache[source->source] = result.ok.value(); + + return result; +} \ No newline at end of file diff --git a/lib/source/pl/pattern_language.cpp b/lib/source/pl/pattern_language.cpp index f61e3fd1..f93d5311 100644 --- a/lib/source/pl/pattern_language.cpp +++ b/lib/source/pl/pattern_language.cpp @@ -35,7 +35,10 @@ namespace pl { this->m_internals.preprocessor->setResolver(&this->m_resolvers); - this->m_parserManager.setResolvers(&this->m_resolvers); + this->m_parserManager.setResolver(&this->m_resolvers); + this->m_parserManager.setPatternLanguage(this); + + this->m_internals.parser->setParserManager(&this->m_parserManager); } PatternLanguage::~PatternLanguage() { diff --git a/tests/include/test_patterns/test_pattern_import.hpp b/tests/include/test_patterns/test_pattern_import.hpp new file mode 100644 index 00000000..49edeb3c --- /dev/null +++ b/tests/include/test_patterns/test_pattern_import.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "test_pattern.hpp" + +namespace pl::test { + + class TestPatternImport : public TestPattern { + public: + TestPatternImport() : TestPattern("Import") { + } + ~TestPatternImport() override = default; + + [[nodiscard]] std::string getSourceCode() const override { + return R"( + import IA as A; + + fn main() { + A::a(); + B::b(); + C::c(); + }; + )"; + } + }; + +} \ No newline at end of file diff --git a/tests/source/main.cpp b/tests/source/main.cpp index 95cd69be..7111b0f7 100644 --- a/tests/source/main.cpp +++ b/tests/source/main.cpp @@ -59,6 +59,7 @@ int runTests(int argc, char **argv) { fmt::print("{}\n", message); }); + // Include test runtime.addSource(R"( #include #include @@ -78,6 +79,32 @@ int runTests(int argc, char **argv) { fn c() {}; )", "C"); + // Imports test + runtime.addSource(R"( + import IB; + import IC as C; + + fn a() {}; + )", "IA"); + + runtime.addSource(R"( + #pragma namespace B + + import IC as C; + + fn b() {}; + )", "IB"); + + runtime.addSource(R"( + #pragma once + + fn c() {}; + )", "IC"); + + runtime.getResolver().registerProtocol("test", [&](const auto&) { + return hlp::Result::good(api::Source("fn test()", "test")); + }); + auto &test = testPatterns[testName]; auto result = runtime.executeString(test->getSourceCode(), ""); diff --git a/tests/source/tests.cpp b/tests/source/tests.cpp index cd1379e3..29a1134b 100644 --- a/tests/source/tests.cpp +++ b/tests/source/tests.cpp @@ -22,6 +22,7 @@ #include "test_patterns/test_pattern_doc_comments.hpp" #include "test_patterns/test_pattern_strings.hpp" #include "test_patterns/test_pattern_include.hpp" +#include "test_patterns/test_pattern_import.hpp" std::array Tests = { TEST(Placement), @@ -41,6 +42,7 @@ std::array Tests = { TEST(RValues), TEST(Namespaces), TEST(Include), + TEST(Import), TEST(ExtraSemicolon), TEST(Pointers), TEST(Arrays),