From 07e5fc400493093cd03493ef469b9b97fa1fe87a Mon Sep 17 00:00:00 2001
From: A404M <ahmadmahmoudiprogrammer@gmail.com>
Date: Thu, 24 Apr 2025 02:30:09 +0330
Subject: add @import

---
 src/compiler/ast-tree.c | 380 +++++++++++++++++++++++++++++++++++-------------
 src/compiler/ast-tree.h |  13 +-
 src/utils/file.c        |  43 ++++++
 src/utils/file.h        |   4 +-
 src/utils/string.c      |   7 +
 src/utils/string.h      |   2 +
 6 files changed, 344 insertions(+), 105 deletions(-)

(limited to 'src')

diff --git a/src/compiler/ast-tree.c b/src/compiler/ast-tree.c
index c93b477..2a44e39 100644
--- a/src/compiler/ast-tree.c
+++ b/src/compiler/ast-tree.c
@@ -2,6 +2,7 @@
 
 #include "compiler/parser.h"
 #include "runner/runner.h"
+#include "utils/file.h"
 #include "utils/log.h"
 #include "utils/memory.h"
 #include "utils/string.h"
@@ -824,10 +825,16 @@ bool astTreeShouldDelete(AstTree *tree) {
 }
 
 void astTreeRootDelete(AstTreeRoot *root) {
+  for (size_t i = 0; i < root->trees.size; ++i) {
+    astTreeDelete(root->trees.data[i]);
+  }
   for (size_t i = 0; i < root->variables.size; ++i) {
     astTreeVariableDelete(root->variables.data[i]);
   }
   free(root->variables.data);
+  free(root->trees.data);
+  free(root->filePath);
+  free(root->imports);
   free(root);
 }
 
