#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
#include <ctype.h>


#include "spvm_compiler.h"
#include "spvm_list.h"
#include "spvm_hash.h"
#include "spvm_compiler_allocator.h"
#include "spvm_yacc_util.h"
#include "spvm_op.h"
#include "spvm_op_checker.h"
#include "spvm_sub.h"
#include "spvm_constant.h"
#include "spvm_field.h"
#include "spvm_my.h"
#include "spvm_var.h"
#include "spvm_enumeration_value.h"
#include "spvm_type.h"
#include "spvm_enumeration.h"
#include "spvm_package.h"
#include "spvm_field_access.h"
#include "spvm_call_sub.h"
#include "spvm_type.h"
#include "spvm_switch_info.h"
#include "spvm_limit.h"
#include "spvm_package_var.h"
#include "spvm_package_var_access.h"
#include "spvm_block.h"
#include "spvm_basic_type.h"
#include "spvm_case_info.h"
#include "spvm_array_field_access.h"

SPVM_OP* SPVM_OP_CHECKER_new_op_var_tmp(SPVM_COMPILER* compiler, SPVM_OP* op_sub, SPVM_TYPE* type, const char* file, int32_t line) {

  // Temparary variable name
  char* name = SPVM_COMPILER_ALLOCATOR_safe_malloc_zero(compiler, strlen("@tmp2147483647") + 1);
  sprintf(name, "@tmp%d", compiler->tmp_var_length);
  compiler->tmp_var_length++;
  SPVM_OP* op_name = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_NAME, file, line);
  op_name->uv.name = name;
  SPVM_OP* op_var = SPVM_OP_build_var(compiler, op_name);
  SPVM_MY* my = SPVM_MY_new(compiler);
  SPVM_OP* op_my = SPVM_OP_new_op_my(compiler, my, file, line);
  SPVM_OP* op_type = NULL;
  if (type) {
    op_type = SPVM_OP_new_op_type(compiler, type, file, line);
  }
  SPVM_OP_build_my(compiler, op_my, op_var, op_type);

  // Add op mys
  if (op_sub) {
    SPVM_LIST_push(op_sub->uv.sub->mys, my);
  }
  
  return op_var;
}

void SPVM_OP_CHECKER_apply_unary_string_convertion(SPVM_COMPILER* compiler, SPVM_OP* op_term) {
  
  SPVM_TYPE* type = SPVM_OP_get_type(compiler, op_term);
  
  SPVM_TYPE* dist_type;
  if (SPVM_TYPE_is_numeric_type(compiler, type->basic_type->id, type->dimension, type->flag)) {
    SPVM_OP* op_dist_type = SPVM_OP_new_op_string_type(compiler, op_term->file, op_term->line);
    dist_type = op_dist_type->uv.type;
  }
  else {
    return;
  }
  
  SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_term);
  
  SPVM_OP* op_convert = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CONVERT, op_term->file, op_term->line);
  SPVM_OP* op_dist_type = SPVM_OP_new_op_type(compiler, dist_type, op_term->file, op_term->line);
  SPVM_OP_build_convert(compiler, op_convert, op_dist_type, op_term);
  
  SPVM_OP_replace_op(compiler, op_stab, op_convert);
}

void SPVM_OP_CHECKER_apply_unary_numeric_convertion(SPVM_COMPILER* compiler, SPVM_OP* op_term) {
  
  SPVM_TYPE* type = SPVM_OP_get_type(compiler, op_term);
  
  SPVM_TYPE* dist_type;
  if (type->dimension == 0 && type->basic_type->id <= SPVM_BASIC_TYPE_C_ID_INT) {
    SPVM_OP* op_dist_type = SPVM_OP_new_op_int_type(compiler, op_term->file, op_term->line);
    dist_type = op_dist_type->uv.type;
  }
  else {
    return;
  }
  
  if (!(type->basic_type->id == dist_type->basic_type->id && type->dimension == dist_type->dimension)) {
    SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_term);
    
    SPVM_OP* op_convert = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CONVERT, op_term->file, op_term->line);
    SPVM_OP* op_dist_type = SPVM_OP_new_op_type(compiler, dist_type, op_term->file, op_term->line);
    SPVM_OP_build_convert(compiler, op_convert, op_dist_type, op_term);
    
    SPVM_OP_replace_op(compiler, op_stab, op_convert);
  }
}

void SPVM_OP_apply_binary_numeric_convertion(SPVM_COMPILER* compiler, SPVM_OP* op_first, SPVM_OP* op_last) {
  
  SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_first);
  SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_last);
  
  SPVM_TYPE* dist_type;
  if ((first_type->dimension == 0 && first_type->basic_type->id == SPVM_BASIC_TYPE_C_ID_DOUBLE) || (last_type->dimension == 0 && last_type->basic_type->id == SPVM_BASIC_TYPE_C_ID_DOUBLE)) {
    SPVM_OP* op_dist_type = SPVM_OP_new_op_double_type(compiler, op_first->file, op_first->line);
    dist_type = op_dist_type->uv.type;
  }
  else if ((first_type->dimension == 0 && first_type->basic_type->id == SPVM_BASIC_TYPE_C_ID_FLOAT) || (last_type->dimension == 0 && last_type->basic_type->id == SPVM_BASIC_TYPE_C_ID_FLOAT)) {
    SPVM_OP* op_dist_type = SPVM_OP_new_op_float_type(compiler, op_first->file, op_first->line);
    dist_type = op_dist_type->uv.type;
  }
  else if ((first_type->dimension == 0 && first_type->basic_type->id == SPVM_BASIC_TYPE_C_ID_LONG) || (last_type->dimension == 0 && last_type->basic_type->id == SPVM_BASIC_TYPE_C_ID_LONG)) {
    SPVM_OP* op_dist_type = SPVM_OP_new_op_long_type(compiler, op_first->file, op_first->line);
    dist_type = op_dist_type->uv.type;
  }
  else {
    SPVM_OP* op_dist_type = SPVM_OP_new_op_int_type(compiler, op_first->file, op_first->line);
    dist_type = op_dist_type->uv.type;
  }
  
  if (!(first_type->basic_type->id == dist_type->basic_type->id && first_type->dimension == dist_type->dimension)) {
    SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_first);
    
    SPVM_OP* op_convert = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CONVERT, op_first->file, op_first->line);
    SPVM_OP* op_dist_type = SPVM_OP_new_op_type(compiler, dist_type, op_first->file, op_first->line);
    SPVM_OP_build_convert(compiler, op_convert, op_dist_type, op_first);
    
    SPVM_OP_replace_op(compiler, op_stab, op_convert);
  }
  
  if (!(last_type->basic_type->id == dist_type->basic_type->id && last_type->dimension == dist_type->dimension)) {
    SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_last);
    
    SPVM_OP* op_convert = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CONVERT, op_last->file, op_last->line);
    SPVM_OP* op_dist_type = SPVM_OP_new_op_type(compiler, dist_type, op_last->file, op_last->line);
    SPVM_OP_build_convert(compiler, op_convert, op_dist_type, op_last);
    SPVM_OP_replace_op(compiler, op_stab, op_convert);
  }
}

