Quick actions

cmd+k|ctrl+k

Navigation

Languages

CYaRon! lang C Impls

Snippet info

Language

C

Visibility

public

Author

sdfghjk86

Created

2025-12-05T15:22:50.321055Z

Updated

2025-12-05T15:25:38.001635Z

#include "ctype.h"
#include "stddef.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
typedef struct DynArr {
  void *items;
  size_t item_size;
  size_t item_cnts;
  size_t capacity;
} DynArr;
void da_init(DynArr *dyn_arr, size_t item_size, size_t capacity);
DynArr *da_create(size_t item_size, size_t capacity);
void *da_try_push_back(DynArr *dyn_arr);
void *da_push_back(DynArr *dyn_arr, void *item);
static inline void *da_get(DynArr *dyn_arr, size_t idx) {
  void *item = dyn_arr->items + idx * dyn_arr->item_size;
  return item;
}
static inline void da_set(DynArr *dyn_arr, size_t idx, void *item) {
  void *dest = dyn_arr->items + idx * dyn_arr->item_size;
  memcpy(dest, item, dyn_arr->item_size);
  return;
}
void da_free(DynArr *dyn_arr);
typedef struct StrPoolNode {
  char *str;
  size_t len;
  char alloc_by_pool;
} StrPoolNode;
typedef struct StrPool {
  char *mempool;
  size_t mempool_offset;
  size_t mempool_size;
  DynArr pool_nodes;
} StrPool;
void str_pool_init(StrPool *pool, size_t mempool_size, size_t capacity);
StrPool *str_pool_create(size_t mempool_size, size_t capacity);
void str_pool_free(StrPool *str_pool);
char *str_pool_intern(StrPool *str_pool, const char *string, size_t len);
typedef struct VarIntData {
  int val;
} VarIntData;
typedef struct VarArrData {
  int *arr;
} VarArrData;
typedef union VarData {
  VarIntData i;
  VarArrData a;
} VarData;
typedef struct Interpreter {
  DynArr *stmts;
  DynArr *var_decls;
} Interpreter;
Interpreter *interpreter_create(DynArr *stmts, DynArr *var_decls);
void interpreter_free(Interpreter *interpreter);
void interpreter_execute(Interpreter *interpreter);
enum TokType {
  TOK_EOF,
  TOK_IDENT,
  TOK_INT,
  TOK_PLUS,
  TOK_MINUS,
  TOK_COMMA,
  TOK_COLON,
  TOK_LBRACE,
  TOK_RBRACE,
  TOK_LBRACKET,
  TOK_RBRACKET,
  TOK_DOTDOT,
  TOK_KEYWORD_VARS,
  TOK_KEYWORD_SET,
  TOK_KEYWORD_YOSORO,
  TOK_KEYWORD_IHU,
  TOK_KEYWORD_HOR,
  TOK_KEYWORD_WHILE,
  TOK_KEYWORD_INT,
  TOK_KEYWORD_ARRAY,
  TOK_CMP_LT,
  TOK_CMP_GT,
  TOK_CMP_LE,
  TOK_CMP_GE,
  TOK_CMP_EQ,
  TOK_CMP_NEQ,
};
typedef struct Token {
  enum TokType type;
  const char *str;
} Token;
typedef struct Lexer {
  char *src;
  size_t src_len;
  size_t ch_pos;
  DynArr toks;
  StrPool tok_val_pool;
} Lexer;
Lexer *lexer_create(char *src);
void lexer_free(Lexer *lexer);
void token_init(Token *tok, enum TokType tok_type, const char *str);
Token *token_create(enum TokType tok_type, const char *str);
void lexer_tokenize(Lexer *lexer);
enum OperandTyp {
  OPERAND_INT_VAR,
  OPERAND_ARR_ELEM,
};
enum ExprTermTyp {
  EXPR_TERM_CONST,
  EXPR_TERM_OPERAND,
};
typedef struct Expr {
  DynArr terms;
} Expr;
typedef struct VarDecl VarDecl;
typedef struct Operand {
  enum OperandTyp typ;
  size_t decl_idx;
  union {
    Expr idx_expr;
  };
} Operand;
typedef struct ExprTerm {
  int coefficient;
  enum ExprTermTyp typ;
  union {
    int constant;
    Operand operand;
  };
} ExprTerm;
enum StmtType {
  STMT_IHU_BLK = 0x00,
  STMT_WHILE_BLK,
  STMT_HOR_BLK,
  STMT_YOSORO_CMD = 0xf0,
  STMT_SET_CMD,
};
enum VarType {
  VAR_INT,
  VAR_ARR,
};
typedef struct VarDecl {
  enum VarType typ;
  VarData data;
  const char *name;
  struct {
    size_t start;
    size_t end;
  };
} VarDecl;
typedef struct Cond {
  enum TokType typ;
  Expr left;
  Expr right;
} Cond;
typedef struct IhuStmt {
  Cond cond;
  DynArr stmts;
} IhuStmt;
typedef struct WhileStmt {
  Cond cond;
  DynArr stmts;
} WhileStmt;
typedef struct HorStmt {
  Operand var;
  Expr start;
  Expr end;
  DynArr stmts;
} HorStmt;
typedef struct YosoroStmt {
  Expr expr;
} YosoroStmt;
typedef struct SetStmt {
  Operand operand;
  Expr expr;
} SetStmt;
typedef struct Stmt {
  enum StmtType typ;
  union {
    IhuStmt ihu;
    WhileStmt while_stmt;
    HorStmt hor;
    YosoroStmt yosoro;
    SetStmt set;
  } inner;
} Stmt;
typedef struct Parser {
  DynArr *toks;
  size_t pos;
  DynArr stmts;
  DynArr var_decls;
} Parser;
Parser *parser_create(DynArr *toks);
void parser_free(Parser *parser);
size_t operand_finder(Parser *parser, const char *var_name,
                      enum OperandTyp type);
