#include "tree_parser.h"

#include <compiler/error_helper/error_helper.h>
#include <compiler/lexer/lexer.h>
#include <compiler/parser/parser.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <utils/file.h>
#include <utils/memory/memory.h>
#include <utils/types.h>

const char *TREE_TOKEN_STRINGS[] = {
    "TREE_TOKEN_NONE",
    "TREE_TOKEN_ROOT",
    "TREE_TOKEN_GLOBAL_SCOPE",
    "TREE_TOKEN_LOCAL_SCOPE",
    "TREE_TOKEN_FUNCTION_CALL",
    "TREE_TOKEN_DEFINE_VARIABLE",
    "TREE_TOKEN_DEFINE_CONSTANT",
    "TREE_TOKEN_IDENTIFIER",
    "TREE_TOKEN_VALUE_STRING",
    "TREE_TOKEN_STRUCT",
    "TREE_TOKEN_FUNCTION",
};

void _printParsedTreeNode(const ParsedTree *parsedTree, int indent) {
  if (parsedTree == NULL) {
    for (int i = 0; i < indent; ++i) printf(" ");
    printf("null\n");
  }
  printf("{token=%s", TREE_TOKEN_STRINGS[parsedTree->token]);
  switch (parsedTree->token) {
    case TREE_TOKEN_NONE:
      goto RETURN_SUCCESS;
    case TREE_TOKEN_ROOT:
    case TREE_TOKEN_GLOBAL_SCOPE:
    case TREE_TOKEN_LOCAL_SCOPE:
    case TREE_TOKEN_FUNCTION_CALL:
    case TREE_TOKEN_DEFINE_VARIABLE:
    case TREE_TOKEN_IDENTIFIER:
    case TREE_TOKEN_VALUE_STRING:
    case TREE_TOKEN_STRUCT:
    case TREE_TOKEN_DEFINE_CONSTANT:
    case TREE_TOKEN_FUNCTION:
  }
  fprintf(stderr, "bad parsed tree token %d at %s:%d", parsedTree->token,
          __FILE_NAME__, __LINE__);
  exit(1);
RETURN_SUCCESS:
  printf("}\n");
};

void printParsedTreeNode(const ParsedTree *parsedTree) {
  _printParsedTreeNode(parsedTree, 0);
}

void deleteParsedTree(ParsedTree *parsedTree) {
  if (parsedTree == NULL) {
    return;
  }
  switch (parsedTree->token) {
    case TREE_TOKEN_NONE:
      goto RETURN_SUCCESS;
    case TREE_TOKEN_ROOT:
    case TREE_TOKEN_GLOBAL_SCOPE:
    case TREE_TOKEN_LOCAL_SCOPE: {
      TreeScopeMetadata *metadata = parsedTree->metadata;
      for (size_t i = 0; i < metadata->lines_size; ++i) {
        deleteParsedTree(metadata->lines[i]);
      }
      free(metadata->lines);
      free(metadata->variables);
      free(metadata);
      goto RETURN_SUCCESS;
    }
    case TREE_TOKEN_FUNCTION_CALL: {
      TreeFunctionCallMetadata *metadata = parsedTree->metadata;
      for (size_t i = 0; i < metadata->values_size; ++i) {
        deleteParsedTree(metadata->values[i]);
      }
      free(metadata->values);
      free(metadata);
      goto RETURN_SUCCESS;
    }
    case TREE_TOKEN_DEFINE_VARIABLE:
    case TREE_TOKEN_DEFINE_CONSTANT: {
      TreeDefineVariableMetadata *metadata = parsedTree->metadata;
      deleteParsedTree(metadata->value);
      free(metadata);
      goto RETURN_SUCCESS;
    }
    case TREE_TOKEN_IDENTIFIER: {
      TreeIdentifierMetadata *metadata = parsedTree->metadata;
      free(metadata);
      goto RETURN_SUCCESS;
    }
    case TREE_TOKEN_VALUE_STRING: {
      TreeStringValueMetadata *metadata = parsedTree->metadata;
      free(metadata->str);
      free(metadata);
      goto RETURN_SUCCESS;
    }
    case TREE_TOKEN_STRUCT: {
      TreeStructMetadata *metadata = parsedTree->metadata;
      free(metadata);
      goto RETURN_SUCCESS;
    }
    case TREE_TOKEN_FUNCTION: {
      TreeFunctionMetadata *metadata = parsedTree->metadata;
      TreeScopeMetadata *scope = metadata->scope;
      if (scope != NULL) {
        for (size_t i = 0; i < scope->lines_size; ++i) {
          deleteParsedTree(scope->lines[i]);
        }
        free(scope->lines);
        free(scope->variables);
      }
      for (size_t i = 0; i < metadata->params_size; ++i) {
        deleteParsedTree(metadata->params[i]->value);
        free(metadata->params[i]);
      }
      free(metadata->params);
      free(metadata);
      goto RETURN_SUCCESS;
    }
  }
  fprintf(stderr, "bad parsed tree token %d at %s:%d", parsedTree->token,
          __FILE_NAME__, __LINE__);
  exit(1);
RETURN_SUCCESS:
  free(parsedTree);
}

