Skip to content

Commit

Permalink
imports are correctly parsed, the right errors are shown and module i…
Browse files Browse the repository at this point in the history
…mports work.
  • Loading branch information
Ze7111 committed Jun 29, 2024
1 parent 0b9ae2b commit 52d635c
Show file tree
Hide file tree
Showing 12 changed files with 510 additions and 149 deletions.
4 changes: 3 additions & 1 deletion source/include/error/error.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
18 changes: 18 additions & 0 deletions source/include/error/error.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
}
};

/**
Expand All @@ -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.
Expand Down
42 changes: 17 additions & 25 deletions source/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,40 +21,32 @@
#include <token/include/token.hh>

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<double> diff = end - start;

//std::unique_ptr<parser::ast::ProgramNode> _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<double> diff = end - start;
std::cout << "time taken: " << diff.count()*1e+9 << "ns\n";
std::cout << " " << diff.count()*1000 << "ms\n";
return 0;
}

179 changes: 154 additions & 25 deletions source/parser/preprocessor/include/preprocessor.hh
Original file line number Diff line number Diff line change
Expand Up @@ -14,44 +14,159 @@
#ifndef __PRE_H__
#define __PRE_H__

#include <cstddef>
#include <filesystem>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include <token/include/token.hh>

namespace parser::preprocessor {
using namespace token;

struct ImportNode {
std::string module_name;
std::vector<ImportNode *> 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<int>(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<TokenList> 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<ImportTree> import_tree;
inline std::vector<import_helix> 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.
Expand All @@ -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.
Expand All @@ -76,36 +192,44 @@ 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.
*/
class Preprocessor {
private:
std::array<std::string, abi::reserved_map.size()> allowed_abi;

std::vector<import_helix> imports;
std::vector<std::string> rel_path; // if importing a module that would be the current rel path
std::vector<std::filesystem::path>
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<ImportTree> &import_tree, ImportNode *parent_node = nullptr);

void parse_import_alias(TokenList &alias, const std::vector<TokenList> &explicit_imports);
void handle_import_tokens(u32 &brace_level, bool &captured_import, bool &captured_specific,
std::vector<TokenList> &explicit_imports, TokenList &import_path,
TokenList &current_feature, TokenList &alias, u32 &import_end);
std::vector<TokenList> &explicit_imports, TokenList &import_path,
TokenList &current_feature, TokenList &alias, u32 &import_end);
void handle_open_brace_in_import(u32 &brace_level, bool &captured_specific,
std::vector<TokenList> &explicit_imports,
TokenList &current_feature);
void handle_using_abi_imports();
void parse_import_within_using();
void handle_invalid_abi_option(const std::optional<Token> &next_token);

static void process_import_path(TokenList &import_path,
std::vector<TokenList> &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<std::filesystem::path> &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]; }
Expand Down Expand Up @@ -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<ImportTree>(std::string(tokens[0].file_name()));
}
}

void parse();
TokenList parse(ImportNode *parent_node = nullptr);
};

} // namespace parser::preprocessor
Expand Down
Loading

0 comments on commit 52d635c

Please sign in to comment.