From 52d635c382af14233f56c1ed398e46c1eb96010e Mon Sep 17 00:00:00 2001 From: Dhruvan Date: Sun, 30 Jun 2024 00:24:16 +0400 Subject: [PATCH] imports are correctly parsed, the right errors are shown and module imports work. --- source/include/error/error.cc | 4 +- source/include/error/error.hh | 18 ++ source/main.cc | 42 +-- .../preprocessor/include/preprocessor.hh | 179 +++++++++-- .../preprocessor/source/preprocessor.cc | 292 ++++++++++++++---- source/token/include/token.hh | 27 +- source/token/source/token.cc | 24 +- source/tools/controllers/include/compiler.hh | 40 +++ source/tools/controllers/source/resolve.cc | 5 - tests/main.hlx | 25 +- tests/{ => test_imports}/some_import.hlx | 2 + tests/test_imports/test_imports.hlx | 1 + 12 files changed, 510 insertions(+), 149 deletions(-) create mode 100644 source/tools/controllers/include/compiler.hh rename tests/{ => test_imports}/some_import.hlx (71%) create mode 100644 tests/test_imports/test_imports.hlx diff --git a/source/include/error/error.cc b/source/include/error/error.cc index c60a27d3..10a3089e 100644 --- a/source/include/error/error.cc +++ b/source/include/error/error.cc @@ -34,6 +34,7 @@ namespace error { * @param error The error information. */ Error::Error(const Line &error) { + HAS_ERRORED = true; if (error.file_name.starts_with("<") && error.file_name.ends_with(">")) { print_start(error.message, error.level); print_info(error.message, error.file_name, error.line_number, error.column, error.offset); @@ -81,13 +82,14 @@ Error::Error(const Line &error) { * @param message The error message. * @param level The level of the error. */ -Error::Error(const std::string &message, const Level &level) { print_start(message, level); } +Error::Error(const std::string &message, const Level &level) { HAS_ERRORED = true; print_start(message, level); } /** * @brief Constructs an Error object with the given compiler information. * @param compiler The compiler information. */ Error::Error(const Compiler &compiler) { + HAS_ERRORED = true; print_start(compiler.message, compiler.level, compiler.file_name); if (!compiler.fix.empty()) { print_fix(compiler.fix, 0, 0); diff --git a/source/include/error/error.hh b/source/include/error/error.hh index afe1071f..a41f233c 100644 --- a/source/include/error/error.hh +++ b/source/include/error/error.hh @@ -73,6 +73,22 @@ struct Line { , message(std::move(message)) , level(level) , fix(std::move(fix)) {} + + Line(const token::TokenList &tokens, std::string message, const Level &level = ERR, + std::string fix = "") + : file_name(tokens[0].file_name()) + , line_number(tokens[0].line_number()) + , column(tokens[0].column_number()) + , offset(0) + , message(std::move(message)) + , level(level) + , fix(std::move(fix)) { + for (const auto &tok : tokens) { + if (tok.line_number() == line_number) { + offset += tok.value().size(); + } + } + } }; /** @@ -86,6 +102,8 @@ struct Compiler { std::string fix; ///< Suggested fix message. }; +inline bool HAS_ERRORED = false; + /** * @class Error * @brief Class for handling and printing errors. diff --git a/source/main.cc b/source/main.cc index e720c7bc..8fba91f2 100644 --- a/source/main.cc +++ b/source/main.cc @@ -21,40 +21,32 @@ #include int main() { + // start the timer to measure execution time auto start = std::chrono::high_resolution_clock::now(); - #if defined(_WIN32) || defined(_WIN64) - std::string file_name = "tests/main.hlx"; - #else - std::string file_name = "/Volumes/Container/Projects/Helix/helix-lang/tests/main.hlx"; - #endif // !1 - auto tokens = lexer::Lexer(file_system::read_file(file_name), file_name).tokenize(); - - // token::print_tokens(tokens); - //for (token::Token _token : tokens) { - // std::cout << _token.to_string() << "\n"; - //} + // define the input file name + std::string file_name = "tests/main.hlx"; - auto preprocessor = parser::preprocessor::Preprocessor(tokens); + // read the file and tokenize its contents + auto tokens = lexer::Lexer(file_system::read_file(file_name), file_name).tokenize(); - preprocessor.parse(); + // preprocess the tokens + tokens = parser::preprocessor::Preprocessor(tokens, "main").parse(); - //std::string rootPath = "../"; - //file_system::SourceTree tree(rootPath); + // print the import tree + parser::preprocessor::import_tree->print_tree(parser::preprocessor::import_tree->get_root()); - //std::cout << "Source Tree for .hlx files:\n"; - //tree.print(); + // print the preprocessed tokens + // token::print_tokens(tokens); - //auto _parser = parser::Parser(tokens); + // end the timer and calculate the duration + auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration diff = end - start; - //std::unique_ptr _ast = _parser.parse(); - - //std::cout << _ast->to_string(0) << "\n"; + // Print the time taken in nanoseconds and milliseconds + std::cout << "time taken: " << diff.count() * 1e+9 << " ns\n"; + std::cout << " " << diff.count() * 1000 << " ms\n"; - auto end = std::chrono::high_resolution_clock::now(); - const std::chrono::duration diff = end - start; - std::cout << "time taken: " << diff.count()*1e+9 << "ns\n"; - std::cout << " " << diff.count()*1000 << "ms\n"; return 0; } diff --git a/source/parser/preprocessor/include/preprocessor.hh b/source/parser/preprocessor/include/preprocessor.hh index 5db6ca92..effb0312 100644 --- a/source/parser/preprocessor/include/preprocessor.hh +++ b/source/parser/preprocessor/include/preprocessor.hh @@ -14,8 +14,12 @@ #ifndef __PRE_H__ #define __PRE_H__ +#include +#include +#include #include #include +#include #include #include @@ -23,35 +27,146 @@ namespace parser::preprocessor { using namespace token; +struct ImportNode { + std::string module_name; + std::vector children; + ImportNode *parent; + + explicit ImportNode(std::string name, ImportNode *parent_node = nullptr) + : module_name(std::move(name)) + , parent(parent_node) {} +}; + +class ImportTree { + public: + explicit ImportTree(std::string name) + : root(new ImportNode(std::move(name))) {} + + ImportNode *add_import(const std::string &module_name, ImportNode *parent = nullptr) { + auto *node = new ImportNode(module_name, parent); + if (parent != nullptr) { + parent->children.push_back(node); + } else { + root->children.push_back(node); + } + return node; + } + + void print_tree(ImportNode *node, const std::string& prefix = "", bool is_last = true, int depth = 0) { + if (node == nullptr) { + return; + } + + // Define ANSI color codes + const std::string RESET = "\033[0m"; + const std::string CYAN = "\033[36m"; + const std::string MAGENTA = "\033[35m"; + const std::string BLUE = "\033[34m"; + const std::string YELLOW = "\033[33m"; + + std::cout << prefix; + + if (depth != 0) { + std::cout << (is_last ? "└── " : "├── "); + } + std::cout << (depth == 0 ? CYAN : (depth % 2 == 0 ? MAGENTA : BLUE)) << node->module_name << RESET << std::endl; + + std::string new_prefix = prefix + (is_last ? std::string(static_cast(depth * 4), ' ') : "│ "); + + for (size_t i = 0; i < node->children.size(); ++i) { + print_tree(node->children[i], new_prefix, i == node->children.size() - 1, depth + 1); + } + } + + ImportNode *get_root() { return root; } + + private: + ImportNode *root; +}; + /** + * @brief helix handles imports in a slightly different manner then a language like c++ where + * imports have to be linked. in helix imports are inplace replaced in place of an import statement, + * thus having the source in place, making 1 giant helix file that would contain all the the + * project, this would not only speed up ast gen, and clang compile times, it would also allow for + * circular imports, BUT if a file still needs to be linked rather then this way an `extern import` + * statement can always be used to allow for that functionality. + * * The import_helix struct encapsulates information related to Helix imports, * detailing how and where a module is imported within the Helix source code. * * Members: - * - filename: The path to the imported file relative to the project root (e.g., "src/foo/hello.hlx"). - * This indicates that the file is part of the "foo" module. + * - filename: The path to the imported file relative to the project root (e.g., + * "src/foo/hello.hlx"). This indicates that the file is part of the "foo" module. * - module: The name of the module to which the file belongs (e.g., "foo"). * - module_base: The base file of the module (e.g., "src/foo/foo.hlx"). * - relative: The namespace under which the module should be included (e.g., "foo"). - * - source: A list of tokens representing the source code to be inserted in place of the import statement. + * - source: A list of tokens representing the source code to be inserted in place of the import + * statement. */ struct import_helix { - std::string filename; //> src/foo/hello.hlx - a part of the foo module - std::string module; //> foo - std::string module_base; //> src/foo/foo.hlx - std::string relative; //> foo - the namespace to include it too - TokenList source; //> source tokens to go in place + std::filesystem::path filepath; //> src/foo/hello.hlx - a part of the foo module + std::filesystem::path module_base; //> src/foo/foo.hlx + TokenList module; //> foo + TokenList relative; //> foo - the namespace to include it too + TokenList source; //> source tokens to go in place + std::vector explicit_imports; //> if a specific part of the module is imported + u32 start; //> the start of the import statement itself + u32 end; //> the end of the import statement + + std::string get_module() { + std::string mod; + + for (auto const &tok : module) { + mod += tok.value(); + } + + return mod; + } + + std::string get_namespace() { + std::string name_space; + + for (auto const &tok : relative) { + name_space += tok.value(); + } + + return name_space; + } + + static void print_data(import_helix data) { + std::cout << std::string(120, '-') << '\n'; + std::cout << "filepath: " << data.filepath << "\n"; + std::cout << "module_base: " << data.module_base << "\n"; + std::cout << "module: "; + print_tokens(data.module); + std::cout << "relative: "; + print_tokens(data.relative); + std::cout << "source: "; + print_tokens(data.source); + std::cout << "explicit_imports: [" + << "\n"; + for (auto token_list : data.explicit_imports) { + print_tokens(token_list); + } + std::cout << "]" << '\n' << '\n'; + } }; +inline std::unique_ptr import_tree; +inline std::vector imports; + /** * The Preprocessor class is responsible for handling context-dependent elements in the source code, - * such as imports and macro expansions. It processes the source tokens to produce a fully context-aware - * token list. This token list provides all necessary information for subsequent stages of the compiler, - * such as the AST dependency resolver, ensuring accurate parsing and code analysis. + * such as imports and macro expansions. It processes the source tokens to produce a fully + * context-aware token list. This token list provides all necessary information for subsequent + * stages of the compiler, such as the AST dependency resolver, ensuring accurate parsing and code + * analysis. * * Members: * - allowed_abi: An array of allowed ABI (Application Binary Interface) strings. - * - imports: A vector of import_helix structures that represent the imports found in the source code. + * - imports: A vector of import_helix structures that represent the imports found in the source + * code. * - rel_path: A vector of strings representing the relative path for imported modules. * - source_tokens: A list of tokens representing the source code to be processed. * - current_pos: The current position in the token list being processed. @@ -62,7 +177,8 @@ struct import_helix { * - parse_using: Handles the parsing of using statements related to ABI in the source code. * - parse_import_alias: Parses the alias part of an import statement. * - handle_import_tokens: Processes tokens within an import statement. - * - handle_open_brace_in_import: Processes tokens when an open brace is encountered in an import statement. + * - handle_open_brace_in_import: Processes tokens when an open brace is encountered in an import + * statement. * - handle_using_abi_imports: Handles ABI-specific imports within a using statement. * - parse_import_within_using: Parses imports found within a using statement. * - handle_invalid_abi_option: Handles invalid ABI options in a using statement. @@ -76,7 +192,8 @@ struct import_helix { * - not_start: Checks if the specified position is not before the start of the token list. * * Constructor: - * - Preprocessor(TokenList &tokens): Initializes the Preprocessor with a list of source tokens and sets up allowed ABI options. + * - Preprocessor(TokenList &tokens): Initializes the Preprocessor with a list of source tokens and + * sets up allowed ABI options. * * Public Method: * - parse: Main method to start the preprocessing of the source tokens. @@ -84,21 +201,19 @@ struct import_helix { class Preprocessor { private: std::array allowed_abi; - - std::vector imports; - std::vector rel_path; // if importing a module that would be the current rel path + std::vector + rel_path; // if importing a module that would be the current rel path TokenList source_tokens; - u32 current_pos{}; - u32 end; + u32 current_pos{}; + u32 end; - void parse_import(); - void parse_using(); + import_helix parse_import(std::unique_ptr &import_tree, ImportNode *parent_node = nullptr); void parse_import_alias(TokenList &alias, const std::vector &explicit_imports); void handle_import_tokens(u32 &brace_level, bool &captured_import, bool &captured_specific, - std::vector &explicit_imports, TokenList &import_path, - TokenList ¤t_feature, TokenList &alias, u32 &import_end); + std::vector &explicit_imports, TokenList &import_path, + TokenList ¤t_feature, TokenList &alias, u32 &import_end); void handle_open_brace_in_import(u32 &brace_level, bool &captured_specific, std::vector &explicit_imports, TokenList ¤t_feature); @@ -106,6 +221,15 @@ class Preprocessor { void parse_import_within_using(); void handle_invalid_abi_option(const std::optional &next_token); + static void process_import_path(TokenList &import_path, + std::vector &explicit_imports, bool &module_import); + static std::string resolve_import_path(const TokenList &import_path); + static std::string resolve_import_path_with_namespace(const TokenList &import_path); + bool if_missing_relative_parent(std::string &string_import_path, + std::optional &temp_path, + TokenList &explicit_imports, TokenList &import_path, u32 depth); + + void parse_using(); void increment_pos(u32 n = 1) { current_pos += n; } Token current() { return source_tokens[current_pos]; } @@ -147,14 +271,19 @@ class Preprocessor { inline bool not_start(u32 n = 0) const { return current_pos >= n; } public: - explicit Preprocessor(TokenList &tokens) + explicit Preprocessor(TokenList &tokens, const std::string& name = "") : source_tokens(tokens) , end(tokens.size() - 1) { + rel_path.push_back(std::filesystem::path(tokens[0].file_name()).parent_path()); std::transform(abi::reserved_map.begin(), abi::reserved_map.end(), allowed_abi.begin(), [](const auto &pair) { return std::string(pair.second); }); + + if (!name.empty() || name == "main") { + import_tree = std::make_unique(std::string(tokens[0].file_name())); + } } - void parse(); + TokenList parse(ImportNode *parent_node = nullptr); }; } // namespace parser::preprocessor diff --git a/source/parser/preprocessor/source/preprocessor.cc b/source/parser/preprocessor/source/preprocessor.cc index 6d33cb91..d68ced66 100644 --- a/source/parser/preprocessor/source/preprocessor.cc +++ b/source/parser/preprocessor/source/preprocessor.cc @@ -11,15 +11,21 @@ * @note This code is provided by the creators of Helix. Visit our website at: * https://helix-lang.com/ for more information. */ +#include +#include #include #include +#include #include +#include #include -#include #include +#include #include +#include +#include namespace parser::preprocessor { /** @@ -28,10 +34,10 @@ namespace parser::preprocessor { * all such context dependencies and producing a fully context-aware token list. This token * list includes all necessary information for the AST dependency resolver to operate effectively. */ -void Preprocessor::parse() { +TokenList Preprocessor::parse(ImportNode *parent_node) { /// print_tokens(source_tokens); /// std::cout << std::string(60, '-') << "\n"; - + /* things parsed here: - imports: - import xyz @@ -61,13 +67,9 @@ void Preprocessor::parse() { imports - working on now defines - working on now macros - - imports - rel_path - source_tokens - current_pos - end */ + import_helix _import; + while (not_end()) { const Token ¤t_token = current(); current_token_type = current_token.token_kind(); @@ -77,74 +79,49 @@ void Preprocessor::parse() { parse_using(); break; case tokens::KEYWORD_IMPORT: - parse_import(); + _import = parse_import(import_tree, parent_node); break; default: // std::cout << (peek().has_value() ? peek()->file_name() : "null") << "\n"; break; } + if (!_import.source.empty()) { + source_tokens.insert_remove(_import.source, _import.start, _import.end + 1); + current_pos += _import.source.size() - ((_import.end + 1) - _import.start); + end += _import.source.size(); + _import.source.clear(); + } + increment_pos(); } - + return source_tokens; /// print_tokens(source_tokens); /// std::cout << std::string(60, '-') << "\n"; } inline void print_debug(auto explicit_imports, auto import_path, auto alias, auto current_feature, - auto import_start, auto import_end, auto source_tokens) { - std::cout << "explicit_imports: " - << "\n"; - for (auto token_list : explicit_imports) { - print_tokens(token_list); - } - std::cout << "import_path: " - << "\n"; - print_tokens(import_path); - std::cout << "alias: " - << "\n"; - print_tokens(alias); - std::cout << "current_feature: " - << "\n"; - print_tokens(current_feature); - std::cout << "start: " << std::to_string(import_start) << "\n"; - std::cout << "end: " << std::to_string(import_end) << "\n"; - TokenList temp_sec = source_tokens.slice(u64(import_start), u64(import_end)); - std::cout << "slice: " - << "\n"; - print_tokens(temp_sec); - std::cout << std::string(60, '-') << "\n"; -} + auto import_start, auto import_end, auto source_tokens); /*=====---------------------------------- private function ----------------------------------=====*/ -void Preprocessor::parse_import() { - // helix imports are relative to the base file, or module file. - /* - example while compiling src/hello.hlx - - src/hello.hlx: - ```helix - import helper // this is expected to be at src/helper.hlx - import foo::bar // expected to be at src/foo/helper.hlx - ``` - - src/zam/zam.hlx: - ``` - import zoom::far // expected to be at src/zam/zoom/far.hlx - // another way of writing this import would be import zam::zoom::far - ``` - - src/zam/zoom/far.hlx: - ``` - import helper // expected at src/zam/helper.hlx not src/helper.hlx, - // since making a src/zam/zam.hlx is a module declaration - // and src/zam is considered as its own standalone module. - ``` - */ +bool is_circular_import(ImportNode *node) { + std::unordered_set visited; + ImportNode *current = node; + + while (current != nullptr) { + if (visited.find(current->module_name) != visited.end()) { + return true; // Circular import detected + } + visited.insert(current->module_name); + current = current->parent; + } + return false; +} +import_helix Preprocessor::parse_import(std::unique_ptr &import_tree, ImportNode *parent_node) { std::vector explicit_imports; TokenList import_path; TokenList alias; @@ -159,8 +136,8 @@ void Preprocessor::parse_import() { bool captured_specific = false; if (peek().has_value() && peek()->token_kind() != tokens::IDENTIFIER) { - error::Error(error::Line(peek().value(), "got string instead of identifier", error::ERR, - "change the string import to a direct import.")); + error::Error(error::Line(peek().value(), "expected an identifier but got a string", + error::ERR, "change the string import to a direct import.")); } while (!captured_import && not_end()) { @@ -169,7 +146,198 @@ void Preprocessor::parse_import() { import_path, current_feature, alias, import_end); } - // print_debug(explicit_imports, import_path, alias, current_feature, import_start, import_end, - // source_tokens); + if (!current_feature.empty()) { + error::Error(error::Line(current_feature, "incomplete import statement", error::FATAL, + "complete the import statement.")); + std::exit(1); + } + + std::string string_import_path = resolve_import_path(import_path); + + std::optional temp_path = + file_system::resolve_path(string_import_path + ".hlx", rel_path.back().string()); + + if (!temp_path.has_value()) { + string_import_path = resolve_import_path(import_path.slice(0, import_path.size() - 2)); + temp_path = + file_system::resolve_path(string_import_path + ".hlx", rel_path.back().string()); + explicit_imports.push_back(import_path.slice(import_path.size() - 1, import_path.size())); + } + + if (!temp_path.has_value()) { + string_import_path = resolve_import_path_with_namespace(import_path); + temp_path = + file_system::resolve_path(string_import_path + ".hlx", rel_path.back().string()); + + if (!temp_path.has_value()) { + TokenList tmp; + if_missing_relative_parent(string_import_path, temp_path, tmp, import_path, 1); + } + } + + // TODO: handle module imports from PATH and manually included dirs. + + if (!temp_path.has_value()) { + error::Error(error::Line(import_path, "import path not found", error::FATAL, + "validate the import path provided. if you meant to do an " + "explicit import, use import ..::{...} syntax.")); + } + + string_import_path = temp_path->string(); + + // add the import to the import tree + ImportNode *current_node = import_tree->add_import(string_import_path, parent_node); + // check for circular imports + if (is_circular_import(current_node)) { + error::Error(error::Line(import_path, "circular import detected", error::FATAL, + "the module is already imported earlier in the import chain.")); + } + + if (error::HAS_ERRORED) { + std::exit(1); + } + + TokenList namespace_name = alias.empty() ? import_path : alias; + TokenList parsed_source = + lexer::Lexer(file_system::read_file(string_import_path), string_import_path).tokenize(); + + parsed_source = parser::preprocessor::Preprocessor(parsed_source).parse(current_node); + parsed_source.pop_back(); + parsed_source.insert(parsed_source.begin(), + Token(tokens::PUNCTUATION_OPEN_BRACE, string_import_path)); + + for (auto const &tok : std::ranges::reverse_view(namespace_name)) { + parsed_source.insert(parsed_source.begin(), tok); + } + + parsed_source.insert(parsed_source.begin(), + Token(tokens::KEYWORD_NAMESPACE, string_import_path)); + parsed_source.insert(parsed_source.end(), + Token(tokens::PUNCTUATION_CLOSE_BRACE, string_import_path)); + + complete_import = { + .filepath = temp_path.value(), + .module_base = rel_path.back(), + .module = import_path, + .relative = namespace_name, + .source = parsed_source, + .explicit_imports = explicit_imports, + .start = import_start, + .end = import_end, + }; + + imports.push_back(complete_import); + + return complete_import; +} + +bool Preprocessor::if_missing_relative_parent(std::string &string_import_path, + std::optional &temp_path, + TokenList &explicit_imports, TokenList &import_path, + u32 depth) { + if (depth > u32(import_path.size() / 2)) { + return false; + } + + // try resolving with namespace and without last token + string_import_path = + resolve_import_path_with_namespace(import_path.slice(0, import_path.size() - (depth + 1))); + + temp_path = file_system::resolve_path(string_import_path + ".hlx", rel_path.back().string()); + + // add the last token back as explicit import + explicit_imports.push_back( + import_path.slice(import_path.size() - depth, import_path.size())[0]); + + import_path = import_path.slice(0, import_path.size() - (depth + 1)); + + return true; +} + +// void Preprocessor::process_import_path(TokenList &import_path, +// std::vector &explicit_imports, +// bool &module_import) { +// if (explicit_imports.size() == 1) { // handle single explicit import +// Token temp = explicit_imports[0][0]; +// import_path.push_back(Token(temp.line_number(), temp.column_number(), temp.length(), +// temp.offset(), "::", std::string(temp.file_name()))); +// +// for (const auto &tok_list : explicit_imports) { +// for (const auto &tok : tok_list) { +// import_path.push_back(Token(tok.line_number(), tok.column_number(), tok.length(), +// tok.offset(), tok.value(), +// std::string(tok.file_name()), +// tok.token_kind_repr())); +// } +// } +// +// explicit_imports.clear(); // clear explicit imports after processing +// } +// } + +std::string Preprocessor::resolve_import_path(const TokenList &import_path) { + std::string string_import_path; + for (const auto &tok : import_path) { + switch (tok.token_kind()) { + case tokens::IDENTIFIER: + string_import_path += tok.value(); // append identifier to path + break; + case tokens::OPERATOR_SCOPE: + string_import_path += "/"; // replace scope operator with path separator + break; + default: + break; + } + } + return string_import_path; // return the resolved path as a string +} + +std::string Preprocessor::resolve_import_path_with_namespace(const TokenList &import_path) { + std::string string_import_path; + Token _tok; + + for (const auto &tok : import_path) { + switch (tok.token_kind()) { + case tokens::IDENTIFIER: + string_import_path += tok.value(); // append identifier to path + _tok = tok; + break; + case tokens::OPERATOR_SCOPE: + string_import_path += "/"; // replace scope operator with path separator + break; + default: + break; + } + } + + string_import_path += "/"; // add a path separator before the last token + string_import_path += _tok.value(); // append the last token value to the path + return string_import_path; // return the resolved path with namespace as a string +} + +/*=====----------------------------------- debug functions ----------------------------------=====*/ +inline void print_debug(auto explicit_imports, auto import_path, auto alias, auto current_feature, + auto import_start, auto import_end, auto source_tokens) { + std::cout << "explicit_imports: " + << "\n"; + for (auto token_list : explicit_imports) { + print_tokens(token_list); + } + std::cout << "import_path: " + << "\n"; + print_tokens(import_path); + std::cout << "alias: " + << "\n"; + print_tokens(alias); + std::cout << "current_feature: " + << "\n"; + print_tokens(current_feature); + std::cout << "start: " << std::to_string(import_start) << "\n"; + std::cout << "end: " << std::to_string(import_end) << "\n"; + TokenList temp_sec = source_tokens.slice(u64(import_start), u64(import_end)); + std::cout << "slice: " + << "\n"; + print_tokens(temp_sec); + std::cout << std::string(60, '-') << "\n"; } } // namespace parser::preprocessor diff --git a/source/token/include/token.hh b/source/token/include/token.hh index 9e2032b0..d2f17cab 100644 --- a/source/token/include/token.hh +++ b/source/token/include/token.hh @@ -19,9 +19,8 @@ #include #include #include -#include - #include +#include namespace token { @@ -88,6 +87,8 @@ struct Token { */ Token(); + explicit Token(tokens token_type, const std::string &filename, std::string value = ""); + /** * @brief Destructor. */ @@ -171,6 +172,12 @@ struct Token { * @param value String file name. */ void set_file_name(const std::string &file_name); + + bool operator==(const Token &rhs) const { + return (line == rhs.line && column == rhs.column && len == rhs.len && + _offset == rhs._offset && kind == rhs.kind && val == rhs.val && + filename == rhs.filename); + } }; /** @@ -289,6 +296,22 @@ class TokenList : public std::vector { * @param end End index of the range to remove. */ void insert_remove(TokenList &tokens, u64 start, u64 end); + + bool operator==(const TokenList &rhs) const { + // First, compare sizes + if (size() != rhs.size()) { + return false; + } + + // Then, compare each element + for (size_t i = 0; i < size(); ++i) { + if (at(i) != rhs.at(i)) { // Assuming Token has operator== + return false; + } + } + + return true; + } }; /** diff --git a/source/token/source/token.cc b/source/token/source/token.cc index 24683a1b..1a3040ac 100644 --- a/source/token/source/token.cc +++ b/source/token/source/token.cc @@ -12,13 +12,13 @@ * https://helix-lang.com/ for more information. */ -#include - +#include #include #include #include +#include -#include +#include "token/include/generate.hh" namespace token { @@ -50,6 +50,19 @@ Token::Token() : kind(tokens::WHITESPACE) , val("<>") {} +// custom intrinsics constructor +Token::Token(tokens token_type, const std::string &filename, std::string value) + : kind(token_type) + , filename(filename) { + + if (value.empty()) { + value = std::string(tokens_map.at(token_type).value()); + } + + len = value.length(); + val = std::move(value); +} + // Copy Constructor Token::Token(const Token &other) { line = other.line; @@ -194,14 +207,13 @@ TokenList TokenList::slice(u64 start, u64 end) { if (end > this->size()) { end = this->size(); } - + auto start_index = static_cast::difference_type>(start); auto end_index = static_cast::difference_type>(end); - + return {this->filename, this->begin() + start_index, this->begin() + end_index}; } - /** * @brief Replaces tokens in the list from start to end with the provided tokens. * diff --git a/source/tools/controllers/include/compiler.hh b/source/tools/controllers/include/compiler.hh new file mode 100644 index 00000000..e1fcad5d --- /dev/null +++ b/source/tools/controllers/include/compiler.hh @@ -0,0 +1,40 @@ +/** + * @author Dhruvan Kartik + * @copyright Copyright (c) 2024 (CC BY 4.0) + * + * @note This code is part of the Helix Language Project and is licensed under the Attribution 4.0 + * International license (CC BY 4.0). You are allowed to use, modify, redistribute, and create + * derivative works, even for commercial purposes, provided that you give appropriate credit, + * provide a link to the license, and indicate if changes were made. For more information, please + * visit: https://creativecommons.org/licenses/by/4.0/ SPDX-License-Identifier: CC-BY-4.0 + * + * @note This code is provided by the creators of Helix. Visit our website at: + * https://helix-lang.com/ for more information. + */ +#ifndef __COMPILER_H__ +#define __COMPILER_H__ + +#include + +namespace clang { +using namespace std; + +struct Arguments { + vector args; //> additional arguments to pass + vector libs; //> lib dirs, such as for libcxx + vector incs; //> include dirs + vector link; //> link dirs +}; + +class Compiler { + private: + Arguments args; + + public: + Compiler() = default; + explicit Compiler(Arguments args); +}; +} + + +#endif // __COMPILER_H__ \ No newline at end of file diff --git a/source/tools/controllers/source/resolve.cc b/source/tools/controllers/source/resolve.cc index 75f4fe58..60ca03ec 100644 --- a/source/tools/controllers/source/resolve.cc +++ b/source/tools/controllers/source/resolve.cc @@ -62,11 +62,6 @@ std::optional resolve_path(const std::string &resolve, return path; } - std::filesystem::path parent_path = path.parent_path(); - if (std::filesystem::exists(parent_path)) { - return path; - } - return std::nullopt; } } \ No newline at end of file diff --git a/tests/main.hlx b/tests/main.hlx index c6a92673..8a42d7fd 100644 --- a/tests/main.hlx +++ b/tests/main.hlx @@ -1,26 +1,5 @@ -using "c++" { - import "test"; // test.cc - import "test.hh"; // redundant -} - -import some_import; - -import foo::bar; - -import foo::bar as other_thing; - -import foo::bar::{print, this}; - -import std::foo::{bar} as foo; - -/* `import some_import` would expand too: - -namespace some_import { - fn add_sum(a: int, b: int) -> int { - return a + b; - } -} -*/ +import test_imports; // module import +import test_imports::some_import; // file import fn main() { print("hey there"); diff --git a/tests/some_import.hlx b/tests/test_imports/some_import.hlx similarity index 71% rename from tests/some_import.hlx rename to tests/test_imports/some_import.hlx index eb2f231b..ef3ec3ac 100644 --- a/tests/some_import.hlx +++ b/tests/test_imports/some_import.hlx @@ -1,3 +1,5 @@ +import test_imports; + fn add_sum(a: int, b: int) -> int { return a + b; } \ No newline at end of file diff --git a/tests/test_imports/test_imports.hlx b/tests/test_imports/test_imports.hlx new file mode 100644 index 00000000..01eef10c --- /dev/null +++ b/tests/test_imports/test_imports.hlx @@ -0,0 +1 @@ +import some_import; \ No newline at end of file