ParsedTree *treeParser(SourceCode *code) {
  ParserScopeMetadata *scope = a404m_malloc(sizeof(*scope));
  scope->operands_size = 0;
  scope->operands = a404m_malloc(scope->operands_size * sizeof(ParsedNode *));
  ParsedNode *const parsedNode =
      newParsedNode(NULL, NULL, PARSED_TOKEN_ROOT, scope, NULL);

  for (size_t i = 0; i < code->size; ++i) {
    ParsedNode *const nParsedNode = parser(code, i);
    ParserScopeMetadata *const nscope = nParsedNode->metadata;
    for (size_t j = 0; j < nscope->operands_size; ++j) {
      size_t scopeSize =
          a404m_malloc_usable_size(scope->operands) / sizeof(ParsedNode *);
      if (scopeSize == scope->operands_size) {
        scopeSize += scopeSize / 2 + 1;
        scope->operands =
            a404m_realloc(scope->operands, scopeSize * sizeof(ParsedNode *));
      }
      scope->operands[scope->operands_size] = nscope->operands[j];
      scope->operands_size += 1;
    }
    free(nscope->operands);
    nscope->operands = NULL;
    nscope->operands_size = 0;
    deleteParsedNode(nParsedNode);
  }
  if (parsedNode == NULL) {
    return NULL;
  }
  ParsedTree *tree = _treeParser(parsedNode, code);
  deleteParsedNode(parsedNode);
  return tree;
}

ParsedTree *_treeParser(const ParsedNode *node, SourceCode *code) {
  if (node->token == PARSED_TOKEN_ROOT) {
    return treeParseRoot(node, code);
  } else {
    return NULL;
  }
}

ParsedTree *treeParseNode(const ParsedNode *node, SourceCode *code,
                          TreeScopeMetadata *scopes[], size_t scopes_size) {
  switch (node->token) {
    case PARSED_TOKEN_ROOT:
    case PARSED_TOKEN_NONE:
      fprintf(stderr, "parsed token not allowed %s",
              PARSED_TOKEN_STRINGS[node->token]);
      return NULL;
    case PARSED_TOKEN_EOL:
      return treeParseNode((ParserEOLMetadata *)node->metadata, code, scopes,
                           scopes_size);
    case PARSED_TOKEN_IDENTIFIER:
      return treeParseIdentifier(node, code, scopes, scopes_size);
    case PARSED_TOKEN_FUNCTION_CALL:
      return treeParseFunctionCall(node, code, scopes, scopes_size);
    case PARSED_TOKEN_COMMA:
      return treeParseNode((ParserCommaMetadata *)node->metadata, code, scopes,
                           scopes_size);
    case PARSED_TOKEN_PARENTHESIS:
      return treeParseNode((ParserParenthesisMetadata *)node->metadata, code,
                           scopes, scopes_size);
    case PARSED_TOKEN_CODE_BODY:
      return treeParseLocalScope(node, code, scopes, scopes_size);
    case PARSED_TOKEN_VALUE_STRING:
      return treeParseValueString(node, code);
    case PARSED_TOKEN_DEFINE_VARIABLE:
      return treeParseVariableDefinition(node, code, scopes, scopes_size,
                                         TREE_TOKEN_DEFINE_VARIABLE);
    case PARSED_TOKEN_DEFINE_CONSTANT:
      return treeParseVariableDefinition(node, code, scopes, scopes_size,
                                         TREE_TOKEN_DEFINE_CONSTANT);
    case PARSED_TOKEN_STRUCT:
      return treeParseStruct(node, code, scopes, scopes_size);
    case PARSED_TOKEN_FUNCTION:
      return treeParseFunction(node, code, scopes, scopes_size);
    case PARSED_TOKEN_IMPORT:
      return treeParseImport(node, code, scopes, scopes_size);
    case PARSED_TOKEN_FUNCTION_PARAMS:
  }
  fprintf(stderr, "bad parsed token %d at %s:%d", node->token, __FILE_NAME__,
          __LINE__);
  return NULL;
}