void SPVM_OP_CHECKER_check(SPVM_COMPILER* compiler) {
  
  // Resolve types
  SPVM_OP_CHECKER_resolve_types(compiler);
  if (compiler->error_count > 0) {
    return;
  }
  
  // Resolve basic types
  SPVM_OP_CHECKER_resolve_basic_types(compiler);
  
  // Resolve packages
  SPVM_OP_CHECKER_resolve_packages(compiler);
  if (compiler->error_count > 0) {
    return;
  }

  // Temporary buffer
  char tmp_buffer[UINT16_MAX];
  char tmp_buffer2[UINT16_MAX];
  
  // Check trees
  int32_t sub_id = 0;
  {
    int32_t package_index;
    for (package_index = 0; package_index < compiler->packages->length; package_index++) {
      SPVM_PACKAGE* package = SPVM_LIST_fetch(compiler->packages, package_index);
      SPVM_LIST* subs = package->subs;
      {
        int32_t sub_index;
        for (sub_index = 0; sub_index < subs->length; sub_index++) {
          
          SPVM_SUB* sub = SPVM_LIST_fetch(subs, sub_index);
          SPVM_PACKAGE* package = sub->package;
          SPVM_TYPE* package_type = package->op_type->uv.type;
          
          // Set subroutine id
          sub->id = sub_id++;
          
          
          // Destructor must receive own package object
          if (sub->flag & SPVM_SUB_C_FLAG_IS_DESTRUCTOR) {
            // DESTROY argument must be 0
            int32_t error = 0;
            if (sub->args->length != 1) {
              error = 1;
            }
            else {
              SPVM_MY* arg_my = SPVM_LIST_fetch(sub->args, 0);
              SPVM_TYPE* arg_type = SPVM_OP_get_type(compiler, arg_my->op_my);
              
              if (!(arg_type->basic_type->id == package_type->basic_type->id && arg_type->dimension == package_type->dimension)) {
                error = 1;
              }
            }
            
            if (error) {
              SPVM_yyerror_format(compiler, "DESTROY argument must be self\n", sub->op_sub->file, sub->op_sub->line);
            }
          }
          
          {
            int32_t arg_index;
            for (arg_index = 0 ; arg_index < sub->args->length; arg_index++) {
              SPVM_MY* arg_my = SPVM_LIST_fetch(sub->args, arg_index);
              SPVM_TYPE* arg_type = SPVM_OP_get_type(compiler, arg_my->op_my);
              if (SPVM_TYPE_is_object_type(compiler, arg_type->basic_type->id, arg_type->dimension, arg_type->flag)) {
                SPVM_LIST_push(sub->object_arg_ids, (void*)(intptr_t)arg_index);
              }
            }
          }
          
          if (package->category == SPVM_PACKAGE_C_CATEGORY_INTERFACE && (sub->op_block || sub->flag & SPVM_SUB_C_FLAG_HAVE_NATIVE_DESC)) {
            SPVM_yyerror_format(compiler, "Interface sub can't have implementation\n", sub->op_sub->file, sub->op_sub->line);
          }
          
          // Check subroutine - First tree traversal
          if (!(sub->flag & SPVM_SUB_C_FLAG_HAVE_NATIVE_DESC)) {
            int32_t eval_block_stack_length = 0;
            int32_t loop_block_stack_length = 0;
            
            // My stack
            SPVM_LIST* my_stack = SPVM_LIST_new(0);
            
            // Block my base stack
            SPVM_LIST* block_my_base_stack = SPVM_LIST_new(0);
            
            // Switch stack
            SPVM_LIST* op_switch_stack = SPVM_LIST_new(0);
            
            // Run OPs
            SPVM_OP* op_base = SPVM_OP_get_op_block_from_op_sub(compiler, sub->op_sub);
            SPVM_OP* op_cur = op_base;
            int32_t finish = 0;
            while (op_cur) {
              int32_t rel_line = op_cur->line - sub->op_sub->line;
              if (op_cur->line >= SPVM_LIMIT_C_OPCODE_OPERAND_VALUE_MAX) {
                SPVM_yyerror_format(compiler, "Too many lines at %s line %d\n", op_cur->file, op_cur->line);
              }
              
              // [START]Preorder traversal position
              if (!op_cur->no_need_check) {
                switch (op_cur->id) {
                  // Start scope
                  case SPVM_OP_C_ID_BLOCK: {
                    int32_t block_my_base = my_stack->length;
                    SPVM_LIST_push(block_my_base_stack, (void*)(intptr_t)block_my_base);
                    
                    if (op_cur->uv.block->id == SPVM_BLOCK_C_ID_LOOP_STATEMENTS) {
                      loop_block_stack_length++;
                    }
                    else if (op_cur->uv.block->id == SPVM_BLOCK_C_ID_EVAL) {
                      // Eval block max length
                      eval_block_stack_length++;
                      if (eval_block_stack_length > sub->eval_stack_max_length) {
                        sub->eval_stack_max_length = eval_block_stack_length;
                      }
                    }
                    
                    break;
                  }
                  case SPVM_OP_C_ID_SWITCH: {
                    SPVM_LIST_push(op_switch_stack, op_cur);
                    break;
                  }
                  case SPVM_OP_C_ID_NEW: {
                    // If new package { ... } syntax, replace package to type
                    if (op_cur->first->id == SPVM_OP_C_ID_PACKAGE) {
                      SPVM_OP* op_package = op_cur->first;
                      SPVM_PACKAGE* package = op_package->uv.package;

                      SPVM_TYPE* type = package->op_type->uv.type;
                      
                      SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type, op_package->file, op_package->line);
                      
                      SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_cur->first);
                      SPVM_OP_replace_op(compiler, op_stab, op_type);
                    }
                    
                    break;
                  }
                }
              }
              // [END]Preorder traversal position
              
              if (op_cur->first) {
                op_cur = op_cur->first;
              }
              else {
                while (1) {
                  // [START]Postorder traversal position
                  if (!op_cur->no_need_check) {
                    switch (op_cur->id) {
                      case SPVM_OP_C_ID_ARRAY_INIT: {
                        SPVM_OP* op_array_init = op_cur;
                        
                        SPVM_OP* op_list_elements = op_cur->first;
                        SPVM_OP* op_type_new_default = op_cur->last;
                        
                        const char* file = op_list_elements->file;
                        int32_t line = op_list_elements->line;
                        
                        SPVM_OP* op_new = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_NEW, file, line);

                        SPVM_OP* op_type_new = NULL;
                        SPVM_OP* op_type_element = NULL;
                        
                        if (op_type_new_default->id == SPVM_OP_C_ID_TYPE) {
                          op_type_new = op_type_new_default;
                          
                          // Create element type
                          SPVM_TYPE* type_element = SPVM_TYPE_new(compiler);
                          type_element->basic_type = op_type_new->uv.type->basic_type;
                          type_element->dimension = op_type_new->uv.type->dimension - 1;
                          type_element->flag = op_type_new->uv.type->flag;
                          op_type_element = SPVM_OP_new_op_type(compiler, type_element, op_type_new_default->file, op_type_new_default->line);
                        }
                        
                        SPVM_OP* op_sequence = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_SEQUENCE, file, line);
                        SPVM_OP* op_assign_new = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, file, line);
                        SPVM_OP* op_var_tmp_new = SPVM_OP_CHECKER_new_op_var_tmp(compiler, sub->op_sub, NULL, file, line);
                        SPVM_LIST_push(my_stack, op_var_tmp_new->uv.var->my);
                        
                        SPVM_OP_build_assign(compiler, op_assign_new, op_var_tmp_new, op_new);

                        SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_new);
                        
                        int32_t length;
                        {
                          SPVM_OP* op_term_element = op_list_elements->first;
                          int32_t index = 0;
                          while ((op_term_element = SPVM_OP_sibling(compiler, op_term_element))) {
                            if (index == 0) {
                              
                              if (op_term_element->id == SPVM_OP_C_ID_UNDEF) {
                                SPVM_yyerror_format(compiler, "Array initialization first element must not be undef at %s line %d\n", file, line);
                              }

                              SPVM_TYPE* type_term_element = SPVM_OP_get_type(compiler, op_term_element);
                              
                              op_term_element->no_need_check = 1;

                              // Create element type
                              if (op_type_element == NULL) {
                                SPVM_TYPE* type_element = SPVM_TYPE_new(compiler);
                                type_element->basic_type = type_term_element->basic_type;
                                type_element->dimension = type_term_element->dimension;
                                if (type_term_element->flag & SPVM_TYPE_C_FLAG_CONST) {
                                  type_element->flag |= SPVM_TYPE_C_FLAG_CONST;
                                }
                                op_type_element = SPVM_OP_new_op_type(compiler, type_element, file, line);
                              }
                              
                              if (!SPVM_TYPE_is_numeric_type(compiler, op_type_element->uv.type->basic_type->id,op_type_element->uv.type->dimension, op_type_element->uv.type->flag)) {
                                if (sub->info_types->length >= SPVM_LIMIT_C_OPCODE_OPERAND_VALUE_MAX) {
                                  SPVM_yyerror_format(compiler, "Too many types at %s line %d\n", op_type_element->file, op_type_element->line);
                                }
                                op_type_element->uv.type->sub_rel_id = sub->info_types->length;
                                SPVM_LIST_push(sub->info_types, op_type_element->uv.type);
                              }
                                                      
                              // Create array type
                              if (op_type_new == NULL) {
                                SPVM_TYPE* type_new = SPVM_TYPE_new(compiler);
                                type_new->basic_type = type_term_element->basic_type;
                                type_new->dimension = type_term_element->dimension + 1;
                                if (type_term_element->flag & SPVM_TYPE_C_FLAG_CONST) {
                                  type_new->flag |= SPVM_TYPE_C_FLAG_CONST;
                                }
                                op_type_new = SPVM_OP_new_op_type(compiler, type_new, file, line);
                              }

                              if (!SPVM_TYPE_is_numeric_type(compiler, op_type_new->uv.type->basic_type->id, op_type_new->uv.type->dimension, op_type_new->uv.type->flag)) {
                                if (sub->info_types->length >= SPVM_LIMIT_C_OPCODE_OPERAND_VALUE_MAX) {
                                  SPVM_yyerror_format(compiler, "Too many types at %s line %d\n", op_type_element->file, op_type_element->line);
                                }
                                op_type_new->uv.type->sub_rel_id = sub->info_types->length;
                                SPVM_LIST_push(sub->info_types, op_type_new->uv.type);
                              }
                              
                              op_var_tmp_new->uv.var->my->type = op_type_new->uv.type;
                            }
                            
                            SPVM_OP* op_assign_array_access = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, file, line);
                            SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_term_element);
                            
                            SPVM_OP* op_array_access = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ARRAY_ACCESS, file, line);

                            SPVM_OP* op_var_tmp_array_access = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_VAR, file, line);
                            op_var_tmp_array_access->uv.var = op_var_tmp_new->uv.var;
                            SPVM_OP_insert_child(compiler, op_array_access, op_array_access->last, op_var_tmp_array_access);

                            SPVM_OP* op_constant_index = SPVM_OP_new_op_constant_int(compiler, index, file, line);
                            SPVM_OP_insert_child(compiler, op_array_access, op_array_access->last, op_constant_index);
                            
                            SPVM_OP_build_assign(compiler, op_assign_array_access, op_array_access, op_term_element);
                            
                            SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_array_access);
                            
                            index++;
                            op_term_element = op_stab;
                          }
                          length = index;
                        }
                        
                        SPVM_OP_insert_child(compiler, op_new, op_new->last, op_type_new);
                        SPVM_OP_insert_child(compiler, op_type_new, op_type_new->last, op_type_element);

                        SPVM_OP* op_constant_length = SPVM_OP_new_op_constant_int(compiler, length, file, line);
                        SPVM_OP_insert_child(compiler, op_type_new, op_type_new->last, op_constant_length);
                        
                        SPVM_OP* op_var_tmp_ret = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_VAR, file, line);
                        op_var_tmp_ret->uv.var = op_var_tmp_new->uv.var;
                        
                        SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_var_tmp_ret);

                        if (length == 0) {
                          SPVM_yyerror_format(compiler, "Array initialization need at least one element at %s line %d\n", file, line);
                        }
                        
                        SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_cur);
                        
                        SPVM_OP_replace_op(compiler, op_stab, op_sequence);
                        
                        op_array_init->first = op_sequence;
                        
                        op_cur = op_sequence->first;
                        
                        break;
                      }
                      case SPVM_OP_C_ID_NEXT: {
                        if (loop_block_stack_length == 0) {
                          SPVM_yyerror_format(compiler, "next statement must be in loop block at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        break;
                      }
                      case SPVM_OP_C_ID_LAST: {
                        if (loop_block_stack_length == 0 && op_switch_stack->length == 0) {
                          SPVM_yyerror_format(compiler, "last statement must be in loop block or switch block at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        break;
                      }
                      case SPVM_OP_C_ID_CONSTANT: {
                        SPVM_TYPE* type = SPVM_OP_get_type(compiler, op_cur);
                        
                        int32_t add_constant = 0;
                        
                        // long or double
                        if (type->dimension == 0) {
                          switch (type->basic_type->id) {
                            case SPVM_BASIC_TYPE_C_ID_LONG: {
                              add_constant = 1;

                              if (sub->info_long_constants->length >= SPVM_LIMIT_C_OPCODE_OPERAND_VALUE_MAX) {
                                SPVM_yyerror_format(compiler, "Too many long constants at %s line %d\n", op_cur->file, op_cur->line);
                              }
                              op_cur->uv.constant->sub_rel_info_long_id = sub->info_long_constants->length;
                              SPVM_LIST_push(sub->info_long_constants, op_cur->uv.constant);
                            }
                            case SPVM_BASIC_TYPE_C_ID_DOUBLE: {
                              add_constant = 1;
                              if (sub->info_double_constants->length >= SPVM_LIMIT_C_OPCODE_OPERAND_VALUE_MAX) {
                                SPVM_yyerror_format(compiler, "Too many double constants at %s line %d\n", op_cur->file, op_cur->line);
                              }
                              op_cur->uv.constant->sub_rel_info_double_id = sub->info_double_constants->length;
                              SPVM_LIST_push(sub->info_double_constants, op_cur->uv.constant);
                            }
                          }
                        }
                        // String
                        else if (type->dimension == 1 && type->basic_type->id == SPVM_BASIC_TYPE_C_ID_BYTE) {
                          add_constant = 1;
                          
                          if (sub->info_string_constants->length >= SPVM_LIMIT_C_OPCODE_OPERAND_VALUE_MAX) {
                            SPVM_yyerror_format(compiler, "Too many string constants at %s line %d\n", op_cur->file, op_cur->line);
                          }
                          op_cur->uv.constant->sub_rel_info_string_id = sub->info_string_constants->length;
                          SPVM_LIST_push(sub->info_string_constants, op_cur->uv.constant);
                        }
                        
                        if (add_constant) {
                          if (sub->info_constants->length >= SPVM_LIMIT_C_OPCODE_OPERAND_VALUE_MAX) {
                            SPVM_yyerror_format(compiler, "Too many constant at %s line %d\n", op_cur->file, op_cur->line);
                          }
                          op_cur->uv.constant->sub_rel_id = sub->info_constants->length;
                          SPVM_LIST_push(sub->info_constants, op_cur->uv.constant);
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_SWITCH: {
                        
                        SPVM_OP* op_switch_condition = op_cur->first;
                        
                        SPVM_TYPE* term_type = SPVM_OP_get_type(compiler, op_switch_condition->first);
                        
                        // Check type
                        if (!term_type || !(term_type->dimension == 0 && term_type->basic_type->id == SPVM_BASIC_TYPE_C_ID_INT)) {
                          SPVM_yyerror_format(compiler, "Switch condition must be int value at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        SPVM_SWITCH_INFO* switch_info = op_cur->uv.switch_info;
                        SPVM_LIST* cases = switch_info->cases;
                        int32_t length = cases->length;
                        
                        // Check case type
                        {
                          int32_t i;
                          for (i = 0; i < length; i++) {
                            SPVM_CASE_INFO* case_info = SPVM_LIST_fetch(cases, i);
                            SPVM_OP* op_constant = case_info->op_case_info->first;
                            SPVM_CONSTANT* constant = op_constant->uv.constant;

                            if (op_constant->id != SPVM_OP_C_ID_CONSTANT) {
                              SPVM_yyerror_format(compiler, "case value must be constant at %s line %d\n", op_cur->file, op_cur->line);
                            }
                            
                            SPVM_TYPE* case_value_type = SPVM_OP_get_type(compiler, op_constant);
                            if (!(case_value_type->basic_type->id == SPVM_BASIC_TYPE_C_ID_INT && case_value_type->dimension == 0)) {
                              SPVM_yyerror_format(compiler, "case value must be int constant at %s line %d\n", case_info->op_case_info->file, case_info->op_case_info->line);
                            }
                            case_info->constant = constant;
                          }
                        }
                        
                        switch_info->id = SPVM_SWITCH_INFO_C_ID_LOOKUP_SWITCH;
                        
                        SPVM_LIST_pop(op_switch_stack);

                        if (sub->info_switch_infos->length >= SPVM_LIMIT_C_OPCODE_OPERAND_VALUE_MAX) {
                          SPVM_yyerror_format(compiler, "Too many switch at %s line %d\n", op_cur->file, op_cur->line);
                        }
                        op_cur->uv.switch_info->sub_rel_id = sub->info_switch_infos->length;
                        SPVM_LIST_push(sub->info_switch_infos, op_cur->uv.switch_info);
                      
                        break;
                      }
                      case SPVM_OP_C_ID_CASE: {
                        if (op_switch_stack->length > 0) {
                          SPVM_OP* op_switch = SPVM_LIST_fetch(op_switch_stack, op_switch_stack->length - 1);
                          SPVM_SWITCH_INFO* switch_info = op_switch->uv.switch_info;
                          op_cur->uv.case_info->index = switch_info->cases->length;
                          SPVM_LIST_push(switch_info->cases, op_cur->uv.case_info);
                        }
                        break;
                      }
                      case SPVM_OP_C_ID_DEFAULT: {
                        if (op_switch_stack->length > 0) {
                          SPVM_OP* op_switch = SPVM_LIST_fetch(op_switch_stack, op_switch_stack->length - 1);
                          SPVM_SWITCH_INFO* switch_info = op_switch->uv.switch_info;
                          
                          if (switch_info->op_default) {
                            SPVM_yyerror_format(compiler, "Duplicate default statement at %s line %d\n", op_cur->file, op_cur->line);
                          }
                          else {
                            switch_info->op_default = op_cur;
                          }
                        }
                        break;
                      }
                      case SPVM_OP_C_ID_BOOL: {
                        SPVM_OP* op_first = op_cur->first;
                        
                        // undef
                        if (op_first->id == SPVM_OP_C_ID_UNDEF) {
                          
                          SPVM_OP* op_false = SPVM_OP_new_op_constant_int(compiler, 0, op_first->file, op_first->line);
                          
                          SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_first);
                          
                          SPVM_OP_replace_op(compiler, op_stab, op_false);
                          
                          op_cur = op_false;
                        }
                        else {
                          if (op_first->id == SPVM_OP_C_ID_IF) {
                            // OK
                          }
                          else {
                            SPVM_TYPE* type = SPVM_OP_get_type(compiler, op_first);
                            
                            // Numeric or object
                            if (SPVM_TYPE_is_numeric_type(compiler, type->basic_type->id, type->dimension, type->flag) || SPVM_TYPE_is_object_type(compiler, type->basic_type->id, type->dimension, type->flag))
                            {
                              // OK
                            }
                            else {
                              SPVM_yyerror_format(compiler, "Invalid boolean type at %s line %d\n", op_cur->file, op_cur->line);
                            }
                          }
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_EQ: {
                        SPVM_OP* op_first = op_cur->first;
                        SPVM_OP* op_last = op_cur->last;
                        
                        // undef == undef
                        if (op_first->id == SPVM_OP_C_ID_UNDEF && op_last->id == SPVM_OP_C_ID_UNDEF) {
                          
                          SPVM_OP* op_true = SPVM_OP_new_op_constant_int(compiler, 1, op_first->file, op_first->line);
                          SPVM_OP* op_bool = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_BOOL, op_first->file, op_first->line);
                          
                          SPVM_OP_insert_child(compiler, op_bool, op_bool->last, op_true);
                          
                          SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_cur);
                          
                          SPVM_OP_replace_op(compiler, op_stab, op_bool);
                          
                          op_cur = op_true;
                        }
                        // term == term
                        else if (op_first->id != SPVM_OP_C_ID_UNDEF && op_last->id != SPVM_OP_C_ID_UNDEF) {
                          SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                          SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                          
                          // numeric == numeric
                          if (SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag) && SPVM_TYPE_is_numeric_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                            SPVM_OP_apply_binary_numeric_convertion(compiler, op_cur->first, op_cur->last);
                          }
                          // object == object
                          else if (SPVM_TYPE_is_object_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag) && SPVM_TYPE_is_object_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                            // OK
                          }
                          else {
                            SPVM_yyerror_format(compiler, "Invalid == comparison at %s line %d\n", op_cur->file, op_cur->line);
                          }
                        }
                        // term == undef
                        else if (op_first->id != SPVM_OP_C_ID_UNDEF && op_last->id == SPVM_OP_C_ID_UNDEF) {
                          SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                          if (!SPVM_TYPE_is_object_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                            SPVM_yyerror_format(compiler, "== left value must be object at %s line %d\n", op_cur->file, op_cur->line);
                          }
                        }
                        // undef == term
                        else if (op_first->id == SPVM_OP_C_ID_UNDEF && op_last->id != SPVM_OP_C_ID_UNDEF) {
                          SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                          if (!SPVM_TYPE_is_object_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                            SPVM_yyerror_format(compiler, "== right value must be object at %s line %d\n", op_cur->file, op_cur->line);
                          }
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_NE: {
                        SPVM_OP* op_first = op_cur->first;
                        SPVM_OP* op_last = op_cur->last;

                        // undef != undef
                        if (op_first->id == SPVM_OP_C_ID_UNDEF && op_last->id == SPVM_OP_C_ID_UNDEF) {
                          
                          SPVM_OP* op_false = SPVM_OP_new_op_constant_int(compiler, 0, op_first->file, op_first->line);
                          SPVM_OP* op_bool = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_BOOL, op_first->file, op_first->line);
                          
                          SPVM_OP_insert_child(compiler, op_bool, op_bool->last, op_false);
                          
                          SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_cur);
                          
                          SPVM_OP_replace_op(compiler, op_stab, op_bool);
                          
                          op_cur = op_false;
                        }
                        // term != term
                        else if (op_first->id != SPVM_OP_C_ID_UNDEF && op_last->id != SPVM_OP_C_ID_UNDEF) {
                          SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                          SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);

                          // numeric != numeric
                          if (SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag) && SPVM_TYPE_is_numeric_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                            SPVM_OP_apply_binary_numeric_convertion(compiler, op_cur->first, op_cur->last);
                          }
                          // numeric != object
                          else if (SPVM_TYPE_is_object_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag) && SPVM_TYPE_is_object_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                            // OK
                          }
                          else {
                            SPVM_yyerror_format(compiler, "Invalid != comparison at %s line %d\n", op_cur->file, op_cur->line);
                          }
                        }
                        // term != undef
                        else if (op_first->id != SPVM_OP_C_ID_UNDEF && op_last->id == SPVM_OP_C_ID_UNDEF) {
                          SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);

                          if (SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                            SPVM_yyerror_format(compiler, "!= left value must be object at %s line %d\n", op_cur->file, op_cur->line);
                          }
                        }
                        // undef != term
                        else if (op_first->id == SPVM_OP_C_ID_UNDEF && op_last->id != SPVM_OP_C_ID_UNDEF) {
                          SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);

                          if (SPVM_TYPE_is_numeric_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                            SPVM_yyerror_format(compiler, "!= right value must be object at %s line %d\n", op_cur->file, op_cur->line);
                          }
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_LT: {

                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        if (SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag) && SPVM_TYPE_is_numeric_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_OP_apply_binary_numeric_convertion(compiler, op_cur->first, op_cur->last);
                        }
                        else {
                          SPVM_yyerror_format(compiler, "Invalid < comparison at %s line %d\n", op_cur->file, op_cur->line);
                        }

                        break;
                      }
                      case SPVM_OP_C_ID_LE: {

                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);

                        if (SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag) && SPVM_TYPE_is_numeric_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_OP_apply_binary_numeric_convertion(compiler, op_cur->first, op_cur->last);
                        }
                        else {
                          SPVM_yyerror_format(compiler, "Invalid <= comparison at %s line %d\n", op_cur->file, op_cur->line);
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_GT: {

                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);

                        if (SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag) && SPVM_TYPE_is_numeric_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_OP_apply_binary_numeric_convertion(compiler, op_cur->first, op_cur->last);
                        }
                        else {
                          SPVM_yyerror_format(compiler, "Invalid > comparison at %s line %d\n", op_cur->file, op_cur->line);
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_GE: {

                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);

                        if (SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag) && SPVM_TYPE_is_numeric_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_OP_apply_binary_numeric_convertion(compiler, op_cur->first, op_cur->last);
                        }
                        else {
                          SPVM_yyerror_format(compiler, "Invalid <= comparison at %s line %d\n", op_cur->file, op_cur->line);
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_LEFT_SHIFT: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // Can receive only numeric type
                        if (SPVM_TYPE_is_integral_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_OP_CHECKER_apply_unary_numeric_convertion(compiler, op_cur->first);
                        }
                        else {
                          SPVM_yyerror_format(compiler, "<< operator left value must be integral at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        if (SPVM_TYPE_is_integral_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_OP_CHECKER_apply_unary_numeric_convertion(compiler, op_cur->last);
                          
                          if (last_type->dimension == 0 && last_type->basic_type->id >= SPVM_BASIC_TYPE_C_ID_LONG) {
                            SPVM_yyerror_format(compiler, "<< operator right value must be int at %s line %d\n", op_cur->file, op_cur->line);
                            
                            return;
                          }
                        }
                        else {
                          SPVM_yyerror_format(compiler, "<< operator right value must be int at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_RIGHT_SHIFT: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // Can receive only numeric type
                        if (SPVM_TYPE_is_integral_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_OP_CHECKER_apply_unary_numeric_convertion(compiler, op_cur->first);
                        }
                        else {
                          SPVM_yyerror_format(compiler, ">> operator left value must be integral at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        if (SPVM_TYPE_is_integral_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_OP_CHECKER_apply_unary_numeric_convertion(compiler, op_cur->last);
                          
                          if (last_type->dimension == 0 && last_type->basic_type->id >= SPVM_BASIC_TYPE_C_ID_LONG) {
                            SPVM_yyerror_format(compiler, ">> operator right value must be int at %s line %d\n", op_cur->file, op_cur->line);
                            
                            return;
                          }
                        }
                        else {
                          SPVM_yyerror_format(compiler, ">> operator right value must be int at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_RIGHT_SHIFT_UNSIGNED: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // Can receive only numeric type
                        if (SPVM_TYPE_is_integral_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_OP_CHECKER_apply_unary_numeric_convertion(compiler, op_cur->first);
                        }
                        else {
                          SPVM_yyerror_format(compiler, ">>> operator left value must be integral at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        if (SPVM_TYPE_is_integral_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_OP_CHECKER_apply_unary_numeric_convertion(compiler, op_cur->last);
                          
                          if (last_type->dimension == 0 && last_type->basic_type->id >= SPVM_BASIC_TYPE_C_ID_LONG) {
                            SPVM_yyerror_format(compiler, ">>> operator right value must be int at %s line %d\n", op_cur->file, op_cur->line);
                            
                            return;
                          }
                        }
                        else {
                          SPVM_yyerror_format(compiler, ">>> operator right value must be int at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_NEW: {
                        assert(op_cur->first);

                        if (op_cur->first->id == SPVM_OP_C_ID_TYPE) {
                          SPVM_OP* op_type = op_cur->first;
                          
                          SPVM_TYPE* type = op_type->uv.type;
                          
                          // Array
                          if (SPVM_TYPE_is_array_type(compiler, type->basic_type->id, type->dimension, type->flag)) {
                            
                            SPVM_OP* op_index_term = op_type->last;

                            SPVM_TYPE* index_type = SPVM_OP_get_type(compiler, op_index_term);
                            
                            assert(index_type);
                            if (SPVM_TYPE_is_numeric_type(compiler, index_type->basic_type->id, index_type->dimension, index_type->flag)) {
                              SPVM_OP_CHECKER_apply_unary_numeric_convertion(compiler, op_index_term);
                              
                              SPVM_TYPE* index_type = SPVM_OP_get_type(compiler, op_index_term);
                              
                              if (!(index_type->dimension == 0 && index_type->basic_type->id >= SPVM_BASIC_TYPE_C_ID_BYTE && index_type->basic_type->id <= SPVM_BASIC_TYPE_C_ID_INT)) {
                                char* type_name = tmp_buffer;
                                SPVM_TYPE_sprint_type_name(compiler, type_name, type->basic_type->id, type->dimension, type->flag);
                                SPVM_yyerror_format(compiler, "new operator can't create array which don't have int length \"%s\" at %s line %d\n", type_name, op_cur->file, op_cur->line);
                                return;
                              }
                            }
                            else {
                              char* type_name = tmp_buffer;
                              SPVM_TYPE_sprint_type_name(compiler, type_name, type->basic_type->id, type->dimension, type->flag);
                              SPVM_yyerror_format(compiler, "new operator can't create array which don't have numeric length \"%s\" at %s line %d\n", type_name, op_cur->file, op_cur->line);
                              return;
                            }
                          }
                          // Numeric type
                          else if (SPVM_TYPE_is_numeric_type(compiler, type->basic_type->id, type->dimension, type->flag)) {
                            SPVM_yyerror_format(compiler, "new operator can't receive numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          }
                          // Object type
                          else if (SPVM_TYPE_is_object_type(compiler, type->basic_type->id, type->dimension, type->flag)) {
                            SPVM_PACKAGE* package = SPVM_HASH_fetch(compiler->package_symtable, type->basic_type->name, strlen(type->basic_type->name));
                            
                            if (package->category == SPVM_PACKAGE_C_CATEGORY_INTERFACE) {
                              SPVM_yyerror_format(compiler, "Can't create object of interface package at %s line %d\n", op_cur->file, op_cur->line);
                            }
                            else if (package->category == SPVM_PACKAGE_C_CATEGORY_VALUE_T) {
                              SPVM_yyerror_format(compiler, "Can't create object of value_t package at %s line %d\n", op_cur->file, op_cur->line);
                            }
                            else if (package->flag & SPVM_PACKAGE_C_FLAG_IS_POINTER) {
                              SPVM_yyerror_format(compiler, "Can't create object of struct package at %s line %d\n", op_cur->file, op_cur->line);
                            }
                            else if (package->flag & SPVM_PACKAGE_C_FLAG_IS_PRIVATE) {
                              if (strcmp(package->op_name->uv.name, sub->package->op_name->uv.name) != 0) {
                                SPVM_yyerror_format(compiler, "Can't create object of private package at %s line %d\n", op_cur->file, op_cur->line);
                              }
                            }
                          }
                          else {
                            assert(0);
                          }
                          
                          if (!SPVM_TYPE_is_numeric_type(compiler, op_type->uv.type->basic_type->id, op_type->uv.type->dimension, op_type->uv.type->flag)) {
                            if (sub->info_types->length >= SPVM_LIMIT_C_OPCODE_OPERAND_VALUE_MAX) {
                              SPVM_yyerror_format(compiler, "Too many types at %s line %d\n", op_cur->file, op_cur->line);
                            }
                            op_type->uv.type->sub_rel_id = sub->info_types->length;
                            SPVM_LIST_push(sub->info_types, op_type->uv.type);
                          }
                        }
                        else if (op_cur->first->id == SPVM_OP_C_ID_CONSTANT) {
                          // Constant string
                        }
                        else {
                          assert(0);
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_BIT_XOR: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // Can receive only integral type
                        if (!SPVM_TYPE_is_integral_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag) || !SPVM_TYPE_is_integral_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_yyerror_format(compiler,
                            "^ operator can receive only integral type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        SPVM_OP_apply_binary_numeric_convertion(compiler, op_cur->first, op_cur->last);
                        
                        break;
                      }
                      case SPVM_OP_C_ID_BIT_OR: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // Can receive only integral type
                        if (!SPVM_TYPE_is_integral_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag) || !SPVM_TYPE_is_integral_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_yyerror_format(compiler,
                            "| operator can receive only integral type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        
                        SPVM_OP_apply_binary_numeric_convertion(compiler, op_cur->first, op_cur->last);
                        
                        break;
                      }
                      case SPVM_OP_C_ID_BIT_AND: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // Can receive only integral type
                        if (!SPVM_TYPE_is_integral_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag) || !SPVM_TYPE_is_integral_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_yyerror_format(compiler,
                            "& operator can receive only integral type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        SPVM_OP_apply_binary_numeric_convertion(compiler, op_cur->first, op_cur->last);
                        
                        break;
                      }
                      case SPVM_OP_C_ID_ISA: {
                        SPVM_TYPE* term_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_OP* op_type = op_cur->last;
                        
                        // Left term must be object type
                        if (!SPVM_TYPE_is_object_type(compiler, term_type->basic_type->id, term_type->dimension, term_type->flag)) {
                          SPVM_yyerror_format(compiler, "isa left value must be object type at %s line %d\n", op_cur->file, op_cur->line);
                        }
                        
                        // Right type must be object type
                        if (SPVM_TYPE_is_object_type(compiler, op_type->uv.type->basic_type->id, op_type->uv.type->dimension, op_type->uv.type->flag)) {
                          // Right type must be not any object type
                          if (SPVM_TYPE_is_any_object_type(compiler, op_type->uv.type->basic_type->id, op_type->uv.type->dimension, op_type->uv.type->flag)) {
                            SPVM_yyerror_format(compiler, "isa rigth type must be not any object type at %s line %d\n", op_cur->file, op_cur->line);
                          }
                        }
                        else {
                          SPVM_yyerror_format(compiler, "isa rigth type must be object type at %s line %d\n", op_cur->file, op_cur->line);
                        }

                        if (!SPVM_TYPE_is_numeric_type(compiler, op_type->uv.type->basic_type->id, op_type->uv.type->dimension, op_type->uv.type->flag)) {
                          if (sub->info_types->length >= SPVM_LIMIT_C_OPCODE_OPERAND_VALUE_MAX) {
                            SPVM_yyerror_format(compiler, "Too many types at %s line %d\n", op_cur->file, op_cur->line);
                          }
                          op_type->uv.type->sub_rel_id = sub->info_types->length;
                          SPVM_LIST_push(sub->info_types, op_type->uv.type);
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_STRING_EQ: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // undef check
                        if (!first_type) {
                          SPVM_yyerror_format(compiler, "eq left value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        if (!last_type) {
                          SPVM_yyerror_format(compiler, "eq right value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        
                        // Can receive only numeric type
                        if (!SPVM_TYPE_is_string_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "eq left type must be string at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        if (!SPVM_TYPE_is_string_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_yyerror_format(compiler, "eq right type must be string at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_STRING_NE: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // undef check
                        if (!first_type) {
                          SPVM_yyerror_format(compiler, "ne left value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        if (!last_type) {
                          SPVM_yyerror_format(compiler, "ne right value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        
                        // Can receive only numeric type
                        if (!SPVM_TYPE_is_string_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "ne left type must be string at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        if (!SPVM_TYPE_is_string_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_yyerror_format(compiler, "ne right type must be string at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_STRING_GT: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // undef check
                        if (!first_type) {
                          SPVM_yyerror_format(compiler, "gt left value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        if (!last_type) {
                          SPVM_yyerror_format(compiler, "gt right value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        
                        // Can receive only numeric type
                        if (!SPVM_TYPE_is_string_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "gt left type must be string at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        if (!SPVM_TYPE_is_string_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_yyerror_format(compiler, "gt right type must be string at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_STRING_GE: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // undef check
                        if (!first_type) {
                          SPVM_yyerror_format(compiler, "ge left value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        if (!last_type) {
                          SPVM_yyerror_format(compiler, "ge right value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        
                        // Can receive only numeric type
                        if (!SPVM_TYPE_is_string_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "ge left type must be string at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        if (!SPVM_TYPE_is_string_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_yyerror_format(compiler, "ge right type must be string at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_STRING_LT: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // undef check
                        if (!first_type) {
                          SPVM_yyerror_format(compiler, "lt left value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        if (!last_type) {
                          SPVM_yyerror_format(compiler, "lt right value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        
                        // Can receive only numeric type
                        if (!SPVM_TYPE_is_string_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "lt left type must be string at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        if (!SPVM_TYPE_is_string_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_yyerror_format(compiler, "lt right type must be string at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_STRING_LE: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // undef check
                        if (!first_type) {
                          SPVM_yyerror_format(compiler, "le left value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        if (!last_type) {
                          SPVM_yyerror_format(compiler, "le right value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        
                        // Can receive only numeric type
                        if (!SPVM_TYPE_is_string_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "le left type must be string at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        if (!SPVM_TYPE_is_string_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_yyerror_format(compiler, "le right type must be string at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_ARRAY_LENGTH: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        
                        // First value must be array
                        if (!SPVM_TYPE_is_array_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "right of @ or len must be array at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_PRE_INC:
                      {
                        SPVM_OP* op_first = op_cur->first;
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_first);
                        
                        // Numeric type
                        if (!SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "increment operand must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        // Convert PRE_INC
                        // [before]
                        // PRE_INC
                        //   TERM_MUTABLE
                        // 
                        // [after]
                        // ASSIGN
                        //   ADD
                        //     TERM_MUTABLE
                        //     CONST 1
                        //   TERM_MUTABLE_CLONE
                        SPVM_OP* op_term_mutable = op_cur->first;
                        
                        op_term_mutable->no_need_check = 1;

                        SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_cur);
                        SPVM_OP_cut_op(compiler, op_term_mutable);
                        
                        SPVM_OP* op_term_mutable_clone = SPVM_OP_new_op_term_mutable_clone(compiler, op_term_mutable);
                        
                        SPVM_OP* op_add = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ADD, op_cur->file, op_cur->line);
                        SPVM_OP* op_constant = SPVM_OP_new_op_constant_int(compiler, 1, op_cur->file, op_cur->line);
                        SPVM_OP_build_binop(compiler, op_add, op_term_mutable, op_constant);
                        
                        SPVM_OP* op_assign = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_cur->file, op_cur->line);
                        
                        SPVM_TYPE* term_mutable_type = SPVM_OP_get_type(compiler, op_term_mutable);
                        if (SPVM_TYPE_is_byte_type(compiler, term_mutable_type->basic_type->id, term_mutable_type->dimension, term_mutable_type->flag)
                          || SPVM_TYPE_is_short_type(compiler, term_mutable_type->basic_type->id, term_mutable_type->dimension, term_mutable_type->flag))
                        {
                          SPVM_OP* op_convert = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CONVERT, op_cur->file, op_cur->line);
                          SPVM_OP* op_convert_type = SPVM_OP_new_op_type(compiler, term_mutable_type, op_cur->file, op_cur->line);
                          SPVM_OP_build_convert(compiler, op_convert, op_convert_type, op_add);
                          SPVM_OP_build_assign(compiler, op_assign, op_term_mutable_clone, op_convert);
                        }
                        else {
                          SPVM_OP_build_assign(compiler, op_assign, op_term_mutable_clone, op_add);
                        }
                        
                        SPVM_OP_replace_op(compiler, op_stab, op_assign);
                        
                        op_cur = op_term_mutable;
                        
                        break;
                      }
                      case SPVM_OP_C_ID_PRE_DEC:
                      {
                        SPVM_OP* op_first = op_cur->first;
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_first);
                        
                        // Numeric type
                        if (!SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "decrement operand must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        // Convert PRE_DEC
                        // [before]
                        // PRE_DEC
                        //   TERM_MUTABLE
                        // 
                        // [after]
                        // ASSIGN
                        //   SUBTRACT
                        //     TERM_MUTABLE
                        //     CONST 1
                        //   TERM_MUTABLE_CLONE
                        SPVM_OP* op_term_mutable = op_cur->first;
                        
                        op_term_mutable->no_need_check = 1;

                        SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_cur);
                        SPVM_OP_cut_op(compiler, op_term_mutable);
                        
                        SPVM_OP* op_term_mutable_clone = SPVM_OP_new_op_term_mutable_clone(compiler, op_term_mutable);
                        
                        SPVM_OP* op_subtract = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_SUBTRACT, op_cur->file, op_cur->line);
                        SPVM_OP* op_constant = SPVM_OP_new_op_constant_int(compiler, 1, op_cur->file, op_cur->line);
                        SPVM_OP_build_binop(compiler, op_subtract, op_term_mutable, op_constant);
                        
                        SPVM_OP* op_assign = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_cur->file, op_cur->line);
                        
                        SPVM_TYPE* term_mutable_type = SPVM_OP_get_type(compiler, op_term_mutable);
                        if (SPVM_TYPE_is_byte_type(compiler, term_mutable_type->basic_type->id, term_mutable_type->dimension, term_mutable_type->flag)
                          || SPVM_TYPE_is_short_type(compiler, term_mutable_type->basic_type->id, term_mutable_type->dimension, term_mutable_type->flag))
                        {
                          SPVM_OP* op_convert = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CONVERT, op_cur->file, op_cur->line);
                          SPVM_OP* op_convert_type = SPVM_OP_new_op_type(compiler, term_mutable_type, op_cur->file, op_cur->line);
                          SPVM_OP_build_convert(compiler, op_convert, op_convert_type, op_subtract);
                          SPVM_OP_build_assign(compiler, op_assign, op_term_mutable_clone, op_convert);
                        }
                        else {
                          SPVM_OP_build_assign(compiler, op_assign, op_term_mutable_clone, op_subtract);
                        }
                        
                        SPVM_OP_replace_op(compiler, op_stab, op_assign);
                        
                        op_cur = op_term_mutable;
                        
                        break;
                      }
                      case SPVM_OP_C_ID_POST_INC:
                      {
                        SPVM_OP* op_first = op_cur->first;
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_first);
                        
                        // Numeric type
                        if (!SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "increment operand must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }

                        // Convert POST_INC
                        // [before]
                        // POST_INC
                        //   TERM_MUTABLE
                        // 
                        // [after]
                        // SEQUENCE
                        //   ASSIGN_TMP
                        //     TERM_MUTABLE
                        //     VAR_TMP1
                        //   ASSIGN_ADD
                        //     ADD
                        //       VAR_TMP2
                        //       CONST 1
                        //     TERM_MUTABLE_CLONE
                        //   VAR_TMP3
                        
                        SPVM_OP* op_term_mutable = op_cur->first;

                        op_term_mutable->no_need_check = 1;

                        SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_cur);
                        SPVM_OP_cut_op(compiler, op_term_mutable);

                        SPVM_TYPE* term_mutable_type = SPVM_OP_get_type(compiler, op_term_mutable);
                        
                        SPVM_OP* op_sequence = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_SEQUENCE, op_cur->file, op_cur->line);
                        SPVM_OP* op_var_tmp1 = SPVM_OP_CHECKER_new_op_var_tmp(compiler, sub->op_sub, term_mutable_type, op_cur->file, op_cur->line);
                        SPVM_OP* op_var_tmp2 = SPVM_OP_new_op_var_clone(compiler, op_var_tmp1, op_cur->file, op_cur->line);
                  
                        SPVM_OP* op_assign_tmp = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_cur->file, op_cur->line);
                        SPVM_OP_build_assign(compiler, op_assign_tmp, op_var_tmp1, op_term_mutable);

                        SPVM_OP* op_add = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ADD, op_cur->file, op_cur->line);
                        SPVM_OP* op_constant = SPVM_OP_new_op_constant_int(compiler, 1, op_cur->file, op_cur->line);
                        SPVM_OP_build_binop(compiler, op_add, op_var_tmp2, op_constant);
                        SPVM_OP* op_assign_add = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_cur->file, op_cur->line);
                        
                        SPVM_OP* op_term_mutable_clone = SPVM_OP_new_op_term_mutable_clone(compiler, op_term_mutable);
                        if (SPVM_TYPE_is_byte_type(compiler, term_mutable_type->basic_type->id, term_mutable_type->dimension, term_mutable_type->flag)
                          || SPVM_TYPE_is_short_type(compiler, term_mutable_type->basic_type->id, term_mutable_type->dimension, term_mutable_type->flag))
                        {
                          SPVM_OP* op_convert = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CONVERT, op_cur->file, op_cur->line);
                          SPVM_OP* op_convert_type = SPVM_OP_new_op_type(compiler, term_mutable_type, op_cur->file, op_cur->line);
                          SPVM_OP_build_convert(compiler, op_convert, op_convert_type, op_add);
                          SPVM_OP_build_assign(compiler, op_assign_add, op_term_mutable_clone, op_convert);
                        }
                        else {
                          SPVM_OP_build_assign(compiler, op_assign_add, op_term_mutable_clone, op_add);
                        }

                        SPVM_OP* op_var_tmp3 = SPVM_OP_new_op_var_clone(compiler, op_var_tmp1, op_cur->file, op_cur->line);
                        SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_tmp);
                        SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_add);
                        SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_var_tmp3);
                        
                        SPVM_OP_replace_op(compiler, op_stab, op_sequence);
                        
                        op_cur = op_term_mutable;
                        
                        
                        break;
                      }
                      case SPVM_OP_C_ID_POST_DEC:
                      {
                        SPVM_OP* op_first = op_cur->first;
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_first);
                        
                        // Numeric type
                        if (!SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "decrement operand must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }

                        // Convert POST_DEC
                        // [before]
                        // POST_DEC
                        //   TERM_MUTABLE
                        // 
                        // [after]
                        // SEQUENCE
                        //   ASSIGN_TMP
                        //     TERM_MUTABLE
                        //     VAR_TMP1
                        //   ASSIGN_ADD
                        //     SUBTRACT
                        //       VAR_TMP2
                        //       CONST 1
                        //     TERM_MUTABLE_CLONE
                        //   VAR_TMP3
                        
                        SPVM_OP* op_term_mutable = op_cur->first;

                        op_term_mutable->no_need_check = 1;

                        SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_cur);
                        SPVM_OP_cut_op(compiler, op_term_mutable);

                        SPVM_TYPE* term_mutable_type = SPVM_OP_get_type(compiler, op_term_mutable);
                        
                        SPVM_OP* op_sequence = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_SEQUENCE, op_cur->file, op_cur->line);
                        SPVM_OP* op_var_tmp1 = SPVM_OP_CHECKER_new_op_var_tmp(compiler, sub->op_sub, term_mutable_type, op_cur->file, op_cur->line);
                        SPVM_OP* op_var_tmp2 = SPVM_OP_new_op_var_clone(compiler, op_var_tmp1, op_cur->file, op_cur->line);
                  
                        SPVM_OP* op_assign_tmp = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_cur->file, op_cur->line);
                        SPVM_OP_build_assign(compiler, op_assign_tmp, op_var_tmp1, op_term_mutable);

                        SPVM_OP* op_subtract = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_SUBTRACT, op_cur->file, op_cur->line);
                        SPVM_OP* op_constant = SPVM_OP_new_op_constant_int(compiler, 1, op_cur->file, op_cur->line);
                        SPVM_OP_build_binop(compiler, op_subtract, op_var_tmp2, op_constant);
                        SPVM_OP* op_assign_subtract = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_cur->file, op_cur->line);
                        
                        SPVM_OP* op_term_mutable_clone = SPVM_OP_new_op_term_mutable_clone(compiler, op_term_mutable);
                        if (SPVM_TYPE_is_byte_type(compiler, term_mutable_type->basic_type->id, term_mutable_type->dimension, term_mutable_type->flag)
                          || SPVM_TYPE_is_short_type(compiler, term_mutable_type->basic_type->id, term_mutable_type->dimension, term_mutable_type->flag))
                        {
                          SPVM_OP* op_convert = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CONVERT, op_cur->file, op_cur->line);
                          SPVM_OP* op_convert_type = SPVM_OP_new_op_type(compiler, term_mutable_type, op_cur->file, op_cur->line);
                          SPVM_OP_build_convert(compiler, op_convert, op_convert_type, op_subtract);
                          SPVM_OP_build_assign(compiler, op_assign_subtract, op_term_mutable_clone, op_convert);
                        }
                        else {
                          SPVM_OP_build_assign(compiler, op_assign_subtract, op_term_mutable_clone, op_subtract);
                        }

                        SPVM_OP* op_var_tmp3 = SPVM_OP_new_op_var_clone(compiler, op_var_tmp1, op_cur->file, op_cur->line);
                        SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_tmp);
                        SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_subtract);
                        SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_var_tmp3);
                        
                        SPVM_OP_replace_op(compiler, op_stab, op_sequence);
                        
                        op_cur = op_term_mutable;
                        
                        break;
                      }
                      case SPVM_OP_C_ID_SPECIAL_ASSIGN: {
                        SPVM_OP* op_term_src = op_cur->first;
                        SPVM_OP* op_term_mutable = op_cur->last;
                        
                        // Convert SPECIAL_ASSIGN
                        // [before]
                        // SPECIAL_ASSIGN
                        //   TERM_SRC
                        //   TERM_MUTABLE
                        // [after]
                        // ASSIGN
                        //   CULC
                        //     TERM_MUTABLE
                        //     TERM_SRC
                        //   TERM_MUTABLE_CLONE
                        
                        op_term_mutable->no_need_check = 1;
                        op_term_src->no_need_check = 1;

                        SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_cur);
                        SPVM_OP_cut_op(compiler, op_term_mutable);
                        SPVM_OP_cut_op(compiler, op_term_src);
                        
                        SPVM_OP* op_term_mutable_clone = SPVM_OP_new_op_term_mutable_clone(compiler, op_term_mutable);
                        op_term_mutable_clone->is_lvalue = 1;
                        
                        int32_t culc_op_id;
                        switch (op_cur->flag) {
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_ADD:
                            culc_op_id = SPVM_OP_C_ID_ADD;
                            break;
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_SUBTRACT:
                            culc_op_id = SPVM_OP_C_ID_SUBTRACT;
                            break;
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_MULTIPLY:
                            culc_op_id = SPVM_OP_C_ID_MULTIPLY;
                            break;
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_DIVIDE:
                            culc_op_id = SPVM_OP_C_ID_DIVIDE;
                            break;
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_REMAINDER:
                            culc_op_id = SPVM_OP_C_ID_REMAINDER;
                            break;
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_LEFT_SHIFT:
                            culc_op_id = SPVM_OP_C_ID_LEFT_SHIFT;
                            break;
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_RIGHT_SHIFT:
                            culc_op_id = SPVM_OP_C_ID_RIGHT_SHIFT;
                            break;
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_RIGHT_SHIFT_UNSIGNED:
                            culc_op_id = SPVM_OP_C_ID_RIGHT_SHIFT_UNSIGNED;
                            break;
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_BIT_XOR:
                            culc_op_id = SPVM_OP_C_ID_BIT_XOR;
                            break;
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_BIT_OR:
                            culc_op_id = SPVM_OP_C_ID_BIT_OR;
                            break;
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_BIT_AND:
                            culc_op_id = SPVM_OP_C_ID_BIT_AND;
                            break;
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_CONCAT:
                            culc_op_id = SPVM_OP_C_ID_CONCAT;
                            break;
                          default:
                            assert(0);
                            break;
                        }
                        
                        SPVM_OP* op_culc = SPVM_OP_new_op(compiler, culc_op_id, op_cur->file, op_cur->line);
                        
                        SPVM_OP_build_binop(compiler, op_culc, op_term_mutable, op_term_src);
                        
                        SPVM_OP* op_assign = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_cur->file, op_cur->line);
                        
                        SPVM_TYPE* term_mutable_type = SPVM_OP_get_type(compiler, op_term_mutable);
                        SPVM_TYPE* term_src_type = SPVM_OP_get_type(compiler, op_term_src);
                        
                        int32_t need_conversion = 0;
                        switch (op_cur->flag) {
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_ADD:
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_SUBTRACT:
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_MULTIPLY:
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_DIVIDE:
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_REMAINDER:
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_BIT_XOR:
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_BIT_OR:
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_BIT_AND:
                            if (SPVM_TYPE_is_numeric_type(compiler, term_src_type->basic_type->id, term_src_type->dimension, term_src_type->flag)) {
                              if (SPVM_TYPE_is_numeric_type(compiler, term_mutable_type->basic_type->id, term_mutable_type->dimension, term_mutable_type->flag)) {
                                if (term_src_type->basic_type->id > term_mutable_type->basic_type->id) {
                                  need_conversion = 1;
                                }
                              }
                            }
                            
                            break;

                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_LEFT_SHIFT:
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_RIGHT_SHIFT:
                          case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_RIGHT_SHIFT_UNSIGNED:
                            if (SPVM_TYPE_is_byte_type(compiler, term_mutable_type->basic_type->id, term_mutable_type->dimension, term_mutable_type->flag)
                              || SPVM_TYPE_is_short_type(compiler, term_mutable_type->basic_type->id, term_mutable_type->dimension, term_mutable_type->flag))
                            {
                              need_conversion = 1;
                            }
                            break;
                        }
                        
                        if (need_conversion) {
                          SPVM_OP* op_convert = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CONVERT, op_cur->file, op_cur->line);
                          SPVM_OP* op_convert_type = SPVM_OP_new_op_type(compiler, term_mutable_type, op_cur->file, op_cur->line);
                          SPVM_OP_build_convert(compiler, op_convert, op_convert_type, op_culc);
                          SPVM_OP_build_assign(compiler, op_assign, op_term_mutable_clone, op_convert);
                        }
                        else {
                          SPVM_OP_build_assign(compiler, op_assign, op_term_mutable_clone, op_culc);
                        }
                        
                        SPVM_OP_replace_op(compiler, op_stab, op_assign);
                        
                        op_cur = op_term_mutable;
                        
                        break;
                      }
                      case SPVM_OP_C_ID_ASSIGN: {
                        SPVM_OP* op_term_dist = op_cur->last;
                        SPVM_OP* op_term_src = op_cur->first;
                        
                        SPVM_TYPE* dist_type = SPVM_OP_get_type(compiler, op_term_dist);
                        SPVM_TYPE* src_type = SPVM_OP_get_type(compiler, op_term_src);

                        // Type inference
                        if (op_term_dist->id == SPVM_OP_C_ID_VAR) {
                          SPVM_MY* my = op_term_dist->uv.var->my;
                          if (my->type == NULL) {
                            my->type = SPVM_OP_get_type(compiler, op_term_src);
                          }
                          if (my->type == NULL) {
                            SPVM_yyerror_format(compiler, "Type can't be detected at %s line %d\n", my->op_my->file, my->op_my->line);
                            return;
                          }
                        }
                        
                        // Check if source can be assigned to dist
                        // If needed, numeric convertion op is added
                        SPVM_OP_CHECKER_check_assign(compiler, op_term_dist, op_term_src);
                        
                        break;
                      }
                      case SPVM_OP_C_ID_RETURN: {
                        
                        SPVM_OP* op_term = op_cur->first;
                        
                        if (op_term) {
                          SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_term);
                          SPVM_TYPE* sub_return_type = SPVM_OP_get_type(compiler, sub->return_type->op_type);
                          
                          int32_t is_invalid = 0;
                          
                          // Can't return refernece type
                          if (SPVM_TYPE_is_ref_type(compiler, sub_return_type->basic_type->id, sub_return_type->dimension, sub_return_type->flag)) {
                            SPVM_yyerror_format(compiler, "Can't return reference type at %s line %d\n", op_cur->file, op_cur->line);
                          }
                          
                          // Undef
                          if (op_term->id == SPVM_OP_C_ID_UNDEF) {
                            if (sub->return_type->dimension == 0 && sub->return_type->basic_type->id == SPVM_BASIC_TYPE_C_ID_VOID) {
                              is_invalid = 1;
                            }
                            else {
                              if (SPVM_TYPE_is_numeric_type(compiler, sub_return_type->basic_type->id, sub_return_type->dimension, sub_return_type->flag)) {
                                is_invalid = 1;
                              }
                            }
                          }
                          else if (op_term->id == SPVM_OP_C_ID_CALL_SUB) {
                            SPVM_CALL_SUB* call_sub = op_term->uv.call_sub;
                            SPVM_SUB* sub = call_sub->sub;
                            if (sub->return_type->dimension == 0 && sub->return_type->basic_type->id == SPVM_BASIC_TYPE_C_ID_VOID) {
                              SPVM_yyerror_format(compiler, "Can't return value of void subroutine at %s line %d\n", op_cur->file, op_cur->line);
                            }
                          }
                          // Normal
                          else if (op_term) {
                            if (!(first_type->basic_type->id == sub_return_type->basic_type->id && first_type->dimension == sub_return_type->dimension)) {
                              is_invalid = 1;
                            }
                          }
                          // Empty
                          else {
                            if (!(sub->return_type->dimension == 0 && sub->return_type->basic_type->id == SPVM_BASIC_TYPE_C_ID_VOID)) {
                              is_invalid = 1;
                            }
                          }
                          
                          if (is_invalid) {
                            SPVM_yyerror_format(compiler, "Invalid return type at %s line %d\n", op_cur->file, op_cur->line);
                            
                            return;
                          }
                        }
                        break;
                      }
                      case SPVM_OP_C_ID_NEGATE: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        
                        // Must be numeric type
                        if (!SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "- operator right value must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        SPVM_OP_CHECKER_apply_unary_numeric_convertion(compiler, op_cur->first);
                        
                        break;
                      }
                      case SPVM_OP_C_ID_COMPLEMENT: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        
                        // Must be numeric type
                        if (!SPVM_TYPE_is_integral_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "~ operator right value must be integral type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }

                        SPVM_OP_CHECKER_apply_unary_numeric_convertion(compiler, op_cur->first);
                        
                        break;
                      }
                      case SPVM_OP_C_ID_PLUS: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        
                        // Must be numeric type
                        if (!SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "+ operator right value must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }

                        SPVM_OP_CHECKER_apply_unary_numeric_convertion(compiler, op_cur->first);
                        
                        break;
                      }
                      case SPVM_OP_C_ID_ADD: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // Left value must not be undef
                        if (!first_type) {
                          SPVM_yyerror_format(compiler, "+ operator left value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        // Right value must not be undef
                        if (!last_type) {
                          SPVM_yyerror_format(compiler, "+ operator right value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }

                        // Left value must not be object type
                        if (!SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "+ operator left value must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }

                        // Right value must not be object type
                        if (!SPVM_TYPE_is_numeric_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_yyerror_format(compiler, "+ operator right value must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        // Upgrade type
                        SPVM_OP_apply_binary_numeric_convertion(compiler, op_cur->first, op_cur->last);
                                                        
                        break;
                      }
                      case SPVM_OP_C_ID_SUBTRACT: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // Left value must not be undef
                        if (!first_type) {
                          SPVM_yyerror_format(compiler, "- operator left value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        // Right value Must not be undef
                        if (!last_type) {
                          SPVM_yyerror_format(compiler, "- operator right value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }

                        // Left value must not be object type
                        if (!SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "- operator left value must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }

                        // Right value must not be object type
                        if (!SPVM_TYPE_is_numeric_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_yyerror_format(compiler, "- operator right value must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        // Upgrade type
                        SPVM_OP_apply_binary_numeric_convertion(compiler, op_cur->first, op_cur->last);
                        
                        break;
                      }
                      case SPVM_OP_C_ID_MULTIPLY: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // Left value must not be undef
                        if (!first_type) {
                          SPVM_yyerror_format(compiler, "* operator left value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        // Right value Must not be undef
                        if (!last_type) {
                          SPVM_yyerror_format(compiler, "* operator right value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        // Left value must not be object type
                        if (!SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "* operator left value must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }

                        // Right value must not be object type
                        if (!SPVM_TYPE_is_numeric_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_yyerror_format(compiler, "* operator right value must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }

                        // Upgrade type
                        SPVM_OP_apply_binary_numeric_convertion(compiler, op_cur->first, op_cur->last);
                        
                        break;
                      }
                      case SPVM_OP_C_ID_DIVIDE: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // Left value must not be undef
                        if (!first_type) {
                          SPVM_yyerror_format(compiler, "/ operator left value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        // Right value Must not be undef
                        if (!last_type) {
                          SPVM_yyerror_format(compiler, "/ operator right value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }

                        // Left value must not be object type
                        if (!SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "/ operator left value must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }

                        // Right value must not be object type
                        if (!SPVM_TYPE_is_numeric_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_yyerror_format(compiler, "/ operator right value must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        // Upgrade type
                        SPVM_OP_apply_binary_numeric_convertion(compiler, op_cur->first, op_cur->last);

                        break;
                      }
                      case SPVM_OP_C_ID_REMAINDER: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // Left value must not be undef
                        if (!first_type) {
                          SPVM_yyerror_format(compiler, "%% operator left value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        // Right value Must not be undef
                        if (!last_type) {
                          SPVM_yyerror_format(compiler, "%% operator right value must be not undef at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }

                        // Left value must not be object type
                        if (!SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "%% operator left value must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }

                        // Right value must not be object type
                        if (!SPVM_TYPE_is_numeric_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_yyerror_format(compiler, "%% operator right value must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        // Upgrade type
                        SPVM_OP_apply_binary_numeric_convertion(compiler, op_cur->first, op_cur->last);
                                                        
                        break;
                      }
                      case SPVM_OP_C_ID_CONCAT: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // Left type is numeric type
                        if (SPVM_TYPE_is_numeric_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_OP_CHECKER_apply_unary_string_convertion(compiler, op_cur->first);
                        }
                        // Left type is not string type
                        else if (!SPVM_TYPE_is_string_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "\".\" operator left value must be string at %s line %d\n", op_cur->file, op_cur->line);
                        }
                        
                        // Right value is numeric type
                        if (SPVM_TYPE_is_numeric_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_OP_CHECKER_apply_unary_string_convertion(compiler, op_cur->last);
                        }
                        // Right value is not string type
                        else if (!SPVM_TYPE_is_string_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_yyerror_format(compiler, "\".\" operator right value must be string at %s line %d\n", op_cur->file, op_cur->line);
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_CROAK: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        if (first_type->dimension == 1 && first_type->basic_type->id != SPVM_BASIC_TYPE_C_ID_BYTE) {
                          SPVM_yyerror_format(compiler, "croak argument must be string at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        break;
                      }
                      // End of scope
                      case SPVM_OP_C_ID_BLOCK: {
                        // Pop block my variable base
                        assert(block_my_base_stack->length > 0);
                        int32_t block_my_base = (intptr_t)SPVM_LIST_pop(block_my_base_stack);
                          
                        int32_t my_stack_pop_count = my_stack->length - block_my_base;
                        
                        {
                          int32_t i;
                          for (i = 0; i < my_stack_pop_count; i++) {
                            SPVM_LIST_pop(my_stack);
                          }
                        }

                        // Pop loop block my variable base
                        if (op_cur->uv.block->id == SPVM_BLOCK_C_ID_LOOP_STATEMENTS) {
                          loop_block_stack_length--;
                        }
                        // Pop try block my variable base
                        else if (op_cur->uv.block->id == SPVM_BLOCK_C_ID_EVAL) {
                          eval_block_stack_length--;
                        }

                        
                        break;
                      }
                      case SPVM_OP_C_ID_REF: {
                        
                        SPVM_OP* op_var = op_cur->first;
                        SPVM_TYPE* var_type = SPVM_OP_get_type(compiler, op_var);
                        if (!(SPVM_TYPE_is_numeric_type(compiler, var_type->basic_type->id, var_type->dimension, var_type->flag) || SPVM_TYPE_is_value_type(compiler, var_type->basic_type->id, var_type->dimension, var_type->flag))) {
                          SPVM_yyerror_format(compiler, "Refernece target must be numeric type or value type at %s line %d\n", op_cur->file, op_cur->line);
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_DEREF: {
                        SPVM_OP* op_var = op_cur->first;
                        SPVM_TYPE* var_type = SPVM_OP_get_type(compiler, op_var);
                        
                        if (!(SPVM_TYPE_is_numeric_ref_type(compiler, var_type->basic_type->id, var_type->dimension, var_type->flag) || SPVM_TYPE_is_value_ref_type(compiler, var_type->basic_type->id, var_type->dimension, var_type->flag))) {
                          SPVM_yyerror_format(compiler, "Dereference target must be numeric reference type or value reference type at %s line %d\n", op_cur->file, op_cur->line);
                        }
                        
                        break;
                      }
                      // Add my var
                      case SPVM_OP_C_ID_VAR: {
                        
                        if (op_cur->uv.var->is_declaration) {
                          
                          SPVM_MY* my = op_cur->uv.var->my;
                          
                          // Redeclaration error if same name variable is declare in same block
                          int32_t found = 0;
                          int32_t block_my_base = (intptr_t)SPVM_LIST_fetch(block_my_base_stack, block_my_base_stack->length - 1);
                          {
                            int32_t i;
                            for (i = block_my_base; i < my_stack->length; i++) {
                              SPVM_MY* bef_my = SPVM_LIST_fetch(my_stack, i);
                              if (strcmp(my->op_name->uv.name, bef_my->op_name->uv.name) == 0) {
                                // Temporaly variable is not duplicated
                                if (my->op_name->uv.name[0] != '@') {
                                  found = 1;
                                }
                                break;
                              }
                            }
                          }
                          
                          if (found) {
                            SPVM_yyerror_format(compiler, "redeclaration of my \"%s\" at %s line %d\n", my->op_name->uv.name, my->op_my->file, my->op_my->line);
                            
                            return;
                          }
                          else {
                            SPVM_LIST_push(sub->mys, my);
                            SPVM_LIST_push(my_stack, my);
                          }
                          
                          // Type can't be detected
                          if (!op_cur->is_lvalue && my->type == NULL) {
                            SPVM_yyerror_format(compiler, "Type can't be detected at %s line %d\n", my->op_my->file, my->op_my->line);
                            
                            return;
                          }
                        }
                        
                        SPVM_VAR* var = op_cur->uv.var;
                        
                        // Search same name variable
                        SPVM_MY* found_my = NULL;
                        {
                          int32_t i;
                          for (i = my_stack->length - 1; i >= 0; i--) {
                            SPVM_MY* my = SPVM_LIST_fetch(my_stack, i);
                            assert(my);
                            if (strcmp(var->op_name->uv.name, my->op_name->uv.name) == 0) {
                              found_my = my;
                              break;
                            }
                          }
                        }
                        
                        if (found_my) {
                          // Add my var information to var
                          var->my = found_my;
                        }
                        else {
                          int32_t is_package_var;
                          SPVM_PACKAGE_VAR* found_package_var = SPVM_HASH_fetch(compiler->package_var_symtable, var->op_name->uv.name, strlen(var->op_name->uv.name));
                          if (!found_package_var) {
                            found_package_var = SPVM_HASH_fetch(package->package_var_symtable, var->op_name->uv.name, strlen(var->op_name->uv.name));
                          }
                          if (found_package_var) {
                            // Var OP
                            SPVM_OP* op_name_package_var = SPVM_OP_new_op_name(compiler, op_cur->uv.var->op_name->uv.name, op_cur->file, op_cur->line);
                            SPVM_OP* op_package_var_access = SPVM_OP_new_op_package_var_access(compiler, op_name_package_var);
                            op_package_var_access->uv.package_var_access->package_var = found_package_var;
                            
                            SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_cur);
                            SPVM_OP_replace_op(compiler, op_stab, op_package_var_access);
                            
                            op_package_var_access->is_lvalue = op_cur->is_lvalue;
                            
                            op_cur = op_package_var_access;
                            continue;
                          }
                          // Error
                          else {
                            SPVM_yyerror_format(compiler, "%s is not declared at %s line %d\n", var->op_name->uv.name, op_cur->file, op_cur->line);
                            return;
                          }
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_CALL_SUB: {
                        
                        // Check sub name
                        SPVM_OP_CHECKER_resolve_call_sub(compiler, op_cur, package->op_package);
                        
                        SPVM_OP* op_list_args = op_cur->last;
                        
                        SPVM_CALL_SUB* call_sub = op_cur->uv.call_sub;

                        if (!call_sub->sub) {
                          SPVM_yyerror_format(compiler, "unknown sub \"%s\" at %s line %d\n",
                            op_cur->first->uv.name, op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        if (call_sub->call_type_id != call_sub->sub->call_type_id) {
                          SPVM_yyerror_format(compiler, "Invalid subroutine call \"%s\" at %s line %d\n",
                            op_cur->first->uv.name, op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        const char* sub_abs_name = call_sub->sub->abs_name;
                        const char* sub_name = call_sub->sub->op_name->uv.name;
                        
                        int32_t sub_args_count = call_sub->sub->args->length;
                        
                        SPVM_OP* op_term = op_list_args->first;
                        int32_t call_sub_args_count = 0;
                        while ((op_term = SPVM_OP_sibling(compiler, op_term))) {
                          call_sub_args_count++;
                          if (call_sub_args_count > sub_args_count) {
                            SPVM_yyerror_format(compiler, "Too many arguments \"%s\" at %s line %d\n", sub_abs_name, op_cur->file, op_cur->line);
                            
                            return;
                          }
                          
                          SPVM_MY* sub_arg_my = SPVM_LIST_fetch(call_sub->sub->args, call_sub_args_count - 1);
                          
                          // Check if source can be assigned to dist
                          // If needed, numeric convertion op is added
                          op_term = SPVM_OP_CHECKER_check_assign(compiler, sub_arg_my->op_my, op_term);
                        }
                        
                        if (call_sub_args_count < sub_args_count) {
                          SPVM_yyerror_format(compiler, "Too few argument. sub \"%s\" at %s line %d\n", sub_abs_name, op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        // Constant subroutine
                        if (call_sub->sub->flag & SPVM_SUB_C_FLAG_IS_ENUM) {
                          // Replace sub to constant
                          op_cur->id = SPVM_OP_C_ID_CONSTANT;
                          op_cur->uv.constant = call_sub->sub->op_constant->uv.constant;
                          
                          op_cur->first = NULL;
                          op_cur->last = NULL;
                          break;
                        }
                        
                        // Update operand stack max
                        if (call_sub_args_count > sub->call_sub_arg_stack_max) {
                          sub->call_sub_arg_stack_max = call_sub_args_count;
                        }

                        if (sub->info_sub_ids->length >= SPVM_LIMIT_C_OPCODE_OPERAND_VALUE_MAX) {
                          SPVM_yyerror_format(compiler, "Too many call sub at %s line %d\n", op_cur->file, op_cur->line);
                        }
                        op_cur->uv.call_sub->sub_rel_id = sub->info_sub_ids->length;
                        SPVM_LIST_push(sub->info_sub_ids, (void*)(intptr_t)op_cur->uv.call_sub->sub->id);
                        
                        if (call_sub->sub->flag & SPVM_SUB_C_FLAG_IS_DESTRUCTOR) {
                          SPVM_yyerror_format(compiler, "Can't call DESTROY in yourself at %s line %d\n", op_cur->file, op_cur->line);
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_PACKAGE_VAR_ACCESS: {
                        
                        // Check field name
                        SPVM_OP_CHECKER_resolve_package_var_access(compiler, op_cur, package->op_package);
                        if (!op_cur->uv.package_var_access->package_var) {
                          SPVM_yyerror_format(compiler, "Package variable not found \"%s\" at %s line %d\n",
                            op_cur->uv.package_var_access->op_name->uv.name, op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        if (sub->info_package_var_ids->length >= SPVM_LIMIT_C_OPCODE_OPERAND_VALUE_MAX) {
                          SPVM_yyerror_format(compiler, "Too many package variable access at %s line %d\n", op_cur->file, op_cur->line);
                        }
                        op_cur->uv.package_var_access->sub_rel_id = sub->info_package_var_ids->length;
                        SPVM_LIST_push(sub->info_package_var_ids, (void*)(intptr_t)op_cur->uv.package_var_access->package_var->id);
                        
                        break;
                      }
                      case SPVM_OP_C_ID_ARRAY_ACCESS: {
                        SPVM_TYPE* first_type = SPVM_OP_get_type(compiler, op_cur->first);
                        SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                        
                        // Left value must be array
                        if (!SPVM_TYPE_is_array_type(compiler, first_type->basic_type->id, first_type->dimension, first_type->flag)) {
                          SPVM_yyerror_format(compiler, "left value must be array at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        // Right value must be integer
                        if (SPVM_TYPE_is_numeric_type(compiler, last_type->basic_type->id, last_type->dimension, last_type->flag)) {
                          SPVM_OP_CHECKER_apply_unary_numeric_convertion(compiler, op_cur->last);
                          SPVM_TYPE* last_type = SPVM_OP_get_type(compiler, op_cur->last);
                          
                          if (last_type->dimension == 0 && last_type->basic_type->id != SPVM_BASIC_TYPE_C_ID_INT) {
                            SPVM_yyerror_format(compiler, "array index must be int type at %s line %d\n", op_cur->file, op_cur->line);
                            
                            return;
                          }
                        }
                        else {
                          SPVM_yyerror_format(compiler, "array index must be numeric type at %s line %d\n", op_cur->file, op_cur->line);
                          
                          return;
                        }
                        
                        assert(op_cur->first);
                        assert(op_cur->last);
                        
                        // If array term is not var, create assign operator
                        SPVM_OP* op_term_array = op_cur->first;
                        if (op_term_array->id != SPVM_OP_C_ID_VAR) {
                          op_cur->no_need_check = 1;

                          SPVM_TYPE* term_index_type = SPVM_OP_get_type(compiler, op_term_array);

                          SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_term_array);
                          
                          SPVM_OP* op_var_tmp = SPVM_OP_CHECKER_new_op_var_tmp(compiler, sub->op_sub, term_index_type, op_cur->file, op_cur->line);
                          SPVM_LIST_push(my_stack, op_var_tmp->uv.var->my);
                          SPVM_OP* op_assign = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_cur->file, op_cur->line);

                          SPVM_OP_build_assign(compiler, op_assign, op_var_tmp, op_term_array);
                          
                          // Convert cur new op to var
                          SPVM_OP_replace_op(compiler, op_stab, op_assign);
                        }
                        
                        // If array access index term is not var, create assign operator
                        SPVM_OP* op_term_index = op_cur->last;
                        if (op_term_index->id != SPVM_OP_C_ID_VAR) {
                          
                          op_cur->no_need_check = 1;

                          SPVM_TYPE* term_index_type = SPVM_OP_get_type(compiler, op_term_index);

                          SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_term_index);
                          
                          SPVM_OP* op_var_tmp = SPVM_OP_CHECKER_new_op_var_tmp(compiler, sub->op_sub, term_index_type, op_cur->file, op_cur->line);
                          SPVM_LIST_push(my_stack, op_var_tmp->uv.var->my);
                          SPVM_OP* op_assign = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_cur->file, op_cur->line);

                          SPVM_OP_build_assign(compiler, op_assign, op_var_tmp, op_term_index);
                          
                          // Convert cur new op to var
                          SPVM_OP_replace_op(compiler, op_stab, op_assign);
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_FIELD_ACCESS: {
                        SPVM_OP* op_term_invocker = op_cur->first;
                        SPVM_OP* op_name = op_cur->uv.field_access->op_name;
                        
                        if (op_term_invocker->id == SPVM_OP_C_ID_ASSIGN) {
                          op_term_invocker = op_term_invocker->first;
                        }
                        
                        SPVM_TYPE* type = SPVM_OP_get_type(compiler, op_term_invocker);
                        SPVM_PACKAGE* package = SPVM_HASH_fetch(compiler->package_symtable, type->basic_type->name, strlen(type->basic_type->name));
                        
                        if (!(type && package)) {
                          SPVM_yyerror_format(compiler, "Can't access field at %s line %d\n", op_cur->file, op_cur->line);
                          return;
                        }
                        
                        // Check field name
                        SPVM_OP_CHECKER_resolve_field_access(compiler, op_cur);
                        
                        SPVM_FIELD* field = op_cur->uv.field_access->field;
                        
                        if (!field) {
                          char* type_name = tmp_buffer;
                          SPVM_TYPE_sprint_type_name(compiler, type_name, type->basic_type->id, type->dimension, type->flag);
                          SPVM_yyerror_format(compiler, "Unknown field %s::%s at %s line %d\n",
                            type_name, op_name->uv.name, op_cur->file, op_cur->line);
                          return;
                        }
                        
                        if (field->flag & SPVM_FIELD_C_FLAG_PRIVATE) {
                          if (strcmp(type->basic_type->name, sub->package->op_name->uv.name) != 0) {
                            char* type_name = tmp_buffer;
                            SPVM_TYPE_sprint_type_name(compiler, type_name, type->basic_type->id, type->dimension, type->flag);
                            SPVM_yyerror_format(compiler, "Can't access to private field %s::%s at %s line %d\n",
                              type_name, op_name->uv.name, op_cur->file, op_cur->line);
                          }
                        }
                        
                        if (sub->info_field_ids->length >= SPVM_LIMIT_C_OPCODE_OPERAND_VALUE_MAX) {
                          SPVM_yyerror_format(compiler, "Too many field access at %s line %d\n", op_cur->file, op_cur->line);
                        }
                        op_cur->uv.field_access->sub_rel_id = sub->info_field_ids->length;
                        SPVM_LIST_push(sub->info_field_ids, (void*)(intptr_t)op_cur->uv.field_access->field->id);
                        
                        // If invocker is array access and array access object is value_t, this op become array field access
                        if (op_term_invocker->id == SPVM_OP_C_ID_ARRAY_ACCESS) {
                          SPVM_OP* op_array_access = op_term_invocker;
                          
                          SPVM_TYPE* array_element_type = SPVM_OP_get_type(compiler, op_array_access);
                          
                          int32_t is_basic_type_value_t = SPVM_TYPE_basic_type_is_value_type(compiler, array_element_type->basic_type->id, array_element_type->dimension, array_element_type->flag);
                          if (is_basic_type_value_t) {
                            if (array_element_type->dimension != 0) {
                              SPVM_yyerror_format(compiler, "value_t array field access must be 1-dimension array at %s line %d\n", op_cur->file, op_cur->line);
                            }
                            else {
                              SPVM_OP* op_array_field_access = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ARRAY_FIELD_ACCESS, op_cur->file, op_cur->line);
                              op_array_field_access->is_lvalue = op_cur->is_lvalue;
                              
                              SPVM_ARRAY_FIELD_ACCESS* array_field_access = SPVM_ARRAY_FIELD_ACCESS_new(compiler);
                              array_field_access->field = field;
                              op_array_field_access->uv.array_field_access = array_field_access;
                              
                              SPVM_OP* op_array = op_array_access->first;
                              SPVM_OP* op_index = op_array_access->last;
                              SPVM_OP_cut_op(compiler, op_array_access->first);
                              SPVM_OP_cut_op(compiler, op_array_access->last);
                              
                              SPVM_OP_insert_child(compiler, op_array_field_access, op_array_field_access->last, op_array);
                              SPVM_OP_insert_child(compiler, op_array_field_access, op_array_field_access->last, op_index);
                              
                              SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_cur);
                              SPVM_OP_replace_op(compiler, op_stab, op_array_field_access);
                              
                              op_cur = op_array_field_access;
                            }
                          }
                        }
                        
                        if (op_cur->id == SPVM_OP_C_ID_ARRAY_FIELD_ACCESS) {
                          // If array term is not var, create assign operator
                          SPVM_OP* op_term_array = op_cur->first;
                          if (op_term_array->id != SPVM_OP_C_ID_VAR) {
                            op_cur->no_need_check = 1;

                            SPVM_TYPE* term_index_type = SPVM_OP_get_type(compiler, op_term_array);

                            SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_term_array);
                            
                            SPVM_OP* op_var_tmp = SPVM_OP_CHECKER_new_op_var_tmp(compiler, sub->op_sub, term_index_type, op_cur->file, op_cur->line);
                            SPVM_LIST_push(my_stack, op_var_tmp->uv.var->my);

                            SPVM_OP* op_assign = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_cur->file, op_cur->line);

                            SPVM_OP_build_assign(compiler, op_assign, op_var_tmp, op_term_array);
                            
                            // Convert cur new op to var
                            SPVM_OP_replace_op(compiler, op_stab, op_assign);
                          }
                          
                          // If array access index term is not var, create assign operator
                          SPVM_OP* op_term_index = op_cur->last;
                          if (op_term_index->id != SPVM_OP_C_ID_VAR) {
                            op_cur->no_need_check = 1;

                            SPVM_TYPE* term_index_type = SPVM_OP_get_type(compiler, op_term_index);

                            SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_term_index);
                            
                            SPVM_OP* op_var_tmp = SPVM_OP_CHECKER_new_op_var_tmp(compiler, sub->op_sub, term_index_type, op_cur->file, op_cur->line);
                            SPVM_LIST_push(my_stack, op_var_tmp->uv.var->my);
                            
                            SPVM_OP* op_assign = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_cur->file, op_cur->line);

                            SPVM_OP_build_assign(compiler, op_assign, op_var_tmp, op_term_index);
                            
                            // Convert cur new op to var
                            SPVM_OP_replace_op(compiler, op_stab, op_assign);
                          }
                        }
                        else {
                          // If object term is not var, create assign operator
                          SPVM_OP* op_term_invocker = op_cur->first;
                          if (op_term_invocker->id != SPVM_OP_C_ID_VAR) {
                            op_cur->no_need_check = 1;

                            SPVM_TYPE* term_index_type = SPVM_OP_get_type(compiler, op_term_invocker);

                            SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_term_invocker);
                            
                            SPVM_OP* op_var_tmp = SPVM_OP_CHECKER_new_op_var_tmp(compiler, sub->op_sub, term_index_type, op_cur->file, op_cur->line);
                            SPVM_LIST_push(my_stack, op_var_tmp->uv.var->my);
                            
                            SPVM_OP* op_assign = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_cur->file, op_cur->line);
                            SPVM_OP_build_assign(compiler, op_assign, op_var_tmp, op_term_invocker);
                            
                            // Convert cur new op to var
                            SPVM_OP_replace_op(compiler, op_stab, op_assign);
                          }
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_WEAKEN_FIELD: {
                        SPVM_OP* op_field_access = op_cur->first;
                        
                        SPVM_FIELD* field = op_field_access->uv.field_access->field;
                        
                        SPVM_TYPE* type = SPVM_OP_get_type(compiler, op_field_access);
                        
                        if (!SPVM_TYPE_is_object_type(compiler, type->basic_type->id, type->dimension, type->flag)) {
                          SPVM_yyerror_format(compiler, "weaken is only used for object field \"%s\" \"%s\" at %s line %d\n",
                            field->package->op_name->uv.name, field->op_name->uv.name, op_cur->file, op_cur->line);
                          
                          break;
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_WEAKEN_ARRAY_ELEMENT: {
                        SPVM_OP* op_array_access = op_cur->first;
                        SPVM_OP* op_term_array = op_array_access->first;
                        
                        SPVM_TYPE* type = SPVM_OP_get_type(compiler, op_array_access);
                        if (!SPVM_TYPE_is_object_type(compiler, type->basic_type->id, type->dimension, type->flag)) {
                          SPVM_yyerror_format(compiler, "weaken is only used for object element at %s line %d\n", op_cur->file, op_cur->line);
                          break;
                        }
                        
                        break;
                      }
                      case SPVM_OP_C_ID_CONVERT: {
                        
                        SPVM_OP* op_src = op_cur->first;
                        SPVM_OP* op_dist = op_cur->last;
                        
                        SPVM_TYPE* src_type = SPVM_OP_get_type(compiler, op_src);
                        assert(src_type);
                        
                        SPVM_TYPE* dist_type = SPVM_OP_get_type(compiler, op_dist);
                        assert(dist_type);
                        
                        // Dist type is numeric type
                        int32_t can_convert = 0;
                        int32_t narrowing_convertion_error = 0;
                        if (SPVM_TYPE_is_numeric_type(compiler, dist_type->basic_type->id, dist_type->dimension, dist_type->flag)) {
                          // Soruce type is numeric type
                          if (SPVM_TYPE_is_numeric_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
                            can_convert = 1;
                          }
                          else if (SPVM_TYPE_is_numeric_object_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
                            if (dist_type->basic_type->id + SPVM_BASIC_TYPE_C_NUMERIC_OBJECT_UPGRADE_SHIFT == src_type->basic_type->id) {
                              can_convert = 1;
                            }
                            else {
                              can_convert = 0;
                            }
                          }
                          else if (SPVM_TYPE_is_any_object_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
                            can_convert = 1;
                          }
                          else {
                            can_convert = 0;
                          }
                        }
                        // Dist type is referece type
                        else if (SPVM_TYPE_is_ref_type(compiler, dist_type->basic_type->id, dist_type->dimension, dist_type->flag)) {
                          can_convert = 0;
                        }
                        // Dist type is value type
                        else if (SPVM_TYPE_is_value_type(compiler, dist_type->basic_type->id, dist_type->dimension, dist_type->flag)) {
                          can_convert = 0;
                        }
                        // Dist type is object type
                        else if (SPVM_TYPE_is_object_type(compiler, dist_type->basic_type->id, dist_type->dimension, dist_type->flag)) {
                          // Dist type is string type
                          if (SPVM_TYPE_is_string_type(compiler, dist_type->basic_type->id, dist_type->dimension, dist_type->flag)) {
                            if (SPVM_TYPE_is_numeric_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
                              can_convert = 1;
                            }
                            else if (SPVM_TYPE_is_object_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
                              can_convert = 1;
                            }
                            else {
                              can_convert = 0;
                            }
                          }
                          else if (SPVM_TYPE_is_any_object_type(compiler, dist_type->basic_type->id, dist_type->dimension, dist_type->flag)) {
                            if (SPVM_TYPE_is_numeric_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
                              can_convert = 1;
                            }
                            else if (SPVM_TYPE_is_object_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
                              can_convert = 1;
                            }
                            else {
                              can_convert = 0;
                            }
                          }
                          else if (SPVM_TYPE_is_numeric_object_type(compiler, dist_type->basic_type->id, dist_type->dimension, dist_type->flag)) {
                            if (SPVM_TYPE_is_numeric_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
                              if (dist_type->basic_type->id == src_type->basic_type->id + SPVM_BASIC_TYPE_C_NUMERIC_OBJECT_UPGRADE_SHIFT) {
                                can_convert = 1;
                              }
                              else {
                                can_convert = 0;
                              }
                            }
                            else if (SPVM_TYPE_is_object_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
                              can_convert = 1;
                            }
                            else {
                              can_convert = 0;
                            }
                          }
                          else  {
                            // Source type is object type
                            if (SPVM_TYPE_is_object_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
                              can_convert = 1;
                            }
                            // Source type is undef type
                            else if (SPVM_TYPE_is_undef_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
                              can_convert = 1;
                            }
                            else {
                              can_convert = 0;
                            }
                          }
                        }
                        else {
                          assert(0);
                        }

                        if (!can_convert) {
                          SPVM_TYPE_sprint_type_name(compiler, compiler->buffer1, src_type->basic_type->id, src_type->dimension, src_type->flag);
                          SPVM_TYPE_sprint_type_name(compiler, compiler->buffer2, dist_type->basic_type->id, dist_type->dimension, dist_type->flag);
                          SPVM_yyerror_format(compiler, "Can't convert %s to %s at %s line %d\n", compiler->buffer1, compiler->buffer2, op_src->file, op_src->line);
                        }
                        
                        if (SPVM_TYPE_is_object_type(compiler, op_dist->uv.type->basic_type->id, op_dist->uv.type->dimension, op_dist->uv.type->flag)) {
                          if (sub->info_types->length >= SPVM_LIMIT_C_OPCODE_OPERAND_VALUE_MAX) {
                            SPVM_yyerror_format(compiler, "Too many types at %s line %d\n", op_cur->file, op_cur->line);
                          }
                          op_dist->uv.type->sub_rel_id = sub->info_types->length;
                          SPVM_LIST_push(sub->info_types, op_dist->uv.type);
                          break;
                        }
                      }
                      break;
                    }
                    
                    // [END]Postorder traversal position
                  }
                  
                  if (op_cur == op_base) {

                    // Finish
                    finish = 1;
                    
                    break;
                  }
                  
                  // Next sibling
                  if (op_cur->moresib) {
                    op_cur = SPVM_OP_sibling(compiler, op_cur);
                    break;
                  }
                  // Next is parent
                  else {
                    op_cur = op_cur->sibparent;
                  }
                }
                if (finish) {
                  break;
                }
              }
            }
            // Free list
            SPVM_LIST_free(my_stack);
            SPVM_LIST_free(block_my_base_stack);
            SPVM_LIST_free(op_switch_stack);
          }

          // set assign_to_var flag - Second tree traversal
          if (!(sub->flag & SPVM_SUB_C_FLAG_HAVE_NATIVE_DESC)) {
            // Run OPs
            SPVM_OP* op_base = SPVM_OP_get_op_block_from_op_sub(compiler, sub->op_sub);
            SPVM_OP* op_cur = op_base;
            int32_t finish = 0;
            while (op_cur) {
              // [START]Preorder traversal position
              if (op_cur->first) {
                op_cur = op_cur->first;
              }
              else {
                while (1) {
                  // [START]Postorder traversal position
                  switch (op_cur->id) {
                    case SPVM_OP_C_ID_ASSIGN:
                      if (op_cur->last->id == SPVM_OP_C_ID_VAR) {
                        op_cur->first->is_assigned_to_var = 1;
                      }
                      break;
                  }

                  if (op_cur == op_base) {

                    // Finish
                    finish = 1;
                    
                    break;
                  }
                  
                  // Next sibling
                  if (op_cur->moresib) {
                    op_cur = SPVM_OP_sibling(compiler, op_cur);
                    break;
                  }
                  // Next is parent
                  else {
                    op_cur = op_cur->sibparent;
                  }
                }
                if (finish) {
                  break;
                }
              }
            }
          }
          
          // Create temporary variables for not assigned values - Third tree traversal
          if (!(sub->flag & SPVM_SUB_C_FLAG_HAVE_NATIVE_DESC)) {
            // Run OPs
            SPVM_OP* op_base = SPVM_OP_get_op_block_from_op_sub(compiler, sub->op_sub);
            SPVM_OP* op_cur = op_base;
            int32_t finish = 0;
            while (op_cur) {
              // [START]Preorder traversal position
              if (op_cur->first) {
                op_cur = op_cur->first;
              }
              else {
                while (1) {
                  // Create temporary variable for no is_assigned_to_var term which is not variable
                  int32_t create_tmp_var = 0;
                  SPVM_TYPE* tmp_var_type = SPVM_OP_get_type(compiler, op_cur);
                  
                  // [START]Postorder traversal position
                  if (!op_cur->is_lvalue && !op_cur->is_assigned_to_var) {
                    switch (op_cur->id) {
                      case SPVM_OP_C_ID_CONVERT:
                        create_tmp_var = 1;
                        break;
                      case SPVM_OP_C_ID_ADD:
                      case SPVM_OP_C_ID_SUBTRACT:
                      case SPVM_OP_C_ID_MULTIPLY:
                      case SPVM_OP_C_ID_DIVIDE:
                      case SPVM_OP_C_ID_BIT_AND:
                      case SPVM_OP_C_ID_BIT_OR:
                      case SPVM_OP_C_ID_BIT_XOR:
                      case SPVM_OP_C_ID_BIT_NOT:
                      case SPVM_OP_C_ID_REMAINDER:
                      case SPVM_OP_C_ID_LEFT_SHIFT:
                      case SPVM_OP_C_ID_RIGHT_SHIFT:
                      case SPVM_OP_C_ID_RIGHT_SHIFT_UNSIGNED:
                      case SPVM_OP_C_ID_COMPLEMENT:
                      case SPVM_OP_C_ID_NEGATE:
                      case SPVM_OP_C_ID_PLUS:
                      case SPVM_OP_C_ID_ARRAY_LENGTH:
                      case SPVM_OP_C_ID_NEW:
                      case SPVM_OP_C_ID_CONCAT:
                      case SPVM_OP_C_ID_EXCEPTION_VAR:
                      case SPVM_OP_C_ID_PACKAGE_VAR_ACCESS:
                      case SPVM_OP_C_ID_SWITCH_CONDITION:
                      case SPVM_OP_C_ID_ARRAY_FIELD_ACCESS:
                      case SPVM_OP_C_ID_REF:
                      case SPVM_OP_C_ID_DEREF:
                        create_tmp_var = 1;
                        break;
                      case SPVM_OP_C_ID_CONSTANT: {
                        if (SPVM_TYPE_is_numeric_type(compiler, tmp_var_type->basic_type->id, tmp_var_type->dimension, tmp_var_type->flag) && op_cur->flag != SPVM_OP_C_FLAG_CONSTANT_CASE) {
                          create_tmp_var = 1;
                        }
                        break;
                      }
                      case SPVM_OP_C_ID_FIELD_ACCESS: {
                        if (!(op_cur->flag &= SPVM_OP_C_FLAG_FIELD_ACCESS_WEAKEN)) {
                          create_tmp_var = 1;
                        }
                        break;
                      }
                      case SPVM_OP_C_ID_ARRAY_ACCESS:{
                        if (!(op_cur->flag &= SPVM_OP_C_FLAG_ARRAY_ACCESS_WEAKEN)) {
                          create_tmp_var = 1;
                        }
                        break;
                      }
                      case SPVM_OP_C_ID_CALL_SUB: {
                        if (!(tmp_var_type->dimension == 0 && tmp_var_type->basic_type->id == SPVM_BASIC_TYPE_C_ID_VOID)) {
                          create_tmp_var = 1;
                        }
                        break;
                      }
                    }
                  }

                  // Create temporary variable
                  if (create_tmp_var) {
                    SPVM_OP* op_var_tmp = SPVM_OP_CHECKER_new_op_var_tmp(compiler, sub->op_sub, tmp_var_type, op_cur->file, op_cur->line);
                    
                    if (op_var_tmp == NULL) {
                      return;
                    }
                    
                    // Cut new op
                    SPVM_OP* op_target = op_cur;
                    SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_target);

                    // Assing op
                    SPVM_OP* op_assign = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_cur->file, op_cur->line);
                    SPVM_OP* op_build_assign = SPVM_OP_build_assign(compiler, op_assign, op_var_tmp, op_target);
                    
                    // Convert cur new op to var
                    SPVM_OP_replace_op(compiler, op_stab, op_build_assign);
                    op_target->uv = op_cur->uv;
                    
                    op_cur = op_target;
                  }

                  if (op_cur == op_base) {

                    // Finish
                    finish = 1;
                    
                    break;
                  }
                  
                  // Next sibling
                  if (op_cur->moresib) {
                    op_cur = SPVM_OP_sibling(compiler, op_cur);
                    break;
                  }
                  // Next is parent
                  else {
                    op_cur = op_cur->sibparent;
                  }
                }
                if (finish) {
                  break;
                }
              }
            }
          }
          
          if (compiler->error_count > 0) {
            return;
          }

          assert(sub->file);
          
          // Add op my if need
          if (sub->package->category == SPVM_PACKAGE_C_CATEGORY_INTERFACE) {
            int32_t arg_index;
            for (arg_index = 0; arg_index < sub->args->length; arg_index++) {
              SPVM_MY* arg_my = SPVM_LIST_fetch(sub->args, arg_index);
              SPVM_LIST_push(sub->mys, arg_my);
            }
          }
          
          // Resolve my var id
          {
            int32_t my_index;
            int32_t my_var_id = 0;
            int32_t my_numeric_var_id = 0;
            int32_t my_address_var_id = 0;
            for (my_index = 0; my_index < sub->mys->length; my_index++) {
              SPVM_MY* my = SPVM_LIST_fetch(sub->mys, my_index);
              assert(my);
              SPVM_TYPE* type = SPVM_OP_get_type(compiler, my->op_my);
              
              int32_t width = SPVM_TYPE_get_width(compiler, type->basic_type->id, type->dimension, type->flag);
              if (my_var_id + (width - 1) > SPVM_LIMIT_C_OPCODE_OPERAND_VALUE_MAX) {
                SPVM_yyerror_format(compiler, "Too many variable declarations at %s line %d\n", my->op_my->file, my->op_my->line);
              }
              my->var_id = my_var_id;
              my_var_id += width;
              
              if (SPVM_TYPE_is_object_type(compiler, type->basic_type->id, type->dimension, type->flag) || SPVM_TYPE_is_ref_type(compiler, type->basic_type->id, type->dimension, type->flag)) {
                my->address_var_id = my_var_id;
                my_address_var_id += width;
              }
              else if (SPVM_TYPE_is_numeric_type(compiler, type->basic_type->id, type->dimension, type->flag) || SPVM_TYPE_is_value_type(compiler, type->basic_type->id, type->dimension, type->flag)) {
                my->numeric_var_id = my_var_id;
                my_numeric_var_id += width;
              }
              else {
                assert(0);
              }
            }

            int32_t args_alloc_length = SPVM_SUB_get_arg_alloc_length(compiler, sub);
            sub->args_alloc_length = args_alloc_length;

            sub->vars_alloc_length = my_var_id;
            sub->numeric_vars_alloc_length = my_numeric_var_id;
            sub->address_vars_alloc_length = my_address_var_id;
          }

          // Add more information for opcode building - Fourth tree traversal
          if (!(sub->flag & SPVM_SUB_C_FLAG_HAVE_NATIVE_DESC)) {
            // Block stack
            SPVM_LIST* op_block_stack = SPVM_LIST_new(0);
            
            // Run OPs
            SPVM_OP* op_base = SPVM_OP_get_op_block_from_op_sub(compiler, sub->op_sub);
            SPVM_OP* op_cur = op_base;
            int32_t finish = 0;
            while (op_cur) {
              // [START]Preorder traversal position
              switch (op_cur->id) {
                // Start scope
                case SPVM_OP_C_ID_BLOCK: {
                  // Push block
                  SPVM_LIST_push(op_block_stack, op_cur);
                  
                  break;
                }
              }

              if (op_cur->first) {
                op_cur = op_cur->first;
              }
              else {
                while (1) {
                  // [START]Postorder traversal position
                  switch (op_cur->id) {
                    case SPVM_OP_C_ID_BLOCK: {
                      SPVM_OP* op_block_current = SPVM_LIST_fetch(op_block_stack, op_block_stack->length - 1);

                      SPVM_LIST_pop(op_block_stack);
                      
                      // Parent block need LEAVE_SCOPE if child is needing LEAVE_SCOPE
                      if (op_block_stack->length > 0) {
                        SPVM_OP* op_block_parent = SPVM_LIST_fetch(op_block_stack, op_block_stack->length - 1);
                        if (!op_block_parent->uv.block->have_object_var_decl) {
                          if (op_block_current->uv.block->have_object_var_decl) {
                            op_block_parent->uv.block->have_object_var_decl = 1;
                          }
                        }
                      }
                    
                      break;
                    }
                    case SPVM_OP_C_ID_VAR: {
                      if (op_cur->uv.var->is_declaration) {
                        SPVM_MY* my = op_cur->uv.var->my;
                        
                        SPVM_TYPE* type = SPVM_OP_get_type(compiler, op_cur);
                        int32_t type_is_value_t = SPVM_TYPE_is_value_type(compiler, type->basic_type->id, type->dimension, type->flag);
                        
                        if (SPVM_TYPE_is_object_type(compiler, type->basic_type->id, type->dimension, type->flag) && !type_is_value_t) {
                          SPVM_OP* op_block_current = SPVM_LIST_fetch(op_block_stack, op_block_stack->length - 1);
                          op_block_current->uv.block->have_object_var_decl = 1;
                        }
                      }
                      
                      break;
                    }
                  }

                  if (op_cur == op_base) {

                    // Finish
                    finish = 1;
                    
                    break;
                  }
                  
                  // Next sibling
                  if (op_cur->moresib) {
                    op_cur = SPVM_OP_sibling(compiler, op_cur);
                    break;
                  }
                  // Next is parent
                  else {
                    op_cur = op_cur->sibparent;
                  }
                }
                if (finish) {
                  break;
                }
              }
            }
            SPVM_LIST_free(op_block_stack);
          }
        }
      }
    }
  }

  // Resolve constant id
  {
    int32_t constant_index;
    for (constant_index = 0; constant_index < compiler->op_constants->length; constant_index++) {
      SPVM_OP* op_constant = SPVM_LIST_fetch(compiler->op_constants, constant_index);
      SPVM_CONSTANT* constant = op_constant->uv.constant;
      constant->id = constant_index;
    }
  }
#ifdef SPVM_DEBUG_DUMP
#include "spvm_dumper.h"
  if (compiler->error_count == 0) {
    printf("\n[Basic types]\n");
    SPVM_DUMPER_dump_basic_types(compiler, compiler->basic_types);
    
    printf("\n[Packages]\n");
    SPVM_DUMPER_dump_packages(compiler, compiler->packages);
  }
#endif
}

SPVM_OP* SPVM_OP_CHECKER_check_assign(SPVM_COMPILER* compiler, SPVM_OP* op_dist, SPVM_OP* op_src) {
  SPVM_TYPE* dist_type = SPVM_OP_get_type(compiler, op_dist);
  SPVM_TYPE* src_type = SPVM_OP_get_type(compiler, op_src);
  
  // Dist type is numeric type
  int32_t can_assign = 0;
  int32_t narrowing_convertion_error = 0;
  int32_t need_convertion = 0;
  if (SPVM_TYPE_is_numeric_type(compiler, dist_type->basic_type->id, dist_type->dimension, dist_type->flag)) {
    // Soruce type is numeric type
    if (SPVM_TYPE_is_numeric_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
      // Dist type is same as source type
      if (dist_type->basic_type->id == src_type->basic_type->id) {
        can_assign = 1;
      }
      // Dist type is more wide than source type
      else if (dist_type->basic_type->id > src_type->basic_type->id) {
        can_assign = 1;
        need_convertion = 1;
      }
      // Dist type is narrow than source type
      else if (dist_type->basic_type->id < src_type->basic_type->id) {
        int32_t can_narrowing_convertion = 0;
        if (op_src->id == SPVM_OP_C_ID_CONSTANT) {
          SPVM_CONSTANT* constant = op_src->uv.constant;
          assert(constant->type->dimension == 0);
          if (constant->type->basic_type->id == SPVM_BASIC_TYPE_C_ID_INT || constant->type->basic_type->id == SPVM_BASIC_TYPE_C_ID_LONG) {
            int64_t constant_value;
            if (constant->type->basic_type->id == SPVM_BASIC_TYPE_C_ID_INT) {
              constant_value = constant->value.ival;
            }
            else if (constant->type->basic_type->id == SPVM_BASIC_TYPE_C_ID_LONG) {
              constant_value = constant->value.lval;
            }
            else {
              assert(0);
            }
            
            if (dist_type->basic_type->id == SPVM_BASIC_TYPE_C_ID_BYTE) {
              if (constant_value >= INT8_MIN && constant_value <= INT8_MAX) {
                can_narrowing_convertion = 1;
              }
              else {
                can_narrowing_convertion = 0;
              }
            }
            else if (dist_type->basic_type->id == SPVM_BASIC_TYPE_C_ID_SHORT) {
              if (constant_value >= INT16_MIN && constant_value <= INT16_MAX) {
                can_narrowing_convertion = 1;
              }
              else {
                can_narrowing_convertion = 0;
              }
            }
            else if (dist_type->basic_type->id == SPVM_BASIC_TYPE_C_ID_INT) {
              if (constant_value >= INT32_MIN && constant_value <= INT32_MAX) {
                can_narrowing_convertion = 1;
              }
              else {
                can_narrowing_convertion = 0;
              }
            }
            else {
              assert(0);
            }
          }
          else {
            can_narrowing_convertion = 0;
          }
        }
        else {
          can_assign = 0;
        }
        
        if (can_narrowing_convertion) {
          need_convertion = 1;
          can_assign = 1;
        }
        else {
          narrowing_convertion_error = 1;
          can_assign = 0;
        }
      }
    }
    else if (SPVM_TYPE_is_object_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
      if (SPVM_TYPE_is_any_object_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
        can_assign = 1;
        need_convertion = 1;
      }
      else if (SPVM_TYPE_is_numeric_object_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
        if (src_type->basic_type->id == dist_type->basic_type->id + SPVM_BASIC_TYPE_C_NUMERIC_OBJECT_UPGRADE_SHIFT) {
          can_assign = 1;
          need_convertion = 1;
        }
        else {
          can_assign = 0;
        }
      }
      else {
        can_assign = 0;
      }
    }
    else {
      can_assign = 0;
    }
  }
  // Dist type is referece type
  else if (SPVM_TYPE_is_ref_type(compiler, dist_type->basic_type->id, dist_type->dimension, dist_type->flag)) {
    if (SPVM_TYPE_is_ref_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
      if (dist_type->basic_type->id == src_type->basic_type->id && dist_type->dimension == src_type->dimension) {
        can_assign = 1;
      }
      else {
        can_assign = 0;
      }
    }
    else {
      can_assign = 0;
    }
  }
  // Dist type is value type
  else if (SPVM_TYPE_is_value_type(compiler, dist_type->basic_type->id, dist_type->dimension, dist_type->flag)) {
    if (SPVM_TYPE_is_value_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
      if (dist_type->basic_type->id == src_type->basic_type->id && dist_type->dimension == src_type->dimension) {
        can_assign = 1;
      }
      else {
        can_assign = 0;
      }
    }
    else {
      can_assign = 0;
    }
  }
  // Dist type is object type
  else if (SPVM_TYPE_is_object_type(compiler, dist_type->basic_type->id, dist_type->dimension, dist_type->flag)) {
    // Dist type is string type
    if (SPVM_TYPE_is_string_type(compiler, dist_type->basic_type->id, dist_type->dimension, dist_type->flag)) {
      // Source type is number
      if (SPVM_TYPE_is_numeric_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
        can_assign = 1;
        need_convertion = 1;
      }
      // Source type is string
      else if (SPVM_TYPE_is_string_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
        can_assign = 1;
      }
      else if (SPVM_TYPE_is_undef_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
        can_assign = 1;
      }
      else {
        can_assign = 0;
      }
    }
    // Dist type is any object type
    else if (SPVM_TYPE_is_any_object_type(compiler, dist_type->basic_type->id, dist_type->dimension, dist_type->flag)) {
      // Source type is object type
      if (SPVM_TYPE_is_object_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
        can_assign = 1;
      }
      // Source type is numeric type
      else if (SPVM_TYPE_is_numeric_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
        can_assign = 1;
        need_convertion = 1;
      }
      // Source type is undef type
      else if (SPVM_TYPE_is_undef_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
        can_assign = 1;
      }
      else {
        can_assign = 0;
      }
    }
    // Dist type is numeric object type
    else if (SPVM_TYPE_is_numeric_object_type(compiler, dist_type->basic_type->id, dist_type->dimension, dist_type->flag)) {
      if (SPVM_TYPE_is_numeric_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
        if (dist_type->basic_type->id == src_type->basic_type->id + SPVM_BASIC_TYPE_C_NUMERIC_OBJECT_UPGRADE_SHIFT) {
          can_assign = 1;
          need_convertion = 1;
        }
        else {
          can_assign = 0;
        }
      }
      else if (SPVM_TYPE_is_numeric_object_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
        if (src_type->basic_type->id == dist_type->basic_type->id) {
          can_assign = 1;
        }
        else {
          can_assign = 0;
        }
      }
      else if (SPVM_TYPE_is_undef_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
        can_assign = 1;
      }
      else {
        can_assign = 0;
      }
    }
    // Dist type is object type without string type and any object type and numeric object type
    else {
      // Source type is object type
      if (SPVM_TYPE_is_object_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
        if (dist_type->dimension > 0){
          if (dist_type->basic_type->id == src_type->basic_type->id && dist_type->dimension == src_type->dimension) {
            can_assign = 1;
          }
          else {
            can_assign = 0;
          }
        }
        // Dist type is class or interface
        else if (dist_type->dimension == 0){
          // Dist type is class
          if (SPVM_TYPE_is_class_type(compiler, dist_type->basic_type->id, dist_type->dimension, dist_type->flag)) {
            if (SPVM_TYPE_is_class_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
              if (dist_type->basic_type->id == src_type->basic_type->id) {
                can_assign = 1;
              }
              else {
                can_assign = 0;
              }
            }
            else {
              can_assign = 0;
            }
          }
          // Dist type is interface
          else if (SPVM_TYPE_is_interface_type(compiler, dist_type->basic_type->id, dist_type->dimension, dist_type->flag)) {
            
            // Source type is class or interface
            if (
              SPVM_TYPE_is_class_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)
              || SPVM_TYPE_is_interface_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)
            )
            {
              can_assign = SPVM_TYPE_has_interface(
                compiler,
                src_type->basic_type->id, src_type->dimension, src_type->flag,
                dist_type->basic_type->id, dist_type->dimension, dist_type->flag
              );
            }
            else {
              can_assign = 0;
            }
          }
          else {
            assert(0);
          }
        }
        else {
          assert(0);
        }
      }
      // Source type is undef type
      else if (SPVM_TYPE_is_undef_type(compiler, src_type->basic_type->id, src_type->dimension, src_type->flag)) {
        can_assign = 1;
      }
      else {
        can_assign = 0;
      }
    }
  }
  else {
    SPVM_yyerror_format(compiler, "Can't assign to empty type at %s line %d\n", op_src->file, op_src->line);
  }
    
  if (!can_assign) {
    if (narrowing_convertion_error) {
      SPVM_yyerror_format(compiler, "Can't apply narrowing convertion at %s line %d\n", op_src->file, op_src->line);
    }
    else {
      SPVM_TYPE_sprint_type_name(compiler, compiler->buffer1, src_type->basic_type->id, src_type->dimension, src_type->flag);
      SPVM_TYPE_sprint_type_name(compiler, compiler->buffer2, dist_type->basic_type->id, dist_type->dimension, dist_type->flag);
      SPVM_yyerror_format(compiler, "Can't convert %s to %s at %s line %d\n", compiler->buffer1, compiler->buffer2, op_src->file, op_src->line);
    }
  }

  // Const check
  if (!(dist_type->flag & SPVM_TYPE_C_FLAG_CONST) && (src_type->flag & SPVM_TYPE_C_FLAG_CONST)) {
    SPVM_yyerror_format(compiler, "Can't convert const type to not const type at %s line %d\n", op_src->file, op_src->line);
  }

  if (need_convertion) {
    SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_src);
    
    SPVM_OP* op_convert = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CONVERT, op_src->file, op_src->line);
    SPVM_OP* op_dist_type = SPVM_OP_new_op_type(compiler, dist_type, op_src->file, op_src->line);
    SPVM_OP_build_convert(compiler, op_convert, op_dist_type, op_src);
    
    SPVM_OP_replace_op(compiler, op_stab, op_convert);
    return op_convert;
  }
  
  return op_src;
}

void SPVM_OP_CHECKER_resolve_types(SPVM_COMPILER* compiler) {

  SPVM_LIST* op_types = compiler->op_types;
  
  // Check type names
  for (int32_t i = 0; i < op_types->length; i++) {
    SPVM_OP* op_type = SPVM_LIST_fetch(op_types, i);
    
    SPVM_TYPE* type = op_type->uv.type;
    
    if (type->is_self) {
      continue;
    }
    
    // Basic type name
    const char* basic_type_name = type->basic_type->name;

    // Check if type name is package
    if (type->basic_type->id > SPVM_BASIC_TYPE_C_ID_ANY_OBJECT) {
      
      // Unknonw package
      SPVM_HASH* package_symtable = compiler->package_symtable;
      SPVM_PACKAGE* found_package = SPVM_HASH_fetch(package_symtable, basic_type_name, strlen(basic_type_name));
      if (!found_package) {
        SPVM_yyerror_format(compiler, "Unknown package \"%s\" at %s line %d\n", basic_type_name, op_type->file, op_type->line);
      }
    }
    
    // Reference type must be numeric refernce type or value reference type
    if (SPVM_TYPE_is_ref_type(compiler, type->basic_type->id, type->dimension, type->flag)) {
      if (!(SPVM_TYPE_is_numeric_ref_type(compiler, type->basic_type->id, type->dimension, type->flag) || SPVM_TYPE_is_value_ref_type(compiler, type->basic_type->id, type->dimension, type->flag))) {
        SPVM_yyerror_format(compiler, "Reference type must be numeric refernce type or value_t reference type \"%s\"\\ at %s line %d\n", basic_type_name, op_type->file, op_type->line);
      }
    }
    
    // Interface array is invalid
    if (type->basic_type->package && type->basic_type->package->category == SPVM_PACKAGE_C_CATEGORY_INTERFACE) {
      if (type->dimension > 1) {
        SPVM_yyerror_format(compiler, "Array of interface is invalid at %s line %d\n", op_type->file, op_type->line);
      }
    }
  }
}

void SPVM_OP_CHECKER_resolve_call_sub(SPVM_COMPILER* compiler, SPVM_OP* op_call_sub, SPVM_OP* op_package_current) {
  
  SPVM_CALL_SUB* call_sub = op_call_sub->uv.call_sub;
  
  if (call_sub->sub) {
    return;
  }
  
  SPVM_SUB* found_sub;
  
  const char* sub_name = call_sub->op_name->uv.name;
  // $obj->sub_name
  if (call_sub->call_type_id == SPVM_SUB_C_CALL_TYPE_ID_METHOD) {
    SPVM_TYPE* type = SPVM_OP_get_type(compiler, call_sub->op_invocant);
    const char* basic_type_name = type->basic_type->name;
    const char* sub_abs_name = SPVM_OP_create_abs_name(compiler, basic_type_name, sub_name);
    
    found_sub= SPVM_HASH_fetch(
      compiler->sub_symtable,
      sub_abs_name,
      strlen(sub_abs_name)
    );
  }
  else {
    // Package->sub_name
    if (call_sub->op_invocant) {
      const char* package_name = call_sub->op_invocant->uv.type->basic_type->name;
      const char* sub_abs_name = SPVM_OP_create_abs_name(compiler, package_name, sub_name);
      found_sub= SPVM_HASH_fetch(
        compiler->sub_symtable,
        sub_abs_name,
        strlen(sub_abs_name)
      );
    }
    // sub_name
    else {
      // Search current pacakge
      SPVM_PACKAGE* package = op_package_current->uv.package;
      const char* package_name = package->op_name->uv.name;
      const char* sub_abs_name = SPVM_OP_create_abs_name(compiler, package_name, sub_name);
      found_sub= SPVM_HASH_fetch(
        compiler->sub_symtable,
        sub_abs_name,
        strlen(sub_abs_name)
      );
      
      // Search SPVM::CORE
      if (!found_sub) {
        sub_abs_name = SPVM_OP_create_abs_name(compiler, "SPVM::CORE", sub_name);
        
        found_sub= SPVM_HASH_fetch(
          compiler->sub_symtable,
          sub_abs_name,
          strlen(sub_abs_name)
        );
      }
    }
  }
  
  if (found_sub) {
    call_sub->sub = found_sub;
  }
}

void SPVM_OP_CHECKER_resolve_field_access(SPVM_COMPILER* compiler, SPVM_OP* op_field_access) {

  SPVM_FIELD_ACCESS* field_access = op_field_access->uv.field_access;

  if (field_access->field) {
    return;
  }

  SPVM_OP* op_term = op_field_access->first;
  SPVM_OP* op_name = field_access->op_name;
  
  SPVM_TYPE* invoker_type = SPVM_OP_get_type(compiler, op_term);
  SPVM_PACKAGE* package = SPVM_HASH_fetch(compiler->package_symtable, invoker_type->basic_type->name, strlen(invoker_type->basic_type->name));
  const char* field_name = op_name->uv.name;
  
  SPVM_FIELD* found_field = SPVM_HASH_fetch(
    package->field_symtable,
    field_name,
    strlen(field_name)
  );
  if (found_field) {
    op_field_access->uv.field_access->field = found_field;
  }
}

void SPVM_OP_CHECKER_resolve_package_var_access(SPVM_COMPILER* compiler, SPVM_OP* op_package_var_access, SPVM_OP* op_package) {
  
  SPVM_OP* op_name = op_package_var_access->uv.package_var_access->op_name;
  
  const char* name = op_name->uv.name;
  const char* abs_name;
  if (strchr(name, ':')) {
    abs_name = name;
  }
  else {
    abs_name = SPVM_OP_create_package_var_access_abs_name(compiler, op_package->uv.package->op_name->uv.name, name);
  }
  
  SPVM_PACKAGE_VAR* package_var = SPVM_HASH_fetch(compiler->package_var_symtable, abs_name, strlen(abs_name));
  
  if (package_var) {
    op_package_var_access->uv.package_var_access->package_var = package_var;
  }
}

void SPVM_OP_CHECKER_resolve_basic_types(SPVM_COMPILER* compiler) {
  SPVM_LIST* basic_types = compiler->basic_types;
  
  {
    int32_t basic_type_index;
    for (basic_type_index = 0; basic_type_index < basic_types->length; basic_type_index++) {
      SPVM_BASIC_TYPE* basic_type = SPVM_LIST_fetch(basic_types, basic_type_index);
      int32_t basic_type_id = basic_type->id;
      SPVM_PACKAGE* package = SPVM_HASH_fetch(compiler->package_symtable, basic_type->name, strlen(basic_type->name));
      if (package) {
        basic_type->package = package;
      }
    }
  }
}

void SPVM_OP_CHECKER_resolve_packages(SPVM_COMPILER* compiler) {
  int32_t package_index;
  for (package_index = 0; package_index < compiler->packages->length; package_index++) {
    SPVM_PACKAGE* package = SPVM_LIST_fetch(compiler->packages, package_index);
    
    const char* package_name = package->op_name->uv.name;
    
    // value_t package limitation
    if (package->category == SPVM_PACKAGE_C_CATEGORY_VALUE_T) {
      // Can't have subroutines
      if (package->subs->length > 0) {
        SPVM_yyerror_format(compiler, "value_t package can't have subroutines at %s line %d\n", package->op_package->file, package->op_package->line);
      }
      // Can't have package variables
      if (package->package_vars->length > 0) {
        SPVM_yyerror_format(compiler, "value_t package can't have package variables at %s line %d\n", package->op_package->file, package->op_package->line);
      }
      
      // At least have one field
      if (package->fields->length == 0) {
        SPVM_yyerror_format(compiler, "value_t package have at least one field at %s line %d\n", package->op_package->file, package->op_package->line);
      }
      // Max fields length is 16
      else if (package->fields->length > SPVM_LIMIT_C_VALUE_T_FIELDS_LENGTH_MAX) {
        SPVM_yyerror_format(compiler, "Too many fields at %s line %d\n", package->op_package->file, package->op_package->line);
      }
      else {
        SPVM_LIST* fields = package->fields;
        SPVM_FIELD* first_field = SPVM_LIST_fetch(fields, 0);
        SPVM_TYPE* first_field_type = SPVM_OP_get_type(compiler, first_field->op_field);
        if (!SPVM_TYPE_is_numeric_type(compiler, first_field_type->basic_type->id, first_field_type->dimension, first_field_type->flag)) {
          SPVM_yyerror_format(compiler, "value_t package must have numeric field at %s line %d\n", first_field->op_field->file, first_field->op_field->line);
        }
        else {
          int32_t field_index;
          int32_t numeric_field_error = 0;
          for (field_index = 0; field_index < package->fields->length; field_index++) {
            SPVM_FIELD* field = SPVM_LIST_fetch(fields, field_index);
            SPVM_TYPE* field_type = SPVM_OP_get_type(compiler, field->op_field);
            if (!(field_type->basic_type->id == first_field_type->basic_type->id && field_type->dimension == first_field_type->dimension)) {
              SPVM_yyerror_format(compiler, "field must have %s type at %s line %d\n", field_type->basic_type->name, field->op_field->file, field->op_field->line);
              numeric_field_error = 1;
            }
          }
          if (!numeric_field_error) {
            // Check type name
            char* tail_name = SPVM_COMPILER_ALLOCATOR_safe_malloc_zero(compiler, 255);
            switch (first_field_type->basic_type->id) {
              case SPVM_BASIC_TYPE_C_ID_BYTE:
                sprintf(tail_name, "_b%d", fields->length);
                break;
              case SPVM_BASIC_TYPE_C_ID_SHORT:
                sprintf(tail_name, "_s%d", fields->length);
                break;
              case SPVM_BASIC_TYPE_C_ID_INT:
                sprintf(tail_name, "_i%d", fields->length);
                break;
              case SPVM_BASIC_TYPE_C_ID_LONG:
                sprintf(tail_name, "_l%d", fields->length);
                break;
              case SPVM_BASIC_TYPE_C_ID_FLOAT:
                sprintf(tail_name, "_f%d", fields->length);
                break;
              case SPVM_BASIC_TYPE_C_ID_DOUBLE:
                sprintf(tail_name, "_d%d", fields->length);
                break;
              default:
                assert(0);
            }
            int32_t tail_name_length = (int32_t)strlen(tail_name);
            
            char* found_pos_ptr = strstr(package_name, tail_name);
            if (found_pos_ptr) {
              if (*(found_pos_ptr + tail_name_length) != '\0') {
                SPVM_yyerror_format(compiler, "package name must end with %s at %s line %d\n", tail_name, package->op_package->file, package->op_package->line);
              }
            }
            else {
              SPVM_yyerror_format(compiler, "package name must end with %s at %s line %d\n", tail_name, package->op_package->file, package->op_package->line);
            }
          }
        }
      }
    }
    
    // Check fields
    {
      int32_t field_index;
      for (field_index = 0; field_index < package->fields->length; field_index++) {
        SPVM_FIELD* field = SPVM_LIST_fetch(package->fields, field_index);
        SPVM_TYPE* field_type = SPVM_OP_get_type(compiler, field->op_field);

        // valut_t can't become field
        int32_t is_value_t = SPVM_TYPE_is_value_type(compiler, field_type->basic_type->id, field_type->dimension, field_type->flag);
        if (is_value_t) {
          SPVM_yyerror_format(compiler, "value_t type can't become field at %s line %d\n", field->op_field->file, field->op_field->line);
        }
        else {
          // Add object field indexes
          if (SPVM_TYPE_is_object_type(compiler, field->type->basic_type->id, field->type->dimension, field->type->flag)) {
            SPVM_LIST_push(package->object_field_indexes, (void*)(intptr_t)field->index);
          }
        }
      }
    }
    
    // valut_t can't become package variable
    {
      int32_t package_var_index;
      for (package_var_index = 0; package_var_index < package->package_vars->length; package_var_index++) {
        SPVM_PACKAGE_VAR* package_var = SPVM_LIST_fetch(package->package_vars, package_var_index);
        SPVM_TYPE* package_var_type = SPVM_OP_get_type(compiler, package_var->op_package_var);
        int32_t is_value_t = SPVM_TYPE_is_value_type(compiler, package_var_type->basic_type->id, package_var_type->dimension, package_var_type->flag);
        
        if (is_value_t) {
          SPVM_yyerror_format(compiler, "value_t type can't become package variable at %s line %d\n", package_var->op_package_var->file, package_var->op_package_var->line);
        }
      }
    }

    // Check subroutines
    {
      int32_t i;
      for (i = 0; i < package->subs->length; i++) {
        SPVM_SUB* sub = SPVM_LIST_fetch(package->subs, i);
        
        // Argument limit check
        int32_t arg_allow_count = 0;
        for (int32_t arg_index = 0; arg_index < sub->args->length; arg_index++) {
          SPVM_MY* arg_my = SPVM_LIST_fetch(sub->args, arg_index);

          SPVM_TYPE* arg_type = SPVM_OP_get_type(compiler, arg_my->op_my);
          
          int32_t is_arg_type_is_value_type = SPVM_TYPE_is_value_type(compiler, arg_type->basic_type->id, arg_type->dimension, arg_type->flag);
          int32_t is_arg_type_is_value_ref_type = SPVM_TYPE_is_value_ref_type(compiler, arg_type->basic_type->id, arg_type->dimension, arg_type->flag);
          
          if (is_arg_type_is_value_type || is_arg_type_is_value_ref_type) {
            arg_allow_count += arg_type->basic_type->package->fields->length;
          }
          else {
            arg_allow_count++;
          }
        }
        if (arg_allow_count > 255) {
          SPVM_yyerror_format(compiler, "Over argument limit at %s line %d\n", sub->op_sub->file, sub->op_sub->line);
        }
      }
    }

    // Register sub signature
    {
      int32_t i;
      for (i = 0; i < package->subs->length; i++) {
        SPVM_SUB* sub = SPVM_LIST_fetch(package->subs, i);
        
        const char* sub_signature = SPVM_OP_CHECKER_create_sub_signature(compiler, sub);
        sub->signature = sub_signature;
        SPVM_LIST_push(sub->package->sub_signatures, (char*)sub_signature);
        SPVM_HASH_insert(sub->package->sub_signature_symtable, sub_signature, strlen(sub_signature), sub);
      }
    }

    // Register field signature
    {
      int32_t i;
      for (i = 0; i < package->fields->length; i++) {
        SPVM_FIELD* field = SPVM_LIST_fetch(package->fields, i);
        
        const char* field_signature = SPVM_OP_CHECKER_create_field_signature(compiler, field);
        field->signature = field_signature;
        SPVM_LIST_push(field->package->field_signatures, (char*)field_signature);
        SPVM_HASH_insert(field->package->field_signature_symtable, field_signature, strlen(field_signature), field);
      }
    }

    // Register package_var signature
    {
      int32_t i;
      for (i = 0; i < package->package_vars->length; i++) {
        SPVM_PACKAGE_VAR* package_var = SPVM_LIST_fetch(package->package_vars, i);
        
        const char* package_var_signature = SPVM_OP_CHECKER_create_package_var_signature(compiler, package_var);
        package_var->signature = package_var_signature;
        SPVM_LIST_push(package_var->package->package_var_signatures, (char*)package_var_signature);
        SPVM_HASH_insert(package_var->package->package_var_signature_symtable, package_var_signature, strlen(package_var_signature), package_var);
      }
    }

  }
}

const char* SPVM_OP_CHECKER_create_sub_signature(SPVM_COMPILER* compiler, SPVM_SUB* sub) {
  
  int32_t length = 0;
  
  // Calcurate signature length
  {
    // (
    length += 1;

    // Return type basic type
    length += strlen(sub->return_type->basic_type->name);
    
    // Return type dimension
    length += sub->return_type->dimension * 2;
    
    // )
    length += 1;
    
    // Subroutine name
    length += strlen(sub->op_name->uv.name);
    
    // (
    length += 1;
    
    int32_t arg_index;
    for (arg_index = 0; arg_index < sub->args->length; arg_index++) {
      if (sub->call_type_id == SPVM_SUB_C_CALL_TYPE_ID_METHOD && arg_index == 0) {
        // self
        length += 4;
      }
      else {
        SPVM_MY* arg_my_sub = SPVM_LIST_fetch(sub->args, arg_index);
        SPVM_TYPE* type_arg_sub = SPVM_OP_get_type(compiler, arg_my_sub->op_my);
        
        // Ref
        if (SPVM_TYPE_is_ref_type(compiler, type_arg_sub->basic_type->id, type_arg_sub->dimension, type_arg_sub->flag)) {
          length += 1;
        }
        
        // TYPE
        length += strlen(type_arg_sub->basic_type->name);
        
        // Dimension
        length += type_arg_sub->dimension * 2;
      }
      // ,
      if (arg_index != sub->args->length - 1) {
        length += 1;
      }
    }
    
    // )
    length += 1;
  }
  
  char* sub_signature = SPVM_COMPILER_ALLOCATOR_safe_malloc_zero(compiler, length + 1);
  
  // Calcurate sub signature length
  char* bufptr = sub_signature;
  {
    // (
    memcpy(bufptr, "(", 1);
    bufptr += 1;

    // Return type
    memcpy(bufptr, sub->return_type->basic_type->name, strlen(sub->return_type->basic_type->name));
    bufptr += strlen(sub->return_type->basic_type->name);
    
    int32_t dim_index;
    for (dim_index = 0; dim_index < sub->return_type->dimension; dim_index++) {
      memcpy(bufptr, "[]", 2);
      bufptr += 2;
    }
    
    // )
    *bufptr = ')';
    bufptr += 1;

    // Subroutine name
    memcpy(bufptr, sub->op_name->uv.name, strlen(sub->op_name->uv.name));
    bufptr += strlen(sub->op_name->uv.name);
    
    // (
    *bufptr = '(';
    bufptr += 1;
    
    int32_t arg_index;
    for (arg_index = 0; arg_index < sub->args->length; arg_index++) {
      // self
      if (sub->call_type_id == SPVM_SUB_C_CALL_TYPE_ID_METHOD && arg_index == 0) {
        memcpy(bufptr, "self", 4);
        bufptr += 4;
      }
      else {
        SPVM_MY* arg_my_sub = SPVM_LIST_fetch(sub->args, arg_index);
        SPVM_TYPE* type_arg_sub = SPVM_OP_get_type(compiler, arg_my_sub->op_my);
        
        // Ref
        if (SPVM_TYPE_is_ref_type(compiler, type_arg_sub->basic_type->id, type_arg_sub->dimension, type_arg_sub->flag)) {
          *bufptr = '&';
          bufptr += 1;
        }
        
        // TYPE
        memcpy(bufptr, type_arg_sub->basic_type->name, strlen(type_arg_sub->basic_type->name));
        bufptr += strlen(type_arg_sub->basic_type->name);

        int32_t dim_index;
        for (dim_index = 0; dim_index < type_arg_sub->dimension; dim_index++) {
          memcpy(bufptr, "[]", 2);
          bufptr += 2;
        }
      }

      // ,
      if (arg_index != sub->args->length - 1) {
        memcpy(bufptr, ",", 1);
        bufptr += 1;
      }
    }
    
    // )
    memcpy(bufptr, ")", 1);
    bufptr += 1;
  }
  
  return sub_signature;
}

const char* SPVM_OP_CHECKER_create_field_signature(SPVM_COMPILER* compiler, SPVM_FIELD* field) {
  
  int32_t length = 0;
  
  // Calcurate signature length
  {
    // (
    length += 1;

    // Return type basic type
    length += strlen(field->type->basic_type->name);
    
    // Return type dimension
    length += field->type->dimension * 2;
    
    // )
    length += 1;
    
    // Subroutine name
    length += strlen(field->op_name->uv.name);
  }
  
  char* field_signature = SPVM_COMPILER_ALLOCATOR_safe_malloc_zero(compiler, length + 1);
  
  // Calcurate field signature length
  char* bufptr = field_signature;
  {
    // (
    memcpy(bufptr, "(", 1);
    bufptr += 1;

    // Return type
    memcpy(bufptr, field->type->basic_type->name, strlen(field->type->basic_type->name));
    bufptr += strlen(field->type->basic_type->name);
    
    int32_t dim_index;
    for (dim_index = 0; dim_index < field->type->dimension; dim_index++) {
      memcpy(bufptr, "[]", 2);
      bufptr += 2;
    }
    
    // )
    *bufptr = ')';
    bufptr += 1;

    // Subroutine name
    memcpy(bufptr, field->op_name->uv.name, strlen(field->op_name->uv.name));
    bufptr += strlen(field->op_name->uv.name);
  }
  
  return field_signature;
}

const char* SPVM_OP_CHECKER_create_package_var_signature(SPVM_COMPILER* compiler, SPVM_PACKAGE_VAR* package_var) {
  
  int32_t length = 0;
  
  // Calcurate signature length
  {
    // (
    length += 1;

    // Return type basic type
    length += strlen(package_var->type->basic_type->name);
    
    // Return type dimension
    length += package_var->type->dimension * 2;
    
    // )
    length += 1;
    
    // Subroutine name
    length += strlen(package_var->op_var->uv.var->op_name->uv.name);
  }
  
  char* package_var_signature = SPVM_COMPILER_ALLOCATOR_safe_malloc_zero(compiler, length + 1);
  
  // Calcurate package_var signature length
  char* bufptr = package_var_signature;
  {
    // (
    memcpy(bufptr, "(", 1);
    bufptr += 1;

    // Return type
    memcpy(bufptr, package_var->type->basic_type->name, strlen(package_var->type->basic_type->name));
    bufptr += strlen(package_var->type->basic_type->name);
    
    int32_t dim_index;
    for (dim_index = 0; dim_index < package_var->type->dimension; dim_index++) {
      memcpy(bufptr, "[]", 2);
      bufptr += 2;
    }
    
    // )
    *bufptr = ')';
    bufptr += 1;

    // Subroutine name
    memcpy(bufptr, package_var->op_var->uv.var->op_name->uv.name, strlen(package_var->op_var->uv.var->op_name->uv.name));
    bufptr += strlen(package_var->op_var->uv.var->op_name->uv.name);
  }
  
  return package_var_signature;
}