@@ -1259,12 +1266,7 @@ AstTreeVariables copyAstTreeVariables(AstTreeVariables variables,
 }
 
 AstTreeRoots makeAstTree(const char *filePath) {
-  size_t parserNodes_capacity = 1;
-  ParserNode **parserNodes =
-      a404m_malloc(parserNodes_capacity * sizeof(*parserNodes));
-  size_t parserNodes_size = 1;
-
-  parserNodes[0] = parserFromPath(filePath);
+  char **filePathes = a404m_malloc(0 * sizeof(*filePathes));
 
   size_t roots_size = 0;
   AstTreeRoots roots = {
@@ -1272,106 +1274,139 @@ AstTreeRoots makeAstTree(const char *filePath) {
       .size = 0,
   };
 
-  for (size_t i = 0; i < parserNodes_size; ++i) {
-    ParserNode *parserNode = parserNodes[i];
-    AstTreeRoot *root = makeAstRoot(parserNode);
-    if (root == NULL) {
+  if (getAstTreeRoot(strClone(filePath), &roots, &filePathes) == NULL) {
+    goto RETURN_ERROR;
+  }
+
+  for (size_t i = 0; i < roots.size; ++i) {
+    AstTreeRoot *root = roots.data[i];
+    if (!setAllTypesRoot(root)) {
       goto RETURN_ERROR;
     }
-    for (size_t i = 0; i < root->trees.size; ++i) {
-      AstTree *tree = root->trees.data[i];
-      if (tree->token == AST_TREE_TOKEN_FUNCTION_CALL) {
-        AstTreeFunctionCall *tree_metadata = tree->metadata;
-        AstTree *operand = tree_metadata->function;
-        if (operand->token == AST_TREE_TOKEN_BUILTIN) {
-          AstTreeBuiltin *operand_metadata = operand->metadata;
-          if (operand_metadata->token == AST_TREE_BUILTIN_TOKEN_IMPORT) {
-            if (tree_metadata->parameters_size != 1) {
-              printError(tree->str_begin, tree->str_end,
-                         "Too many or too few arguments");
-              goto RETURN_ERROR;
-            }
-            AstTreeSetTypesHelper helper = {
-                .lookingType = NULL,
-                .dependencies.data = NULL,
-                .dependencies.size = 0,
-                .variables = root->variables,
-            };
-            if (!setAllTypes(tree, helper, NULL, NULL)) {
-              goto RETURN_ERROR;
-            }
-            AstTree *parameter = tree_metadata->parameters[0].value;
-            if (!isConstByValue(parameter)) {
-              printError(parameter->str_begin, parameter->str_end,
-                         "Is not constant");
-              goto RETURN_ERROR;
-            }
-            parameter = getValue(parameter);
-            if (parameter == NULL) {
-              goto RETURN_ERROR;
-            }
+  }
 
-            AstTreeBracket *type_metadata =
-                a404m_malloc(sizeof(*type_metadata));
-            type_metadata->operand = &AST_TREE_U8_TYPE;
+  free(filePathes);
 
-            type_metadata->parameters.size = 0;
-            type_metadata->parameters.data =
-                a404m_malloc(0 * sizeof(*type_metadata->parameters.data));
+  return roots;
 
-            AstTree *type = newAstTree(AST_TREE_TOKEN_TYPE_ARRAY, type_metadata,
-                                       &AST_TREE_TYPE_TYPE, NULL, NULL);
+RETURN_ERROR:
+  return AST_TREE_ROOTS_ERROR;
+}
 
-            if (!typeIsEqual(type, parameter->type)) {
-              printError(parameter->str_begin, parameter->str_end,
-                         "Type mismatch (must be a []u8 aka string)");
-              goto RETURN_ERROR;
-            }
+AstTreeRoot *getAstTreeRoot(char *filePath, AstTreeRoots *roots,
+                            char ***filePathes) {
+  for (size_t i = 0; i < roots->size; ++i) {
+    if (strcmp(*filePathes[i], filePath) == 0) {
+      free(filePath);
+      return roots->data[i];
+    }
+  }
 
-            char *str = u8ArrayToCString(parameter);
+  const size_t filePathes_size =
+      a404m_malloc_usable_size(*filePathes) / sizeof(**filePathes);
+  if (filePathes_size == roots->size) {
+    *filePathes =
+        a404m_realloc(*filePathes, (filePathes_size + filePathes_size / 2 + 1) *
+                                       sizeof(**filePathes));
+  }
+  *filePathes[roots->size] = filePath;
 
-            const size_t imported_size =
-                a404m_malloc_usable_size(root->imported) /
-                sizeof(*root->imported);
-            if (imported_size == root->imported_size) {
-              root->imported = a404m_realloc(
-                  root->imported, (imported_size + imported_size / 2 + 1) *
-                                      sizeof(*root->imported));
-            }
-            root->imported[root->imported_size++] = str;
+  ParserNode *parserNode = parserFromPath(filePath);
+  if (parserNode == NULL) {
+    goto RETURN_ERROR;
+  }
+  AstTreeRoot *root = makeAstRoot(parserNode, filePath);
+  parserNodeDelete(parserNode);
+  if (root == NULL) {
+    goto RETURN_ERROR;
+  }
 
-            astTreeDelete(type);
+  for (size_t i = 0; i < root->trees.size; ++i) {
+    AstTree *tree = root->trees.data[i];
+    if (tree->token == AST_TREE_TOKEN_FUNCTION_CALL) {
+      AstTreeFunctionCall *tree_metadata = tree->metadata;
+      AstTree *operand = tree_metadata->function;
+      if (operand->token == AST_TREE_TOKEN_BUILTIN) {
+        AstTreeBuiltin *operand_metadata = operand->metadata;
+        if (operand_metadata->token == AST_TREE_BUILTIN_TOKEN_IMPORT) {
+          AstTreeSetTypesHelper helper = {
+              .lookingType = NULL,
+              .dependencies.data = NULL,
+              .dependencies.size = 0,
+              .variables = root->variables,
+          };
+          if (!setAllTypes(tree, helper, NULL, NULL)) {
+            goto RETURN_ERROR;
+          }
+          AstTree *parameter = tree_metadata->parameters[0].value;
+          if (!isConstByValue(parameter)) {
+            printError(parameter->str_begin, parameter->str_end,
+                       "Is not constant");
+            goto RETURN_ERROR;
+          }
+          parameter = getValue(parameter);
+          if (parameter == NULL) {
+            goto RETURN_ERROR;
           }
-        }
-      }
-    }
 
-    if (roots_size == roots.size) {
-      roots_size += roots_size / 2 + 13;
-      roots.data = a404m_realloc(roots.data, roots_size);
-    }
-    roots.data[roots.size++] = root;
-  }
+          AstTreeBracket *type_metadata = a404m_malloc(sizeof(*type_metadata));
+          type_metadata->operand = &AST_TREE_U8_TYPE;
 
-  for (size_t i = 0; i < roots.size; ++i) {
-    AstTreeRoot *root = roots.data[i];
-    if (!setAllTypesRoot(root)) {
-      goto RETURN_ERROR;
+          type_metadata->parameters.size = 0;
+          type_metadata->parameters.data =
+              a404m_malloc(0 * sizeof(*type_metadata->parameters.data));
+
+          AstTree *type = newAstTree(AST_TREE_TOKEN_TYPE_ARRAY, type_metadata,
+                                     &AST_TREE_TYPE_TYPE, NULL, NULL);
+
+          if (!typeIsEqual(type, parameter->type)) {
+            printError(parameter->str_begin, parameter->str_end,
+                       "Type mismatch (must be a []u8 aka string)");
+            goto RETURN_ERROR;
+          }
+
+          char *str = u8ArrayToCString(parameter);
+          astTreeDelete(parameter);
+
+          const size_t imports_size =
+              a404m_malloc_usable_size(root->imports) / sizeof(*root->imports);
+          if (imports_size == root->imports_size) {
+            root->imports = a404m_realloc(
+                root->imports,
+                (imports_size + imports_size / 2 + 1) * sizeof(*root->imports));
+          }
+
+          AstTreeRoot *import =
+              getAstTreeRoot(joinToPathOf(filePath, str), roots, filePathes);
+          free(str);
+
+          if (import == NULL) {
+            goto RETURN_ERROR;
+          }
+
+          root->imports[root->imports_size++] = import;
+
+          astTreeDelete(type);
+        }
+      }
     }
   }
 
-  for (size_t i = 0; i < parserNodes_size; ++i) {
-    parserNodeDelete(parserNodes[i]);
+  const size_t roots_size =
+      a404m_malloc_usable_size(roots->data) / sizeof(*roots->data);
+  if (roots_size == roots->size) {
+    roots->data = a404m_realloc(roots->data, (roots_size + roots_size / 2 + 1) *
+                                                 sizeof(*roots->data));
   }
-  free(parserNodes);
+  roots->data[roots->size++] = root;
 
-  return roots;
+  return root;
 
 RETURN_ERROR:
-  return AST_TREE_ROOTS_ERROR;
+  return NULL;
 }
 
-AstTreeRoot *makeAstRoot(ParserNode *parsedRoot) {
+AstTreeRoot *makeAstRoot(ParserNode *parsedRoot, char *filePath) {
   if (parsedRoot->token != PARSER_TOKEN_ROOT) {
     return NULL;
   }
@@ -1383,8 +1418,9 @@ AstTreeRoot *makeAstRoot(ParserNode *parsedRoot) {
   root->variables.data =
       a404m_malloc(nodes->size * sizeof(*root->variables.data));
   root->variables.size = 0;
-  root->imported = a404m_malloc(0 * sizeof(*root->imported));
-  root->imported_size = 0;
+  root->imports = a404m_malloc(0 * sizeof(*root->imports));
+  root->imports_size = 0;
+  root->filePath = filePath;
 
   AstTreeVariables *variables = &root->variables;
   static const size_t variables_size = 1;
@@ -1402,7 +1438,8 @@ AstTreeRoot *makeAstRoot(ParserNode *parsedRoot) {
       goto RETURN_ERROR;
     }
     ParserNode *node = (ParserNodeSingleChildMetadata *)eol->metadata;
-    if (node->token == PARSER_TOKEN_KEYWORD_COMPTIME) {
+    if (node->token == PARSER_TOKEN_KEYWORD_COMPTIME ||
+        node->token == PARSER_TOKEN_FUNCTION_CALL) {
       continue;
     } else if (node->token == PARSER_TOKEN_CONSTANT ||
                node->token == PARSER_TOKEN_VARIABLE) {
@@ -1429,7 +1466,7 @@ AstTreeRoot *makeAstRoot(ParserNode *parsedRoot) {
                     root->variables.size * sizeof(*root->variables.data));
 
   root->trees.data = a404m_malloc((nodes->size - root->variables.size) *
-                                  sizeof(*root->variables.data));
+                                  sizeof(*root->trees.data));
   root->trees.size = 0;
 
   for (size_t i = 0; i < nodes->size; ++i) {
@@ -1440,24 +1477,44 @@ AstTreeRoot *makeAstRoot(ParserNode *parsedRoot) {
       goto RETURN_ERROR;
     }
     ParserNode *node = (ParserNodeSingleChildMetadata *)eol->metadata;
+    AstTree *tree;
     if (node->token == PARSER_TOKEN_KEYWORD_COMPTIME) {
-      AstTree *tree = astTreeParse(node, &helper);
+      tree = astTreeParse(node, &helper);
+      if (tree == NULL) {
+        goto RETURN_ERROR;
+      }
+    } else if (node->token == PARSER_TOKEN_FUNCTION_CALL) {
+      tree = astTreeParse(node, &helper);
       if (tree == NULL) {
         goto RETURN_ERROR;
       }
-      root->trees.data[root->trees.size] = tree;
-      root->trees.size += 1;
+
+      AstTreeFunctionCall *tree_metadata = tree->metadata;
+      AstTree *operand = tree_metadata->function;
+      if (operand->token == AST_TREE_TOKEN_BUILTIN) {
+        AstTreeBuiltin *operand_metadata = operand->metadata;
+        if (operand_metadata->token == AST_TREE_BUILTIN_TOKEN_IMPORT) {
+          if (tree_metadata->parameters_size == 1) {
+            goto PUSH;
+          }
+        }
+      }
+      printError(tree->str_begin, tree->str_end, "Bad node");
+      goto RETURN_ERROR;
     } else {
       continue;
     }
+  PUSH:
+    root->trees.data[root->trees.size++] = tree;
   }
 
-  for (size_t i = 0; i < nodes->size; ++i) {
+  for (size_t j = 0, i = 0; j < nodes->size; ++j) {
     ParserNode *node =
-        (ParserNodeSingleChildMetadata *)nodes->data[i]->metadata;
+        (ParserNodeSingleChildMetadata *)nodes->data[j]->metadata;
     ParserNodeVariableMetadata *node_metadata = node->metadata;
 
-    if (node->token == PARSER_TOKEN_KEYWORD_COMPTIME) {
+    if (node->token == PARSER_TOKEN_KEYWORD_COMPTIME ||
+        node->token == PARSER_TOKEN_FUNCTION_CALL) {
       continue;
     } else if (node->token == PARSER_TOKEN_CONSTANT ||
                node->token == PARSER_TOKEN_VARIABLE) {
@@ -1573,6 +1630,7 @@ AstTreeRoot *makeAstRoot(ParserNode *parsedRoot) {
         root->variables.data[i]->value = NULL;
         root->variables.data[i]->initValue = value;
       }
+      ++i;
     } else {
       printError(node->str_begin, node->str_end,
                  "Only variables are allowed here");
@@ -3509,32 +3567,77 @@ bool isEqualVariable(AstTreeVariable *left, AstTreeVariable *right) {
   }
 }
 
+void allOfVariablesWithImport(AstTreeVariables *variables, AstTreeRoot *root,
+                              AstTreeRoots *checkedRoots) {
+  for (size_t i = 0; i < checkedRoots->size; ++i) {
+    if (checkedRoots->data[i] == root) {
+      return;
+    }
+  }
+
+  size_t checkedRoots_size = a404m_malloc_usable_size(checkedRoots->data) /
+                             sizeof(*checkedRoots->data);
+  if (checkedRoots_size == checkedRoots->size) {
+    checkedRoots->data = a404m_realloc(
+        checkedRoots->data, (checkedRoots_size + checkedRoots_size / 2 + 1) *
+                                sizeof(*checkedRoots->data));
+  }
+  checkedRoots->data[checkedRoots->size++] = root;
+
+  variables->data =
+      a404m_realloc(variables->data, (variables->size + root->variables.size) *
+                                         sizeof(*variables->data));
+  for (size_t i = 0; i < root->variables.size; ++i) {
+    variables->data[variables->size++] = root->variables.data[i];
+  }
+
+  for (size_t i = 0; i < root->imports_size; ++i) {
+    allOfVariablesWithImport(variables, root->imports[i], checkedRoots);
+  }
+}
+
 bool setAllTypesRoot(AstTreeRoot *root) {
-  AstTreeSetTypesHelper setTypesHelper = {
+  AstTreeRoots checkedRoots = {
+      .data = a404m_malloc(0),
+      .size = 0,
+  };
+  AstTreeVariables variables = (AstTreeVariables){
+      .data = a404m_malloc(0),
+      .size = 0,
+  };
+  allOfVariablesWithImport(&variables, root, &checkedRoots);
+  AstTreeSetTypesHelper helper = {
       .lookingType = NULL,
       .dependencies =
           {
               .data = NULL,
               .size = 0,
           },
-      .variables = root->variables,
+      .variables = variables,
   };
 
   for (size_t i = 0; i < root->variables.size; ++i) {
     AstTreeVariable *variable = root->variables.data[i];
-    if (!setTypesAstVariable(variable, setTypesHelper)) {
-      return false;
+    if (!setTypesAstVariable(variable, helper)) {
+      goto RETURN_ERROR;
     }
   }
 
   for (size_t i = 0; i < root->trees.size; ++i) {
     AstTree *tree = root->trees.data[i];
-    if (!setAllTypes(tree, setTypesHelper, NULL, NULL)) {
-      return false;
+    if (!setAllTypes(tree, helper, NULL, NULL)) {
+      goto RETURN_ERROR;
     }
   }
 
+  free(checkedRoots.data);
+  free(variables.data);
   return true;
+
+RETURN_ERROR:
+  free(checkedRoots.data);
+  free(variables.data);
+  return false;
 }
 
 bool setAllTypes(AstTree *tree, AstTreeSetTypesHelper helper,
@@ -4738,6 +4841,68 @@ bool setTypesBuiltin(AstTree *tree, AstTreeSetTypesHelper helper,
     }
     return false;
   }
+  case AST_TREE_BUILTIN_TOKEN_IMPORT: {
+    if (functionCall->parameters_size == 1) {
+      AstTree *file = NULL;
+
+      static char VARIABLE_STR[] = "variable";
+      static const size_t VARIABLE_STR_SIZE =
+          sizeof(VARIABLE_STR) / sizeof(*VARIABLE_STR) - sizeof(*VARIABLE_STR);
+
+      for (size_t i = 0; i < functionCall->parameters_size; ++i) {
+        AstTreeFunctionCallParam param = functionCall->parameters[i];
+        const size_t param_name_size = param.nameEnd - param.nameBegin;
+
+        if (param_name_size == 0) {
+          if (file == NULL) {
+            file = param.value;
+          } else {
+            printError(param.value->str_begin, param.value->str_end,
+                       "Bad paramter");
+            return false;
+          }
+        } else if (param_name_size == VARIABLE_STR_SIZE &&
+                   strncmp(param.nameBegin, VARIABLE_STR, VARIABLE_STR_SIZE) ==
+                       0 &&
+                   file == NULL) {
+          file = param.value;
+        } else {
+          printError(param.value->str_begin, param.value->str_end,
+                     "Bad paramter");
+          return false;
+        }
+      }
+
+      if (file == NULL) {
+        return false;
+      }
+
+      AstTreeTypeFunction *type_metadata = a404m_malloc(sizeof(*type_metadata));
+      type_metadata->arguments_size = 1;
+      type_metadata->arguments = a404m_malloc(
+          type_metadata->arguments_size * sizeof(*type_metadata->arguments));
+
+      // TODO: change
+      type_metadata->returnType = copyAstTree(&AST_TREE_TYPE_TYPE);
+
+      type_metadata->arguments[0] = (AstTreeTypeFunctionArgument){
+          .type = copyAstTree(file->type),
+          .name_begin = VARIABLE_STR,
+          .name_end = VARIABLE_STR + VARIABLE_STR_SIZE,
+          .str_begin = NULL,
+          .str_end = NULL,
+      };
+
+      tree->type = newAstTree(AST_TREE_TOKEN_TYPE_FUNCTION, type_metadata,
+                              &AST_TREE_TYPE_TYPE, NULL, NULL);
+      return true;
+    } else {
+      printError(tree->str_begin, tree->str_end,
+                 "Too many or too few arguments");
+      return false;
+    }
+    return false;
+  }
   case AST_TREE_BUILTIN_TOKEN__SIZE__:
   }
   UNREACHABLE;
@@ -4845,5 +5010,18 @@ char *u8ArrayToCString(AstTree *tree) {
   for (size_t i = 0; i < object->variables.size; ++i) {
     str[i] = *(AstTreeInt *)object->variables.data[i]->value->metadata;
   }
+  str[object->variables.size] = '\0';
   return str;
 }
+
+AstTree *makeStringType() {
+  AstTreeBracket *type_metadata = a404m_malloc(sizeof(*type_metadata));
+  type_metadata->operand = &AST_TREE_U8_TYPE;
+
+  type_metadata->parameters.size = 0;
+  type_metadata->parameters.data =
+      a404m_malloc(0 * sizeof(*type_metadata->parameters.data));
+
+  return newAstTree(AST_TREE_TOKEN_TYPE_ARRAY, type_metadata,
+                    &AST_TREE_TYPE_TYPE, NULL, NULL);
+}
diff --git a/src/compiler/ast-tree.h b/src/compiler/ast-tree.h
index 300be41..ce24f6d 100644
--- a/src/compiler/ast-tree.h
+++ b/src/compiler/ast-tree.h
@@ -126,10 +126,11 @@ typedef struct AstTrees {
 } AstTrees;
 
 typedef struct AstTreeRoot {
+  char *filePath;
   AstTreeVariables variables;
   AstTrees trees;
-  char **imported;
-  size_t imported_size;
+  struct AstTreeRoot **imports;
+  size_t imports_size;
 } AstTreeRoot;
 
 typedef struct AstTreeRoots {
@@ -280,7 +281,9 @@ AstTreeVariables copyAstTreeVariables(AstTreeVariables variables,
                                       size_t variables_size);
 
 AstTreeRoots makeAstTree(const char *filePath);
-AstTreeRoot *makeAstRoot(ParserNode *parsedRoot);
+AstTreeRoot *getAstTreeRoot(char *filePath, AstTreeRoots *roots,
+                            char ***filePathes);
+AstTreeRoot *makeAstRoot(ParserNode *parsedRoot, char *filePath);
 
 bool pushVariable(AstTreeHelper *helper, AstTreeVariables *variables,
                   AstTreeVariable *variable);
@@ -331,6 +334,8 @@ bool isIntType(AstTree *type);
 bool isEqual(AstTree *left, AstTree *right);
 bool isEqualVariable(AstTreeVariable *left, AstTreeVariable *right);
 
+void allOfVariablesWithImport(AstTreeVariables *variables, AstTreeRoot *root,
+                              AstTreeRoots *checkedRoots);
 bool setAllTypesRoot(AstTreeRoot *root);
 bool setAllTypes(AstTree *tree, AstTreeSetTypesHelper helper,
                  AstTreeFunction *function, AstTreeFunctionCall *functionCall);
@@ -382,3 +387,5 @@ bool setTypesAstVariable(AstTreeVariable *variable,
 bool setTypesAstInfix(AstTreeInfix *infix, AstTreeSetTypesHelper helper);
 
 char *u8ArrayToCString(AstTree *tree);
+
+AstTree *makeStringType();
diff --git a/src/utils/file.c b/src/utils/file.c
index 714dc2c..340875c 100644
--- a/src/utils/file.c
+++ b/src/utils/file.c
@@ -80,3 +80,46 @@ size_t getFileIndex(const char *filePath) {
 
   return fileCodes_length;
 }
+
+char *joinToPathOf(const char *original, const char *file) {
+  size_t result_size = 0;
+  for (size_t i = 0; original[i] != '\0'; ++i) {
+    if (original[i] == '/') {
+      result_size = i;
+    }
+  }
+  char *result =
+      a404m_malloc((result_size + 1 + strlen(file) + 1) * sizeof(*result));
+
+  strncpy(result, original, result_size);
+  result[result_size++] = '/';
+
+  bool startOfDirName = true;
+  for (size_t i = 0; file[i] != '\0'; ++i) {
+    if (startOfDirName && file[i + 1] == '.') {
+      if (file[i + 2] == '/') {
+        i += 2;
+        continue;
+      } else if (file[i + 2] == '.' && file[i + 3] == '/') {
+        bool found = false;
+        for (size_t j = i - 2; j != -1ULL; --j) {
+          if (result[j] == '/') {
+            result_size = j;
+            found = true;
+            break;
+          }
+        }
+        if (!found) {
+          result_size = 0;
+        }
+        continue;
+      }
+    }
+    result[result_size++] = file[i];
+  }
+
+  result[result_size++] = '\0';
+  result = a404m_realloc(result, result_size);
+
+  return result;
+}
diff --git a/src/utils/file.h b/src/utils/file.h
index f0b0d8c..7b0dc37 100644
--- a/src/utils/file.h
+++ b/src/utils/file.h
@@ -10,7 +10,9 @@ extern size_t fileCodes_length;
 void fileInit();
 void fileDelete();
 
-void filePush(const char *filePath,char *code);
+void filePush(const char *filePath, char *code);
 
 char *readWholeFile(const char *filePath);
 size_t getFileIndex(const char *filePath);
+
+char *joinToPathOf(const char *original, const char *file);
diff --git a/src/utils/string.c b/src/utils/string.c
index 16e9f9b..55d2c3e 100644
--- a/src/utils/string.c
+++ b/src/utils/string.c
@@ -85,3 +85,10 @@ char *u64ToString(u64 value) {
 
   return a404m_realloc(str, (i + 1) * sizeof(*str));
 }
+
+char *strClone(const char *str) {
+  const size_t str_length = strlen(str) + 1;
+  char *result = a404m_malloc(str_length * sizeof(*result));
+  strncpy(result, str, str_length);
+  return result;
+}
diff --git a/src/utils/string.h b/src/utils/string.h
index 54130e5..a5a0319 100644
--- a/src/utils/string.h
+++ b/src/utils/string.h
@@ -10,3 +10,5 @@ size_t searchInStringArray(const char *array[], size_t array_size,
 u64 decimalToU64(char *str_begin, char *str_end, bool *success);
 f128 numberToFloat(char *str_begin, char *str_end, bool *success);
 char* u64ToString(u64 value);
+
+char *strClone(const char *str);
-- 
cgit v1.2.3