ParsedTree *treeParseExpr(const ParsedNode *node, SourceCode *code,
                          TreeScopeMetadata *scopes[], size_t scopes_size) {
  switch (node->token) {
    case PARSED_TOKEN_ROOT:
    case PARSED_TOKEN_NONE:
    case PARSED_TOKEN_EOL:
    case PARSED_TOKEN_COMMA:
    case PARSED_TOKEN_DEFINE_VARIABLE:
    case PARSED_TOKEN_DEFINE_CONSTANT:
    case PARSED_TOKEN_CODE_BODY:
    case PARSED_TOKEN_IMPORT:
      printError("Parsed token %s is not an expression", code, node->strBegin,
                 node->strEnd, PARSED_TOKEN_STRINGS[node->token]);
      return NULL;
    case PARSED_TOKEN_PARENTHESIS:
    case PARSED_TOKEN_FUNCTION_CALL:
    case PARSED_TOKEN_VALUE_STRING:
    case PARSED_TOKEN_IDENTIFIER:
    case PARSED_TOKEN_STRUCT:
    case PARSED_TOKEN_FUNCTION:
      return treeParseNode(node, code, scopes, scopes_size);
    case PARSED_TOKEN_FUNCTION_PARAMS:
  }
  fprintf(stderr, "bad parsed token %d", node->token);
  return NULL;
}

ParsedTree *treeParseRoot(const ParsedNode *node, SourceCode *code) {
  return treeParseLocalScope(node, code, NULL, 0);
}

ParsedTree *treeParseLocalScope(const ParsedNode *node, SourceCode *code,
                                TreeScopeMetadata *scopes[],
                                size_t scopes_size) {
  const ParserScopeMetadata *node_metadata = node->metadata;
  ParsedNode **const operands = node_metadata->operands;
  const size_t operands_size = node_metadata->operands_size;

  ParsedTree *tree = a404m_malloc(sizeof(*tree));
  tree->token = TREE_TOKEN_LOCAL_SCOPE;
  tree->strBegin = node->strBegin;
  tree->strEnd = node->strEnd;

  TreeScopeMetadata *metadata = tree->metadata =
      a404m_malloc(sizeof(*metadata));

  metadata->variables = a404m_malloc(0);
  metadata->variables_size = 0;

  metadata->lines = a404m_malloc(0);
  metadata->lines_size = 0;

  const size_t newScopes_size = scopes_size + 1;
  TreeScopeMetadata *newScopes[newScopes_size];

  memcpy(newScopes, scopes, scopes_size);
  newScopes[newScopes_size - 1] = metadata;

  for (size_t i = 0; i < operands_size; ++i) {
    const ParsedNode *const operand = operands[i];
    ParsedTree *const parsedTree =
        treeParseNode(operand, code, newScopes, newScopes_size);
    if (parsedTree == NULL) {
      goto RETURN_ERROR;
    }
    switch (parsedTree->token) {
      case TREE_TOKEN_ROOT:
        printError("It is not allowed here", code, operand->strBegin,
                   operand->strEnd);
        goto RETURN_ERROR;
      case TREE_TOKEN_DEFINE_CONSTANT:
      case TREE_TOKEN_DEFINE_VARIABLE: {
        TreeDefineVariableMetadata *const variableDefine = parsedTree->metadata;
        if (variableDefine == NULL) {
          goto RETURN_ERROR;
        }
        pushVariableToScope(metadata, variableDefine);
      }
      /* fall through */
      default:
        pushLineToScope(metadata, parsedTree);
        continue;
    }
    printError("'%s' Is not allowed here", code, operand->strBegin,
               operand->strEnd, PARSED_TOKEN_STRINGS[operand->token]);
    goto RETURN_ERROR;
  }

  metadata->variables =
      a404m_realloc(metadata->variables, metadata->variables_size);

  return tree;

RETURN_ERROR:
  free(tree);
  free(metadata);
  return NULL;
}