DynArr *parser_parse(Parser *parser);
void var_decls_init_data(DynArr *var_decls) {
  for (int i = 0; i < var_decls->item_cnts; i++) {
    VarDecl *var_decl = da_get(var_decls, i);
    switch (var_decl->typ) {
    case VAR_INT: {
      VarIntData *int_data = &var_decl->data.i;
      int_data->val = 0;
    } break;
    case VAR_ARR: {
      VarArrData *arr_data = &var_decl->data.a;
      arr_data->arr =
          malloc((var_decl->end - var_decl->start + 1) * sizeof(int));
    } break;
    }
  }
}
void var_decls_free_data(DynArr *var_decls) {
  for (int i = 0; i < var_decls->item_cnts; i++) {
    VarDecl *var_decl = da_get(var_decls, i);
    switch (var_decl->typ) {
    case VAR_INT:
      break;
    case VAR_ARR: {
      VarArrData *arr_data = &var_decl->data.a;
      free(arr_data->arr);
    } break;
    }
  }
}
Interpreter *interpreter_create(DynArr *stmts, DynArr *var_decls) {
  Interpreter *interpreter = malloc(sizeof(Interpreter));
  interpreter->stmts = stmts;
  var_decls_init_data(var_decls);
  interpreter->var_decls = var_decls;
  return interpreter;
}
void interpreter_free(Interpreter *interpreter) {
  var_decls_free_data(interpreter->var_decls);
  free(interpreter);
}
int eval_expr(Interpreter *interpreter, Expr *expr);
void write_operand(Interpreter *interpreter, Operand *operand,
                          int data) {
  size_t decl_idx = operand->decl_idx;
  VarDecl *decl = da_get(interpreter->var_decls, decl_idx);
  switch (operand->typ) {
  case OPERAND_INT_VAR:
    decl->data.i.val = data;
    return;
  case OPERAND_ARR_ELEM:
    decl->data.a.arr[eval_expr(interpreter, &operand->idx_expr) - decl->start] =
        data;
    return;
  }
}
int read_operand(Interpreter *interpreter, Operand *operand) {
  size_t decl_idx = operand->decl_idx;
  VarDecl *decl = da_get(interpreter->var_decls, decl_idx);
  int data = 0;
  switch (operand->typ) {
  case OPERAND_INT_VAR:
    data = decl->data.i.val;
    break;
  case OPERAND_ARR_ELEM:
    data = decl->data.a
               .arr[eval_expr(interpreter, &operand->idx_expr) - decl->start];
    break;
  }
  return data;
}
inline int eval_expr(Interpreter *interpreter, Expr *expr) {
  DynArr *terms = &expr->terms;
  int res = 0;
  for (int i = 0; i < terms->item_cnts; i++) {
    ExprTerm *term = da_get(terms, i);
    switch (term->typ) {
    case EXPR_TERM_CONST:
      res += term->coefficient * term->constant;
      break;
    case EXPR_TERM_OPERAND:
      res += term->coefficient * read_operand(interpreter, &term->operand);
      break;
    }
  }
  return res;
}
void execute_stmts(Interpreter *interpreter, DynArr *stmts);
char execute_cond(Interpreter *interpreter, Cond *cond) {
  int left = eval_expr(interpreter, &cond->left);
  int right = eval_expr(interpreter, &cond->right);
  switch (cond->typ) {
  case TOK_CMP_LT:
    return left < right;
  case TOK_CMP_GT:
    return left > right;
  case TOK_CMP_LE:
    return left <= right;
  case TOK_CMP_GE:
    return left >= right;
  case TOK_CMP_EQ:
    return left == right;
  case TOK_CMP_NEQ:
    return left != right;
  default:
    return 0;
  }
}
void execute_stmt(Interpreter *interpreter, Stmt *stmt) {
  switch (stmt->typ) {
  case STMT_IHU_BLK: {
    IhuStmt *ihu = &stmt->inner.ihu;
    if (execute_cond(interpreter, &ihu->cond)) {
      execute_stmts(interpreter, &ihu->stmts);
    }
  } break;
  case STMT_WHILE_BLK: {
    WhileStmt *while_stmt = &stmt->inner.while_stmt;
    while (execute_cond(interpreter, &while_stmt->cond)) {
      execute_stmts(interpreter, &while_stmt->stmts);
    }
  } break;
  case STMT_HOR_BLK: {
    HorStmt *hor = &stmt->inner.hor;
    int start = eval_expr(interpreter, &hor->start);
    int end = eval_expr(interpreter, &hor->end);
    for (int i = start; i <= end; i++) {
      write_operand(interpreter, &hor->var, i);
      execute_stmts(interpreter, &hor->stmts);
    }
    write_operand(interpreter, &hor->var, end);
  } break;
  case STMT_YOSORO_CMD: {
    YosoroStmt *yosoro = &stmt->inner.yosoro;
    int res = eval_expr(interpreter, &yosoro->expr);
    printf("%d ", res);
  } break;
  case STMT_SET_CMD: {
    SetStmt *set = &stmt->inner.set;
    int res = eval_expr(interpreter, &set->expr);
    write_operand(interpreter, &set->operand, res);
  } break;
  }
}
void execute_stmts(Interpreter *interpreter, DynArr *stmts) {
  Stmt *stmt;
  for (int i = 0; i < stmts->item_cnts; i++) {
    stmt = da_get(stmts, i);
    execute_stmt(interpreter, stmt);
  }
}
void interpreter_execute(Interpreter *interpreter) {
  DynArr *stmts = interpreter->stmts;
  execute_stmts(interpreter, stmts);
}
Lexer *lexer_create(char *src) {
  Lexer *lexer = malloc(sizeof(Lexer));
  str_pool_init(&lexer->tok_val_pool, 512, 16);
  lexer->src_len = strlen(src);
  lexer->src = src;
  lexer->ch_pos = 0;
  da_init(&lexer->toks, sizeof(Token), 128);
  return lexer;
}
void lexer_free(Lexer *lexer) {
  da_free(&lexer->toks);
  str_pool_free(&lexer->tok_val_pool);
  free(lexer);
}
void token_init(Token *tok, enum TokType tok_type, const char *str) {
  tok->type = tok_type;
  tok->str = str;
  return;
}
Token *token_create(enum TokType tok_type, const char *str) {
  Token *tok = malloc(sizeof(Token));
  token_init(tok, tok_type, str);
  return tok;
}
inline static void add_token(Lexer *lexer, enum TokType tok_type,
                             const char *val) {
  Token *tok = da_try_push_back(&lexer->toks);
  token_init(tok, tok_type, val);
  return;
}
inline static char is_identifier_char(char c) { return isalpha(c) || c == '_'; }
const struct KeyWords {
  const enum TokType typ;
  const char *val;
  const size_t len;
} keywords[] = {
    {TOK_KEYWORD_VARS, "vars", sizeof("vars") - 1},
    {TOK_KEYWORD_SET, "set", sizeof("set") - 1},
    {TOK_KEYWORD_YOSORO, "yosoro", sizeof("yosoro") - 1},
    {TOK_KEYWORD_IHU, "ihu", sizeof("ihu") - 1},
    {TOK_KEYWORD_HOR, "hor", sizeof("hor") - 1},
    {TOK_KEYWORD_WHILE, "while", sizeof("while") - 1},
    {TOK_KEYWORD_INT, "int", sizeof("int") - 1},
    {TOK_KEYWORD_ARRAY, "array", sizeof("array") - 1},
    {TOK_CMP_LT, "lt", sizeof("lt") - 1},
    {TOK_CMP_GT, "gt", sizeof("gt") - 1},
    {TOK_CMP_LE, "le", sizeof("le") - 1},
    {TOK_CMP_GE, "ge", sizeof("ge") - 1},
    {TOK_CMP_EQ, "eq", sizeof("eq") - 1},
    {TOK_CMP_NEQ, "neq", sizeof("neq") - 1},
};
const size_t keyword_cnts = sizeof(keywords) / sizeof(*keywords);
int check_keyword(const char *tok_val, size_t len) {
  for (int i = 0; i < keyword_cnts; ++i) {
    if (keywords[i].len == len && strncmp(tok_val, keywords[i].val, len) == 0) {
      return i;
    }
  }
  return -1;
}
void lexer_tokenize(Lexer *lexer) {
  while (lexer->ch_pos < lexer->src_len) {
    char ch = lexer->src[lexer->ch_pos];
    if (isspace(ch)) {
      lexer->ch_pos++;
      continue;
    }
    if (isdigit(ch)) {
      size_t start = lexer->ch_pos;
      while (lexer->ch_pos < lexer->src_len &&
             isdigit(lexer->src[lexer->ch_pos])) {
        lexer->ch_pos++;
      }
      size_t len = lexer->ch_pos - start;
      char *val = lexer->src + start;
      val = str_pool_intern(&lexer->tok_val_pool, val, len);
      add_token(lexer, TOK_INT, val);
      continue;
    }
    if (is_identifier_char(ch)) {
      size_t start = lexer->ch_pos;
      while (lexer->ch_pos < lexer->src_len &&
             isalnum(lexer->src[lexer->ch_pos])) {
        lexer->ch_pos++;
      }
      size_t len = lexer->ch_pos - start;
      const char *val = lexer->src + start;
      int keyword_idx = check_keyword(val, len);
      enum TokType type;
      if (keyword_idx != -1) {
        type = keywords[keyword_idx].typ;
        val = keywords[keyword_idx].val;
      } else {
        type = TOK_IDENT;
        val = str_pool_intern(&lexer->tok_val_pool, val, len);
      }
      add_token(lexer, type, val);
      continue;
    }
    switch (ch) {
    case '+':
      add_token(lexer, TOK_PLUS, "'+'");
      break;
    case '-':
      add_token(lexer, TOK_MINUS, "'-'");
      break;
    case ',':
      add_token(lexer, TOK_COMMA, "','");
      break;
    case ':':
      add_token(lexer, TOK_COLON, "':'");
      break;
    case '{':
      add_token(lexer, TOK_LBRACE, "'{'");
      break;
    case '}':
      add_token(lexer, TOK_RBRACE, "'}'");
      break;
    case '[':
      add_token(lexer, TOK_LBRACKET, "'['");
      break;
    case ']':
      add_token(lexer, TOK_RBRACKET, "']'");
      break;
    default:
      if (ch == '.' && lexer->ch_pos + 1 < lexer->src_len &&
          lexer->src[lexer->ch_pos + 1] == '.') {
        add_token(lexer, TOK_DOTDOT, "..");
        lexer->ch_pos += 2;
        continue;
      } else {
        lexer->ch_pos++;
        continue;
      }
      break;
    }
    lexer->ch_pos++;
  }
  add_token(lexer, TOK_EOF, NULL);
  return;
}
void read_src(char **src) {
//   FILE *input = stdin;
//   fseek(input, 0, SEEK_END);
//   size_t input_size = ftell(input);
//   fseek(input, 0, SEEK_SET);
//   *src = malloc(input_size + 1);
//   fread(*src, 512, input_size / 512, input);
//   size_t remaining = input_size % 512;
//   fread(*src + input_size - remaining, 1, remaining, input);
//   (*src)[input_size] = '\0';
    const char test_src[] = 
    "{ vars\n"
    "fuck:int\n"
    "ccf:int\n"
    "}\n"
    ":set ccf, 38\n"
    ":set fuck, 250\n"
    ":yosoro 114514 - ccf + fuck\n"
    ;
    *src = malloc(sizeof(test_src));
    memcpy(*src,test_src,sizeof(test_src));
}
int main() {
  const char *src;
  read_src(&src);
  Lexer *lexer = lexer_create(src);
  lexer_tokenize(lexer);
  Parser *parser = parser_create(&lexer->toks);
  parser_parse(parser);
  Interpreter *interpreter =
      interpreter_create(&parser->stmts, &parser->var_decls);
  interpreter_execute(interpreter);
  interpreter_free(interpreter);
  parser_free(parser);
  lexer_free(lexer);
  free(src);
  return 0;
}
Parser *parser_create(DynArr *toks) {
  Parser *parser = malloc(sizeof(Parser));
  parser->toks = toks;
  parser->pos = 0;
  da_init(&parser->stmts, sizeof(Stmt), 16);
  da_init(&parser->var_decls, sizeof(VarDecl), 16);
  return parser;
}
void free_operand(Operand *op);
void free_expr(Expr *expr) {
  for (int i = 0; i < expr->terms.item_cnts; i++) {
    ExprTerm *term = da_get(&expr->terms, i);
    switch (term->typ) {
    case EXPR_TERM_CONST:
      continue;
    case EXPR_TERM_OPERAND:
      free_operand(&term->operand);
      break;
    }
  }
  da_free(&expr->terms);
}
void free_operand(Operand *op) {
  if (op->typ == OPERAND_ARR_ELEM) {
    free_expr(&op->idx_expr);
  }
}
void free_stmts(DynArr *stmts) {
  for (int i = 0; i < stmts->item_cnts; i++) {
    Stmt *stmt = da_get(stmts, i);
    switch (stmt->typ) {
    case STMT_YOSORO_CMD:
      free_expr(&stmt->inner.yosoro.expr);
      break;
    case STMT_SET_CMD:
      free_operand(&stmt->inner.set.operand);
      free_expr(&stmt->inner.set.expr);
      break;
    case STMT_IHU_BLK:
      free_expr(&stmt->inner.ihu.cond.left);
      free_expr(&stmt->inner.ihu.cond.right);
      free_stmts(&stmt->inner.ihu.stmts);
      break;
    case STMT_WHILE_BLK:
      free_expr(&stmt->inner.while_stmt.cond.left);
      free_expr(&stmt->inner.while_stmt.cond.right);
      free_stmts(&stmt->inner.while_stmt.stmts);
      break;
    case STMT_HOR_BLK:
      free_operand(&stmt->inner.hor.var);
      free_expr(&stmt->inner.hor.start);
      free_expr(&stmt->inner.hor.end);
      free_stmts(&stmt->inner.hor.stmts);
    }
  }
  da_free(stmts);
}
void parser_free(Parser *parser) {
  free_stmts(&parser->stmts);
  da_free(&parser->var_decls);
  free(parser);
}
static Token *current_token(Parser *parser) {
  if (parser->pos >= parser->toks->item_cnts) {
    return NULL;
  }
  return da_get(parser->toks, parser->pos);
}
static Token *peek_token(Parser *parser, size_t offset) {
  if (parser->pos + offset >= parser->toks->item_cnts) {
    return NULL;
  }
  return da_get(parser->toks, parser->pos + offset);
}
static void consume_token(Parser *parser) { parser->pos++; }
static int match_token(Parser *parser, enum TokType typ) {
  Token *token = current_token(parser);
  return token && token->type == typ;
}
static Token *next_token(Parser *parser) {
  Token *token = current_token(parser);
  consume_token(parser);
  return token;
}
void parse_vars(Parser *parser) {
  consume_token(parser);
  consume_token(parser);
  DynArr *var_decls = &parser->var_decls;
  size_t cnt = 0;
  while (!match_token(parser, TOK_RBRACE)) {
    Token *ident = next_token(parser);
    consume_token(parser);
    VarDecl *decl = da_try_push_back(var_decls);
    decl->name = ident->str;
    if (match_token(parser, TOK_KEYWORD_INT)) {
      consume_token(parser);
      decl->typ = VAR_INT;
    } else if (match_token(parser, TOK_KEYWORD_ARRAY)) {
      consume_token(parser);
      consume_token(parser);
      consume_token(parser);
      consume_token(parser);
      decl->typ = VAR_ARR;
      decl->start = atoll(next_token(parser)->str);
      consume_token(parser);
      decl->end = atoll(next_token(parser)->str);
      consume_token(parser);
    }
    cnt++;
  }
  consume_token(parser);
}
void parse_blk(Parser *parser, DynArr *stmts);
void parse_expr(Parser *parser, Expr *expr);
size_t operand_finder(Parser *parser, const char *var_name,
                      enum OperandTyp type) {
  enum VarType target_type = 0xff;
  switch (type) {
  case OPERAND_INT_VAR:
    target_type = VAR_INT;
    break;
  case OPERAND_ARR_ELEM:
    target_type = VAR_ARR;
    break;
  }
  for (int i = 0; i < parser->var_decls.item_cnts; i++) {
    VarDecl *decl = da_get(&parser->var_decls, i);
    if ((target_type == 0xff || decl->typ == target_type) &&
        decl->name == var_name) {
      return i;
    }
  }
  return (size_t)-1;
}
void parse_operand(Parser *parser, Operand *operand) {
  Token *operand_tok = current_token(parser);
  consume_token(parser);
  operand->typ = OPERAND_INT_VAR;
  if (match_token(parser, TOK_LBRACKET)) {
    operand->typ = OPERAND_ARR_ELEM;
    consume_token(parser);
    parse_expr(parser, &operand->idx_expr);
    consume_token(parser);
  }
  operand->decl_idx = operand_finder(parser, operand_tok->str, operand->typ);
}
int is_operand_eq(Operand *a, Operand *b);
int is_expr_eq(Expr *a, Expr *b) {
  int cnts = a->terms.item_cnts;
  if (cnts != b->terms.item_cnts) {
    return 0;
  }
  for (int i = 0; i < cnts; i++) {
    ExprTerm *a_term = da_get(&a->terms, i);
    ExprTerm *b_term = da_get(&b->terms, i);
    if (a_term->typ != b_term->typ) {
      return 0;
    }
    switch (a_term->typ) {
    case EXPR_TERM_CONST:
      return a_term->constant == b_term->constant;
    case EXPR_TERM_OPERAND:
      return is_operand_eq(&a_term->operand, &b_term->operand);
    }
  }
  return 1;
}
int is_operand_eq(Operand *a, Operand *b) {
  if (a->typ != b->typ || a->decl_idx != b->decl_idx) {
    return 0;
  }
  if (a->typ == OPERAND_INT_VAR) {
    return 1;
  } else {
    return is_expr_eq(&a->idx_expr, &b->idx_expr);
  }
}
void terms_add_operand(DynArr *terms, Operand op, int sign) {
  for (int i = 0; i < terms->item_cnts; i++) {
    ExprTerm *term = da_get(terms, i);
    if (term->typ == EXPR_TERM_OPERAND && is_operand_eq(&term->operand, &op)) {
      term->coefficient += sign;
      if (op.typ == OPERAND_ARR_ELEM) {
        da_free(&op.idx_expr.terms);
      }
      return;
    }
  }
  ExprTerm *term = da_try_push_back(terms);
  term->coefficient = sign;
  term->typ = EXPR_TERM_OPERAND;
  term->operand = op;
  return;
}
void parse_expr(Parser *parser, Expr *expr) {
  da_init(&expr->terms, sizeof(ExprTerm), 8);
  DynArr *terms = &expr->terms;
  int const_val = 0;
  short sign = 1;
  while (current_token(parser) &&
         !(match_token(parser, TOK_EOF) || match_token(parser, TOK_COLON) ||
           match_token(parser, TOK_COMMA) || match_token(parser, TOK_RBRACE) ||
           match_token(parser, TOK_LBRACE) ||
           match_token(parser, TOK_RBRACKET))) {
    sign = 1;
    if (match_token(parser, TOK_PLUS)) {
      consume_token(parser);
    } else if (match_token(parser, TOK_MINUS)) {
      sign = -1;
      consume_token(parser);
    }
    Token *term_tok = current_token(parser);
    if (term_tok->type == TOK_INT) {
      const_val += atoi(term_tok->str) * sign;
      consume_token(parser);
      continue;
    }
    Operand op;
    parse_operand(parser, &op);
    terms_add_operand(terms, op, sign);
  }
  if (const_val != 0 || terms->item_cnts == 0) {
    ExprTerm *const_term = da_try_push_back(terms);
    const_term->coefficient = 1;
    const_term->typ = EXPR_TERM_CONST;
    const_term->constant = const_val;
  }
}
void parse_cond(Parser *parser, Cond *cond) {
  cond->typ = next_token(parser)->type;
  consume_token(parser);
  parse_expr(parser, &cond->left);
  consume_token(parser);
  parse_expr(parser, &cond->right);
}
void parse_ihu(Parser *parser, Stmt *stmt) {
  consume_token(parser);
  consume_token(parser);
  stmt->typ = STMT_IHU_BLK;
  IhuStmt *ihu = &stmt->inner.ihu;
  parse_cond(parser, &ihu->cond);
  da_init(&ihu->stmts, sizeof(Stmt), 16);
  parse_blk(parser, &ihu->stmts);
  consume_token(parser);
}
void parse_while(Parser *parser, Stmt *stmt) {
  consume_token(parser);
  consume_token(parser);
  stmt->typ = STMT_WHILE_BLK;
  WhileStmt *while_stmt = &stmt->inner.while_stmt;
  parse_cond(parser, &while_stmt->cond);
  da_init(&while_stmt->stmts, sizeof(Stmt), 16);
  parse_blk(parser, &while_stmt->stmts);
  consume_token(parser);
}
void parse_hor(Parser *parser, Stmt *stmt) {
  consume_token(parser);
  consume_token(parser);
  stmt->typ = STMT_HOR_BLK;
  HorStmt *hor = &stmt->inner.hor;
  parse_operand(parser, &hor->var);
  consume_token(parser);
  parse_expr(parser, &hor->start);
  consume_token(parser);
  parse_expr(parser, &hor->end);
  da_init(&hor->stmts, sizeof(Stmt), 16);
  parse_blk(parser, &hor->stmts);
  consume_token(parser);
}
void parse_yosoro(Parser *parser, Stmt *stmt) {
  consume_token(parser);
  consume_token(parser);
  stmt->typ = STMT_YOSORO_CMD;
  YosoroStmt *yosoro = &stmt->inner.yosoro;
  parse_expr(parser, &yosoro->expr);
}
void parse_set(Parser *parser, Stmt *stmt) {
  consume_token(parser);
  consume_token(parser);
  stmt->typ = STMT_SET_CMD;
  SetStmt *set = &stmt->inner.set;
  parse_operand(parser, &set->operand);
  consume_token(parser);
  parse_expr(parser, &set->expr);
}
static void parse_stmts(Parser *parser, DynArr *stmts) {
  Token *tok = current_token(parser);
  if (!tok) {
    return;
  }
  Stmt *cur_stmt;
  switch (tok->type) {
  case TOK_LBRACE:
    tok = peek_token(parser, 1);
    if (!tok) {
      consume_token(parser);
      return;
    }
    switch (tok->type) {
    case TOK_KEYWORD_VARS:
      parse_vars(parser);
      break;
    case TOK_KEYWORD_IHU:
      cur_stmt = da_try_push_back(stmts);
      parse_ihu(parser, cur_stmt);
      break;
    case TOK_KEYWORD_WHILE:
      cur_stmt = da_try_push_back(stmts);
      parse_while(parser, cur_stmt);
      break;
    case TOK_KEYWORD_HOR:
      cur_stmt = da_try_push_back(stmts);
      parse_hor(parser, cur_stmt);
      break;
    default:
      break;
    }
    return;
  case TOK_COLON:
    tok = peek_token(parser, 1);
    if (!tok) {
      consume_token(parser);
      return;
    }
    cur_stmt = da_try_push_back(stmts);
    switch (tok->type) {
    case TOK_KEYWORD_YOSORO:
      parse_yosoro(parser, cur_stmt);
      break;
    case TOK_KEYWORD_SET:
      parse_set(parser, cur_stmt);
      break;
    default:
      break;
    }
    break;
  default:
    consume_token(parser);
    return;
  }
}
void parse_blk(Parser *parser, DynArr *stmts) {
  while (!match_token(parser, TOK_RBRACE) &&
         parser->pos < parser->toks->item_cnts &&
         !match_token(parser, TOK_EOF)) {
    parse_stmts(parser, stmts);
  }
}
DynArr *parser_parse(Parser *parser) {
  while (parser->pos < parser->toks->item_cnts &&
         !match_token(parser, TOK_EOF)) {
    parse_stmts(parser, &parser->stmts);
  }
  return &parser->stmts;
}
void da_init(DynArr *dyn_arr, size_t item_size, size_t capacity) {
  dyn_arr->item_cnts = 0;
  dyn_arr->capacity = capacity;
  dyn_arr->item_size = item_size;
  dyn_arr->items = malloc(capacity * dyn_arr->item_size);
}
DynArr *da_create(size_t item_size, size_t capacity) {
  DynArr *dyn_arr = malloc(sizeof(DynArr));
  da_init(dyn_arr, item_size, capacity);
  return dyn_arr;
}
void *da_try_push_back(DynArr *dyn_arr) {
  if (dyn_arr->item_cnts >= dyn_arr->capacity) {
    dyn_arr->capacity *= 2;
    dyn_arr->items =
        realloc(dyn_arr->items, dyn_arr->capacity * dyn_arr->item_size);
  }
  void *dest = dyn_arr->items + dyn_arr->item_cnts * dyn_arr->item_size;
  ++dyn_arr->item_cnts;
  return dest;
}
void *da_push_back(DynArr *dyn_arr, void *item) {
  void *dest = da_try_push_back(dyn_arr);
  memcpy(dest, item, dyn_arr->item_size);
  return dest;
}
void da_free(DynArr *dyn_arr) { free(dyn_arr->items); }
void str_pool_init(StrPool *pool, size_t mempool_size, size_t capacity) {
  pool->mempool_size = mempool_size;
  pool->mempool = malloc(mempool_size);
  da_init(&pool->pool_nodes, sizeof(StrPoolNode), capacity);
  return;
}
StrPool *str_pool_create(size_t mempool_size, size_t capacity) {
  StrPool *pool = malloc(sizeof(StrPool));
  str_pool_init(pool, mempool_size, capacity);
  return pool;
}
void str_pool_free(StrPool *str_pool) {
  StrPoolNode *node;
  for (size_t i = 0; i < str_pool->pool_nodes.item_cnts; i++) {
    node = da_get(&str_pool->pool_nodes, i);
    if (!node->alloc_by_pool) {
      free(node->str);
    }
  }
  da_free(&str_pool->pool_nodes);
  free(str_pool->mempool);
  return;
}
char *str_pool_intern(StrPool *str_pool, const char *string, size_t len) {
  StrPoolNode *dest_node = NULL;
  size_t str_node_cnts = str_pool->pool_nodes.item_cnts;
  for (size_t i = 0; i < str_node_cnts; i++) {
    dest_node = da_get(&str_pool->pool_nodes, i);
    if (dest_node->len == len && (strncmp(dest_node->str, string, len) == 0)) {
      return dest_node->str;
    }
  }
  dest_node = da_try_push_back(&str_pool->pool_nodes);
  dest_node->len = len;
  if (len + 1 < str_pool->mempool_size - str_pool->mempool_offset) {
    dest_node->alloc_by_pool = 1;
    dest_node->str = &str_pool->mempool[str_pool->mempool_offset];
    str_pool->mempool_offset += len + 1;
  } else {
    dest_node->alloc_by_pool = 0;
    dest_node->str = malloc(len + 1);
  }
  memcpy(dest_node->str, string, len);
  dest_node->str[len] = '\0';
  return dest_node->str;
}
INFO