TreeDefineVariableMetadata *treeParseDefineVariable(ParsedTree *tree,
                                                    const ParsedNode *node,
                                                    SourceCode *code,
                                                    TreeScopeMetadata *scopes[],
                                                    size_t scopes_size) {
  TreeDefineVariableMetadata *define = a404m_malloc(sizeof(*define));
  define->tree = tree;

  ParserVariableDefineMetadata *metadata = node->metadata;

  if (metadata->value == NULL) {
    define->value = NULL;
  } else if ((define->value = treeParseExpr(metadata->value, code, scopes,
                                            scopes_size)) == NULL) {
    goto RETURN_ERROR;
  }

  if (metadata->name->token == PARSED_TOKEN_IDENTIFIER) {
    define->nameBegin = metadata->name->strBegin;
    define->nameEnd = metadata->name->strEnd;
  } else {
    printError("Names should be an identifier", code, metadata->name->strBegin,
               metadata->name->strEnd);
    goto RETURN_ERROR;
  }

  if (metadata->type == NULL) {
    define->type = getTreeExpressionType(define->value);
  } else if (metadata->type->token == PARSED_TOKEN_IDENTIFIER) {
    const TreeDefineVariableMetadata *variable =
        getVariable(metadata->type->strBegin, metadata->type->strEnd, code,
                    scopes, scopes_size);
    if (variable == NULL) {
      goto RETURN_ERROR;
    }
    define->type = getType(variable);
  } else {
    printError("Types should be an identifier (for now)", code,
               metadata->type->strBegin, metadata->type->strEnd);
    goto RETURN_ERROR;
  }

  if (define->type == NULL) {
    printError("Can't specify type", code, node->strBegin, node->strEnd);
    goto RETURN_ERROR;
  }

  return define;

RETURN_ERROR:
  free(define);
  return NULL;
}

TreeDefineVariableMetadata *getVariable(const char *strBegin,
                                        const char *strEnd, SourceCode *code,
                                        TreeScopeMetadata *scopes[],
                                        size_t scopes_size) {
  const size_t size = strEnd - strBegin;
  const char *str = strBegin;

  for (size_t i = 0; i < scopes_size; ++i) {
    TreeScopeMetadata *scope = scopes[i];
    for (size_t j = scope->variables_size - 1; j != (typeof(j))-1; --j) {
      TreeDefineVariableMetadata *variable = scope->variables[j];
      const size_t variable_str_size = variable->nameEnd - variable->nameBegin;
      if (size == variable_str_size &&
          strncmp(str, variable->nameBegin, size) == 0) {
        return variable;
      }
    }
  }

  printError("Identifier is not defined", code, strBegin, strEnd);
  return NULL;
}

ParsedTree *treeParseIdentifier(const ParsedNode *node, SourceCode *code,
                                TreeScopeMetadata *scopes[],
                                size_t scopes_size) {
  TreeDefineVariableMetadata *variable =
      getVariable(node->strBegin, node->strEnd, code, scopes, scopes_size);
  if (variable != NULL) {
    ParsedTree *tree = a404m_malloc(sizeof(*tree));
    tree->token = TREE_TOKEN_IDENTIFIER;
    tree->strBegin = node->strBegin;
    tree->strEnd = node->strEnd;
    TreeIdentifierMetadata *metadata = tree->metadata =
        a404m_malloc(sizeof(TreeIdentifierMetadata));
    metadata->variable = variable;
    return tree;
  }
  return NULL;
}

ParsedTree *treeParseFunctionCall(const ParsedNode *node, SourceCode *code,
                                  TreeScopeMetadata *scopes[],
                                  size_t scopes_size) {
  ParserFunctionCallMetadata *node_metadata = node->metadata;

  ParsedTree *tree = a404m_malloc(sizeof(*tree));
  tree->token = TREE_TOKEN_FUNCTION_CALL;
  tree->strBegin = node->strBegin;
  tree->strEnd = node->strEnd;

  TreeFunctionCallMetadata *metadata = tree->metadata =
      a404m_malloc(sizeof(*metadata));

  size_t metadata_values_size = 0;
  metadata->values = a404m_malloc(metadata_values_size * sizeof(ParsedTree *));
  metadata->values_size = 0;

  TreeDefineVariableMetadata *variable =
      getVariable(node_metadata->functionNameBegin,
                  node_metadata->functionNameEnd, code, scopes, scopes_size);

  if (variable == NULL) {
    goto RETURN_ERROR;
  }
  metadata->function = variable;

  for (size_t i = 0; i < node_metadata->scope->operands_size; ++i) {
    ParsedNode *const operand = node_metadata->scope->operands[i];
    ParsedTree *const operandTree =
        treeParseNode(operand, code, scopes, scopes_size);
    if (operandTree == NULL) {
      goto RETURN_ERROR;
    }
    // TODO: check types
    if (metadata->values_size == metadata_values_size) {
      metadata_values_size += metadata_values_size / 2 + 1;
      metadata->values =
          a404m_malloc(metadata_values_size * sizeof(ParsedTree *));
    }
    metadata->values[metadata->values_size] = operandTree;
    metadata->values_size += 1;
  }

  metadata->values = a404m_realloc(
      metadata->values, metadata->values_size * sizeof(ParsedTree *));
  return tree;

RETURN_ERROR:
  free(metadata);
  free(tree);
  return NULL;
}

ParsedTree *treeParseValueString(const ParsedNode *node, SourceCode *code) {
  ParsedTree *const tree = a404m_malloc(sizeof(*tree));
  tree->token = TREE_TOKEN_VALUE_STRING;
  tree->strBegin = node->strBegin;
  tree->strEnd = node->strEnd;
  if ((tree->metadata = nodeToString(node, code)) == NULL) {
    free(tree);
    return NULL;
  }
  return tree;
}

ParsedTree *treeParseVariableDefinition(const ParsedNode *node,
                                        SourceCode *code,
                                        TreeScopeMetadata *scopes[],
                                        size_t scopes_size, TreeToken token) {
  ParsedTree *const tree = a404m_malloc(sizeof(*tree));
  tree->token = token;
  tree->strBegin = node->strBegin;
  tree->strEnd = node->strEnd;
  if ((tree->metadata = treeParseDefineVariable(tree, node, code, scopes,
                                                scopes_size)) == NULL) {
    free(tree);
    return NULL;
  }
  return tree;
}

ParsedTree *treeParseStruct(const ParsedNode *node, SourceCode *code,
                            TreeScopeMetadata *scopes[], size_t scopes_size) {
  const ParserStructMetadata *node_metadata = node->metadata;

  ParsedTree *const tree = a404m_malloc(sizeof(*tree));
  tree->token = TREE_TOKEN_STRUCT;
  tree->strBegin = node->strBegin;
  tree->strEnd = node->strEnd;
  TreeStructMetadata *const metadata = tree->metadata =
      a404m_malloc(sizeof(*metadata));

  if (node_metadata->body != NULL) {
    const ParserScopeMetadata *node_body = node_metadata->body->metadata;
    for (size_t i = 0; i < node_body->operands_size; ++i) {
      // TODO: implement
      printError("Not implemented", code, node->strBegin, node->strEnd);

      free(metadata);
      free(tree);
      return NULL;
    }
  }

  return tree;
}

ParsedTree *treeParseFunction(const ParsedNode *node, SourceCode *code,
                              TreeScopeMetadata *scopes[], size_t scopes_size) {
  const ParserFunctionMetadata *node_metadata = node->metadata;

  ParsedTree *const tree = a404m_malloc(sizeof(*tree));
  tree->token = TREE_TOKEN_FUNCTION;
  tree->strBegin = node->strBegin;
  tree->strEnd = node->strEnd;

  TreeFunctionMetadata *const metadata = tree->metadata =
      a404m_malloc(sizeof(*metadata));

  ParsedTree *parsedTree =
      treeParseNode(node_metadata->type, code, scopes, scopes_size);

  if (parsedTree == NULL) {
    goto RETURN_ERROR;
  }
  metadata->returnType = getTreeExpressionType(parsedTree);
  // TODO: this is not right
  deleteParsedTree(parsedTree);

  const ParserScopeMetadata *params = node_metadata->params->metadata;

  metadata->params = a404m_malloc(0);
  metadata->params_size = 0;

  for (size_t i = 0; i < params->operands_size; ++i) {
    const ParsedNode *operand = params->operands[i];
    TreeDefineVariableMetadata *define =
        treeParseDefineVariable(tree, operand, code, scopes, scopes_size);
    if (define == NULL) {
      goto RETURN_ERROR;
    }
    size_t metadata_params_size = a404m_malloc_usable_size(metadata->params) /
                                  sizeof(ParserScopeMetadata *);
    if (metadata->params_size == metadata_params_size) {
      metadata_params_size += metadata_params_size / 2 + 1;
      metadata->params = a404m_realloc(
          metadata->params,
          metadata_params_size * sizeof(TreeDefineVariableMetadata *));
    }
    metadata->params[metadata->params_size] = define;
    metadata->params_size += 1;
  }
  metadata->params =
      a404m_realloc(metadata->params, metadata->params_size *
                                          sizeof(TreeDefineVariableMetadata *));

  if (node_metadata->body != NULL) {
    printError("Not implemented", code, node->strBegin, node->strEnd);

    goto RETURN_ERROR;
    return NULL;
  } else {
    metadata->scope = NULL;
  }

  return tree;
RETURN_ERROR:

  free(metadata);
  free(tree);
  return NULL;
}

ParsedTree *treeParseImport(const ParsedNode *node, SourceCode *code,
                            TreeScopeMetadata *scopes[], size_t scopes_size) {
  const ParserImportMetadata *node_metadata = node->metadata;
  SizedString *path = nodeToString(node_metadata, code);
  if (path == NULL) {
    return NULL;
  }

  Code *fileCode = read_whole_file(path->str);
  if (fileCode == NULL) {
    goto RETURN_ERROR;
  }
  pushToSourceCode(code, fileCode);

  ParsedNode *parsedNode = parser(code, code->size - 1);

  if (parsedNode == NULL) {
    goto RETURN_ERROR;
  }
  return _treeParser(parsedNode, code);
RETURN_ERROR:
  free(path->str);
  free(path);
  return NULL;
}

TypeId getTreeExpressionType(ParsedTree *tree) {
  switch (tree->token) {
    case TREE_TOKEN_FUNCTION_CALL:
      return ((TreeFunctionMetadata *)((TreeFunctionCallMetadata *)
                                           tree->metadata)
                  ->function->type->metadata)
          ->returnType;
    case TREE_TOKEN_DEFINE_VARIABLE:
    case TREE_TOKEN_DEFINE_CONSTANT:
      return ((TreeDefineVariableMetadata *)tree->metadata)->type;
    case TREE_TOKEN_IDENTIFIER:
      return getType(((TreeIdentifierMetadata *)tree->metadata)->variable);
    case TREE_TOKEN_VALUE_STRING:
    case TREE_TOKEN_STRUCT:
    case TREE_TOKEN_GLOBAL_SCOPE:
    case TREE_TOKEN_LOCAL_SCOPE:
    case TREE_TOKEN_FUNCTION:  // TODO: find a better way for function
      return tree;
    case TREE_TOKEN_NONE:
    case TREE_TOKEN_ROOT:
  }
  fprintf(stderr, "bad parsed tree token %d at %d:%s", tree->token, __LINE__,
          __FILE_NAME__);
  exit(1);
}

TypeId getType(const TreeDefineVariableMetadata *define) {
  if (define->value == NULL || !isType(define->value)) {
    return define->type;
  } else {
    return define->tree;
  }
}

bool isType(ParsedTree *const tree) {
  switch (tree->token) {
    case TREE_TOKEN_FUNCTION_CALL:
    case TREE_TOKEN_DEFINE_VARIABLE:
    case TREE_TOKEN_DEFINE_CONSTANT:
    case TREE_TOKEN_IDENTIFIER:
    case TREE_TOKEN_VALUE_STRING:
    case TREE_TOKEN_GLOBAL_SCOPE:
    case TREE_TOKEN_LOCAL_SCOPE:
    case TREE_TOKEN_ROOT:
      return false;
    case TREE_TOKEN_STRUCT:
    case TREE_TOKEN_FUNCTION:  // TODO: find a better way for function
      return true;
    case TREE_TOKEN_NONE:
      break;
  }
  fprintf(stderr, "bad parsed tree token %d at %d:%s", tree->token, __LINE__,
          __FILE_NAME__);
  exit(1);
}

SizedString *nodeToString(ParsedNode const *node, SourceCode *code) {
  const char *strBegin = node->strBegin + 1;
  const char *strEnd = node->strEnd - 1;

  char *str = a404m_malloc((strEnd - strBegin + 1) * sizeof(char));
  size_t inserted = 0;

  for (char const *iter = strBegin; iter < strEnd; ++iter) {
    char c = *iter;
    if (c == '\\') {
      if (++iter < strEnd) {
        switch (*iter) {
          case '\'':
            c = '\'';
            break;
          case '\"':
            c = '\"';
            break;
          case '\\':
            c = '\\';
            break;
          case 'a':
            c = '\a';
            break;
          case 'b':
            c = '\b';
            break;
          case 'f':
            c = '\f';
            break;
          case 'n':
            c = '\n';
            break;
          case 'r':
            c = '\r';
            break;
          case 't':
            c = '\t';
            break;
          case 'v':
            c = '\v';
            break;
          /*case 'u':*/  // TODO: do it
          /*  c = '';*/
          /*  break;*/
          default:
            printError("Bad escape code '\\%s'", code, node->strBegin,
                       node->strEnd, *iter);
            goto RETURN_ERROR;
        }
      } else {
        printError("Expected character after '\\'", code, node->strBegin,
                   node->strEnd);
        goto RETURN_ERROR;
      }
    }
    str[inserted] = c;
    ++inserted;
  }

  str[inserted] = '\0';
  SizedString *const string = a404m_malloc(sizeof(SizedString));
  string->str = a404m_realloc(str, (inserted + 1) * sizeof(char));
  string->size = inserted;
  return string;
RETURN_ERROR:
  free(str);
  return NULL;
}

void pushVariableToScope(TreeScopeMetadata *scope,
                         TreeDefineVariableMetadata *variable) {
  size_t scope_variables_size = a404m_malloc_usable_size(scope->variables) /
                                sizeof(TreeDefineVariableMetadata *);
  if (scope->variables_size == scope_variables_size) {
    scope_variables_size += scope_variables_size / 2 + 1;
    scope->variables = a404m_realloc(
        scope->variables,
        scope_variables_size * sizeof(TreeDefineVariableMetadata *));
  }
  scope->variables[scope->variables_size] = variable;
  scope->variables_size += 1;
}

void pushLineToScope(TreeScopeMetadata *scope, ParsedTree *line) {
  size_t scope_lines_size =
      a404m_malloc_usable_size(scope->lines) / sizeof(ParsedTree *);
  if (scope->lines_size == scope_lines_size) {
    scope_lines_size += scope_lines_size / 2 + 1;
    scope->lines =
        a404m_realloc(scope->lines, scope_lines_size * sizeof(ParsedTree *));
  }
  scope->lines[scope->lines_size] = line;
  scope->lines_size += 1;
}