// Copyright (c) 2023 Yuki Kimoto
// MIT License

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <assert.h>
#include <inttypes.h>
#include <ctype.h>
#include "spvm_compiler.h"
#include "spvm_list.h"
#include "spvm_hash.h"
#include "spvm_op.h"
#include "spvm_method.h"
#include "spvm_constant.h"
#include "spvm_field.h"
#include "spvm_var_decl.h"
#include "spvm_var.h"
#include "spvm_type.h"
#include "spvm_field_access.h"
#include "spvm_call_method.h"
#include "spvm_type.h"
#include "spvm_opcode_builder.h"
#include "spvm_check.h"
#include "spvm_switch_info.h"
#include "spvm_attribute.h"
#include "spvm_allocator.h"
#include "spvm_use.h"
#include "spvm_class_var.h"
#include "spvm_class_var_access.h"
#include "spvm_precompile.h"
#include "spvm_block.h"
#include "spvm_basic_type.h"
#include "spvm_case_info.h"
#include "spvm_array_field_access.h"
#include "spvm_string_buffer.h"
#include "spvm_allow.h"
#include "spvm_interface.h"
#include "spvm_string.h"
#include "spvm_dumper.h"
#include "spvm_version_from.h"






















































const char* const* SPVM_OP_C_ID_NAMES(void) {

  static const char* const id_names[] = {
    "GRAMMAR",
    "LIST",
    "PUSHMARK",
    "DO_NOTHING",
    "NAME",
    "ATTRIBUTE",
    "OUTMOST_CLASS",
    "CLASS",
    "EXTENDS",
    "CLASS_BLOCK",
    "END_OF_FILE",
    "VERSION_DECL",
    "VERSION_FROM",
    "IF",
    "UNLESS",
    "ELSIF",
    "ELSE",
    "CONDITION",
    "CONDITION_NOT",
    "SWITCH",
    "CASE",
    "DEFAULT",
    "SWITCH_CONDITION",
    "BREAK",
    "FOR",
    "WHILE",
    "LOOP",
    "LOOP_INCREMENT",
    "LAST",
    "NEXT",
    "VAR_DECL",
    "FIELD",
    "METHOD",
    "ENUM",
    "ENUMERATION_ITEM",
    "ENUM_BLOCK",
    "BLOCK",
    "EVAL",
    "TYPE",
    "MUTABLE",
    "VOID",
    "BYTE",
    "SHORT",
    "INT",
    "LONG",
    "FLOAT",
    "DOUBLE",
    "STRING",
    "OBJECT",
    "DOT3",
    "OF",
    "USE",
    "AS",
    "ALIAS",
    "REQUIRE",
    "IF_REQUIRE",
    "INIT",
    "INTERFACE",
    "ALLOW",
    "DIE",
    "RETURN",
    "CONSTANT",
    "MINUS",
    "PLUS",
    "INC",
    "DEC",
    "PRE_INC",
    "POST_INC",
    "PRE_DEC",
    "POST_DEC",
    "ADD",
    "SUBTRACT",
    "MULTIPLY",
    "DIVIDE",
    "DIVIDE_UNSIGNED_INT",
    "DIVIDE_UNSIGNED_LONG",
    "BIT_AND",
    "BIT_OR",
    "BIT_XOR",
    "BIT_NOT",
    "MODULO",
    "MODULO_UNSIGNED_INT",
    "MODULO_UNSIGNED_LONG",
    "LEFT_SHIFT",
    "RIGHT_ARITHMETIC_SHIFT",
    "RIGHT_LOGICAL_SHIFT",
    "LOGICAL_AND",
    "LOGICAL_OR",
    "LOGICAL_NOT",
    "ARRAY_ELEMENT_ACCESS",
    "ASSIGN",
    "FIELD_ACCESS",
    "HASH_VALUE_ACCESS",
    "VAR",
    "UNDEF",
    "ARRAY_LENGTH",
    "SCALAR",
    "EXCEPTION_VAR",
    "SPECIAL_ASSIGN",
    "STRING_LENGTH",
    "STRING_CONCAT",
    "CLASS_VAR",
    "CLASS_VAR_ACCESS",
    "NUMERIC_COMPARISON_EQ",
    "NUMERIC_COMPARISON_NE",
    "NUMERIC_COMPARISON_LT",
    "NUMERIC_COMPARISON_LE",
    "NUMERIC_COMPARISON_GT",
    "NUMERIC_COMPARISON_GE",
    "NUMERIC_COMPARISON_CMP",
    "STRING_COMPARISON_EQ",
    "STRING_COMPARISON_NE",
    "STRING_COMPARISON_GT",
    "STRING_COMPARISON_GE",
    "STRING_COMPARISON_LT",
    "STRING_COMPARISON_LE",
    "STRING_COMPARISON_CMP",
    "ARRAY_FIELD_ACCESS",
    "NEW",
    "ARRAY_INIT",
    "WARN",
    "WARN_LEVEL",
    "PRINT",
    "SAY",
    "DUMP",
    "TRUE",
    "FALSE",
    "NEW_STRING_LEN",
    "IS_READ_ONLY",
    "MAKE_READ_ONLY",
    "IS_FIXED_LENGTH",
    "MAKE_FIXED_LENGTH",
    "COPY",
    "TYPE_CAST",
    "CONDITION_EVALUATION",
    "ISA",
    "ISA_ERROR",
    "IS_TYPE",
    "IS_ERROR",
    "IS_COMPILE_TYPE",
    "CAN",
    "BASIC_TYPE_ID",
    "TYPE_NAME",
    "COMPILE_TYPE_NAME",
    "OUTMOST_CLASS_NAME",
    "ARGS_WIDTH",
    "CALL_METHOD",
    "WEAKEN",
    "WEAKEN_FIELD",
    "UNWEAKEN",
    "UNWEAKEN_FIELD",
    "ISWEAK",
    "ISWEAK_FIELD",
    "REFERENCE",
    "DEREFERENCE",
    "EVAL_ERROR_ID",
    "SEQUENCE",
    "DEFINED_OR",
    "TERNARY_OP",
    "COPY_FIELDS",
    "ADDRESS",
    "ENABLE_OPTIONS",
    "DISABLE_OPTIONS",
    "IS_OPTIONS",
    "GET",
    "SET",
    "EXISTS",
    "DELETE",
    "ELEMENT",
    "SET_LENGTH",
    "CAPACITY",
    "SET_CAPACITY",
    "STDERR",
    "CALLER",
    "BREAK_POINT",
    "END",
  };
  
  return id_names;
}

SPVM_OP* SPVM_OP_build_class(SPVM_COMPILER* compiler, SPVM_OP* op_class, SPVM_OP* op_type, SPVM_OP* op_block, SPVM_OP* op_list_attributes, SPVM_OP* op_extends) {
  
  const char* basic_type_name;
  
  // Class
  if (op_type) {
    basic_type_name = op_type->uv.type->unresolved_basic_type_name;
  }
  // Anon class(not a class generated by an anon method)
  else {
    basic_type_name = compiler->current_outmost_class_name;
    
    SPVM_OP* op_name_basic_type = SPVM_OP_new_op_name(compiler, basic_type_name, op_class->file, op_class->line);
    op_type = SPVM_OP_build_basic_type(compiler, op_name_basic_type);
  }
  
  SPVM_TYPE* type = op_type->uv.type;
  
  SPVM_BASIC_TYPE* basic_type = SPVM_HASH_get(compiler->basic_type_symtable, basic_type_name, strlen(basic_type_name));
  if (!basic_type) {
    basic_type = SPVM_COMPILER_add_basic_type(compiler, basic_type_name);
    
    SPVM_BASIC_TYPE* outmost_basic_type = SPVM_HASH_get(compiler->basic_type_symtable, basic_type_name, strlen(basic_type_name));
    
    assert(outmost_basic_type);
  }
  
  type->basic_type = basic_type;
  
  type->basic_type->op_class = op_class;
  type->basic_type->op_extends = op_extends;
  
  type->basic_type->class_dir = compiler->current_class_dir;
  type->basic_type->class_rel_file = compiler->current_class_rel_file;
  type->basic_type->file = compiler->current_file;
  
  if (op_extends) {
    SPVM_OP* op_type_parent_basic_type = op_extends->first;
    
    SPVM_OP* op_name_parent_basic_type = SPVM_OP_new_op_name(compiler, op_type_parent_basic_type->uv.type->unresolved_basic_type_name, op_type_parent_basic_type->file, op_type_parent_basic_type->line);
    
    type->basic_type->parent_name = op_name_parent_basic_type->uv.name;
    
    // add use stack
    SPVM_OP* op_use = SPVM_OP_new_op_use(compiler, op_name_parent_basic_type->file, op_name_parent_basic_type->line);
    SPVM_OP* op_name_alias = NULL;
    int32_t is_require = 0;
    SPVM_OP_build_use(compiler, op_use, op_type_parent_basic_type, op_name_alias, is_require);
  }
  
  if (type->basic_type->class_dir) {
    SPVM_STRING_new(compiler, type->basic_type->class_dir, strlen(type->basic_type->class_dir));
  }
  SPVM_STRING_new(compiler, type->basic_type->class_rel_file, strlen(type->basic_type->class_rel_file));
  if (type->basic_type->file) {
    SPVM_STRING_new(compiler, type->basic_type->file, strlen(type->basic_type->file));
  }
  
  // Assert
  SPVM_BASIC_TYPE* found_basic_type = SPVM_HASH_get(compiler->basic_type_symtable, basic_type_name, strlen(basic_type_name));
  
  if (strstr(basic_type_name, "::anon_method::")) {
    type->basic_type->access_control_type = SPVM_ATTRIBUTE_C_ID_PUBLIC;
    basic_type->is_generated_from_anon_method = 1;
  }
  else if (strstr(basic_type_name, "::anon_class::")) {
    basic_type->is_anon = 1;
  }
  
  if (!(basic_type->is_anon || basic_type->is_generated_from_anon_method)) {
    
    // If class name is different from the class name corresponding to the class file, compile error occur.
    if (strcmp(basic_type_name, compiler->current_outmost_class_name) != 0) {
      SPVM_COMPILER_error(compiler, "The class name '%s' must be '%s'.\n  at %s line %d", basic_type_name, compiler->current_outmost_class_name, op_class->file, op_class->line);
      return op_class;
    }
    
    SPVM_LIST* anon_op_types = compiler->current_anon_op_types;
    for (int32_t i = 0; i < anon_op_types->length; i++) {
      SPVM_OP* anon_op_type = SPVM_LIST_get(anon_op_types, i);
      const char* anon_unresolved_basic_type_name = anon_op_type->uv.type->unresolved_basic_type_name;
      SPVM_BASIC_TYPE* anon_basic_type = SPVM_HASH_get(compiler->basic_type_symtable, anon_unresolved_basic_type_name, strlen(anon_unresolved_basic_type_name));
      assert(anon_basic_type);
      SPVM_LIST_push(basic_type->anon_basic_types, anon_basic_type);
    }
  }
  
  // Module attributes
  int32_t class_attributes_count = 0;
  int32_t access_control_attributes_count = 0;
  if (op_list_attributes) {
    SPVM_OP* op_attribute = op_list_attributes->first;
    while ((op_attribute = SPVM_OP_sibling(compiler, op_attribute))) {
      SPVM_ATTRIBUTE* attribute = op_attribute->uv.attribute;
      switch (attribute->id) {
        case SPVM_ATTRIBUTE_C_ID_POINTER: {
          type->basic_type->category = SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_CLASS;
          basic_type->is_pointer = 1;
          class_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_MULNUM_T: {
          type->basic_type->category = SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_MULNUM;
          basic_type->category = SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_MULNUM;
          class_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_PRIVATE: {
          basic_type->access_control_type = SPVM_ATTRIBUTE_C_ID_PRIVATE;
          access_control_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_PROTECTED: {
          basic_type->access_control_type = SPVM_ATTRIBUTE_C_ID_PROTECTED;
          access_control_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_PUBLIC: {
          basic_type->access_control_type = SPVM_ATTRIBUTE_C_ID_PUBLIC;
          access_control_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_PRECOMPILE: {
          basic_type->is_precompile = 1;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_INTERFACE_T: {
          type->basic_type->category = SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_INTERFACE;
          basic_type->category = SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_INTERFACE;
          class_attributes_count++;
          break;
        }
        default: {
          SPVM_COMPILER_error(compiler, "Invalid class attribute '%s'.\n  at %s line %d", SPVM_ATTRIBUTE_get_name(compiler, attribute->id), op_class->file, op_class->line);
        }
      }
    }
    if (class_attributes_count > 1) {
      SPVM_COMPILER_error(compiler, "Only one of class attributes 'mulnum_t', 'pointer' or 'interface_t' can be specified.\n  at %s line %d", op_list_attributes->file, op_list_attributes->line);
    }
    if (access_control_attributes_count > 1) {
      SPVM_COMPILER_error(compiler, "Only one of class attributes 'private', 'protected' or 'public' can be specified.\n  at %s line %d", op_list_attributes->file, op_list_attributes->line);
    }
  }
  
  if (basic_type->category == 0) {
    basic_type->category = SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_CLASS;
  }
  
  // The default of the access controll is protected
  if (basic_type->access_control_type == SPVM_ATTRIBUTE_C_ID_UNKNOWN) {
    basic_type->access_control_type = SPVM_ATTRIBUTE_C_ID_PROTECTED;
  }
  
  // Declarations
  if (op_block) {
    SPVM_OP* op_decls = op_block->first;
    SPVM_OP* op_decl = op_decls->first;
    while ((op_decl = SPVM_OP_sibling(compiler, op_decl))) {
      // version statement
      if (op_decl->id == SPVM_OP_C_ID_VERSION_DECL) {
        if (type->basic_type->version_string) {
          SPVM_COMPILER_error(compiler, "The version has already been declared.\n  at %s line %d", op_decl->file, op_decl->line);
          break;
        }
        
        if (type->basic_type->basic_type_name_in_version_from) {
          SPVM_COMPILER_error(compiler, "Both version statement and version_from statement cannnot be used at the same time.\n  at %s line %d", op_decl->file, op_decl->line);
          break;
        }
        
        SPVM_OP* op_version_string = op_decl->first;
        SPVM_CONSTANT* version_string_constant = op_version_string->uv.constant;
        SPVM_TYPE* version_string_constant_type = version_string_constant->type;
        if (!SPVM_TYPE_is_string_type(compiler, version_string_constant_type->basic_type->id, version_string_constant_type->dimension, version_string_constant_type->flag)) {
          SPVM_COMPILER_error(compiler, "The operand type of version statement must be string type.\n  at %s line %d", op_decl->file, op_decl->line);
          break;
        }
        
        const char* version_string = version_string_constant->value.address;
        int32_t version_string_length = version_string_constant->string_length;
        
        // Version string validation
        {
          int32_t dot_count = 0;
          int32_t digits_after_dot = 0;
          int32_t invalid_char = 0;
          for (int32_t version_global_string_address_id = 0; version_global_string_address_id < version_string_length; version_global_string_address_id++) {
            char ch = version_string[version_global_string_address_id];
            
            if (!(ch == '.' || isdigit(ch))) {
              invalid_char = 1;
              break;
            }
            
            if (ch == '.') {
              dot_count++;
            }
            
            if (dot_count > 0 && isdigit(ch)) {
              digits_after_dot++;
            }
          }
          
          if (invalid_char) {
            SPVM_COMPILER_error(compiler, "A character in a version string must be a number or \".\".\n  at %s line %d", op_decl->file, op_decl->line);
            break;
          }
          
          if (!isdigit(version_string[0])) {
            SPVM_COMPILER_error(compiler, "A version string must begin with a number.\n  at %s line %d", op_decl->file, op_decl->line);
            break;
          }
          
          if (!isdigit(version_string[version_string_length - 1])) {
            SPVM_COMPILER_error(compiler, "A version string must end with a number.\n  at %s line %d", op_decl->file, op_decl->line);
            break;
          }
          
          if (!(dot_count <= 1)) {
            SPVM_COMPILER_error(compiler, "The number of '.' in a version string must be less than or equal to 1.\n  at %s line %d", op_decl->file, op_decl->line);
            break;
          }
          
          if (!(digits_after_dot % 3 == 0)) {
            SPVM_COMPILER_error(compiler, "The length of characters after '.' in a version string must be divisible by 3.\n  at %s line %d", op_decl->file, op_decl->line);
            break;
          }
          
          {
            char *end;
            double double_value = strtod(version_string, &end);
            if (*end != '\0') {
              SPVM_COMPILER_error(compiler, "A version string must be able to be parsed by the 'strtod' C function.\n  at %s line %d", op_decl->file, op_decl->line);
              break;
            }
          }
        }
        
        SPVM_STRING_new(compiler, version_string, version_string_length);
        type->basic_type->version_string = version_string;
      }
      // version_from statement
      else if (op_decl->id == SPVM_OP_C_ID_VERSION_FROM) {
        if (type->basic_type->version_from) {
          SPVM_COMPILER_error(compiler, "version_from has already been declared.\n  at %s line %d", op_decl->file, op_decl->line);
          break;
        }
        
        if (type->basic_type->version_string) {
          SPVM_COMPILER_error(compiler, "Both version statement and version_from statement cannnot be used at the same time.\n  at %s line %d", op_decl->file, op_decl->line);
          break;
        }
        
        SPVM_OP* op_version_from = op_decl;
        
        type->basic_type->version_from = op_version_from->uv.version_from;
        
        type->basic_type->basic_type_name_in_version_from = op_version_from->uv.version_from->basic_type_name;
      }
      // use statement
      else if (op_decl->id == SPVM_OP_C_ID_USE) {
        SPVM_OP* op_use = op_decl;
        
        SPVM_LIST_push(type->basic_type->use_basic_type_names, (void*)op_use->uv.use->op_type->uv.type->unresolved_basic_type_name);
        
        // Class alias
        const char* alias_name = op_use->uv.use->alias_name;
        if (alias_name) {
    
          // Basic type name must begin with upper case. Otherwise, compiler error occur.
          // (Invalid example) Foo::bar
          if (islower(alias_name[0])) {
            SPVM_COMPILER_error(compiler, "The alias name '%s' must begin with an upper case character.\n  at %s line %d", alias_name, op_decl->file, op_decl->line);
          }
          else {
            const char* use_basic_type_name = op_use->uv.use->op_type->uv.type->unresolved_basic_type_name;
            const char* use_basic_type_name_exists = SPVM_HASH_get(type->basic_type->alias_symtable, alias_name, strlen(alias_name));
            if (use_basic_type_name_exists) {
              SPVM_COMPILER_error(compiler, "The alias name '%s' is already used.\n  at %s line %d", alias_name, op_decl->file, op_decl->line);
            }
            else {
              SPVM_HASH_set(type->basic_type->alias_symtable, alias_name, strlen(alias_name), (void*)use_basic_type_name);
            }
          }
        }
      }
      // allow statement
      else if (op_decl->id == SPVM_OP_C_ID_ALLOW) {
        SPVM_LIST_push(basic_type->allows, op_decl->uv.allow);
      }
      // interface statement
      else if (op_decl->id == SPVM_OP_C_ID_INTERFACE) {
        if (type->basic_type->category == SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_MULNUM) {
          SPVM_COMPILER_error(compiler, "The interface statement cannnot be used in the definition of the multi-numeric type.\n  at %s line %d", op_decl->file, op_decl->line);
        }
        const char* interface_name = op_decl->uv.interface->op_type->uv.type->unresolved_basic_type_name;
        
        SPVM_LIST_push(type->basic_type->interface_decls, op_decl->uv.interface);
      }
      // Class var declarations
      else if (op_decl->id == SPVM_OP_C_ID_CLASS_VAR) {
        SPVM_CLASS_VAR* class_var = op_decl->uv.class_var;
        
        if (type->basic_type->category == SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_INTERFACE) {
          SPVM_COMPILER_error(compiler, "An interface cannnot have class variables.\n  at %s line %d", op_decl->file, op_decl->line);
        }
        
        // Getter
        if (class_var->has_getter) {
          // static method FOO : TYPE () {
          //   return $FOO;
          // }
          
          SPVM_OP* op_method = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_METHOD, op_decl->file, op_decl->line);
          SPVM_STRING* method_name_string = SPVM_STRING_new(compiler, class_var->name + 1, strlen(class_var->name) - 1);
          const char* method_name = method_name_string->value;
          SPVM_OP* op_name_method = SPVM_OP_new_op_name(compiler, method_name, op_decl->file, op_decl->line);
          
          // If the type of the class_var is byte or short, the return type becomes int
          SPVM_TYPE* class_var_type = class_var->type;
          SPVM_TYPE* return_type;
          if (SPVM_TYPE_is_byte_type(compiler, class_var_type->basic_type->id, class_var_type->dimension, class_var_type->flag)
            || SPVM_TYPE_is_short_type(compiler, class_var_type->basic_type->id, class_var_type->dimension, class_var_type->flag))
          {
            return_type = SPVM_OP_new_op_int_type(compiler, op_decl->file, op_decl->line)->uv.type;
          }
          else {
            return_type = class_var->type;
          }
          
          SPVM_OP* op_return_type = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_TYPE, op_decl->file, op_decl->line);
          op_return_type->uv.type = return_type;
          
          SPVM_OP* op_args = SPVM_OP_new_op_list(compiler, op_decl->file, op_decl->line);
          
          SPVM_OP* op_list_attributes = SPVM_OP_new_op_list(compiler, op_decl->file, op_decl->line);
          SPVM_OP* op_attribute_static = SPVM_OP_new_op_attribute(compiler, SPVM_ATTRIBUTE_C_ID_STATIC, op_decl->file, op_decl->line);
          SPVM_OP_insert_child(compiler, op_list_attributes, op_list_attributes->first, op_attribute_static);
          
          SPVM_OP* op_block = NULL;
          if (class_var->op_getter) {
            SPVM_OP* op_block_tmp = class_var->op_getter->last;
            if (!(op_block_tmp->id == SPVM_OP_C_ID_DO_NOTHING)) {
              op_block = op_block_tmp;
            }
          }
          
          if (op_block) {
            // Do nothing
          }
          else {
            op_block = SPVM_OP_new_op_block(compiler, op_decl->file, op_decl->line);
            SPVM_OP* op_statements = SPVM_OP_new_op_list(compiler, op_decl->file, op_decl->line);
            SPVM_OP* op_return = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_RETURN, op_decl->file, op_decl->line);
            
            SPVM_OP* op_name_basic_type_var = SPVM_OP_new_op_name(compiler, class_var->name, op_decl->file, op_decl->line);
            SPVM_OP* op_var_class_var = SPVM_OP_new_op_var(compiler, op_name_basic_type_var);
            
            SPVM_OP_insert_child(compiler, op_return, op_return->last, op_var_class_var);
            SPVM_OP_insert_child(compiler, op_statements, op_statements->last, op_return);
            SPVM_OP_insert_child(compiler, op_block, op_block->last, op_statements);
          }
          
          SPVM_OP_build_method(compiler, op_method, op_name_method, op_return_type, op_args, op_list_attributes, op_block);
          
          SPVM_LIST_push(type->basic_type->methods, op_method->uv.method);
        }
        
        // Setter
        if (class_var->has_setter) {
          
          // method SET_FOO : void ($value : TYPE) {
          //   $FOO = $value;
          // }
          
          SPVM_OP* op_method = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_METHOD, op_decl->file, op_decl->line);
          char* method_name_tmp = SPVM_ALLOCATOR_alloc_memory_block_permanent(compiler->current_each_compile_allocator, 4 + strlen(class_var->name) - 1 + 1);
          memcpy(method_name_tmp, "SET_", 4);
          memcpy(method_name_tmp + 4, class_var->name + 1, strlen(class_var->name) - 1);
          
          SPVM_STRING* method_name_string = SPVM_STRING_new(compiler, method_name_tmp, strlen(method_name_tmp));
          const char* method_name = method_name_string->value;
          
          SPVM_OP* op_name_method = SPVM_OP_new_op_name(compiler, method_name, op_decl->file, op_decl->line);
          SPVM_OP* op_return_type = SPVM_OP_new_op_void_type(compiler, op_decl->file, op_decl->line);
          SPVM_OP* op_args = SPVM_OP_new_op_list(compiler, op_decl->file, op_decl->line);
          
          // If the type of the class_var is byte or short, the arg type becomes int
          SPVM_TYPE* class_var_type = class_var->type;
          SPVM_TYPE* arg_type;
          if (SPVM_TYPE_is_byte_type(compiler, class_var_type->basic_type->id, class_var_type->dimension, class_var_type->flag)
            || SPVM_TYPE_is_short_type(compiler, class_var_type->basic_type->id, class_var_type->dimension, class_var_type->flag))
          {
            arg_type = SPVM_OP_new_op_int_type(compiler, op_decl->file, op_decl->line)->uv.type;
          }
          else {
            arg_type = class_var->type;
          }
          SPVM_OP* op_type_value = SPVM_OP_new_op_type(compiler, arg_type->unresolved_basic_type_name, arg_type->basic_type, arg_type->dimension, arg_type->flag, op_decl->file, op_decl->line);
          
          SPVM_OP* op_var_value_name = SPVM_OP_new_op_name(compiler, "$_", op_decl->file, op_decl->line);
          SPVM_OP* op_var_value = SPVM_OP_new_op_var(compiler, op_var_value_name);
          SPVM_OP* op_arg_value = SPVM_OP_build_arg(compiler, op_var_value, op_type_value, NULL, NULL);
          
          SPVM_OP_insert_child(compiler, op_args, op_args->last, op_arg_value);
          
          SPVM_OP* op_list_attributes = SPVM_OP_new_op_list(compiler, op_decl->file, op_decl->line);
          SPVM_OP* op_attribute_static = SPVM_OP_new_op_attribute(compiler, SPVM_ATTRIBUTE_C_ID_STATIC, op_decl->file, op_decl->line);
          SPVM_OP_insert_child(compiler, op_list_attributes, op_list_attributes->first, op_attribute_static);
          
          SPVM_OP* op_return_type_tmp = NULL;
          if (class_var->op_setter) {
            op_return_type_tmp = SPVM_OP_sibling(compiler, class_var->op_setter->first);
            if (!(op_return_type_tmp->id == SPVM_OP_C_ID_DO_NOTHING)) {
              SPVM_COMPILER_error(compiler, "A return type cannot be specified for a class variable setter.\n  at %s line %d", op_decl->file, op_decl->line);
            }
          }
          
          SPVM_OP* op_block = NULL;
          if (class_var->op_setter) {
            SPVM_OP* op_block_tmp = class_var->op_setter->last;
            if (!(op_block_tmp->id == SPVM_OP_C_ID_DO_NOTHING)) {
              op_block = op_block_tmp;
            }
          }
          
          if (op_block) {
            op_block = class_var->op_setter->last;
            assert(op_block);
            
            SPVM_OP* op_arg = class_var->op_setter->first;
            if (!(op_arg->id == SPVM_OP_C_ID_DO_NOTHING)) {
              SPVM_OP_cut_op(compiler, op_arg);
              op_args = SPVM_OP_new_op_list(compiler, op_decl->file, op_decl->line);
              SPVM_OP_insert_child(compiler, op_args, op_args->last, op_arg);
            }
          }
          else {
            op_block = SPVM_OP_new_op_block(compiler, op_decl->file, op_decl->line);
            SPVM_OP* op_statements = SPVM_OP_new_op_list(compiler, op_decl->file, op_decl->line);
            
            SPVM_OP* op_name_basic_type_var = SPVM_OP_new_op_name(compiler, class_var->name, op_decl->file, op_decl->line);
            SPVM_OP* op_var_class_var = SPVM_OP_new_op_var(compiler, op_name_basic_type_var);
            
            SPVM_OP* op_var_assign_value_name = SPVM_OP_new_op_name(compiler, "$_", op_decl->file, op_decl->line);
            SPVM_OP* op_var_assign_value = SPVM_OP_new_op_var(compiler, op_var_assign_value_name);
            
            SPVM_OP* op_type_cast = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_TYPE_CAST, op_decl->file, op_decl->line);
            SPVM_OP* op_type_for_cast = SPVM_OP_new_op_type(compiler, class_var_type->unresolved_basic_type_name, class_var_type->basic_type, class_var_type->dimension, class_var_type->flag, op_decl->file, op_decl->line);
            
            SPVM_OP_build_type_cast(compiler, op_type_cast, op_type_for_cast, op_var_assign_value);
            
            SPVM_OP* op_assign = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_decl->file, op_decl->line);
            SPVM_OP_build_assign(compiler, op_assign, op_var_class_var, op_type_cast);
            
            SPVM_OP_insert_child(compiler, op_statements, op_statements->last, op_assign);
            SPVM_OP_insert_child(compiler, op_block, op_block->last, op_statements);
          }
          
          op_method = SPVM_OP_build_method(compiler, op_method, op_name_method, op_return_type, op_args, op_list_attributes, op_block);
          
          SPVM_LIST_push(type->basic_type->methods, op_method->uv.method);
        }
        
        if (!class_var->is_virtual) {
          SPVM_LIST_push(type->basic_type->class_vars, op_decl->uv.class_var);
        }
      }
      // Field declarations
      else if (op_decl->id == SPVM_OP_C_ID_FIELD) {
        SPVM_FIELD* field = op_decl->uv.field;
        
        if (type->basic_type->category == SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_INTERFACE) {
          SPVM_COMPILER_error(compiler, "An interface cannnot have fields.\n  at %s line %d", op_decl->file, op_decl->line);
        }
        
        // Getter
        if (field->has_getter) {
          // method foo : TYPE () {
          //   return $self->{foo};
          // }
          
          SPVM_OP* op_method = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_METHOD, op_decl->file, op_decl->line);
          SPVM_OP* op_name_method = SPVM_OP_new_op_name(compiler, field->name, op_decl->file, op_decl->line);
          
          // If the type of the field is byte or short, the return type becomes int
          SPVM_TYPE* field_type = field->type;
          SPVM_TYPE* return_type;
          if (SPVM_TYPE_is_byte_type(compiler, field_type->basic_type->id, field_type->dimension, field_type->flag)
            || SPVM_TYPE_is_short_type(compiler, field_type->basic_type->id, field_type->dimension, field_type->flag))
          {
            return_type = SPVM_OP_new_op_int_type(compiler, op_decl->file, op_decl->line)->uv.type;
          }
          else {
            return_type = field->type;
          }
          
          SPVM_OP* op_return_type = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_TYPE, op_decl->file, op_decl->line);
          op_return_type->uv.type = return_type;
          
          SPVM_OP* op_args = SPVM_OP_new_op_list(compiler, op_decl->file, op_decl->line);
          
          SPVM_OP* op_block = NULL;
          if (field->op_getter) {
            SPVM_OP* op_block_tmp = field->op_getter->last;
            if (!(op_block_tmp->id == SPVM_OP_C_ID_DO_NOTHING)) {
              op_block = op_block_tmp;
            }
          }
          
          if (op_block) {
            // Do nothing
          }
          else {
            op_block = SPVM_OP_new_op_block(compiler, op_decl->file, op_decl->line);
            SPVM_OP* op_statements = SPVM_OP_new_op_list(compiler, op_decl->file, op_decl->line);
            SPVM_OP* op_return = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_RETURN, op_decl->file, op_decl->line);
            
            SPVM_OP* op_var_name_invocant = SPVM_OP_new_op_name(compiler, "$self", op_decl->file, op_decl->line);
            SPVM_OP* op_var_self_invocant = SPVM_OP_new_op_var(compiler, op_var_name_invocant);
            SPVM_OP* op_name_field_access = SPVM_OP_new_op_name(compiler, field->name, op_decl->file, op_decl->line);
            
            SPVM_OP* op_field_access = SPVM_OP_new_op_field_access(compiler, op_decl->file, op_decl->line);
            
            SPVM_OP_build_field_access(compiler, op_field_access, op_var_self_invocant, op_name_field_access);
            
            SPVM_OP_insert_child(compiler, op_return, op_return->last, op_field_access);
            SPVM_OP_insert_child(compiler, op_statements, op_statements->last, op_return);
            SPVM_OP_insert_child(compiler, op_block, op_block->last, op_statements);
          }
          
          SPVM_OP_build_method(compiler, op_method, op_name_method, op_return_type, op_args, NULL, op_block);
          
          SPVM_LIST_push(type->basic_type->methods, op_method->uv.method);
        }
        
        // Setter
        if (field->has_setter) {
          // method set_foo : void ($foo : TYPE) {
          //   $self->{foo} = $foo;
          // }
          
          SPVM_OP* op_method = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_METHOD, op_decl->file, op_decl->line);
          char* method_name_tmp = SPVM_ALLOCATOR_alloc_memory_block_permanent(compiler->current_each_compile_allocator, 4 + strlen(field->name) + 1);
          memcpy(method_name_tmp, "set_", 4);
          memcpy(method_name_tmp + 4, field->name, strlen(field->name));
          SPVM_STRING* method_name_string = SPVM_STRING_new(compiler, method_name_tmp, strlen(method_name_tmp));
          const char* method_name = method_name_string->value;
          SPVM_OP* op_name_method = SPVM_OP_new_op_name(compiler, method_name, op_decl->file, op_decl->line);
          SPVM_OP* op_return_type = SPVM_OP_new_op_void_type(compiler, op_decl->file, op_decl->line);
          SPVM_OP* op_args = SPVM_OP_new_op_list(compiler, op_decl->file, op_decl->line);
          
          // If the type of the field is byte or short, the arg type becomes int
          SPVM_TYPE* field_type = field->type;
          SPVM_TYPE* arg_type;
          if (SPVM_TYPE_is_byte_type(compiler, field_type->basic_type->id, field_type->dimension, field_type->flag)
            || SPVM_TYPE_is_short_type(compiler, field_type->basic_type->id, field_type->dimension, field_type->flag))
          {
            arg_type = SPVM_OP_new_op_int_type(compiler, op_decl->file, op_decl->line)->uv.type;
          }
          else {
            arg_type = field->type;
          }
          SPVM_OP* op_type_value = SPVM_OP_new_op_type(compiler, arg_type->unresolved_basic_type_name, arg_type->basic_type, arg_type->dimension, arg_type->flag, op_decl->file, op_decl->line);
          
          const char* arg_name = "$_";
          SPVM_STRING* arg_name_string = SPVM_STRING_new(compiler, arg_name, strlen(arg_name));
          SPVM_OP* op_name_arg = SPVM_OP_new_op_name(compiler, arg_name, op_decl->file, op_decl->line);
          
          SPVM_OP* op_var_value_name = SPVM_OP_new_op_name(compiler, arg_name, op_decl->file, op_decl->line);
          SPVM_OP* op_var_value = SPVM_OP_new_op_var(compiler, op_var_value_name);
          SPVM_OP* op_arg_value = SPVM_OP_build_arg(compiler, op_var_value, op_type_value, NULL, NULL);
          
          SPVM_OP_insert_child(compiler, op_args, op_args->last, op_arg_value);
          
          SPVM_OP* op_return_type_tmp = NULL;
          if (field->op_setter) {
            op_return_type_tmp = SPVM_OP_sibling(compiler, field->op_setter->first);
            if (!(op_return_type_tmp->id == SPVM_OP_C_ID_DO_NOTHING)) {
              op_return_type = op_return_type_tmp;
            }
          }
          
          SPVM_OP* op_block = NULL;
          if (field->op_setter) {
            SPVM_OP* op_block_tmp = field->op_setter->last;
            if (!(op_block_tmp->id == SPVM_OP_C_ID_DO_NOTHING)) {
              op_block = op_block_tmp;
            }
          }
          
          if (op_block) {
            SPVM_OP* op_arg = field->op_setter->first;
            if (!(op_arg->id == SPVM_OP_C_ID_DO_NOTHING)) {
              SPVM_OP_cut_op(compiler, op_arg);
              op_args = SPVM_OP_new_op_list(compiler, op_decl->file, op_decl->line);
              SPVM_OP_insert_child(compiler, op_args, op_args->last, op_arg);
            }
          }
          else {
            op_block = SPVM_OP_new_op_block(compiler, op_decl->file, op_decl->line);
            SPVM_OP* op_statements = SPVM_OP_new_op_list(compiler, op_decl->file, op_decl->line);
            
            SPVM_OP* op_var_name_invocant = SPVM_OP_new_op_name(compiler, "$self", op_decl->file, op_decl->line);
            SPVM_OP* op_var_self_invocant = SPVM_OP_new_op_var(compiler, op_var_name_invocant);
            SPVM_OP* op_name_field_access = SPVM_OP_new_op_name(compiler, field->name, op_decl->file, op_decl->line);
            SPVM_OP* op_field_access = SPVM_OP_new_op_field_access(compiler, op_decl->file, op_decl->line);
            SPVM_OP_build_field_access(compiler, op_field_access, op_var_self_invocant, op_name_field_access);
            
            SPVM_OP* op_var_assign_value_name = SPVM_OP_new_op_name(compiler, arg_name, op_decl->file, op_decl->line);
            SPVM_OP* op_var_assign_value = SPVM_OP_new_op_var(compiler, op_var_assign_value_name);
            
            SPVM_OP* op_type_cast = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_TYPE_CAST, op_decl->file, op_decl->line);
            SPVM_OP* op_type_for_cast = SPVM_OP_new_op_type(compiler, field_type->unresolved_basic_type_name, field_type->basic_type, field_type->dimension, field_type->flag, op_decl->file, op_decl->line);
            
            SPVM_OP_build_type_cast(compiler, op_type_cast, op_type_for_cast, op_var_assign_value);
            
            SPVM_OP* op_assign = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_decl->file, op_decl->line);
            SPVM_OP_build_assign(compiler, op_assign, op_field_access, op_type_cast);
            
            SPVM_OP_insert_child(compiler, op_statements, op_statements->last, op_assign);
            
            if (op_return_type_tmp) {
              SPVM_OP* op_var_self_invocant_for_return_statement = SPVM_OP_clone_op_var(compiler, op_var_self_invocant);
              SPVM_OP* op_return_satement = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_RETURN, op_decl->file, op_decl->line);
              op_return_satement = SPVM_OP_build_return(compiler, op_return_satement, op_var_self_invocant_for_return_statement);
              SPVM_OP_insert_child(compiler, op_statements, op_statements->last, op_return_satement);
            }
            
            SPVM_OP_insert_child(compiler, op_block, op_block->last, op_statements);
          }
          
          SPVM_OP_build_method(compiler, op_method, op_name_method, op_return_type, op_args, NULL, op_block);
          
          SPVM_LIST_push(type->basic_type->methods, op_method->uv.method);
        }
        
        if (!field->is_virtual) {
          SPVM_LIST_push(type->basic_type->original_fields, field);
        }
      }
      // Enumeration definition
      else if (op_decl->id == SPVM_OP_C_ID_ENUM) {
        SPVM_OP* op_enum_block = op_decl->first;
        SPVM_OP* op_enumeration_items = op_enum_block->first;
        SPVM_OP* op_method = op_enumeration_items->first;
        while ((op_method = SPVM_OP_sibling(compiler, op_method))) {
          SPVM_LIST_push(type->basic_type->methods, op_method->uv.method);
        }
      }
      // Method definition
      else if (op_decl->id == SPVM_OP_C_ID_METHOD) {
        SPVM_METHOD* method = op_decl->uv.method;
        
        SPVM_LIST_push(type->basic_type->methods, op_decl->uv.method);
        
        // Fields of anon method
        SPVM_LIST* anon_method_fields = op_decl->uv.method->anon_method_fields;
        for (int32_t i = 0; i < anon_method_fields->length; i++) {
          SPVM_FIELD* anon_method_field = SPVM_LIST_get(anon_method_fields, i);
          
          SPVM_LIST_push(type->basic_type->original_fields, anon_method_field);
          anon_method_field->is_anon_method_field = 1;
        }
      }
      // INIT block
      else if (op_decl->id == SPVM_OP_C_ID_INIT) {
        SPVM_OP* op_init = op_decl;
        
        SPVM_LIST_push(type->basic_type->op_inits, op_init);
      }
      // END block
      else if (op_decl->id == SPVM_OP_C_ID_END) {
        SPVM_OP* op_end = op_decl;
        
        SPVM_LIST_push(type->basic_type->op_ends, op_end);
      }
      else {
        assert(0);
      }
      
    }
    
  }
  
  // Field declarations
  for (int32_t i = 0; i < type->basic_type->original_fields->length; i++) {
    SPVM_FIELD* field = SPVM_LIST_get(type->basic_type->original_fields, i);
    
    // The default of the access controll of the field is private.
    if (field->access_control_type == SPVM_ATTRIBUTE_C_ID_UNKNOWN) {
      // If anon method, field is public
      if (basic_type->is_generated_from_anon_method) {
        field->access_control_type = SPVM_ATTRIBUTE_C_ID_PUBLIC;
      }
      // If multi-numeric type, field is public
      else if (type->basic_type->category == SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_MULNUM) {
        field->access_control_type = SPVM_ATTRIBUTE_C_ID_PUBLIC;
      }
      // Default is protected
      else {
        field->access_control_type = SPVM_ATTRIBUTE_C_ID_PROTECTED;
      }
    }
    
    field->index = i;
    const char* field_name = field->op_name->uv.name;
    
    SPVM_FIELD* found_field = SPVM_HASH_get(type->basic_type->original_field_symtable, field_name, strlen(field_name));
    
    if (found_field) {
      SPVM_COMPILER_error(compiler, "Redeclaration of %s#%s field.\n  at %s line %d", basic_type_name, field_name, field->op_field->file, field->op_field->line);
    }
    else {
      SPVM_HASH_set(type->basic_type->original_field_symtable, field_name, strlen(field_name), field);
      
      // Add op class
      field->current_basic_type = type->basic_type;
    }
  }
  
  // Class variable declarations
  for (int32_t i = 0; i < type->basic_type->class_vars->length; i++) {
    SPVM_CLASS_VAR* class_var = SPVM_LIST_get(type->basic_type->class_vars, i);
    const char* class_var_name = class_var->name;
    
    SPVM_CLASS_VAR* found_class_var = SPVM_HASH_get(type->basic_type->class_var_symtable, class_var_name, strlen(class_var_name));
    
    if (found_class_var) {
      SPVM_COMPILER_error(compiler, "Redeclaration of %s#%s class variable.\n  at %s line %d", basic_type_name, class_var_name, class_var->op_class_var->file, class_var->op_class_var->line);
    }
    else {
      SPVM_HASH_set(type->basic_type->class_var_symtable, class_var_name, strlen(class_var_name), class_var);
      
      // Add op class
      class_var->current_basic_type = type->basic_type;
    }
  }
  
  // INIT blocks
  if (SPVM_TYPE_is_user_defined_type(compiler, type->basic_type->id, type->dimension, type->flag)) {
    
    SPVM_OP* op_merged_block = SPVM_OP_new_op_block(compiler, op_class->file, op_class->line);
    op_merged_block->uv.block->id = SPVM_BLOCK_C_ID_INIT_BLOCK;
    
    SPVM_OP* op_list_statements = SPVM_OP_new_op_list(compiler, op_class->file, op_class->line);
    SPVM_OP_insert_child(compiler, op_merged_block, op_merged_block->last, op_list_statements);
    
    for (int32_t i = 0; i < type->basic_type->op_inits->length; i++) {
      SPVM_OP* op_init = SPVM_LIST_get(type->basic_type->op_inits, i);
      
      SPVM_OP* op_block = op_init->first;
      
      SPVM_OP_cut_op(compiler, op_init->first);
      
      SPVM_OP_insert_child(compiler, op_list_statements, op_list_statements->last, op_block);
    }
    
    SPVM_OP* op_method = SPVM_OP_build_init_method(compiler, NULL, op_merged_block);
    
    SPVM_LIST_push(type->basic_type->methods, op_method->uv.method);
  }
  
  // END blocks
  if (SPVM_TYPE_is_user_defined_type(compiler, type->basic_type->id, type->dimension, type->flag)) {
    
    SPVM_OP* op_merged_block = SPVM_OP_new_op_block(compiler, op_class->file, op_class->line);
    op_merged_block->uv.block->id = SPVM_BLOCK_C_ID_END_BLOCK;
    
    SPVM_OP* op_list_statements = SPVM_OP_new_op_list(compiler, op_class->file, op_class->line);
    SPVM_OP_insert_child(compiler, op_merged_block, op_merged_block->last, op_list_statements);
    
    // Process END blocks in reverse order (LIFO) to match Perl's behavior
    for (int32_t i = type->basic_type->op_ends->length - 1; i >= 0; i--) {
      SPVM_OP* op_end = SPVM_LIST_get(type->basic_type->op_ends, i);
      
      SPVM_OP* op_block = op_end->first;
      
      // Detach the block from the original END OP
      SPVM_OP_cut_op(compiler, op_end->first);
      
      // Insert the block into the merged statement list
      SPVM_OP_insert_child(compiler, op_list_statements, op_list_statements->last, op_block);
    }
    
    SPVM_OP* op_method = SPVM_OP_build_end_method(compiler, NULL, op_merged_block);
    
    SPVM_LIST_push(type->basic_type->methods, op_method->uv.method);
  }
  
  // Method declarations
  for (int32_t i = 0; i < type->basic_type->methods->length; i++) {
    SPVM_METHOD* method = SPVM_LIST_get(type->basic_type->methods, i);
    
    if (!method->is_class_method) {
      SPVM_VAR_DECL* arg_var_decl_first = SPVM_LIST_get(method->var_decls, 0);
      arg_var_decl_first->type->unresolved_basic_type_name = basic_type_name;
    }
    
    SPVM_OP* op_name_method = method->op_name;
    const char* method_name = op_name_method->uv.name;
    
    int32_t must_have_block;
    if (method->is_native) {
      must_have_block = 0;
    }
    else {
      if (type->basic_type->category == SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_INTERFACE) {
        if (method->is_class_method) {
          must_have_block = 1;
        }
        else {
          must_have_block = 0;
        }
      }
      else {
        must_have_block = 1;
      }
    }
    
    if (must_have_block && !method->op_block) {
      SPVM_COMPILER_error(compiler, "A non-native method must have a block.\n  at %s line %d", op_name_method->file, op_name_method->line);
    }
    
    // Method check
    
    // Set first argument type if not set
    if (method->args_length > 0) {
      SPVM_VAR_DECL* arg_var_decl_first = SPVM_LIST_get(method->var_decls, 0);
      SPVM_OP* op_arg_first_type = NULL;
      if (!method->is_class_method) {
        SPVM_TYPE* arg_invocant_type = op_type->uv.type;
        op_arg_first_type = SPVM_OP_new_op_type(compiler, arg_invocant_type->unresolved_basic_type_name, arg_invocant_type->basic_type, arg_invocant_type->dimension, arg_invocant_type->flag, arg_var_decl_first->op_var_decl->file, arg_var_decl_first->op_var_decl->line);
        arg_var_decl_first->type = op_arg_first_type->uv.type;
        assert(arg_invocant_type->basic_type);
      }
      else {
        SPVM_OP* op_type_new_arg_var_decl_first = SPVM_OP_new_op_type(compiler, arg_var_decl_first->type->unresolved_basic_type_name, arg_var_decl_first->type->basic_type, arg_var_decl_first->type->dimension, arg_var_decl_first->type->flag, arg_var_decl_first->op_var_decl->file, arg_var_decl_first->op_var_decl->line);
        
        op_arg_first_type = op_type_new_arg_var_decl_first;
        assert(op_arg_first_type->uv.type->basic_type);
      }
      
      for (int32_t arg_index = 0; arg_index < method->args_length; arg_index++) {
        SPVM_VAR_DECL* arg_var_decl = SPVM_LIST_get(method->var_decls, arg_index);
        SPVM_BASIC_TYPE_add_constant_string(compiler, basic_type, arg_var_decl->var->name, strlen(arg_var_decl->var->name));
        arg_var_decl->name = arg_var_decl->var->name;
      }
    }
    
    // If Method is anon, method must be method
    if (strlen(method_name) == 0 && method->is_class_method) {
      SPVM_COMPILER_error(compiler, "An anon method must be an instance method.\n  at %s line %d", method->op_method->file, method->op_method->line);
    }
    
    if (type->basic_type->category == SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_CLASS) {
      if (method->is_required) {
        SPVM_COMPILER_error(compiler, "%s#%s method cannnot have the method attribute 'required'.\n  at %s line %d", basic_type_name, method->name, method->op_method->file, method->op_method->line);
      }
    }
    
    SPVM_METHOD* found_method = SPVM_HASH_get(type->basic_type->method_symtable, method_name, strlen(method_name));
    
    if (found_method) {
      SPVM_COMPILER_error(compiler, "Redeclaration of %s#%s method.\n  at %s line %d", basic_type_name, method_name, method->op_method->file, method->op_method->line);
    }
    // Unknown method
    else {
      const char* found_method_name = SPVM_HASH_get(type->basic_type->method_symtable, method_name, strlen(method_name));
      if (found_method_name) {
        SPVM_COMPILER_error(compiler, "Redeclaration of the '%s' method.\n  at %s line %d", method_name, method->op_method->file, method->op_method->line);
      }
      else {
        // Bind standard functions
        method->current_basic_type = type->basic_type;
        
        if (method->is_init_method) {
          basic_type->init_method = method;
        }
        
        if (method->is_end_method) {
          basic_type->end_method = method;
        }
        
        assert(method->op_method->file);
        
        // Method absolute name
        // Foo#bar
        int32_t method_abs_name_length = strlen(type->unresolved_basic_type_name) + 1 + strlen(method->name);
        char* method_abs_name = SPVM_ALLOCATOR_alloc_memory_block_permanent(compiler->current_each_compile_allocator, method_abs_name_length + 1);
        memcpy(method_abs_name, type->unresolved_basic_type_name, strlen(type->unresolved_basic_type_name));
        memcpy(method_abs_name + strlen(basic_type_name), "#", 1);
        memcpy(method_abs_name + strlen(basic_type_name) + 1, method_name, strlen(method_name));
        method->abs_name = method_abs_name;
        SPVM_BASIC_TYPE_add_constant_string(compiler, type->basic_type, method->abs_name, strlen(method->abs_name));
        
        // Add the method to the method symtable of the class
        SPVM_HASH_set(type->basic_type->method_symtable, method->name, strlen(method->name), method);
      }
    }
  }
  
  // mulnum_t
  if (type->basic_type->category == SPVM_NATIVE_C_BASIC_TYPE_CATEGORY_MULNUM) {
    for (int32_t i = 0; i < type->basic_type->methods->length; i++) {
      SPVM_METHOD* method = SPVM_LIST_get(type->basic_type->methods, i);
      
      if (!method->is_class_method) {
        SPVM_COMPILER_error(compiler, "A multi-numeric type cannnot have instance methods.\n  at %s line %d", method->op_method->file, method->op_method->line);
      }
    }
    
    if (type->basic_type->class_vars->length > 0) {
      SPVM_COMPILER_error(compiler, "A multi-numeric type cannnot have class variables.\n  at %s line %d", op_class->file, op_class->line);
    }
    if (type->basic_type->original_fields->length == 0) {
      SPVM_COMPILER_error(compiler, "A multi-numeric type must have at least one field.\n  at %s line %d", op_class->file, op_class->line);
    }
    else if (type->basic_type->original_fields->length > 255) {
      SPVM_COMPILER_error(compiler, "The length of the fields defined in the multi-numeric type must be less than or equal to 255.\n  at %s line %d", op_class->file, op_class->line);
    }
  }
  
  // Resolve type aliases in the class scope
  SPVM_LIST* op_types_for_alias_resolution = compiler->current_op_types_for_alias_resolution;
  SPVM_HASH* alias_symtable = type->basic_type->alias_symtable;
  for (int32_t i = 0; i < op_types_for_alias_resolution->length; i++) {
    SPVM_OP* op_type = SPVM_LIST_get(op_types_for_alias_resolution, i);
    SPVM_TYPE* type = op_type->uv.type;
    
    const char* unresolved_basic_type_name_maybe_alias = op_type->uv.type->unresolved_basic_type_name;
    
    SPVM_HASH* alias_symtable = NULL;
    if (basic_type->is_generated_from_anon_method && basic_type->outmost) {
      alias_symtable = basic_type->outmost->alias_symtable;
    }
    else {
      alias_symtable = basic_type->alias_symtable;
    }
    
    const char* unresolved_basic_type_name = SPVM_HASH_get(alias_symtable, unresolved_basic_type_name_maybe_alias, strlen(unresolved_basic_type_name_maybe_alias));
    if (unresolved_basic_type_name) {
      op_type->uv.type->unresolved_basic_type_name = unresolved_basic_type_name;
      op_type->uv.type->basic_type = SPVM_LIST_get(compiler->basic_types, 0);
    }
  }
  
  return op_class;
}

SPVM_OP* SPVM_OP_build_extends(SPVM_COMPILER* compiler, SPVM_OP* op_extends, SPVM_OP* op_type_parent_basic_type) {
  
  SPVM_OP_insert_child(compiler, op_extends, op_extends->last, op_type_parent_basic_type);
  
  return op_extends;
}

SPVM_OP* SPVM_OP_build_version_decl(SPVM_COMPILER* compiler, SPVM_OP* op_version_decl, SPVM_OP* op_version_string) {
  
  SPVM_OP_insert_child(compiler, op_version_decl, op_version_decl->last, op_version_string);
  
  return op_version_decl;
}

SPVM_OP* SPVM_OP_build_version_from(SPVM_COMPILER* compiler, SPVM_OP* op_version_from, SPVM_OP* op_type) {
  
  SPVM_VERSION_FROM* version_from = SPVM_VERSION_FROM_new(compiler);
  op_version_from->uv.version_from = version_from;
  version_from->op_version_from = op_version_from;
  version_from->basic_type_name = op_type->uv.type->unresolved_basic_type_name;
  
  // add use stack
  SPVM_OP* op_use = SPVM_OP_new_op_use(compiler, op_version_from->file, op_version_from->line);
  SPVM_OP* op_name_alias = NULL;
  int32_t is_require = 0;
  SPVM_OP_build_use(compiler, op_use, op_type, op_name_alias, is_require);
  
  return op_version_from;
}

SPVM_OP* SPVM_OP_build_allow(SPVM_COMPILER* compiler, SPVM_OP* op_allow, SPVM_OP* op_type) {
  
  SPVM_ALLOW* allow = SPVM_ALLOW_new(compiler);
  op_allow->uv.allow = allow;
  allow->op_allow = op_allow;
  allow->basic_type_name = op_type->uv.type->unresolved_basic_type_name;
  
  // add use stack
  SPVM_OP* op_use = SPVM_OP_new_op_use(compiler, op_allow->file, op_allow->line);
  SPVM_OP* op_name_alias = NULL;
  int32_t is_require = 0;
  SPVM_OP_build_use(compiler, op_use, op_type, op_name_alias, is_require);
  
  return op_allow;
}

SPVM_OP* SPVM_OP_build_alias(SPVM_COMPILER* compiler, SPVM_OP* op_use, SPVM_OP* op_type, SPVM_OP* op_name_alias) {
  
  SPVM_USE* use = op_use->uv.use;
  use->op_use = op_use;
  use->op_type = op_type;
  const char* alias_name = op_name_alias->uv.name;
  use->alias_name = alias_name;
  
  return op_use;
}

SPVM_OP* SPVM_OP_build_use(SPVM_COMPILER* compiler, SPVM_OP* op_use, SPVM_OP* op_type, SPVM_OP* op_name_alias, int32_t is_require) {
  
  SPVM_USE* use = op_use->uv.use;
  use->op_use = op_use;
  use->op_type = op_type;
  use->is_require = is_require;
  
  if (op_name_alias) {
    const char* alias_name = op_name_alias->uv.name;
    use->alias_name = alias_name;
    
    // A alias name cannnot end with "::"
    int32_t alias_name_length = strlen(alias_name);
    if (alias_name_length >= 2 && alias_name[alias_name_length - 2] == ':' && alias_name[alias_name_length - 1] == ':' ) {
      SPVM_COMPILER_error(compiler, "The alias name '%s' cannnot end with \"::\".\n  at %s line %d", alias_name, op_name_alias->file, op_name_alias->line);
    }
  }
  
  SPVM_LIST_push(compiler->op_use_stack, op_use);
  
  return op_use;
}

SPVM_OP* SPVM_OP_build_enumeration(SPVM_COMPILER* compiler, SPVM_OP* op_enumeration, SPVM_OP* op_enumeration_block, SPVM_OP* op_attributes) {
  
  SPVM_OP_insert_child(compiler, op_enumeration, op_enumeration->last, op_enumeration_block);
  
  SPVM_OP* op_enumeration_items = op_enumeration_block->first;
  SPVM_OP* op_method = op_enumeration_items->first;
  while ((op_method = SPVM_OP_sibling(compiler, op_method))) {
    SPVM_METHOD* method = op_method->uv.method;

    // Enumeration attributes
    int32_t access_control_attributes_count = 0;
    if (op_attributes) {
      SPVM_OP* op_attribute = op_attributes->first;
      while ((op_attribute = SPVM_OP_sibling(compiler, op_attribute))) {
        SPVM_ATTRIBUTE* attribute = op_attribute->uv.attribute;
        
        switch (attribute->id) {
          case SPVM_ATTRIBUTE_C_ID_PRIVATE: {
            method->access_control_type = SPVM_ATTRIBUTE_C_ID_PRIVATE;
            access_control_attributes_count++;
            break;
          }
          case SPVM_ATTRIBUTE_C_ID_PROTECTED: {
            method->access_control_type = SPVM_ATTRIBUTE_C_ID_PROTECTED;
            access_control_attributes_count++;
            break;
          }
          case SPVM_ATTRIBUTE_C_ID_PUBLIC: {
            method->access_control_type = SPVM_ATTRIBUTE_C_ID_PUBLIC;
            access_control_attributes_count++;
            break;
          }
          default: {
            SPVM_COMPILER_error(compiler, "Invalid enumeration attribute '%s'.\n  at %s line %d", SPVM_ATTRIBUTE_get_name(compiler, attribute->id), op_attributes->file, op_attributes->line);
          }
        }
      }
      if (access_control_attributes_count > 1) {
        SPVM_COMPILER_error(compiler, "Only one of enumeration attributes 'private', 'protected' or 'public' can be specified.\n  at %s line %d", op_method->file, op_method->line);
      }
    }
    
    // The default of the access controll of the enumeration is public.
    if (method->access_control_type == SPVM_ATTRIBUTE_C_ID_UNKNOWN) {
      method->access_control_type = SPVM_ATTRIBUTE_C_ID_PUBLIC;
    }
  }
  
  // Reset enum information
  compiler->current_enum_value = 0;
  
  return op_enumeration;
}

SPVM_OP* SPVM_OP_build_enumeration_item(SPVM_COMPILER* compiler, SPVM_OP* op_name, SPVM_OP* op_constant) {
  
  if (op_constant) {
    
    SPVM_CONSTANT* constant = op_constant->uv.constant;
    
    if (constant->type->dimension == 0 && constant->type->basic_type->id == SPVM_NATIVE_C_BASIC_TYPE_ID_INT) {
      compiler->current_enum_value = constant->value.ival;
    }
    else {
      SPVM_COMPILER_error(compiler, "The value of the enumeration must be int type.\n  at %s line %d", op_constant->file, op_constant->line);
    }
    
    compiler->current_enum_value++;
  }
  else {
    op_constant = SPVM_OP_new_op_constant_int(compiler, (int32_t)compiler->current_enum_value, op_name->file, op_name->line);
    
    compiler->current_enum_value++;
  }
  
  // Return
  SPVM_OP* op_return = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_RETURN, op_name->file, op_name->line);
  SPVM_OP_insert_child(compiler, op_return, op_return->last, op_constant);
  
  // Statements
  SPVM_OP* op_list_statements = SPVM_OP_new_op_list(compiler, op_name->file, op_name->line);
  SPVM_OP_insert_child(compiler, op_list_statements, op_list_statements->last, op_return);

  // Block
  SPVM_OP* op_block = SPVM_OP_new_op_block(compiler, op_name->file, op_name->line);
  SPVM_OP_insert_child(compiler, op_block, op_block->last, op_list_statements);
  
  // method
  SPVM_OP* op_method = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_METHOD, op_name->file, op_name->line);
  op_method->file = op_name->file;
  op_method->line = op_name->line;
  
  // Type
  SPVM_TYPE* return_type = op_constant->uv.constant->type;
  SPVM_OP* op_return_type = SPVM_OP_new_op_type(compiler, return_type->unresolved_basic_type_name, return_type->basic_type, return_type->dimension, return_type->flag, op_name->file, op_name->line);
  
  SPVM_OP* op_list_attributes = SPVM_OP_new_op_list(compiler, op_name->file, op_name->line);
  SPVM_OP* op_attribute_static = SPVM_OP_new_op_attribute(compiler, SPVM_ATTRIBUTE_C_ID_STATIC, op_name->file, op_name->line);
  SPVM_OP_insert_child(compiler, op_list_attributes, op_list_attributes->first, op_attribute_static);
  
  // Build method
  op_method = SPVM_OP_build_method(compiler, op_method, op_name, op_return_type, NULL, op_list_attributes, op_block);
  
  // Set constant
  op_method->uv.method->enum_value = op_constant->uv.constant->value.ival;
  
  // Method is enumeration
  op_method->uv.method->is_enum = 1;
  
  return op_method;
}

SPVM_OP* SPVM_OP_build_class_var(SPVM_COMPILER* compiler, SPVM_OP* op_class_var, SPVM_OP* op_name, SPVM_OP* op_attributes, SPVM_OP* op_type, SPVM_OP* op_getter, SPVM_OP* op_setter) {
  
  SPVM_CLASS_VAR* class_var = SPVM_CLASS_VAR_new(compiler);
  
  const char* name = op_name->uv.name;
  class_var->name = op_name->uv.name;
  
  if (strstr(name, "::")) {
    SPVM_COMPILER_error(compiler, "The class varaible name '%s' cannnot contain \"::\".\n  at %s line %d", class_var->name, op_name->file, op_name->line);
  }
  
  // A class variable name cannot start with '^' (The first character is '$')
  if (name[1] == '^') {
    SPVM_COMPILER_error(compiler, "The class variable name '%s' cannot start with '^'.\n  at %s line %d", class_var->name, op_name->file, op_name->line);
  }
  
  class_var->op_name = op_name;
  class_var->type = op_type->uv.type;
  class_var->op_class_var = op_class_var;

  op_class_var->uv.class_var = class_var;

  // Class variable attributes
  if (op_attributes) {
    int32_t field_method_attributes_count = 0;
    int32_t access_control_attributes_count = 0;
    SPVM_OP* op_attribute = op_attributes->first;
    while ((op_attribute = SPVM_OP_sibling(compiler, op_attribute))) {
      SPVM_ATTRIBUTE* attribute = op_attribute->uv.attribute;
      
      switch (attribute->id) {
        case SPVM_ATTRIBUTE_C_ID_PRIVATE: {
          class_var->access_control_type = SPVM_ATTRIBUTE_C_ID_PRIVATE;
          access_control_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_PROTECTED: {
          class_var->access_control_type = SPVM_ATTRIBUTE_C_ID_PROTECTED;
          access_control_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_PUBLIC: {
          class_var->access_control_type = SPVM_ATTRIBUTE_C_ID_PUBLIC;
          access_control_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_RW: {
          class_var->has_setter = 1;
          class_var->has_getter = 1;
          field_method_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_RO: {
          class_var->has_getter = 1;
          field_method_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_WO: {
          class_var->has_setter = 1;
          field_method_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_CACHE: {
          class_var->is_cache = 1;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_VIRTUAL: {
          class_var->is_virtual = 1;
          break;
        }
        default: {
          SPVM_COMPILER_error(compiler, "Invalid class variable attribute '%s'.\n  at %s line %d", SPVM_ATTRIBUTE_get_name(compiler, attribute->id), op_attributes->file, op_attributes->line);
        }
      }
      if (field_method_attributes_count > 1) {
        SPVM_COMPILER_error(compiler, "Only one of class variable attributes 'rw', 'ro', 'wo' can be specifed.\n  at %s line %d", op_class_var->file, op_class_var->line);
      }
      if (access_control_attributes_count > 1) {
        SPVM_COMPILER_error(compiler, "Only one of class variable attributes 'private', 'protected' or 'public' can be specified.\n  at %s line %d", op_class_var->file, op_class_var->line);
      }
    }
  }
  
  if (!class_var->has_getter && op_getter) {
    SPVM_COMPILER_error(compiler, "A class variable attribute of either 'ro' or 'rw' must be specified when the getter is defined.\n  at %s line %d", op_class_var->file, op_class_var->line);
  }
  
  class_var->op_getter = op_getter;
  
  if (!class_var->has_setter && op_setter) {
    SPVM_COMPILER_error(compiler, "A class variable attribute of either 'wo' or 'rw' must be specified when the setter is defined.\n  at %s line %d", op_class_var->file, op_class_var->line);
  }
  
  class_var->op_setter = op_setter;
  
  // The default of the access controll of the class variable is private.
  if (class_var->access_control_type == SPVM_ATTRIBUTE_C_ID_UNKNOWN) {
    class_var->access_control_type = SPVM_ATTRIBUTE_C_ID_PROTECTED;
  }
  
  return op_class_var;
}

SPVM_OP* SPVM_OP_build_field(SPVM_COMPILER* compiler, SPVM_OP* op_field, SPVM_OP* op_name_field, SPVM_OP* op_attributes, SPVM_OP* op_type, SPVM_OP* op_getter, SPVM_OP* op_setter) {

  // Create field information
  SPVM_FIELD* field = SPVM_FIELD_new(compiler);

  // Field Name
  field->op_name = op_name_field;
  field->name = op_name_field->uv.name;

  if (strstr(field->op_name->uv.name, "::")) {
    SPVM_COMPILER_error(compiler, "The field name '%s' cannnot contain \"::\".\n  at %s line %d", field->name, op_name_field->file, op_name_field->line);
  }

  // Type
  field->type = op_type->uv.type;
  
  // Set field informaiton
  op_field->uv.field = field;

  // Field attributes
  if (op_attributes) {
    SPVM_OP* op_attribute = op_attributes->first;
    int32_t field_method_attributes_count = 0;
    int32_t access_control_attributes_count = 0;
    while ((op_attribute = SPVM_OP_sibling(compiler, op_attribute))) {
      SPVM_ATTRIBUTE* attribute = op_attribute->uv.attribute;
      
      switch (attribute->id) {
        case SPVM_ATTRIBUTE_C_ID_PRIVATE: {
          field->access_control_type = SPVM_ATTRIBUTE_C_ID_PRIVATE;
          access_control_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_PROTECTED: {
          field->access_control_type = SPVM_ATTRIBUTE_C_ID_PROTECTED;
          access_control_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_PUBLIC: {
          field->access_control_type = SPVM_ATTRIBUTE_C_ID_PUBLIC;
          access_control_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_RW: {
          field->has_setter = 1;
          field->has_getter = 1;
          field_method_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_RO: {
          field->has_getter = 1;
          field_method_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_WO: {
          field->has_setter = 1;
          field_method_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_VIRTUAL: {
          field->is_virtual = 1;
          break;
        }
        default: {
          SPVM_COMPILER_error(compiler, "Invalid field attribute '%s'.\n  at %s line %d", SPVM_ATTRIBUTE_get_name(compiler, attribute->id), op_attributes->file, op_attributes->line);
        }
      }
      
      if (field_method_attributes_count > 1) {
        SPVM_COMPILER_error(compiler, "Only one of field attributes 'rw', 'ro' or 'wo' can be specifed.\n  at %s line %d", op_field->file, op_field->line);
      }
      
      if (access_control_attributes_count > 1) {
        SPVM_COMPILER_error(compiler, "Only one of field attributes 'private', 'protected' or 'public' can be specified.\n  at %s line %d", op_field->file, op_field->line);
      }
    }
  }
  
      
  if (!field->has_getter && op_getter) {
    SPVM_COMPILER_error(compiler, "A field attribute of either 'ro' or 'rw' must be specified when the getter is defined.\n  at %s line %d", op_field->file, op_field->line);
  }
  
  field->op_getter = op_getter;
  
  if (!field->has_setter && op_setter) {
    SPVM_COMPILER_error(compiler, "A field attribute of either 'wo' or 'rw' must be specified when the setter is defined.\n  at %s line %d", op_field->file, op_field->line);
  }
  
  field->op_setter = op_setter;
  
  field->op_field = op_field;
  
  return op_field;
}

SPVM_OP* SPVM_OP_build_method(SPVM_COMPILER* compiler, SPVM_OP* op_method, SPVM_OP* op_name_method, SPVM_OP* op_return_type, SPVM_OP* op_args, SPVM_OP* op_attributes, SPVM_OP* op_block) {
  SPVM_METHOD* method = SPVM_METHOD_new(compiler);
  
  if (op_name_method == NULL) {
    SPVM_STRING* anon_method_name_string = SPVM_STRING_new(compiler, "", strlen(""));
    const char* anon_method_name = anon_method_name_string->value;
    op_name_method = SPVM_OP_new_op_name(compiler, anon_method_name, op_method->file, op_method->line);
  }
  const char* method_name = op_name_method->uv.name;
  if (strstr(method_name, "::")) {
    SPVM_COMPILER_error(compiler, "The method name '%s' cannnot contain \"::\".\n  at %s line %d", method_name, op_name_method->file, op_name_method->line);
  }
  
  // Create method information
  method->op_name = op_name_method;
  
  method->name = method->op_name->uv.name;
  
  if (op_block && op_block->uv.block->id == SPVM_BLOCK_C_ID_INIT_BLOCK) {
    method->is_init_method = 1;
  }
  
  if (op_block && op_block->uv.block->id == SPVM_BLOCK_C_ID_END_BLOCK) {
    method->is_end_method = 1;
  }
  
  // Block is method block
  if (op_block) {
    op_block->uv.block->id = SPVM_BLOCK_C_ID_METHOD;
  }
  
  if (!method->is_init_method && strcmp(method_name, "INIT") == 0) {
    SPVM_COMPILER_error(compiler, "'INIT' cannnot be used as a method name.\n  at %s line %d", op_name_method->file, op_name_method->line);
  }
  
  if (!method->is_end_method && strcmp(method_name, "END") == 0) {
    SPVM_COMPILER_error(compiler, "'END' cannnot be used as a method name.\n  at %s line %d", op_name_method->file, op_name_method->line);
  }
  
  // Method attributes
  int32_t access_control_attributes_count = 0;
  if (op_attributes) {
    SPVM_OP* op_attribute = op_attributes->first;
    while ((op_attribute = SPVM_OP_sibling(compiler, op_attribute))) {
      SPVM_ATTRIBUTE* attribute = op_attribute->uv.attribute;
      
      switch (attribute->id) {
        case SPVM_ATTRIBUTE_C_ID_PRIVATE: {
          method->access_control_type = SPVM_ATTRIBUTE_C_ID_PRIVATE;
          access_control_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_PROTECTED: {
          method->access_control_type = SPVM_ATTRIBUTE_C_ID_PROTECTED;
          access_control_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_PUBLIC: {
          method->access_control_type = SPVM_ATTRIBUTE_C_ID_PUBLIC;
          access_control_attributes_count++;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_PRECOMPILE: {
          method->is_precompile = 1;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_REQUIRED: {
          method->is_required = 1;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_NATIVE: {
          method->is_native = 1;
          break;
        }
        case SPVM_ATTRIBUTE_C_ID_STATIC: {
          method->is_class_method = 1;
          break;
        }
        default: {
          SPVM_COMPILER_error(compiler, "Invalid method attribute '%s'.\n  at %s line %d", SPVM_ATTRIBUTE_get_name(compiler, attribute->id), op_attributes->file, op_attributes->line);
        }
      }
    }
    
    if (method->is_native && method->is_precompile) {
      SPVM_COMPILER_error(compiler, "Only one of method attributes 'native' and 'precompile' can be specified.\n  at %s line %d", op_attributes->file, op_attributes->line);
    }
    if (access_control_attributes_count > 1) {
      SPVM_COMPILER_error(compiler, "Only one of method attributes 'private', 'protected' or 'public' can be specified.\n  at %s line %d", op_method->file, op_method->line);
    }
  }
  
  // The default of the access controll of the method is publice.
  if (method->access_control_type == SPVM_ATTRIBUTE_C_ID_UNKNOWN) {
    method->access_control_type = SPVM_ATTRIBUTE_C_ID_PUBLIC;
  }
  
  // Native method cannnot have block
  if ((method->is_native) && op_block) {
    SPVM_COMPILER_error(compiler, "A native method cannnot have its block.\n  at %s line %d", op_block->file, op_block->line);
  }
  
  // method args
  if (!op_args) {
    op_args = SPVM_OP_new_op_list(compiler, op_method->file, op_method->line);
  }
  
  // Add $self : self before the first argument
  if (!method->is_class_method) {
    SPVM_OP* op_arg_var_name_self = SPVM_OP_new_op_name(compiler, "$self", op_method->file, op_method->line);
    SPVM_OP* op_arg_var_self = SPVM_OP_new_op_var(compiler, op_arg_var_name_self);
    SPVM_OP* op_self_type = SPVM_OP_new_op_unresolved_type(compiler, NULL, 0, 0, op_method->file, op_method->line);
    SPVM_TYPE* self_type = op_self_type->uv.type;
    SPVM_OP* op_arg_self = SPVM_OP_build_arg(compiler, op_arg_var_self, op_self_type, NULL, NULL);
    SPVM_OP_insert_child(compiler, op_args, op_args->first, op_arg_self);
  }
  
  // Add method arguments
  {
    int32_t found_optional_arg = 0;
    int32_t required_args_length = 0;
    int32_t args_length = 0;
    SPVM_OP* op_arg = op_args->first;
    while ((op_arg = SPVM_OP_sibling(compiler, op_arg))) {
      SPVM_VAR_DECL* arg_var_decl = op_arg->uv.var->var_decl;
      if (!found_optional_arg) {
        if (arg_var_decl->op_arg_default) {
          found_optional_arg = 1;
        }
        else {
          required_args_length++;
        }
      }
      args_length++;
    }
    method->args_length = args_length;
    method->required_args_length = required_args_length;
  }
  
  // Variable declarations of arguments
  SPVM_OP* op_arg = op_args->first;
  while ((op_arg = SPVM_OP_sibling(compiler, op_arg))) {
    SPVM_VAR_DECL* arg_var_decl = op_arg->uv.var->var_decl;
    SPVM_TYPE* arg_var_decl_type = arg_var_decl->type;
    if (arg_var_decl_type->flag & SPVM_NATIVE_C_TYPE_FLAG_VARARGS) {
      if (!(method->var_decls->length == method->args_length - 1)) {
        SPVM_COMPILER_error(compiler, "The use of variable length arguments must be the last argument.\n  at %s line %d", op_method->file, op_method->line);
      }
    }
    
    SPVM_LIST_push(method->var_decls, op_arg->uv.var->var_decl);
  }
  
  // return type
  method->return_type = op_return_type->uv.type;
  
  if (strcmp(method->op_name->uv.name, "DESTROY") == 0) {
    method->is_destroy_method = 1;
    
    // DESTROY return type must be void
    if (!(method->return_type->dimension == 0 && method->return_type->basic_type->id == SPVM_NATIVE_C_BASIC_TYPE_ID_VOID)) {
      SPVM_COMPILER_error(compiler, "The return type of DESTROY method must be the void type.\n  at %s line %d", op_method->file, op_method->line);
    }
    
    // DESTROY is instance method
    if (method->is_class_method) {
      SPVM_COMPILER_error(compiler, "DESTROY method must be an instance method.\n  at %s line %d", op_method->file, op_method->line);
    }
    
    // DESTROY doesn't have arguments without invocant
    if (method->args_length != 1) {
      SPVM_COMPILER_error(compiler, "DESTROY method cannnot have arguments.\n  at %s line %d", op_method->file, op_method->line);
    }
  }
  
  SPVM_OP* op_anon_method_field_var_decl_start = NULL;
  SPVM_OP* op_block_outer = NULL;
  if (op_block) {
    op_block_outer = SPVM_OP_new_op_block(compiler, op_method->file, op_method->line);
    SPVM_OP* op_list_statement_outer = SPVM_OP_new_op_list(compiler, op_method->file, op_method->line);
    SPVM_OP_insert_child(compiler, op_block_outer, op_block_outer->last, op_list_statement_outer);
    
    // Add variable declarations before the first of the statements
    for (int32_t i = method->args_length - 1; i >= 0; i--) {
      SPVM_VAR_DECL* arg_var_decl = SPVM_LIST_get(method->var_decls, i);
      assert(arg_var_decl);
      SPVM_OP* op_name_var = SPVM_OP_new_op_name(compiler, arg_var_decl->var->name, arg_var_decl->op_var_decl->file, arg_var_decl->op_var_decl->line);
      SPVM_OP* op_var = SPVM_OP_new_op_var(compiler, op_name_var);
      op_var->uv.var->var_decl = arg_var_decl;
      op_var->uv.var->is_declaration = 1;
      op_var->uv.var->var_decl = arg_var_decl;
      
      SPVM_OP_insert_child(compiler, op_list_statement_outer, op_list_statement_outer->first, op_var);
    }
    
    // Add condition_flag variable to first of block
    {
      SPVM_OP* op_var = SPVM_OP_new_op_var_condition_flag(compiler, op_list_statement_outer->file, op_list_statement_outer->last->line + 1);
      SPVM_OP* op_var_decl = SPVM_OP_new_op_var_decl(compiler, op_list_statement_outer->file, op_list_statement_outer->last->line + 1);
      SPVM_OP* op_type = SPVM_OP_new_op_int_type(compiler, op_list_statement_outer->file, op_list_statement_outer->line);
      op_var = SPVM_OP_build_var_decl(compiler, op_var_decl, op_var, op_type, NULL);
      SPVM_OP_insert_child(compiler, op_list_statement_outer, op_list_statement_outer->first, op_var);
    }
    
    SPVM_OP* op_list_statement = op_block->first;
    
    op_anon_method_field_var_decl_start = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_DO_NOTHING, op_list_statement->file, op_list_statement->last->line + 1);
    
    SPVM_OP_insert_child(compiler, op_list_statement, op_list_statement->first, op_anon_method_field_var_decl_start);
    
    method->op_anon_method_field_var_decl_start = op_anon_method_field_var_decl_start;
    
    // Add return statement after the last of the statements
    {
      SPVM_OP* op_return = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_RETURN, op_list_statement->file, op_list_statement->last->line + 1);
      SPVM_TYPE* return_type = method->return_type;
      if (SPVM_TYPE_is_void_type(compiler, return_type->basic_type->id, return_type->dimension, return_type->flag)) {
        SPVM_OP_insert_child(compiler, op_list_statement, op_list_statement->last, op_return);
      }
      else {
        // Return variable name
        char* name = "$.return";
        SPVM_OP* op_name = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_NAME, op_list_statement->file, op_list_statement->last->line + 1);
        op_name->uv.name = name;
        SPVM_OP* op_var = SPVM_OP_build_var(compiler, op_name);
        SPVM_OP* op_var_decl = SPVM_OP_new_op_var_decl(compiler, op_list_statement->file, op_list_statement->last->line + 1);
        SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, return_type->unresolved_basic_type_name, return_type->basic_type, return_type->dimension, return_type->flag, op_list_statement->file, op_list_statement->last->line + 1);
        
        op_var = SPVM_OP_build_var_decl(compiler, op_var_decl, op_var, op_type, NULL);
        SPVM_OP_insert_child(compiler, op_return, op_return->last, op_var);
        SPVM_OP_insert_child(compiler, op_list_statement, op_list_statement->last, op_return);
      }
    }
    
    SPVM_OP_insert_child(compiler, op_list_statement_outer, op_list_statement_outer->last, op_block);
  }
  
  method->op_block = op_block_outer;
  
  method->op_method = op_method;
  
  op_method->uv.method = method;
  
  return op_method;
}

void SPVM_OP_attach_anon_method_fields(SPVM_COMPILER* compiler, SPVM_OP* op_method, SPVM_OP* op_anon_method_fields) {
  
  SPVM_METHOD* method = op_method->uv.method;
  
  SPVM_OP* op_block = method->op_block;
  
  SPVM_OP* op_anon_method_field_var_decl_start = method->op_anon_method_field_var_decl_start;
  
  // Fields of anon method
  if (op_anon_method_fields) {
    SPVM_OP* op_anon_method_field = op_anon_method_fields->first;
    while ((op_anon_method_field = SPVM_OP_sibling(compiler, op_anon_method_field))) {
      SPVM_LIST_push(method->anon_method_fields, op_anon_method_field->uv.field);
    }
    
    // Declare local variable for fields in anon method.
    if (op_block) {
      SPVM_OP* op_list_statement = op_block->first;
      
      // 2. Process "self" field last to avoid overriding the invocant $self too early
      op_anon_method_field = op_anon_method_fields->first;
      while ((op_anon_method_field = SPVM_OP_sibling(compiler, op_anon_method_field))) {
        SPVM_FIELD* field = op_anon_method_field->uv.field;
        if (field->is_decl_var_in_anon_method) {
          if (strcmp(field->name, "self") == 0) {
            SPVM_OP* op_assign_var_decl = SPVM_OP_build_anon_method_var_decl(compiler, op_anon_method_field_var_decl_start, field);
            SPVM_OP_insert_child(compiler, op_list_statement, op_anon_method_field_var_decl_start, op_assign_var_decl);
          }
        }
      }
      // 1. Process fields other than "self" first
      SPVM_OP* op_anon_method_field = op_anon_method_fields->first;
      while ((op_anon_method_field = SPVM_OP_sibling(compiler, op_anon_method_field))) {
        SPVM_FIELD* field = op_anon_method_field->uv.field;
        if (field->is_decl_var_in_anon_method) {
          if (!(strcmp(field->name, "self") == 0)) {
            SPVM_OP* op_assign_var_decl = SPVM_OP_build_anon_method_var_decl(compiler, op_anon_method_field_var_decl_start, field);
            SPVM_OP_insert_child(compiler, op_list_statement, op_anon_method_field_var_decl_start, op_assign_var_decl);
          }
        }
      }
    }
  }
  
}

SPVM_OP* SPVM_OP_build_anon_method_var_decl(SPVM_COMPILER* compiler, SPVM_OP* op_anon_method_field_var_decl_start, SPVM_FIELD* field) {
  
  char* var_name = SPVM_ALLOCATOR_alloc_memory_block_permanent(compiler->current_each_compile_allocator, 1 + strlen(field->name) + 1);
  sprintf(var_name, "$%s", field->name);
  
  // my $foo = $self->{foo};
  
  SPVM_OP* op_name_var = SPVM_OP_new_op_name(compiler, var_name, op_anon_method_field_var_decl_start->file, op_anon_method_field_var_decl_start->line);
  SPVM_OP* op_var = SPVM_OP_build_var(compiler, op_name_var);
  SPVM_OP* op_var_decl = SPVM_OP_new_op_var_decl(compiler, op_anon_method_field_var_decl_start->file, op_anon_method_field_var_decl_start->line);
  op_var = SPVM_OP_build_var_decl(compiler, op_var_decl, op_var, NULL, NULL);
  
  SPVM_OP* op_var_name_invocant = SPVM_OP_new_op_name(compiler, "$self", op_anon_method_field_var_decl_start->file, op_anon_method_field_var_decl_start->line);
  SPVM_OP* op_var_self_invocant = SPVM_OP_new_op_var(compiler, op_var_name_invocant);
  SPVM_OP* op_name_field_access = SPVM_OP_new_op_name(compiler, field->name, op_anon_method_field_var_decl_start->file, op_anon_method_field_var_decl_start->line);
  SPVM_OP* op_field_access = SPVM_OP_new_op_field_access(compiler, op_anon_method_field_var_decl_start->file, op_anon_method_field_var_decl_start->line);
  SPVM_OP_build_field_access(compiler, op_field_access, op_var_self_invocant, op_name_field_access);
  
  SPVM_OP* op_assign = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_anon_method_field_var_decl_start->file, op_anon_method_field_var_decl_start->line);
  SPVM_OP_build_assign(compiler, op_assign, op_var, op_field_access);
  
  assert(op_anon_method_field_var_decl_start);
  
  return op_assign;
}

SPVM_OP* SPVM_OP_build_arg(SPVM_COMPILER* compiler, SPVM_OP* op_var, SPVM_OP* op_type, SPVM_OP* op_attributes, SPVM_OP* op_arg_default) {
  
  SPVM_OP* op_var_decl = SPVM_OP_new_op_var_decl_arg(compiler, op_var->file, op_var->line);
  
  op_var_decl->uv.var_decl->op_arg_default = op_arg_default;
  
  op_var = SPVM_OP_build_var_decl(compiler, op_var_decl, op_var, op_type, op_attributes);
  
  // The use of variable length arguments is restricted to object[] type
  if (op_type->uv.type->flag & SPVM_NATIVE_C_TYPE_FLAG_VARARGS) {
    if (op_arg_default) {
      SPVM_COMPILER_error(compiler, "The use of variable length arguments cannot have a default value.\n  at %s line %d", op_type->file, op_type->line);
    }
  }
  
  return op_var;
}

SPVM_OP* SPVM_OP_build_anon_method(SPVM_COMPILER* compiler, SPVM_OP* op_method) {
  
  // Class
  SPVM_OP* op_class = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CLASS, op_method->file, op_method->line);
  
  SPVM_OP* op_class_block = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CLASS_BLOCK, op_method->file, op_method->line);
  SPVM_OP* op_list_definitions = SPVM_OP_new_op_list(compiler, op_method->file, op_method->line);
  SPVM_OP_insert_child(compiler, op_list_definitions, op_list_definitions->last, op_method);
  SPVM_OP_insert_child(compiler, op_class_block, op_class_block->last, op_list_definitions);
  
  // int32_t max length is 10(2147483647)
  int32_t int32_max_length = 10;
  
  // Create anon method class name
  // If Foo::Bar anon method is defined line 123, method keyword start pos 32, the anon method class name become Foo::Bar::anon_method::123::32. This is uniqe in whole program.
  const char* outmost_basic_type_name = compiler->current_outmost_class_name;
  int32_t anon_method_defined_line = op_method->line;
  int32_t anon_method_defined_column = op_method->column;
  int32_t anon_method_basic_type_name_length = 6 + strlen(outmost_basic_type_name) + 2 + int32_max_length + 2 + int32_max_length;
  
  // Anon class name
  char* name_basic_type_tmp = SPVM_ALLOCATOR_alloc_memory_block_permanent(compiler->current_each_compile_allocator, anon_method_basic_type_name_length + 1);
  sprintf(name_basic_type_tmp, "%s::anon_method::%d::%d", outmost_basic_type_name, anon_method_defined_line, anon_method_defined_column);
  
  SPVM_STRING* name_basic_type_string = SPVM_STRING_new(compiler, name_basic_type_tmp, strlen(name_basic_type_tmp));
  const char* name_basic_type = name_basic_type_string->value;
  
  SPVM_OP* op_name_basic_type = SPVM_OP_new_op_name(compiler, name_basic_type, op_method->file, op_method->line);
  SPVM_OP* op_type = SPVM_OP_build_basic_type(compiler, op_name_basic_type);
  
  op_method->uv.method->outmost_basic_type_name = outmost_basic_type_name;
  
  SPVM_OP_build_class(compiler, op_class, op_type, op_class_block, NULL, NULL);
  
  // Type
  SPVM_OP* op_name_new = SPVM_OP_new_op_name(compiler, name_basic_type, op_method->file, op_method->line);
  SPVM_OP* op_type_new = SPVM_OP_build_basic_type(compiler, op_name_new);
  
  // New
  SPVM_OP* op_new = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_NEW, op_method->file, op_method->line);
  
  SPVM_OP* op_anon_method = SPVM_OP_build_new(compiler, op_new, op_type_new, NULL);
  
  SPVM_LIST_push(compiler->current_anon_op_types, op_type);
  
  return op_anon_method;
}

SPVM_OP* SPVM_OP_build_anon_method_field(SPVM_COMPILER* compiler, SPVM_OP* op_field, SPVM_OP* op_name_field, SPVM_OP* op_attributes, SPVM_OP* op_type, SPVM_OP* op_default) {
  
  int32_t is_decl_var_in_anon_method = 0;
  if (!op_name_field) {
    
    assert(op_default->id == SPVM_OP_C_ID_VAR);
    
    op_name_field = SPVM_OP_new_op_name(compiler, op_default->uv.var->name + 1, op_default->file, op_default->line);
    
    is_decl_var_in_anon_method = 1;
  }
  else if (op_name_field->id == SPVM_OP_C_ID_VAR) {
    
    assert(op_default->id == SPVM_OP_C_ID_VAR);
    
    op_name_field = SPVM_OP_new_op_name(compiler, op_name_field->uv.var->name + 1, op_name_field->file, op_name_field->line);
    
    is_decl_var_in_anon_method = 1;
  }
  
  SPVM_OP* op_field_definition = SPVM_OP_build_field(compiler, op_field, op_name_field, op_attributes, op_type, NULL, NULL);
  
  op_field_definition->uv.field->op_anon_method_field_default = op_default;
  
  op_field_definition->uv.field->is_decl_var_in_anon_method = is_decl_var_in_anon_method;
  
  return op_field_definition;
}

SPVM_OP* SPVM_OP_build_init_method(SPVM_COMPILER* compiler, SPVM_OP* op_init, SPVM_OP* op_block) {
    
  SPVM_OP* op_method = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_METHOD, op_block->file, op_block->line);
  SPVM_STRING* method_name_string = SPVM_STRING_new(compiler, "INIT", strlen("INIT"));
  const char* method_name = method_name_string->value;
  SPVM_OP* op_method_name = SPVM_OP_new_op_name(compiler, "INIT", op_block->file, op_block->line);
  SPVM_OP* op_void_type = SPVM_OP_new_op_void_type(compiler, op_block->file, op_block->line);
  
  SPVM_OP* op_list_attributes = SPVM_OP_new_op_list(compiler, op_block->file, op_block->line);
  SPVM_OP* op_attribute_static = SPVM_OP_new_op_attribute(compiler, SPVM_ATTRIBUTE_C_ID_STATIC, op_block->file, op_block->line);
  SPVM_OP_insert_child(compiler, op_list_attributes, op_list_attributes->first, op_attribute_static);
  
  SPVM_OP_build_method(compiler, op_method, op_method_name, op_void_type, NULL, op_list_attributes, op_block);
  
  return op_method;
}

SPVM_OP* SPVM_OP_build_init_block(SPVM_COMPILER* compiler, SPVM_OP* op_init, SPVM_OP* op_block) {
    
  SPVM_OP_insert_child(compiler, op_init, op_init->first, op_block);
  
  op_block->uv.block->id = SPVM_BLOCK_C_ID_INIT_BLOCK;
  
  return op_init;
}

SPVM_OP* SPVM_OP_build_end_method(SPVM_COMPILER* compiler, SPVM_OP* op_end, SPVM_OP* op_block) {
    
  SPVM_OP* op_method = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_METHOD, op_block->file, op_block->line);
  SPVM_STRING* method_name_string = SPVM_STRING_new(compiler, "END", strlen("END"));
  const char* method_name = method_name_string->value;
  SPVM_OP* op_method_name = SPVM_OP_new_op_name(compiler, "END", op_block->file, op_block->line);
  SPVM_OP* op_void_type = SPVM_OP_new_op_void_type(compiler, op_block->file, op_block->line);
  
  SPVM_OP* op_list_attributes = SPVM_OP_new_op_list(compiler, op_block->file, op_block->line);
  SPVM_OP* op_attribute_static = SPVM_OP_new_op_attribute(compiler, SPVM_ATTRIBUTE_C_ID_STATIC, op_block->file, op_block->line);
  SPVM_OP_insert_child(compiler, op_list_attributes, op_list_attributes->first, op_attribute_static);
  
  SPVM_OP_build_method(compiler, op_method, op_method_name, op_void_type, NULL, op_list_attributes, op_block);
  
  return op_method;
}

SPVM_OP* SPVM_OP_build_end_block(SPVM_COMPILER* compiler, SPVM_OP* op_end, SPVM_OP* op_block) {
    
  SPVM_OP_insert_child(compiler, op_end, op_end->first, op_block);
  
  op_block->uv.block->id = SPVM_BLOCK_C_ID_END_BLOCK;
  
  return op_end;
}

SPVM_OP* SPVM_OP_build_var(SPVM_COMPILER* compiler, SPVM_OP* op_var_name) {
  
  SPVM_OP* op_var = SPVM_OP_new_op_var(compiler, op_var_name);
  
  return op_var;
}

SPVM_OP* SPVM_OP_build_eval(SPVM_COMPILER* compiler, SPVM_OP* op_eval, SPVM_OP* op_eval_block) {
  
  SPVM_OP_insert_child(compiler, op_eval, op_eval->last, op_eval_block);
  
  op_eval_block->uv.block->id = SPVM_BLOCK_C_ID_EVAL;
  
  return op_eval;
}

SPVM_OP* SPVM_OP_build_switch_statement(SPVM_COMPILER* compiler, SPVM_OP* op_switch, SPVM_OP* op_condition_operand, SPVM_OP* op_block) {
  
  SPVM_OP* op_condition = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_SWITCH_CONDITION, op_condition_operand->file, op_condition_operand->line);
  SPVM_OP_insert_child(compiler, op_condition, op_condition->last, op_condition_operand);
  
  SPVM_OP* op_condition_do_nothing = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_DO_NOTHING, op_condition->file, op_condition->line);
  SPVM_OP_insert_child(compiler, op_condition_do_nothing, op_condition_do_nothing->last, op_condition);
  
  SPVM_OP_insert_child(compiler, op_switch, op_switch->last, op_condition_do_nothing);
  SPVM_OP_insert_child(compiler, op_switch, op_switch->last, op_block);
  
  SPVM_SWITCH_INFO* switch_info = SPVM_SWITCH_INFO_new(compiler);
  op_switch->uv.switch_info = switch_info;
  
  op_condition->uv.switch_info = switch_info;
  
  SPVM_OP* op_block_outer = SPVM_OP_new_op_block(compiler, op_switch->file, op_switch->line);
  op_block_outer->uv.block->id = SPVM_BLOCK_C_ID_SWITCH_OUTER;
  SPVM_OP_insert_child(compiler, op_block_outer, op_block_outer->last, op_switch);
      
  return op_block_outer;
}

SPVM_OP* SPVM_OP_build_switch_block(SPVM_COMPILER* compiler, SPVM_OP* op_switch_block, SPVM_OP* op_case_statements, SPVM_OP* op_default_statement) {
  
  op_switch_block->uv.block->id = SPVM_BLOCK_C_ID_SWITCH_INNER;
  op_switch_block->uv.block->no_scope = 1;
  
  if (op_case_statements->id != SPVM_OP_C_ID_LIST) {
    SPVM_OP* op_list = SPVM_OP_new_op_list(compiler, op_case_statements->file, op_case_statements->line);
    SPVM_OP_insert_child(compiler, op_list, op_list->last, op_case_statements);
    op_case_statements = op_list;
  }
  
  SPVM_OP_insert_child(compiler, op_switch_block, op_switch_block->last, op_case_statements);
  if (op_default_statement) {
    SPVM_OP_insert_child(compiler, op_switch_block, op_switch_block->last, op_default_statement);
  }
  
  return op_switch_block;
}

SPVM_OP* SPVM_OP_build_case_statement(SPVM_COMPILER* compiler, SPVM_OP* op_case_info, SPVM_OP* op_case_operand, SPVM_OP* op_block) {
  
  SPVM_CASE_INFO* case_info = SPVM_CASE_INFO_new(compiler);
  
  SPVM_OP_insert_child(compiler, op_case_info, op_case_info->last, op_case_operand);
  
  if (op_block) {
    SPVM_OP* op_statements = op_block->first;
    if (op_statements) {
      SPVM_OP* op_right_operand_statement = op_statements->last;
      
      // Add "break" statement if it doesn't exist
      {
        // No statement
        if (!op_right_operand_statement) {
          SPVM_OP* op_break = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_BREAK, op_statements->file, op_statements->line);
          SPVM_OP_insert_child(compiler, op_statements, op_statements->last, op_break);
        }
        // The last statement is not "break" statement
        else if (op_right_operand_statement->id != SPVM_OP_C_ID_BREAK) {
          SPVM_OP* op_break = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_BREAK, op_right_operand_statement->file, op_right_operand_statement->line + 1);
          SPVM_OP_insert_child(compiler, op_statements, op_statements->last, op_break);
        }
      }
    }
    
    SPVM_OP_insert_child(compiler, op_case_info, op_case_info->last, op_block);
  }
  
  case_info->op_case_info = op_case_info;
  
  op_case_info->uv.case_info = case_info;
  
  return op_case_info;
}

SPVM_OP* SPVM_OP_build_default_statement(SPVM_COMPILER* compiler, SPVM_OP* op_default, SPVM_OP* op_block) {
  
  if (op_block) {
    SPVM_OP_insert_child(compiler, op_default, op_default->last, op_block);
  }
  
  return op_default;
}

SPVM_OP* SPVM_OP_build_condition(SPVM_COMPILER* compiler, SPVM_OP* op_condition_operand, int32_t is_not) {
  
  int32_t id;
  if (is_not) {
    id = SPVM_OP_C_ID_CONDITION_NOT;
  }
  else {
    id = SPVM_OP_C_ID_CONDITION;
  }
  SPVM_OP* op_condition = SPVM_OP_new_op(compiler, id, op_condition_operand->file, op_condition_operand->line);
  
  SPVM_OP* op_bool = SPVM_OP_new_op_bool(compiler, op_condition_operand, op_condition_operand->file, op_condition_operand->line);
  SPVM_OP_insert_child(compiler, op_condition, op_condition->last, op_bool);
  
  return op_condition;
}

SPVM_OP* SPVM_OP_build_for_statement(SPVM_COMPILER* compiler, SPVM_OP* op_for, SPVM_OP* op_init, SPVM_OP* op_condition_operand, SPVM_OP* op_increment, SPVM_OP* op_block) {
  
  SPVM_OP* op_loop = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_LOOP, op_for->file, op_for->line);
  
  if (op_condition_operand->id == SPVM_OP_C_ID_DO_NOTHING) {
    op_condition_operand = SPVM_OP_new_op_constant_int(compiler, 1, op_for->file, op_for->line);
  }
  
  SPVM_OP_build_loop_statement(compiler, op_loop, op_init, op_condition_operand, op_increment, op_block);
  
  return op_loop;
}

SPVM_OP* SPVM_OP_build_foreach_statement(SPVM_COMPILER* compiler, SPVM_OP* op_for, SPVM_OP* op_var_element, SPVM_OP* op_array, SPVM_OP* op_block_statements) {

  /*
    {
      
      my $.array = $array;
      my $.array_length = @$.array;
      for (my $.i = 0; $.i < $.array_length; $.i++) {
        my $element = $.array->[$.i];
        
        # ...
      }
    }
  */

  SPVM_OP* op_var_init_name = SPVM_OP_new_op_name_tmp_var(compiler, op_for->file, op_for->line);
  SPVM_OP* op_var_init_orig = SPVM_OP_new_op_var(compiler, op_var_init_name);
  SPVM_OP* op_var_array_name = SPVM_OP_new_op_name(compiler, "$.array", op_for->file, op_for->line);
  SPVM_OP* op_var_array_orig = SPVM_OP_new_op_var(compiler, op_var_array_name);
  SPVM_OP* op_var_array_length_name = SPVM_OP_new_op_name(compiler, "$.array_length", op_for->file, op_for->line);
  SPVM_OP* op_var_array_length_orig = SPVM_OP_new_op_var(compiler, op_var_array_length_name);
  
  // my $.i
  SPVM_OP* op_var_init_for_decl = SPVM_OP_clone_op_var(compiler, op_var_init_orig);
  SPVM_OP* op_var_decl_init = SPVM_OP_new_op_var_decl(compiler, op_for->file, op_for->line);
  SPVM_OP_build_var_decl(compiler, op_var_decl_init, op_var_init_for_decl, NULL, NULL);
  
  // 0
  SPVM_OP* op_constant_zero = SPVM_OP_new_op_constant_int(compiler, 0, op_for->file, op_for->line);
  
  // my $.i = 0
  SPVM_OP* op_assign_init = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_for->file, op_for->line);
  SPVM_OP_build_assign(compiler, op_assign_init, op_var_init_for_decl, op_constant_zero);
  
  // my $.array
  SPVM_OP* op_var_array_for_assign = SPVM_OP_clone_op_var(compiler, op_var_array_orig);
  SPVM_OP* op_var_decl_array = SPVM_OP_new_op_var_decl(compiler, op_for->file, op_for->line);
  SPVM_OP_build_var_decl(compiler, op_var_decl_array, op_var_array_for_assign, NULL, NULL);
  
  // my $.array = $array
  SPVM_OP* op_assign_array = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_for->file, op_for->line);
  SPVM_OP_build_assign(compiler, op_assign_array,  op_var_array_for_assign, op_array);
  
  // @$.array
  SPVM_OP* op_var_array_for_length = SPVM_OP_clone_op_var(compiler, op_var_array_orig);
  SPVM_OP* op_array_length = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ARRAY_LENGTH, op_for->file, op_for->line);
  SPVM_OP_build_array_length(compiler, op_array_length, op_var_array_for_length);
  
  // my $.array_length
  SPVM_OP* op_var_array_length_for_assign = SPVM_OP_clone_op_var(compiler, op_var_array_length_orig);
  SPVM_OP* op_var_decl_array_length = SPVM_OP_new_op_var_decl(compiler, op_for->file, op_for->line);
  SPVM_OP_build_var_decl(compiler, op_var_decl_array_length, op_var_array_length_for_assign, NULL, NULL);
  
  // my $.array_length = @$.array
  SPVM_OP* op_assign_array_length = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_for->file, op_for->line);
  SPVM_OP_build_assign(compiler, op_assign_array_length, op_var_array_length_for_assign, op_array_length);
  
  // $.i < $array_length
  SPVM_OP* op_var_init_for_condition = SPVM_OP_clone_op_var(compiler, op_var_init_orig);
  SPVM_OP* op_var_array_length_for_condition = SPVM_OP_clone_op_var(compiler, op_var_array_length_orig);
  SPVM_OP* op_numlt = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_NUMERIC_COMPARISON_LT, op_for->file, op_for->line);
  SPVM_OP* op_numlt_comparison = SPVM_OP_build_binary_op(compiler, op_numlt, op_var_init_for_condition, op_var_array_length_for_condition);
  
  // $.array->[$.i]
  SPVM_OP* op_var_init_for_element_access = SPVM_OP_clone_op_var(compiler, op_var_init_orig);
  SPVM_OP* op_var_array_for_element_access = SPVM_OP_clone_op_var(compiler, op_var_array_orig);
  SPVM_OP* op_element_access = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ARRAY_ELEMENT_ACCESS, op_for->file, op_for->line);
  op_element_access = SPVM_OP_build_element_access(compiler, op_element_access, op_var_array_for_element_access, op_var_init_for_element_access);
  
  // my $element = $.array->[$.i]
  SPVM_OP* op_assign_element = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_for->file, op_for->line);
  SPVM_OP_build_assign(compiler, op_assign_element, op_var_element, op_element_access);
  
  // $.i
  SPVM_OP* op_var_init_for_increament = SPVM_OP_clone_op_var(compiler, op_var_init_orig);
  
  // ++$.i;
  SPVM_OP* op_inc_increament = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_PRE_INC, op_for->file, op_for->line);
  op_inc_increament = SPVM_OP_build_inc(compiler, op_inc_increament, op_var_init_for_increament);
  
  SPVM_OP* op_statements = op_block_statements->first;
  if (!op_statements) {
    op_statements = SPVM_OP_new_op_list(compiler, op_for->file, op_for->line);
    SPVM_OP_insert_child(compiler, op_block_statements, op_block_statements->first, op_statements);
  }
  SPVM_OP_insert_child(compiler, op_statements, op_statements->first, op_assign_element);
  
  op_for = SPVM_OP_build_for_statement(compiler, op_for, op_assign_init, op_numlt_comparison, op_inc_increament, op_block_statements);
  
  SPVM_OP* op_block_new = SPVM_OP_new_op_block(compiler, op_for->file, op_for->line);
  SPVM_OP* op_statements_new = SPVM_OP_new_op_list(compiler, op_for->file, op_for->line);
  SPVM_OP_insert_child(compiler, op_block_new, op_block_new->last, op_statements_new);
  
  SPVM_OP_insert_child(compiler, op_statements_new, op_statements_new->last, op_assign_array);
  SPVM_OP_insert_child(compiler, op_statements_new, op_statements_new->last, op_assign_array_length);
  SPVM_OP_insert_child(compiler, op_statements_new, op_statements_new->last, op_for);
  
  return op_block_new;
}

SPVM_OP* SPVM_OP_build_while_statement(SPVM_COMPILER* compiler, SPVM_OP* op_while, SPVM_OP* op_condition_operand, SPVM_OP* op_block) {
  
  SPVM_OP* op_loop = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_LOOP, op_while->file, op_while->line);
  
  SPVM_OP_build_loop_statement(compiler, op_loop, NULL, op_condition_operand, NULL, op_block);
  
  return op_loop;
}

SPVM_OP* SPVM_OP_build_loop_statement(SPVM_COMPILER* compiler, SPVM_OP* op_loop, SPVM_OP* op_init, SPVM_OP* op_condition_operand, SPVM_OP* op_increment, SPVM_OP* op_block_inner) {
  
  if (!op_init) {
    op_init = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_DO_NOTHING, op_loop->file, op_loop->line);
  }
  
  SPVM_OP* op_if = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_IF, op_condition_operand->file, op_condition_operand->line);
  int32_t no_scope = 1;
  SPVM_OP* op_do_nothing = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_DO_NOTHING, op_condition_operand->file, op_condition_operand->line);
  SPVM_OP* op_last = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_LAST, op_condition_operand->file, op_condition_operand->line);
  SPVM_OP_build_if_statement(compiler, op_if, op_condition_operand, op_do_nothing, op_last, no_scope);
  
  SPVM_OP* op_list_op_block_inner = op_block_inner->first;
  SPVM_OP_insert_child(compiler, op_list_op_block_inner, op_list_op_block_inner->first, op_if);
  
  op_block_inner->uv.block->id = SPVM_BLOCK_C_ID_LOOP_INNER;
  
  SPVM_OP* op_block_outer = SPVM_OP_new_op_block(compiler, op_loop->file, op_loop->line);
  op_block_outer->uv.block->id = SPVM_BLOCK_C_ID_LOOP_OUTER;
  
  if (!op_increment) {
    op_increment = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_DO_NOTHING, op_loop->file, op_loop->line);
  }
  
  SPVM_OP* op_loop_increment = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_LOOP_INCREMENT, op_loop->file, op_loop->line);
  SPVM_OP_insert_child(compiler, op_loop_increment, op_loop_increment->last, op_increment);
  SPVM_OP_insert_child(compiler, op_list_op_block_inner, op_list_op_block_inner->last, op_loop_increment);
  
  SPVM_OP_insert_child(compiler, op_block_outer, op_block_outer->last, op_init);
  SPVM_OP_insert_child(compiler, op_block_outer, op_block_outer->last, op_block_inner);
  
  SPVM_OP_insert_child(compiler, op_loop, op_loop->last, op_block_outer);
  
  return op_loop;
}

SPVM_OP* SPVM_OP_build_if_require_statement(SPVM_COMPILER* compiler, SPVM_OP* op_if_require, SPVM_OP* op_use, SPVM_OP* op_block_true, SPVM_OP* op_block_false) {
  
  SPVM_OP_insert_child(compiler, op_if_require, op_if_require->last, op_use);
  SPVM_OP_insert_child(compiler, op_if_require, op_if_require->last, op_block_true);
  if (!op_block_false) {
    op_block_false = SPVM_OP_new_op_block(compiler, op_block_true->file, op_block_true->line);
  }
  SPVM_OP_insert_child(compiler, op_if_require, op_if_require->last, op_block_false);
  
  return op_if_require;
}

SPVM_OP* SPVM_OP_build_if_statement(SPVM_COMPILER* compiler, SPVM_OP* op_if, SPVM_OP* op_if_operand, SPVM_OP* op_block_true, SPVM_OP* op_block_false, int32_t no_scope) {
  
  // Condition
  SPVM_OP* op_condition = SPVM_OP_build_condition(compiler, op_if_operand, 0);
  
  // Create true block if needed
  if (op_block_true->id != SPVM_OP_C_ID_BLOCK) {
    SPVM_OP* op_not_block = op_block_true;
    
    op_block_true = SPVM_OP_new_op_block(compiler, op_not_block->file, op_not_block->line);
    SPVM_OP* op_list = SPVM_OP_new_op_list(compiler, op_not_block->file, op_not_block->line);
    SPVM_OP_insert_child(compiler, op_list, op_list->last, op_not_block);
    SPVM_OP_insert_child(compiler, op_block_true, op_block_true->last, op_list);
  }
  op_block_true->uv.block->id = SPVM_BLOCK_C_ID_IF;
  op_block_true->uv.block->no_scope = no_scope;
  
  // Create false block if needed
  if (op_block_false->id != SPVM_OP_C_ID_BLOCK) {
    SPVM_OP* op_not_block = op_block_false;
    
    // Create block
    op_block_false = SPVM_OP_new_op_block(compiler, op_not_block->file, op_not_block->line);
    SPVM_OP* op_list = SPVM_OP_new_op_list(compiler, op_not_block->file, op_not_block->line);
    SPVM_OP_insert_child(compiler, op_list, op_list->last, op_not_block);
    SPVM_OP_insert_child(compiler, op_block_false, op_block_false->last, op_list);
  }
  op_block_false->uv.block->id = SPVM_BLOCK_C_ID_ELSE;
  op_block_false->uv.block->no_scope = no_scope;
  
  SPVM_OP_insert_child(compiler, op_if, op_if->last, op_condition);
  SPVM_OP_insert_child(compiler, op_if, op_if->last, op_block_true);
  SPVM_OP_insert_child(compiler, op_if, op_if->last, op_block_false);
  
  return op_if;
}

SPVM_OP* SPVM_OP_build_array_length(SPVM_COMPILER* compiler, SPVM_OP* op_array_length, SPVM_OP* op_length) {
  
  SPVM_OP_insert_child(compiler, op_array_length, op_array_length->last, op_length);
  
  return op_array_length;
}

SPVM_OP* SPVM_OP_build_string_length(SPVM_COMPILER* compiler, SPVM_OP* op_string_length, SPVM_OP* op_length) {
  
  SPVM_OP_insert_child(compiler, op_string_length, op_string_length->last, op_length);
  
  return op_string_length;
}

SPVM_OP* SPVM_OP_build_new(SPVM_COMPILER* compiler, SPVM_OP* op_new, SPVM_OP* op_type, SPVM_OP* op_length) {
  
  SPVM_OP_insert_child(compiler, op_new, op_new->last, op_type);
  
  if (op_length) {
    SPVM_OP_insert_child(compiler, op_new, op_new->last, op_length);
  }
  
  if (op_type->id == SPVM_OP_C_ID_TYPE && strstr(op_type->uv.type->unresolved_basic_type_name, "::anon_method::")) {
    
    const char* anon_basic_type_name = op_type->uv.type->unresolved_basic_type_name;
    SPVM_BASIC_TYPE* anon_basic_type = SPVM_HASH_get(compiler->basic_type_symtable, anon_basic_type_name, strlen(anon_basic_type_name));
    
    assert(anon_basic_type);
    
    // Anon method
    SPVM_METHOD* anon_method = SPVM_LIST_get(anon_basic_type->methods, 0);
    
    if (anon_method->anon_method_fields->length) {
      // [Before]
      // new Foo::anon_method::Line::Column
      
      // [After]
      //(
      //  my $invocant = new Foo::anon_method::Line::Column,
      //  $invocant->{FIELD_NAME0} = DEFAULT0,
      //  $invocant->{FIELD_NAME1} = DEFAULT1,
      //  $invocant,
      //)
      
      SPVM_OP* op_sequence = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_SEQUENCE, op_new->file, op_new->line);
      
      // my $invocant = new TYPE;
      SPVM_OP* op_assign_new = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_new->file, op_new->line);
      SPVM_OP* op_var_invocant_new_name = SPVM_OP_new_op_name_tmp_var(compiler, op_new->file, op_new->line);
      SPVM_OP* op_var_invocant_new = SPVM_OP_new_op_var(compiler, op_var_invocant_new_name);
      SPVM_OP* op_var_decl_invocant_new = SPVM_OP_new_op_var_decl_arg(compiler, op_new->file, op_new->line);
      op_var_invocant_new = SPVM_OP_build_var_decl(compiler, op_var_decl_invocant_new, op_var_invocant_new, NULL, NULL);
      SPVM_OP_build_assign(compiler, op_assign_new, op_var_invocant_new, op_new);
      SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_new);
      
      for (int32_t caputre_index = 0; caputre_index < anon_method->anon_method_fields->length; caputre_index++) {
        SPVM_FIELD* anon_method_field = SPVM_LIST_get(anon_method->anon_method_fields, caputre_index);
        const char* anon_method_field_name = anon_method_field->name;
        SPVM_OP* op_anon_method_field_default = anon_method_field->op_anon_method_field_default;
        
        // $invocant->{FIELD_NAME} = DEFAULT
        if (op_anon_method_field_default) {
          // 
          SPVM_OP* op_invocant_set_field = SPVM_OP_clone_op_var(compiler, op_var_invocant_new);
          SPVM_OP* op_name_field = SPVM_OP_new_op_name(compiler, anon_method_field->name, op_new->file, op_new->line);
          SPVM_OP* op_field_access = SPVM_OP_new_op_field_access(compiler, op_new->file, op_new->line);
          SPVM_OP_build_field_access(compiler, op_field_access, op_invocant_set_field, op_name_field);
          SPVM_OP* op_assign_field_access = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_new->file, op_new->line);
          SPVM_OP_build_assign(compiler, op_assign_field_access, op_field_access, op_anon_method_field_default);
          SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_field_access);
        }
      }
      
      // $invocant
      SPVM_OP* op_var_invocant_ret = SPVM_OP_clone_op_var(compiler, op_var_invocant_new);
      SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_var_invocant_ret);
      
      op_new = op_sequence;
    }
  }
  
  return op_new;
}

SPVM_OP* SPVM_OP_build_array_init(SPVM_COMPILER* compiler, SPVM_OP* op_array_init, SPVM_OP* op_list_elements, int32_t is_key_value_pairs) {
  
  // Array length
  int32_t length = 0;
  if (op_list_elements->id == SPVM_OP_C_ID_LIST) {
    SPVM_OP* op_element = op_list_elements->first;
    int32_t index = 0;
    while ((op_element = SPVM_OP_sibling(compiler, op_element))) {
      length++;
    }
  }
  else {
    length = 1;
  }
  
  if (is_key_value_pairs && length % 2 != 0) {
    SPVM_COMPILER_error(compiler, "The length of the elements in {} of the array initialization must be an even number.\n  at %s line %d", op_list_elements->file, op_list_elements->line);
    return op_array_init;
  }
  
  SPVM_OP* op_constant_length = SPVM_OP_new_op_constant_int(compiler, length, op_array_init->file, op_array_init->line);
  SPVM_OP* op_new = NULL;
  if (length == 0) {
    op_new = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_NEW, op_array_init->file, op_array_init->line);
    SPVM_OP* op_type = SPVM_OP_new_op_any_object_array_type(compiler, op_array_init->file, op_array_init->line);
    SPVM_OP_build_new(compiler, op_new, op_type, op_constant_length);
    op_array_init = op_new;
  }
  else if (length > 0) {
    {
      SPVM_OP* op_pushmark = op_list_elements->first;
      SPVM_OP* op_element_element = SPVM_OP_sibling(compiler, op_pushmark);
      if (op_element_element->id == SPVM_OP_C_ID_UNDEF) {
        SPVM_COMPILER_error(compiler, "The first element in the array initialization must be defined.\n  at %s line %d", op_array_init->file, op_array_init->line);
        return op_array_init;
      }
    }
    
    if (is_key_value_pairs) {
      SPVM_OP* op_pushmark = op_list_elements->first;
      SPVM_OP* op_element_element = SPVM_OP_sibling(compiler, op_pushmark);
      if (is_key_value_pairs) {
        SPVM_OP* op_type_cast = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_TYPE_CAST, op_array_init->file, op_array_init->line);
        SPVM_OP* op_type_for_cast = SPVM_OP_new_op_any_object_type(compiler, op_array_init->file, op_array_init->line);
        SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_element_element);
        SPVM_OP_build_type_cast(compiler, op_type_cast, op_type_for_cast, op_element_element);
        SPVM_OP_replace_op(compiler, op_stab, op_type_cast);
      }
    }
    
    SPVM_OP* op_pushmark = op_list_elements->first;
    SPVM_OP* op_element_element = SPVM_OP_sibling(compiler, op_pushmark);
    
    SPVM_OP* op_sequence = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_SEQUENCE, op_array_init->file, op_array_init->line);
    
    // my $first_element = FIRST_ELEMENT;
    SPVM_OP* op_var_first_element_name = SPVM_OP_new_op_name_tmp_var(compiler, op_array_init->file, op_array_init->line);
    SPVM_OP* op_var_first_element = SPVM_OP_new_op_var(compiler, op_var_first_element_name);
    SPVM_OP* op_var_decl_first_element = SPVM_OP_new_op_var_decl_arg(compiler, op_array_init->file, op_array_init->line);
    op_var_first_element = SPVM_OP_build_var_decl(compiler, op_var_decl_first_element, op_var_first_element, NULL, NULL);
    SPVM_OP* op_assign_first_element = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_array_init->file, op_array_init->line);
    SPVM_OP_cut_op(compiler, op_element_element);
    SPVM_OP_build_assign(compiler, op_assign_first_element, op_var_first_element, op_element_element);
    SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_first_element);
    
    // my $array = new $first_element;
    op_new = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_NEW, op_array_init->file, op_array_init->line);
    SPVM_OP* op_var_first_element_new = SPVM_OP_clone_op_var(compiler, op_var_first_element);
    op_new = SPVM_OP_build_new(compiler, op_new, op_var_first_element_new, op_constant_length);
    SPVM_OP* op_assign_new = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_array_init->file, op_array_init->line);
    SPVM_OP* op_var_array_new_name = SPVM_OP_new_op_name_tmp_var(compiler, op_array_init->file, op_array_init->line);
    SPVM_OP* op_var_array_new = SPVM_OP_new_op_var(compiler, op_var_array_new_name);
    SPVM_OP* op_var_decl_array_new = SPVM_OP_new_op_var_decl_arg(compiler, op_array_init->file, op_array_init->line);
    op_var_array_new = SPVM_OP_build_var_decl(compiler, op_var_decl_array_new, op_var_array_new, NULL, NULL);
    SPVM_OP_build_assign(compiler, op_assign_new, op_var_array_new, op_new);
    SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_new);
    
    {
      int32_t index = 0;
      SPVM_OP* op_element = op_list_elements->first;
      while ((op_element = SPVM_OP_sibling(compiler, op_element))) {
        SPVM_OP* op_assign_element_access = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_array_init->file, op_array_init->line);
        SPVM_OP* op_element_access = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ARRAY_ELEMENT_ACCESS, op_array_init->file, op_array_init->line);
        SPVM_OP* op_var_array_set = SPVM_OP_clone_op_var(compiler, op_var_array_new);
        SPVM_OP_insert_child(compiler, op_element_access, op_element_access->last, op_var_array_set);
        SPVM_OP* op_constant_index = SPVM_OP_new_op_constant_int(compiler, index, op_array_init->file, op_array_init->line);
        SPVM_OP_insert_child(compiler, op_element_access, op_element_access->last, op_constant_index);
        
        if (index == 0) {
          // $array->[0] = $first_element;
          SPVM_OP* op_var_first_element_array_set = SPVM_OP_clone_op_var(compiler, op_var_first_element);
          SPVM_OP_build_assign(compiler, op_assign_element_access, op_element_access, op_var_first_element_array_set);
        }
        else {
          // $array->[$i] = ELEMENT
          SPVM_OP* op_stab = SPVM_OP_cut_op(compiler, op_element);
          SPVM_OP_build_assign(compiler, op_assign_element_access, op_element_access, op_element);
          op_element = op_stab;
        }
        
        SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_element_access);
        
        index++;
      }
    }
    
    if (is_key_value_pairs) {
      // enable_options $array
      SPVM_OP* op_var_array_enable_options = SPVM_OP_clone_op_var(compiler, op_var_array_new);
      SPVM_OP* op_enable_options = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ENABLE_OPTIONS, op_array_init->file, op_array_init->line);
      op_enable_options = SPVM_OP_build_enable_options(compiler, op_enable_options, op_var_array_enable_options);
      SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_enable_options);
    }
    
    // $array
    SPVM_OP* op_var_array_ret = SPVM_OP_clone_op_var(compiler, op_var_array_new);
    SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_var_array_ret);
    
    op_array_init = op_sequence;
  }
  
  return op_array_init;
}

SPVM_OP* SPVM_OP_build_element_access(SPVM_COMPILER* compiler, SPVM_OP* op_element_access, SPVM_OP* op_array, SPVM_OP* op_index) {
  
  SPVM_OP_insert_child(compiler, op_element_access, op_element_access->last, op_array);
  SPVM_OP_insert_child(compiler, op_element_access, op_element_access->last, op_index);
  
  return op_element_access;
}

SPVM_OP* SPVM_OP_build_field_access(SPVM_COMPILER* compiler, SPVM_OP* op_field_access, SPVM_OP* op_invocant, SPVM_OP* op_name_field) {
  
  SPVM_OP_insert_child(compiler, op_field_access, op_field_access->last, op_invocant);
  
  SPVM_FIELD_ACCESS* field_access = op_field_access->uv.field_access;
  
  field_access->op_name = op_name_field;
  
  return op_field_access;
}

SPVM_OP* SPVM_OP_build_can(SPVM_COMPILER* compiler, SPVM_OP* op_can, SPVM_OP* op_var, SPVM_OP* op_name) {
  
  if (op_name->id == SPVM_OP_C_ID_CONSTANT) {
    SPVM_OP* op_constant = op_name;
    SPVM_CONSTANT* constant = op_constant->uv.constant;
    SPVM_TYPE* constant_type = constant->type;
    const char* constant_chars = (const char*)constant->value.oval;
    int32_t is_empty_string = 0;
    if (SPVM_TYPE_is_string_type(compiler, constant_type->basic_type->id, constant_type->dimension, constant_type->flag)) {
      if (strcmp(constant_chars, "") == 0) {
        is_empty_string = 1;
      }
    }
    
    if (!is_empty_string) {
      SPVM_COMPILER_error(compiler, "If the right operand of can operator is a constant value, it must be an empty string \"\".\n  at %s line %d", op_name->file, op_name->line);
    }
    
    op_name = SPVM_OP_new_op_name(compiler, "", op_name->file, op_name->line);
  }
  
  SPVM_OP_insert_child(compiler, op_can, op_can->last, op_var);
  SPVM_OP_insert_child(compiler, op_can, op_can->last, op_name);
  
  return op_can;
}

SPVM_OP* SPVM_OP_build_weaken_field(SPVM_COMPILER* compiler, SPVM_OP* op_weaken, SPVM_OP* op_field_access) {
  
  SPVM_OP* op_weaken_field = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_WEAKEN_FIELD, op_weaken->file, op_weaken->line);
  SPVM_OP_insert_child(compiler, op_weaken_field, op_weaken_field->last, op_field_access);
  
  op_field_access->flag |= SPVM_OP_C_FLAG_FIELD_ACCESS_WEAKEN;
  
  return op_weaken_field;
}

SPVM_OP* SPVM_OP_build_unweaken_field(SPVM_COMPILER* compiler, SPVM_OP* op_unweaken, SPVM_OP* op_field_access) {
  
  SPVM_OP* op_unweaken_field = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_UNWEAKEN_FIELD, op_unweaken->file, op_unweaken->line);
  SPVM_OP_insert_child(compiler, op_unweaken_field, op_unweaken_field->last, op_field_access);
  
  op_field_access->flag |= SPVM_OP_C_FLAG_FIELD_ACCESS_UNWEAKEN;
  
  return op_unweaken_field;
}

SPVM_OP* SPVM_OP_build_isweak_field(SPVM_COMPILER* compiler, SPVM_OP* op_isweak, SPVM_OP* op_field_access) {
  
  SPVM_OP* op_isweak_field = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ISWEAK_FIELD, op_isweak->file, op_isweak->line);
  SPVM_OP_insert_child(compiler, op_isweak_field, op_isweak_field->last, op_field_access);
  op_field_access->flag |= SPVM_OP_C_FLAG_FIELD_ACCESS_ISWEAK;
  
  return op_field_access;
}

SPVM_OP* SPVM_OP_build_exists(SPVM_COMPILER* compiler, SPVM_OP* op_exists, SPVM_OP* op_field_access) {
  
  SPVM_OP_insert_child(compiler, op_exists, op_exists->last, op_field_access);
  op_field_access->flag |= SPVM_OP_C_FLAG_FIELD_ACCESS_EXISTS;
  
  return op_field_access;
}

SPVM_OP* SPVM_OP_build_delete(SPVM_COMPILER* compiler, SPVM_OP* op_exists, SPVM_OP* op_field_access) {
  
  SPVM_OP_insert_child(compiler, op_exists, op_exists->last, op_field_access);
  op_field_access->flag |= SPVM_OP_C_FLAG_FIELD_ACCESS_DELETE;
  
  return op_field_access;
}

SPVM_OP* SPVM_OP_build_type_cast(SPVM_COMPILER* compiler, SPVM_OP* op_type_cast, SPVM_OP* op_type, SPVM_OP* op_operand) {
  
  SPVM_OP_insert_child(compiler, op_type_cast, op_type_cast->last, op_operand);
  SPVM_OP_insert_child(compiler, op_type_cast, op_type_cast->last, op_type);
  
  op_type_cast->file = op_type->file;
  op_type_cast->line = op_type->line;
  
  return op_type_cast;
}

SPVM_OP* SPVM_OP_build_generic_type(SPVM_COMPILER* compiler, SPVM_OP* op_container_type, SPVM_OP* op_element_type) {
  
  op_container_type->uv.type->of = op_element_type->uv.type;
  op_container_type->uv.type->union_types = op_element_type->uv.type->union_types;
  
  return op_container_type;
}

SPVM_OP* SPVM_OP_build_interface_statement(SPVM_COMPILER* compiler, SPVM_OP* op_interface, SPVM_OP* op_type) {
  
  SPVM_INTERFACE* interface = SPVM_INTERFACE_new(compiler);
  op_interface->uv.interface = interface;
  interface->op_interface = op_interface;
  interface->op_type = op_type;
  
  // add use stack
  SPVM_OP* op_use = SPVM_OP_new_op_use(compiler, op_interface->file, op_interface->line);
  SPVM_OP* op_name_alias = NULL;
  int32_t is_require = 0;
  SPVM_OP_build_use(compiler, op_use, op_type, op_name_alias, is_require);
  
  return op_interface;
}

SPVM_OP* SPVM_OP_build_var_decl(SPVM_COMPILER* compiler, SPVM_OP* op_var_decl, SPVM_OP* op_var, SPVM_OP* op_type, SPVM_OP* op_attributes) {
  
  // Declaration
  op_var->uv.var->is_declaration = 1;
  
  // Create var_decl var information
  SPVM_VAR_DECL* var_decl = op_var_decl->uv.var_decl;
  if (op_type) {
    var_decl->type = op_type->uv.type;
    SPVM_OP_insert_child(compiler, op_var, op_var->last, op_type);
  }
  
  // Local temporary variable
  if (strcmp(op_var->uv.var->name, "$_") == 0) {
    char* name = SPVM_ALLOCATOR_alloc_memory_block_permanent(compiler->current_each_compile_allocator, strlen("$_.line_2147483647_column_2147483647") + 1);
    sprintf(name, "$_.line_%d_column_%d", op_var_decl->line, op_var_decl->column);
    op_var->uv.var->name = name;
    var_decl->name = name;
  }
  
  var_decl->var = op_var->uv.var;
  
  op_var->uv.var->var_decl = var_decl;
  
  if (strstr(op_var->uv.var->name, "::")) {
    SPVM_COMPILER_error(compiler, "The local variable name '%s' cannnot contain \"::\".\n  at %s line %d", op_var->uv.var->name, op_var->file, op_var->line);
  }
  
  return op_var;
}

SPVM_OP* SPVM_OP_build_call_method(SPVM_COMPILER* compiler, SPVM_OP* op_call_method, SPVM_OP* op_invocant, SPVM_OP* op_name_method, SPVM_OP* op_list_operands) {
  
  SPVM_OP_insert_child(compiler, op_call_method, op_call_method->last, op_list_operands);
  
  SPVM_CALL_METHOD* call_method = op_call_method->uv.call_method;
  
  call_method->line = op_call_method->line;
  
  const char* method_name = op_name_method->uv.name;
  
  // Class method call
  assert(op_invocant->id != SPVM_OP_C_ID_NAME);
  if (op_invocant->id == SPVM_OP_C_ID_TYPE || op_invocant->id == SPVM_OP_C_ID_OUTMOST_CLASS) {
    call_method->is_class_method = 1;
    call_method->op_name = op_name_method;
    if (op_invocant->id == SPVM_OP_C_ID_TYPE) {
      call_method->basic_type_name = op_invocant->uv.type->unresolved_basic_type_name;
      SPVM_OP_insert_child(compiler, op_call_method, op_call_method->last, op_invocant);
      SPVM_LIST_push(compiler->current_op_types_for_alias_resolution, op_invocant);
    }
    else if (op_invocant->id == SPVM_OP_C_ID_OUTMOST_CLASS) {
      call_method->is_current = 1;
    }
  }
  // Instance method call
  else {
    call_method->op_name = op_name_method;
    
    if (op_invocant->id == SPVM_OP_C_ID_VAR) {
      op_invocant->uv.var->call_method = call_method;
    }
    
    SPVM_OP_insert_child(compiler, op_list_operands, op_list_operands->first, op_invocant);
  }
  
  return op_call_method;
}

SPVM_OP* SPVM_OP_build_unary_op(SPVM_COMPILER* compiler, SPVM_OP* op_unary, SPVM_OP* op_operand) {
  
  SPVM_OP_insert_child(compiler, op_unary, op_unary->last, op_operand);
  
  return op_unary;
}

SPVM_OP* SPVM_OP_build_unary_op_var(SPVM_COMPILER* compiler, SPVM_OP* op_unary, SPVM_OP* op_operand) {
  
  const char* op_name = op_unary->name;
  char op_name_lc[16] = {0};
  snprintf(op_name_lc, 16, "%s", op_name);
  for (int32_t i = 0; i < 16; i++) {
    if (op_name_lc[i] != '\0') {
      op_name_lc[i] += ('a' - 'A');
    }
  }
  
  if (op_operand->id != SPVM_OP_C_ID_VAR) {
    SPVM_COMPILER_error(compiler, "The operand of %s operator must be a variable.\n  at %s line %d", op_name_lc, op_operand->file, op_operand->line);
  }
  
  // Build op
  SPVM_OP_insert_child(compiler, op_unary, op_unary->last, op_operand);
  
  return op_unary;
}

SPVM_OP* SPVM_OP_build_binary_op(SPVM_COMPILER* compiler, SPVM_OP* op_bin, SPVM_OP* op_left_operand, SPVM_OP* op_right_operand) {
  
  SPVM_OP_insert_child(compiler, op_bin, op_bin->last, op_left_operand);
  SPVM_OP_insert_child(compiler, op_bin, op_bin->last, op_right_operand);
  
  return op_bin;
}

SPVM_OP* SPVM_OP_build_accessor(SPVM_COMPILER* compiler, SPVM_OP* op_accessor, SPVM_OP* op_arg, SPVM_OP* op_block, SPVM_OP* op_return_type) {
  
  SPVM_OP_insert_child(compiler, op_accessor, op_accessor->last, op_arg);
  SPVM_OP_insert_child(compiler, op_accessor, op_accessor->last, op_return_type);
  SPVM_OP_insert_child(compiler, op_accessor, op_accessor->last, op_block);
  
  return op_accessor;
}

SPVM_OP* SPVM_OP_build_type_check(SPVM_COMPILER* compiler, SPVM_OP* op_is, SPVM_OP* op_operand, SPVM_OP* op_type) {
  
  if (op_is->id == SPVM_OP_C_ID_IS_COMPILE_TYPE) {
    if (op_type->uv.type->union_types) {
      SPVM_COMPILER_error(compiler, "The right type must not contain union types.\n  at %s line %d", op_type->file, op_type->line);
      return op_is;
    }
  }
  else {
    if (op_type->uv.type->of) {
      SPVM_COMPILER_error(compiler, "The right type must not be a generic type.\n  at %s line %d", op_type->file, op_type->line);
      return op_is;
    }
    
    if (op_type->uv.type->union_types) {
      SPVM_COMPILER_error(compiler, "The right type must not be a union type.\n  at %s line %d", op_type->file, op_type->line);
      return op_is;
    }
    
    if (op_type->uv.type->flag) {
      if (!(op_is->id == SPVM_OP_C_ID_IS_COMPILE_TYPE)) {
        SPVM_COMPILER_error(compiler, "The right type must not be reference types, mutable types, object... types.\n  at %s line %d", op_type->file, op_type->line);
        return op_is;
      }
    }
  }
  
  // $@ isa TYPE (TYPE is not string type)
  int32_t check_eval_error_id = 0;
  if (op_is->id == SPVM_OP_C_ID_IS_TYPE || op_is->id == SPVM_OP_C_ID_ISA) {
    if (op_operand->id == SPVM_OP_C_ID_EXCEPTION_VAR) {
      SPVM_TYPE* type = op_type->uv.type;
      if (!(strcmp(type->basic_type->name, "string") == 0 && type->dimension == 0)) {
        check_eval_error_id = 1;
      }
    }
  }
  
  if (check_eval_error_id) {
    SPVM_OP* op_is_error = NULL;
    if (op_is->id == SPVM_OP_C_ID_IS_TYPE) {
      op_is_error = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_IS_ERROR, op_is->file, op_is->line);
    }
    else if (op_is->id == SPVM_OP_C_ID_ISA) {
      op_is_error = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ISA_ERROR, op_is->file, op_is->line);
    }
    else {
      assert(0);
    }
    
    SPVM_OP* op_eval_error_id = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_EVAL_ERROR_ID, op_is->file, op_is->line);
    
    op_is_error = SPVM_OP_build_type_check(compiler, op_is_error, op_eval_error_id, op_type);
    
    SPVM_OP* op_logical_and = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_LOGICAL_AND, op_is->file, op_is->line);
    op_is = SPVM_OP_build_logical_and(compiler, op_logical_and, op_operand, op_is_error);
  }
  else {
    SPVM_OP_insert_child(compiler, op_is, op_is->last, op_operand);
    SPVM_OP_insert_child(compiler, op_is, op_is->last, op_type);
  }
  
  return op_is;
}

SPVM_OP* SPVM_OP_build_special_assign(SPVM_COMPILER* compiler, SPVM_OP* op_special_assign, SPVM_OP* op_dist, SPVM_OP* op_src) {
  
  /*
    ++$var, ++$VAR, ++$@
    (
      my $old = $var,
      $var = $old + 1,
    )

    ++$$var_ref
    (
      my $old = $$var_ref,
      $$var_ref = $old + 1,
    )
    
    ++ARRAY->[INDEX]
    (
      my $array = ARRAY,
      my $index = INDEX,
      my $old = $array->[$index],
      $array->[$index] = $old + 1,
    )

    ++OBJECT->{FIELD_NAME}
    (
      my $object = OBJECT,
      my $old = $object->{FIELD_NAME},
      $object->{FIELD_NAME} = $old + 1,
    )

    ++ARRAY->[INDEX]{FIELD_NAME}
    (
      my $array = ARRAY,
      my $index = INDEX,
      my $old = $array->[$index]{FIELD_NAME},
      $array->[$index]{FIELD_NAME} = $old + 1,
    )

    ACCESS++
    (
      ...,
      ...,
      $old,
    )
  */

  SPVM_OP* op_sequence = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_SEQUENCE, op_special_assign->file, op_special_assign->line);
  
  SPVM_OP* op_assign_save_old = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_special_assign->file, op_special_assign->line);
  
  SPVM_OP* op_name_var_old = SPVM_OP_new_op_name_tmp_var(compiler, op_special_assign->file, op_special_assign->line);
  
  SPVM_OP* op_var_old = SPVM_OP_new_op_var(compiler, op_name_var_old);
  
  SPVM_OP* op_var_old_decl = SPVM_OP_new_op_var_decl(compiler, op_special_assign->file, op_special_assign->line);
  SPVM_OP_build_var_decl(compiler, op_var_old_decl, op_var_old, NULL, NULL);
  
  SPVM_OP* op_assign_update = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_special_assign->file, op_special_assign->line);
  SPVM_OP* op_var_old_clone = SPVM_OP_clone_op_var(compiler, op_var_old);
  
  int32_t culc_op_id;
  
  switch (op_special_assign->id) {
    case SPVM_OP_C_ID_PRE_INC:
    case SPVM_OP_C_ID_POST_INC:
    {
      if (!SPVM_OP_is_mutable(compiler, op_dist)) {
        SPVM_COMPILER_error(compiler, "The operand of ++ operator must be mutable.\n  at %s line %d", op_dist->file, op_dist->line);
        return op_special_assign;
      }
      
      culc_op_id = SPVM_OP_C_ID_ADD;
      break;
    }
    case SPVM_OP_C_ID_PRE_DEC:
    case SPVM_OP_C_ID_POST_DEC:
    {
      if (!SPVM_OP_is_mutable(compiler, op_dist)) {
        SPVM_COMPILER_error(compiler, "The operand of -- operator must be mutable.\n  at %s line %d", op_dist->file, op_dist->line);
        return op_special_assign;
      }
      
      culc_op_id = SPVM_OP_C_ID_SUBTRACT;
      break;
    }
    case SPVM_OP_C_ID_SPECIAL_ASSIGN: {
      if (!SPVM_OP_is_mutable(compiler, op_dist)) {
        SPVM_COMPILER_error(compiler, "The left operand of a special assignment operator must be mutable.\n  at %s line %d", op_dist->file, op_dist->line);
        return op_special_assign;
      }
      
      switch (op_special_assign->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_MODULO: {
          culc_op_id = SPVM_OP_C_ID_MODULO;
          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_ARITHMETIC_SHIFT: {
          culc_op_id = SPVM_OP_C_ID_RIGHT_ARITHMETIC_SHIFT;
          break;
        }
        case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_RIGHT_LOGICAL_SHIFT: {
          culc_op_id = SPVM_OP_C_ID_RIGHT_LOGICAL_SHIFT;
          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_STRING_CONCAT: {
          culc_op_id = SPVM_OP_C_ID_STRING_CONCAT;
          break;
        }
        case SPVM_OP_C_FLAG_SPECIAL_ASSIGN_DEFINED_OR: {
          culc_op_id = SPVM_OP_C_ID_DEFINED_OR;
          break;
        }
        default: {
          assert(0);
          break;
        }
      }
      
      break;
    }
  }
  
  SPVM_OP* op_culc = SPVM_OP_new_op(compiler, culc_op_id, op_special_assign->file, op_special_assign->line);
  
  op_culc->allow_narrowing_conversion = 1;
  op_culc->original_id = op_special_assign->id;
  
  if (culc_op_id == SPVM_OP_C_ID_DEFINED_OR) {
    op_culc = SPVM_OP_build_defined_or(compiler, op_culc, op_var_old_clone, op_src);
  }
  else {
    SPVM_OP_build_binary_op(compiler, op_culc, op_var_old_clone, op_src);
  }
  
  if (op_dist->id == SPVM_OP_C_ID_VAR || op_dist->id == SPVM_OP_C_ID_EXCEPTION_VAR) {
    SPVM_OP_build_assign(compiler, op_assign_save_old, op_var_old, op_dist);
    SPVM_OP* op_var = op_dist;
    SPVM_OP* op_var_clone = SPVM_OP_clone_op_var(compiler, op_var);
    SPVM_OP* op_dist_clone = op_var_clone;
    SPVM_OP_build_assign(compiler, op_assign_update, op_dist_clone, op_culc);
  }
  else if (op_dist->id == SPVM_OP_C_ID_DEREFERENCE) {
    SPVM_OP_build_assign(compiler, op_assign_save_old, op_var_old, op_dist);
    SPVM_OP* op_deref = op_dist;
    SPVM_OP* op_var_deref = op_deref->first;
    SPVM_OP* op_deref_clone = SPVM_OP_clone_op_deref(compiler, op_deref, op_var_deref);
    SPVM_OP* op_dist_clone = op_deref_clone;
    SPVM_OP_build_assign(compiler, op_assign_update, op_dist_clone, op_culc);
  }
  else if (op_dist->id == SPVM_OP_C_ID_ARRAY_ELEMENT_ACCESS || op_dist->id == SPVM_OP_C_ID_HASH_VALUE_ACCESS) {
    
    int32_t is_hash_value_access = op_dist->id == SPVM_OP_C_ID_HASH_VALUE_ACCESS ? 1 : 0;
    
    SPVM_OP* op_element_access = op_dist;
    
    SPVM_OP* op_array = op_element_access->first;
    SPVM_OP* op_index = op_element_access->last;
    
    SPVM_OP_cut_op(compiler, op_element_access->first);
    SPVM_OP_cut_op(compiler, op_element_access->last);
    
    SPVM_OP* op_assign_array_get = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_array->file, op_array->line);
    SPVM_OP* op_name_var_array = SPVM_OP_new_op_name_tmp_var(compiler, op_array->file, op_array->line);
    SPVM_OP* op_var_array_get = SPVM_OP_new_op_var(compiler, op_name_var_array);
    SPVM_OP* op_var_array_get_decl = SPVM_OP_new_op_var_decl(compiler, op_array->file,op_array->line);
    SPVM_OP_build_var_decl(compiler, op_var_array_get_decl, op_var_array_get, NULL, NULL);
    op_assign_array_get = SPVM_OP_build_assign(compiler, op_assign_array_get, op_var_array_get, op_array);
    SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_array_get);
    
    SPVM_OP* op_assign_index_get = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_index->file, op_index->line);
    SPVM_OP* op_name_var_index = SPVM_OP_new_op_name_tmp_var(compiler, op_index->file, op_index->line);
    SPVM_OP* op_var_index_get = SPVM_OP_new_op_var(compiler, op_name_var_index);
    SPVM_OP* op_var_index_get_decl = SPVM_OP_new_op_var_decl(compiler, op_index->file,op_index->line);
    SPVM_OP_build_var_decl(compiler, op_var_index_get_decl, op_var_index_get, NULL, NULL);
    op_assign_index_get = SPVM_OP_build_assign(compiler, op_assign_index_get, op_var_index_get, op_index);
    SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_index_get);
    
    SPVM_OP* op_element_access_clone_get = SPVM_OP_clone_op_element_access(compiler, op_element_access, op_var_array_get, op_var_index_get, is_hash_value_access);
    SPVM_OP_build_assign(compiler, op_assign_save_old, op_var_old, op_element_access_clone_get);
    
    SPVM_OP* op_assign_array_set = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_array->file, op_array->line);
    SPVM_OP* op_var_array_set = SPVM_OP_new_op_var(compiler, op_name_var_array);
    SPVM_OP* op_var_array_set_decl = SPVM_OP_new_op_var_decl(compiler, op_array->file,op_array->line);
    
    SPVM_OP* op_assign_index_set = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_index->file, op_index->line);
    SPVM_OP* op_var_index_set = SPVM_OP_new_op_var(compiler, op_name_var_index);
    SPVM_OP* op_var_index_set_decl = SPVM_OP_new_op_var_decl(compiler, op_index->file,op_index->line);
    
    SPVM_OP* op_element_access_clone_set = SPVM_OP_clone_op_element_access(compiler, op_element_access, op_var_array_set, op_var_index_set, is_hash_value_access);
    SPVM_OP* op_dist_clone_set = op_element_access_clone_set;
    SPVM_OP_build_assign(compiler, op_assign_update, op_dist_clone_set, op_culc);
  }
  else if (op_dist->id == SPVM_OP_C_ID_FIELD_ACCESS) {
    
    SPVM_OP* op_field_access = op_dist;
    
    SPVM_OP* op_invocant = op_field_access->first;
    SPVM_OP* op_name_field = op_field_access->uv.field_access->op_name;
    
    SPVM_OP_cut_op(compiler, op_field_access->first);
    
    if (op_invocant->id == SPVM_OP_C_ID_ARRAY_ELEMENT_ACCESS) {
      SPVM_OP* op_element_access = op_invocant;
      
      SPVM_OP* op_array = op_element_access->first;
      SPVM_OP* op_index = op_element_access->last;
      
      SPVM_OP_cut_op(compiler, op_element_access->first);
      SPVM_OP_cut_op(compiler, op_element_access->last);
      
      SPVM_OP* op_assign_array_get = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_array->file, op_array->line);
      SPVM_OP* op_name_var_array = SPVM_OP_new_op_name_tmp_var(compiler, op_array->file, op_array->line);
      SPVM_OP* op_var_array_get = SPVM_OP_new_op_var(compiler, op_name_var_array);
      SPVM_OP* op_var_array_get_decl = SPVM_OP_new_op_var_decl(compiler, op_array->file,op_array->line);
      SPVM_OP_build_var_decl(compiler, op_var_array_get_decl, op_var_array_get, NULL, NULL);
      op_assign_array_get = SPVM_OP_build_assign(compiler, op_assign_array_get, op_var_array_get, op_array);
      SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_array_get);
      
      SPVM_OP* op_assign_index_get = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_index->file, op_index->line);
      SPVM_OP* op_name_var_index = SPVM_OP_new_op_name_tmp_var(compiler, op_index->file, op_index->line);
      SPVM_OP* op_var_index_get = SPVM_OP_new_op_var(compiler, op_name_var_index);
      SPVM_OP* op_var_index_get_decl = SPVM_OP_new_op_var_decl(compiler, op_index->file,op_index->line);
      SPVM_OP_build_var_decl(compiler, op_var_index_get_decl, op_var_index_get, NULL, NULL);
      op_assign_index_get = SPVM_OP_build_assign(compiler, op_assign_index_get, op_var_index_get, op_index);
      SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_index_get);
      
      SPVM_OP* op_array_field_access_clone_get = SPVM_OP_clone_op_array_field_access(compiler, op_field_access, op_name_field, op_element_access, op_var_array_get, op_var_index_get);
      SPVM_OP_build_assign(compiler, op_assign_save_old, op_var_old, op_array_field_access_clone_get);
      
      SPVM_OP* op_assign_array_set = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_array->file, op_array->line);
      SPVM_OP* op_var_array_set = SPVM_OP_new_op_var(compiler, op_name_var_array);
      SPVM_OP* op_var_array_set_decl = SPVM_OP_new_op_var_decl(compiler, op_array->file,op_array->line);
      SPVM_OP* op_assign_index_set = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_index->file, op_index->line);
      SPVM_OP* op_var_index_set = SPVM_OP_new_op_var(compiler, op_name_var_index);
      SPVM_OP* op_var_index_set_decl = SPVM_OP_new_op_var_decl(compiler, op_index->file,op_index->line);
      SPVM_OP* op_array_field_access_clone_set = SPVM_OP_clone_op_array_field_access(compiler, op_element_access, op_name_field, op_element_access, op_var_array_set, op_var_index_set);
      SPVM_OP* op_dist_clone_set = op_array_field_access_clone_set;
      SPVM_OP_build_assign(compiler, op_assign_update, op_dist_clone_set, op_culc);
    }
    else {
      SPVM_OP* op_assign_invocant_get = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_invocant->file, op_invocant->line);
      SPVM_OP* op_name_var_invocant = SPVM_OP_new_op_name_tmp_var(compiler, op_invocant->file, op_invocant->line);
      SPVM_OP* op_var_invocant_get = SPVM_OP_new_op_var(compiler, op_name_var_invocant);
      SPVM_OP* op_var_invocant_get_decl = SPVM_OP_new_op_var_decl(compiler, op_invocant->file,op_invocant->line);
      SPVM_OP_build_var_decl(compiler, op_var_invocant_get_decl, op_var_invocant_get, NULL, NULL);
      op_assign_invocant_get = SPVM_OP_build_assign(compiler, op_assign_invocant_get, op_var_invocant_get, op_invocant);
      SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_invocant_get);
      
      SPVM_OP* op_name_field_get = SPVM_OP_new_op_name(compiler, op_name_field->uv.name, op_name_field->file, op_name_field->line);
      SPVM_OP* op_field_access_clone_get = SPVM_OP_clone_op_field_access(compiler, op_field_access, op_var_invocant_get, op_name_field_get);
      SPVM_OP_build_assign(compiler, op_assign_save_old, op_var_old, op_field_access_clone_get);
      
      SPVM_OP* op_assign_invocant_set = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_invocant->file, op_invocant->line);
      SPVM_OP* op_var_invocant_set = SPVM_OP_new_op_var(compiler, op_name_var_invocant);
      SPVM_OP* op_var_invocant_set_decl = SPVM_OP_new_op_var_decl(compiler, op_invocant->file,op_invocant->line);
      SPVM_OP* op_name_field_set = SPVM_OP_new_op_name(compiler, op_name_field->uv.name, op_name_field->file, op_name_field->line);
      SPVM_OP* op_field_access_clone_set = SPVM_OP_clone_op_field_access(compiler, op_field_access, op_var_invocant_set, op_name_field_set);
      SPVM_OP* op_dist_clone_set = op_field_access_clone_set;
      SPVM_OP_build_assign(compiler, op_assign_update, op_dist_clone_set, op_culc);
    }
  }
  else {
    assert(0);
  }
  
  SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_save_old);
  SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_assign_update);
  
  // Return the old value
  if (op_special_assign->id == SPVM_OP_C_ID_POST_INC || op_special_assign->id == SPVM_OP_C_ID_POST_DEC) {
    SPVM_OP* op_var_old_clone_ret = SPVM_OP_clone_op_var(compiler, op_var_old_clone);
    SPVM_OP_insert_child(compiler, op_sequence, op_sequence->last, op_var_old_clone_ret);
  }
  
  return op_sequence;
}

SPVM_OP* SPVM_OP_build_inc(SPVM_COMPILER* compiler, SPVM_OP* op_inc, SPVM_OP* op_operand) {
  
  SPVM_OP_insert_child(compiler, op_inc, op_inc->last, op_operand);
  
  SPVM_OP* op_constant = SPVM_OP_new_op_constant_int(compiler, 1, op_operand->file, op_operand->line);
  op_inc = SPVM_OP_build_special_assign(compiler, op_inc, op_operand, op_constant);
  
  return op_inc;
}

SPVM_OP* SPVM_OP_build_dec(SPVM_COMPILER* compiler, SPVM_OP* op_dec, SPVM_OP* op_operand) {
  
  SPVM_OP_insert_child(compiler, op_dec, op_dec->last, op_operand);
  
  SPVM_OP* op_constant = SPVM_OP_new_op_constant_int(compiler, 1, op_operand->file, op_operand->line);
  op_dec = SPVM_OP_build_special_assign(compiler, op_dec, op_operand, op_constant);
  
  return op_dec;
}

SPVM_OP* SPVM_OP_build_logical_and(SPVM_COMPILER* compiler, SPVM_OP* op_logical_and, SPVM_OP* op_left_operand, SPVM_OP* op_right_operand) {
  
  op_logical_and = SPVM_OP_build_binary_op(compiler, op_logical_and, op_left_operand, op_right_operand);
  
  return op_logical_and;
}

SPVM_OP* SPVM_OP_build_logical_or(SPVM_COMPILER* compiler, SPVM_OP* op_logical_or, SPVM_OP* op_left_operand, SPVM_OP* op_right_operand) {
  
  op_logical_or = SPVM_OP_build_binary_op(compiler, op_logical_or, op_left_operand, op_right_operand);
  
  return op_logical_or;
}

SPVM_OP* SPVM_OP_build_logical_not(SPVM_COMPILER* compiler, SPVM_OP* op_logical_not, SPVM_OP* op_operand) {
  
  op_logical_not = SPVM_OP_build_unary_op(compiler, op_logical_not, op_operand);
  
  return op_logical_not;
}

SPVM_OP* SPVM_OP_build_defined_or(SPVM_COMPILER* compiler, SPVM_OP* op_defined_or, SPVM_OP* op_left_operand, SPVM_OP* op_right_operand) {
  
  op_defined_or = SPVM_OP_build_binary_op(compiler, op_defined_or, op_left_operand, op_right_operand);
  
  return op_defined_or;
}

SPVM_OP* SPVM_OP_build_ternary_op(SPVM_COMPILER* compiler, SPVM_OP* op_ternary, SPVM_OP* op_condition, SPVM_OP* op_left_operand, SPVM_OP* op_right_operand) {
  
  SPVM_OP_insert_child(compiler, op_ternary, op_ternary->last, op_condition);
  SPVM_OP_insert_child(compiler, op_ternary, op_ternary->last, op_left_operand);
  SPVM_OP_insert_child(compiler, op_ternary, op_ternary->last, op_right_operand);
  
  return op_ternary;
}

SPVM_OP* SPVM_OP_build_assign(SPVM_COMPILER* compiler, SPVM_OP* op_assign, SPVM_OP* op_dist, SPVM_OP* op_src) {
  
  // Build op
  // Exchange left and right for excecution order
  
  SPVM_OP_insert_child(compiler, op_assign, op_assign->last, op_src);
  SPVM_OP_insert_child(compiler, op_assign, op_assign->last, op_dist);
  
  op_dist->is_dist = 1;
  
  if (!SPVM_OP_is_mutable(compiler, op_dist)) {
    SPVM_COMPILER_error(compiler, "The left operand of assignment operator must be mutable.\n  at %s line %d", op_dist->file, op_dist->line);
  }
  
  return op_assign;
}

SPVM_OP* SPVM_OP_build_return(SPVM_COMPILER* compiler, SPVM_OP* op_return, SPVM_OP* op_operand) {
  
  if (op_operand) {
    SPVM_OP_insert_child(compiler, op_return, op_return->last, op_operand);
  }
  
  return op_return;
}

SPVM_OP* SPVM_OP_build_operator_statement(SPVM_COMPILER* compiler, SPVM_OP* op_operator) {
  
  return op_operator;
}

SPVM_OP* SPVM_OP_build_die(SPVM_COMPILER* compiler, SPVM_OP* op_die, SPVM_OP* op_operand, SPVM_OP* op_type) {
  
  if (!op_operand || op_operand->id == SPVM_OP_C_ID_UNDEF) {
    // Default error message
    op_operand = SPVM_OP_new_op_constant_string(compiler, "Error", strlen("Error"), op_die->file, op_die->line);
  }
  
  // Exception variable
  SPVM_OP* op_exception_var = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_EXCEPTION_VAR, op_operand->file, op_operand->line);
  
  // Assign
  SPVM_OP* op_assign = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_operand->file, op_operand->line);
  SPVM_OP_build_assign(compiler, op_assign, op_exception_var, op_operand);
  
  SPVM_OP_insert_child(compiler, op_die, op_die->last, op_assign);
  
  if (!op_type) {
    if (op_operand->id == SPVM_OP_C_ID_EXCEPTION_VAR) {
      SPVM_OP* op_eval_error_id = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_EVAL_ERROR_ID, op_die->file, op_die->line);
      op_type = op_eval_error_id;
    }
    else {
      op_type = SPVM_OP_new_op_unresolved_type(compiler, "Error", 0, 0, op_die->file, op_die->line);
    }
  }
  
  SPVM_OP_insert_child(compiler, op_die, op_die->last, op_type);
  
  return op_die;
}

SPVM_OP* SPVM_OP_build_warn(SPVM_COMPILER* compiler, SPVM_OP* op_warn, SPVM_OP* op_operand) {
  
  if (!op_operand) {
    op_operand = SPVM_OP_new_op_constant_string(compiler, "Warning", strlen("Warning"), op_warn->file, op_warn->line);
  }
  
  SPVM_OP_insert_child(compiler, op_warn, op_warn->last, op_operand);
  
  return op_warn;
}

SPVM_OP* SPVM_OP_build_warn_level(SPVM_COMPILER* compiler, SPVM_OP* op_warn_level, SPVM_OP* op_operand, SPVM_OP* op_level) {
  
  /*
    Before

    warn_level $string, $level;

    After

    my $tmp_level = $level;
    my $tmp_string = $string;
    if ($tmp_level >= 0) {
      if ($tmp_level > 0) {
        die $tmp_string;
      }
      else {
        warn $tmp_string;
      }
    }
  */
  
  int32_t no_scope = 1;
  
  SPVM_OP* op_statements = SPVM_OP_new_op_list(compiler, op_warn_level->file, op_warn_level->line);

  // my $tmp_level = $level;
  SPVM_OP* op_var_tmp_level_name = SPVM_OP_new_op_name_tmp_var(compiler, op_warn_level->file, op_warn_level->line);
  SPVM_OP* op_var_tmp_level = SPVM_OP_new_op_var(compiler, op_var_tmp_level_name);
  SPVM_OP* op_var_decl_tmp_level = SPVM_OP_new_op_var_decl_arg(compiler, op_warn_level->file, op_warn_level->line);
  op_var_tmp_level = SPVM_OP_build_var_decl(compiler, op_var_decl_tmp_level, op_var_tmp_level, NULL, NULL);
  SPVM_OP* op_assign_tmp_level = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_warn_level->file, op_warn_level->line);
  SPVM_OP_build_assign(compiler, op_assign_tmp_level, op_var_tmp_level, op_level);
  SPVM_OP_insert_child(compiler, op_statements, op_statements->last, op_assign_tmp_level);

  // my $tmp_string = $string;
  SPVM_OP* op_var_tmp_string_name = SPVM_OP_new_op_name_tmp_var(compiler, op_warn_level->file, op_warn_level->line);
  SPVM_OP* op_var_tmp_string = SPVM_OP_new_op_var(compiler, op_var_tmp_string_name);
  SPVM_OP* op_var_decl_tmp_string = SPVM_OP_new_op_var_decl_arg(compiler, op_warn_level->file, op_warn_level->line);
  op_var_tmp_string = SPVM_OP_build_var_decl(compiler, op_var_decl_tmp_string, op_var_tmp_string, NULL, NULL);
  SPVM_OP* op_assign_tmp_string = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ASSIGN, op_warn_level->file, op_warn_level->line);
  SPVM_OP_build_assign(compiler, op_assign_tmp_string, op_var_tmp_string, op_operand);
  SPVM_OP_insert_child(compiler, op_statements, op_statements->last, op_assign_tmp_string);

  // $tmp_level >= 0
  SPVM_OP* op_if_outer = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_IF, op_warn_level->file, op_warn_level->line);
  SPVM_OP* op_ge = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_NUMERIC_COMPARISON_GE, op_warn_level->file, op_warn_level->line);
  SPVM_OP* op_var_tmp_level_ge = SPVM_OP_clone_op_var(compiler, op_var_tmp_level);
  SPVM_OP* op_constant_zero_outer = SPVM_OP_new_op_constant_int(compiler, 0, op_warn_level->file, op_warn_level->line);
  op_ge = SPVM_OP_build_binary_op(compiler, op_ge, op_var_tmp_level_ge, op_constant_zero_outer);

  // $tmp_level > 0
  SPVM_OP* op_if_inner = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_IF, op_warn_level->file, op_warn_level->line);
  SPVM_OP* op_gt = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_NUMERIC_COMPARISON_GT, op_warn_level->file, op_warn_level->line);
  SPVM_OP* op_var_tmp_level_gt = SPVM_OP_clone_op_var(compiler, op_var_tmp_level);
  SPVM_OP* op_constant_zero_inner = SPVM_OP_new_op_constant_int(compiler, 0, op_warn_level->file, op_warn_level->line);
  op_gt = SPVM_OP_build_binary_op(compiler, op_gt, op_var_tmp_level_gt, op_constant_zero_inner);

  // die $tmp_string;
  SPVM_OP* op_die = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_DIE, op_warn_level->file, op_warn_level->line);
  SPVM_OP* op_var_tmp_string_die = SPVM_OP_clone_op_var(compiler, op_var_tmp_string);
  op_die = SPVM_OP_build_die(compiler, op_die, op_var_tmp_string_die, NULL);

  // warn $tmp_string;
  SPVM_OP* op_warn = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_WARN, op_warn_level->file, op_warn_level->line);
  SPVM_OP* op_var_tmp_string_warn = SPVM_OP_clone_op_var(compiler, op_var_tmp_string);
  op_warn = SPVM_OP_build_warn(compiler, op_warn, op_var_tmp_string_warn);
  
  // Build inner IF with ELSE
  op_if_inner = SPVM_OP_build_if_statement(compiler, op_if_inner, op_gt, op_die, op_warn, no_scope);
  
  SPVM_OP* op_block_false = SPVM_OP_new_op_block(compiler, op_warn_level->file, op_warn_level->line);
  
  // Build outer IF
  op_if_outer = SPVM_OP_build_if_statement(compiler, op_if_outer, op_ge, op_if_inner, op_block_false, no_scope);
  
  SPVM_OP_insert_child(compiler, op_statements, op_statements->last, op_if_outer);

  return op_statements;
}

SPVM_OP* SPVM_OP_build_basic_type_id(SPVM_COMPILER* compiler, SPVM_OP* op_basic_type_id, SPVM_OP* op_type) {
  
  SPVM_OP_insert_child(compiler, op_basic_type_id, op_basic_type_id->last, op_type);
  
  return op_basic_type_id;
}

SPVM_OP* SPVM_OP_build_print(SPVM_COMPILER* compiler, SPVM_OP* op_print, SPVM_OP* op_operand, int32_t is_stderr) {
  
  SPVM_OP_insert_child(compiler, op_print, op_print->last, op_operand);
  
  if (is_stderr) {
    op_print->flag |= SPVM_OP_C_FLAG_PRINT_STDERR;
  }
  
  return op_print;
}

SPVM_OP* SPVM_OP_build_make_read_only(SPVM_COMPILER* compiler, SPVM_OP* op_make_read_only, SPVM_OP* op_operand) {
  
  SPVM_OP_insert_child(compiler, op_make_read_only, op_make_read_only->last, op_operand);
  
  return op_make_read_only;
}

SPVM_OP* SPVM_OP_build_make_fixed_length(SPVM_COMPILER* compiler, SPVM_OP* op_make_fixed_length, SPVM_OP* op_operand) {
  
  SPVM_OP_insert_child(compiler, op_make_fixed_length, op_make_fixed_length->last, op_operand);
  
  return op_make_fixed_length;
}

SPVM_OP* SPVM_OP_build_enable_options(SPVM_COMPILER* compiler, SPVM_OP* op_enable_options, SPVM_OP* op_operand) {
  
  SPVM_OP_insert_child(compiler, op_enable_options, op_enable_options->last, op_operand);
  
  return op_enable_options;
}

SPVM_OP* SPVM_OP_build_disable_options(SPVM_COMPILER* compiler, SPVM_OP* op_disable_options, SPVM_OP* op_operand) {
  
  SPVM_OP_insert_child(compiler, op_disable_options, op_disable_options->last, op_operand);
  
  return op_disable_options;
}

SPVM_OP* SPVM_OP_build_basic_type(SPVM_COMPILER* compiler, SPVM_OP* op_name) {
  
  const char* name = op_name->uv.name;
  
  SPVM_OP* op_type;
  
  // Add basic type
  SPVM_BASIC_TYPE* found_basic_type = SPVM_HASH_get(compiler->basic_type_symtable, name, strlen(name));
  if (found_basic_type) {
    op_type = SPVM_OP_new_op_type(compiler, name, found_basic_type, 0, 0, op_name->file, op_name->line);
  }
  else {
    SPVM_BASIC_TYPE* new_basic_type = SPVM_LIST_get(compiler->basic_types, 0);
    op_type = SPVM_OP_new_op_type(compiler, name, new_basic_type, 0, 0, op_name->file, op_name->line);
  }
  
  SPVM_OP_insert_child(compiler, op_type, op_type->last, op_name);
  
  return op_type;
}

SPVM_OP* SPVM_OP_build_ref_type(SPVM_COMPILER* compiler, SPVM_OP* op_type_original) {
  
  // Type
  SPVM_TYPE* type = SPVM_TYPE_new(compiler, op_type_original->uv.type->basic_type->id, op_type_original->uv.type->dimension, SPVM_NATIVE_C_TYPE_FLAG_REF);
  type->unresolved_basic_type_name = op_type_original->uv.type->unresolved_basic_type_name;
  
  // Type OP
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, op_type_original->file, op_type_original->line);
  SPVM_OP_insert_child(compiler, op_type, op_type->last, op_type_original);

  return op_type;
}

SPVM_OP* SPVM_OP_build_mutable_type(SPVM_COMPILER* compiler, SPVM_OP* op_type) {
  
  if (op_type->uv.type->flag & SPVM_NATIVE_C_TYPE_FLAG_MUTABLE) {
    SPVM_COMPILER_error(compiler, "Duplicated mutable type modifier.\n  at %s line %d", op_type->file, op_type->line);
  }
  
  // Type
  SPVM_TYPE* type = SPVM_TYPE_new(compiler, op_type->uv.type->basic_type->id, op_type->uv.type->dimension, op_type->uv.type->flag | SPVM_NATIVE_C_TYPE_FLAG_MUTABLE);
  type->unresolved_basic_type_name = op_type->uv.type->unresolved_basic_type_name;
  
  // Type OP
  SPVM_OP* op_mutable_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, op_type->file, op_type->line);
  
  return op_mutable_type;
}

SPVM_OP* SPVM_OP_build_union_type(SPVM_COMPILER* compiler, SPVM_OP* op_type_left, SPVM_OP* op_type_right) {
  
  SPVM_OP* op_union_type;
  SPVM_TYPE* type_left = op_type_left->uv.type;
  SPVM_TYPE* type_right = op_type_right->uv.type;
  
  // If left is already a union type, reuse its list and append the right type
  if (type_left->union_types) {
    op_union_type = op_type_left;
    SPVM_LIST_push(type_left->union_types, type_right);
  }
  else {
    // Create a new union container (any object type)
    op_union_type = SPVM_OP_new_op_any_object_type(compiler, op_type_left->file, op_type_left->line);
    SPVM_TYPE* union_type = op_union_type->uv.type;
    
    // Allocate permanent list for the union components
    union_type->union_types = SPVM_LIST_new_list_permanent(compiler->current_each_compile_allocator, 0);
    
    // Add both types to the list in order
    SPVM_LIST_push(union_type->union_types, type_left);
    SPVM_LIST_push(union_type->union_types, type_right);
  }
  
  return op_union_type;
}

SPVM_OP* SPVM_OP_build_union(SPVM_COMPILER* compiler, SPVM_OP* op_type) {
  
  if (op_type->uv.type->flag & SPVM_NATIVE_C_TYPE_FLAG_MUTABLE) {
    SPVM_COMPILER_error(compiler, "Duplicated mutable type modifier.\n  at %s line %d", op_type->file, op_type->line);
  }
  
  // Type
  SPVM_TYPE* type = SPVM_TYPE_new(compiler, op_type->uv.type->basic_type->id, op_type->uv.type->dimension, op_type->uv.type->flag | SPVM_NATIVE_C_TYPE_FLAG_MUTABLE);
  type->unresolved_basic_type_name = op_type->uv.type->unresolved_basic_type_name;
  
  // Type OP
  SPVM_OP* op_mutable_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, op_type->file, op_type->line);
  
  return op_mutable_type;
}

SPVM_OP* SPVM_OP_build_array_type(SPVM_COMPILER* compiler, SPVM_OP* op_type_elem, SPVM_OP* op_length, int32_t is_varargs) {
  
  // Type
  SPVM_TYPE* type = SPVM_TYPE_new(compiler, op_type_elem->uv.type->basic_type->id, op_type_elem->uv.type->dimension + 1, 0);
  type->unresolved_basic_type_name = op_type_elem->uv.type->unresolved_basic_type_name;
  
  // Type OP
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, op_type_elem->file, op_type_elem->line);
  SPVM_OP_insert_child(compiler, op_type, op_type->last, op_type_elem);
  
  if (op_length) {
    SPVM_OP_insert_child(compiler, op_type, op_type->last, op_length);
  }
  else {
    SPVM_OP* op_do_nothing = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_DO_NOTHING, op_type_elem->file, op_type_elem->line);
    SPVM_OP_insert_child(compiler, op_type, op_type->last, op_do_nothing);
  }
  
  if (is_varargs) {
    op_type->uv.type->flag |= SPVM_NATIVE_C_TYPE_FLAG_VARARGS;
    
    // The use of variable length arguments is restricted to object[] type
    if (!(strcmp(op_type->uv.type->unresolved_basic_type_name, "object") == 0 && op_type->uv.type->dimension == 1)) {
      SPVM_COMPILER_error(compiler, "The use of variable length arguments is restricted to object[] type.\n  at %s line %d", op_type_elem->file, op_type_elem->line);
    }
  }
  
  return op_type;
}

SPVM_OP* SPVM_OP_build_copy_fields(SPVM_COMPILER* compiler, SPVM_OP* op_copy_fields, SPVM_OP* op_dist, SPVM_OP* op_src, SPVM_OP* op_type) {
  
  SPVM_OP_insert_child(compiler, op_copy_fields, op_copy_fields->last, op_dist);
  SPVM_OP_insert_child(compiler, op_copy_fields, op_copy_fields->last, op_src);
  SPVM_OP_insert_child(compiler, op_copy_fields, op_copy_fields->last, op_type);
  
  return op_copy_fields;
}

SPVM_OP* SPVM_OP_build_break_point(SPVM_COMPILER* compiler, SPVM_OP* op_break_point) {
  
  return op_break_point;
}

SPVM_OP* SPVM_OP_new_op_bool(SPVM_COMPILER* compiler, SPVM_OP* op_operand, const char* file, int32_t line) {
  
  SPVM_OP* op_bool = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CONDITION_EVALUATION, file, line);
  SPVM_OP_insert_child(compiler, op_bool, op_bool->last, op_operand);
  
  return op_bool;
}

SPVM_OP* SPVM_OP_new_op_var_decl(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_OP* op_var_decl = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_VAR_DECL, file, line);
  SPVM_VAR_DECL* var_decl = SPVM_VAR_DECL_new(compiler);
  
  op_var_decl->uv.var_decl = var_decl;
  var_decl->op_var_decl = op_var_decl;
  
  return op_var_decl;
}

SPVM_OP* SPVM_OP_new_op_var_decl_arg(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_OP* op_var_decl = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_VAR_DECL, file, line);
  SPVM_VAR_DECL* var_decl = SPVM_VAR_DECL_new_arg(compiler);
  
  op_var_decl->uv.var_decl = var_decl;
  var_decl->op_var_decl = op_var_decl;
  
  return op_var_decl;
}

SPVM_OP* SPVM_OP_new_op_attribute(SPVM_COMPILER* compiler, int32_t id, const char* file, int32_t line) {
  SPVM_OP* op_attribute = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ATTRIBUTE, file, line);
  
  SPVM_ATTRIBUTE* attribute = SPVM_ATTRIBUTE_new(compiler);
  attribute->id = id;
  op_attribute->uv.attribute = attribute;
  
  return op_attribute;
}

SPVM_OP* SPVM_OP_new_op_undef(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_OP* op_undef = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_UNDEF, file, line);
  
  return op_undef;
}

SPVM_OP* SPVM_OP_new_op_block(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_OP* op_block = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_BLOCK, file, line);
  
  SPVM_BLOCK* block = SPVM_BLOCK_new(compiler);
  op_block->uv.block = block;
  
  return op_block;
}

SPVM_OP* SPVM_OP_new_op_use(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_OP* op_use = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_USE, file, line);
  
  SPVM_USE* use = SPVM_USE_new(compiler);
  op_use->uv.use = use;
  
  return op_use;
}

SPVM_OP* SPVM_OP_new_op_name(SPVM_COMPILER* compiler, const char* name, const char* file, int32_t line) {
  
  SPVM_OP* op_name = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_NAME, file, line);
  
  SPVM_STRING* name_string = SPVM_STRING_new(compiler, name, strlen(name));
  
  name = name_string->value;
  
  op_name->uv.name = name;
  
  return op_name;
}

SPVM_OP* SPVM_OP_new_op_var(SPVM_COMPILER* compiler, SPVM_OP* op_name) {
  
  SPVM_OP* op_var = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_VAR, op_name->file, op_name->line);

  SPVM_VAR* var = SPVM_VAR_new(compiler);
  var->op_name = op_name;
  var->name = op_name->uv.name;
  op_var->uv.var = var;
  
  return op_var;
}

SPVM_OP* SPVM_OP_new_op_var_condition_flag(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  
  SPVM_OP* op_name_var = SPVM_OP_new_op_name(compiler, "$.condition_flag", file, line);
  
  SPVM_OP* op_var = SPVM_OP_new_op_var(compiler, op_name_var);
  
  return op_var;
}

SPVM_OP* SPVM_OP_new_op_class_var_access(SPVM_COMPILER* compiler, SPVM_OP* op_class_var_name) {
  
  const char* class_var_name = op_class_var_name->uv.name;
  
  SPVM_OP* op_class_var_access = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CLASS_VAR_ACCESS, op_class_var_name->file, op_class_var_name->line);
  
  SPVM_CLASS_VAR_ACCESS* class_var_access = SPVM_CLASS_VAR_ACCESS_new(compiler);
  class_var_access->op_name = op_class_var_name;
  op_class_var_access->uv.class_var_access = class_var_access;
  
  return op_class_var_access;
}

SPVM_OP* SPVM_OP_new_op_array_field_access(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  
  SPVM_OP* op_array_field_access = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ARRAY_FIELD_ACCESS, file, line);
  
  SPVM_ARRAY_FIELD_ACCESS* array_field_access = SPVM_ARRAY_FIELD_ACCESS_new(compiler);
  
  op_array_field_access->uv.array_field_access = array_field_access;
  
  return op_array_field_access;
}

SPVM_OP* SPVM_OP_clone_op_var(SPVM_COMPILER* compiler, SPVM_OP* op_var) {
  
  SPVM_OP* op_name = SPVM_OP_new_op_name(compiler, op_var->uv.var->name, op_var->file, op_var->line);
  SPVM_OP* op_var_clone = SPVM_OP_new_op_var(compiler, op_name);
  SPVM_VAR* var_clone = op_var_clone->uv.var;
  
  var_clone->var_decl = op_var->uv.var->var_decl;
  
  return op_var_clone;
}

SPVM_OP* SPVM_OP_clone_op_field_access(SPVM_COMPILER* compiler, SPVM_OP* op_field_access, SPVM_OP* op_var_invocant, SPVM_OP* op_name_field) {
  
  SPVM_OP* op_var_invocant_clone = SPVM_OP_clone_op_var(compiler, op_var_invocant);
  
  SPVM_OP* op_field_access_clone = SPVM_OP_new_op_field_access(compiler, op_field_access->file, op_field_access->line);
  
  SPVM_OP* op_name_field_clone = SPVM_OP_new_op_name(compiler,op_name_field->uv.name, op_name_field->file, op_name_field->line);
  
  op_field_access_clone = SPVM_OP_build_field_access(compiler, op_field_access_clone, op_var_invocant_clone, op_name_field_clone);
  
  return op_field_access_clone;
}

SPVM_OP* SPVM_OP_clone_op_element_access(SPVM_COMPILER* compiler, SPVM_OP* op_element_access, SPVM_OP* op_var_array, SPVM_OP* op_var_index, int32_t is_hash_value_access) {
  SPVM_OP* op_var_array_clone = SPVM_OP_clone_op_var(compiler, op_var_array);
  SPVM_OP* op_var_index_clone = SPVM_OP_clone_op_var(compiler, op_var_index);
  SPVM_OP* op_element_access_clone = SPVM_OP_new_op(compiler, is_hash_value_access ? SPVM_OP_C_ID_HASH_VALUE_ACCESS : SPVM_OP_C_ID_ARRAY_ELEMENT_ACCESS, op_element_access->file, op_element_access->line);
  
  op_element_access_clone = SPVM_OP_build_element_access(compiler, op_element_access_clone, op_var_array_clone, op_var_index_clone);
  
  op_element_access_clone->flag = op_element_access->flag;
  
  return op_element_access_clone;
}

SPVM_OP* SPVM_OP_clone_op_array_field_access(SPVM_COMPILER* compiler, SPVM_OP* op_field_access, SPVM_OP* op_name_field, SPVM_OP* op_element_access, SPVM_OP* op_var_array, SPVM_OP* op_var_index) {
  
  SPVM_OP* op_field_access_clone = SPVM_OP_new_op_field_access(compiler, op_field_access->file, op_field_access->line);
  
  SPVM_OP* op_var_array_clone = SPVM_OP_clone_op_var(compiler, op_var_array);
  SPVM_OP* op_var_index_clone = SPVM_OP_clone_op_var(compiler, op_var_index);
  
  SPVM_OP* op_element_access_clone = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_ARRAY_ELEMENT_ACCESS, op_element_access->file, op_element_access->line);
  op_element_access_clone = SPVM_OP_build_element_access(compiler, op_element_access_clone, op_var_array_clone, op_var_index_clone);
  
  op_element_access_clone->flag = op_element_access->flag;
  
  SPVM_OP* op_name_field_clone = SPVM_OP_new_op_name(compiler, op_name_field->uv.name, op_name_field->file, op_name_field->line);
  
  op_field_access_clone = SPVM_OP_build_field_access(compiler, op_field_access_clone, op_element_access_clone, op_name_field_clone);
  
  return op_field_access_clone;
}

SPVM_OP* SPVM_OP_clone_op_deref(SPVM_COMPILER* compiler, SPVM_OP* op_deref, SPVM_OP* op_var) {
  
  SPVM_OP* op_deref_clone = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_DEREFERENCE, op_deref->file, op_deref->line);
  
  SPVM_OP* op_var_clone = SPVM_OP_clone_op_var(compiler, op_var);
  
  SPVM_OP_build_unary_op(compiler, op_deref_clone, op_var_clone);
  
  return op_deref_clone;
}

SPVM_OP* SPVM_OP_new_op_constant(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_OP* op_constant = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CONSTANT, file, line);
  SPVM_CONSTANT* constant = SPVM_CONSTANT_new(compiler);
  assert(constant);
  op_constant->uv.constant = constant;
  constant->op_constant = op_constant;
  
  return op_constant;
}

SPVM_OP* SPVM_OP_new_op_constant_byte(SPVM_COMPILER* compiler, int8_t value, const char* file, int32_t line) {
  SPVM_OP* op_constant = SPVM_OP_new_op_constant(compiler, file, line);
  SPVM_CONSTANT* constant = op_constant->uv.constant;
  
  constant->value.bval = value;
  SPVM_OP* op_constant_type = SPVM_OP_new_op_byte_type(compiler, file, line);
  constant->type = op_constant_type->uv.type;
  
  return op_constant;
}

SPVM_OP* SPVM_OP_new_op_constant_short(SPVM_COMPILER* compiler, int16_t value, const char* file, int32_t line) {
  SPVM_OP* op_constant = SPVM_OP_new_op_constant(compiler, file, line);
  SPVM_CONSTANT* constant = op_constant->uv.constant;
  
  constant->value.sval = value;
  SPVM_OP* op_constant_type = SPVM_OP_new_op_short_type(compiler, file, line);
  constant->type = op_constant_type->uv.type;
  
  return op_constant;
}

SPVM_OP* SPVM_OP_new_op_constant_int(SPVM_COMPILER* compiler, int32_t value, const char* file, int32_t line) {
  SPVM_OP* op_constant = SPVM_OP_new_op_constant(compiler, file, line);
  SPVM_CONSTANT* constant = op_constant->uv.constant;
  
  assert(op_constant->uv.constant);
  
  constant->value.ival = value;
  SPVM_OP* op_constant_type = SPVM_OP_new_op_int_type(compiler, file, line);
  constant->type = op_constant_type->uv.type;
  
  return op_constant;
}

SPVM_OP* SPVM_OP_new_op_constant_long(SPVM_COMPILER* compiler, int64_t value, const char* file, int32_t line) {
  SPVM_OP* op_constant = SPVM_OP_new_op_constant(compiler, file, line);
  SPVM_CONSTANT* constant = op_constant->uv.constant;
  
  constant->value.lval = value;
  SPVM_OP* op_constant_type = SPVM_OP_new_op_long_type(compiler, file, line);
  constant->type = op_constant_type->uv.type;
  
  return op_constant;
}

SPVM_OP* SPVM_OP_new_op_constant_float(SPVM_COMPILER* compiler, float value, const char* file, int32_t line) {
  SPVM_OP* op_constant = SPVM_OP_new_op_constant(compiler, file, line);
  SPVM_CONSTANT* constant = op_constant->uv.constant;
  
  constant->value.fval = value;
  SPVM_OP* op_constant_type = SPVM_OP_new_op_float_type(compiler, file, line);
  constant->type = op_constant_type->uv.type;
  
  return op_constant;
}

SPVM_OP* SPVM_OP_new_op_constant_double(SPVM_COMPILER* compiler, double value, const char* file, int32_t line) {
  SPVM_OP* op_constant = SPVM_OP_new_op_constant(compiler, file, line);
  SPVM_CONSTANT* constant = op_constant->uv.constant;
  
  constant->value.dval = value;
  SPVM_OP* op_constant_type = SPVM_OP_new_op_double_type(compiler, file, line);
  constant->type = op_constant_type->uv.type;
  
  return op_constant;
}

SPVM_OP* SPVM_OP_new_op_constant_string(SPVM_COMPILER* compiler, const char* string, int32_t length, const char* file, int32_t line) {

  SPVM_OP* op_constant = SPVM_OP_new_op_constant(compiler, file, line);
  SPVM_CONSTANT* constant = op_constant->uv.constant;
  
  SPVM_STRING* cached_string_string = SPVM_STRING_new(compiler, string, length);
  const char* cached_string = cached_string_string->value;

  constant->value.oval = (void*)cached_string;
  SPVM_OP* op_constant_type = SPVM_OP_new_op_string_type(compiler, file, line);
  constant->type = op_constant_type->uv.type;
  constant->string_length = length;
  
  return op_constant;
}

SPVM_OP* SPVM_OP_new_op_void_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_void_type(compiler);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_byte_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_byte_type(compiler);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_short_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_short_type(compiler);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_int_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_int_type(compiler);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_long_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_long_type(compiler);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_float_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_float_type(compiler);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_double_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_double_type(compiler);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_string_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_string_type(compiler);
  
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);
  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_undef_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_undef_type(compiler);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_byte_ref_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_byte_ref_type(compiler);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_short_ref_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_short_ref_type(compiler);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_int_ref_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_int_ref_type(compiler);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_long_ref_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_long_ref_type(compiler);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_float_ref_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_float_ref_type(compiler);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_double_ref_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_double_ref_type(compiler);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_any_object_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_any_object_type(compiler);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_element_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_OP* op_type = SPVM_OP_new_op_any_object_type(compiler, file, line);  
  op_type->uv.type->is_generic_element_type = 1;
  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_element_array_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_OP* op_type = SPVM_OP_new_op_any_object_array_type(compiler, file, line);
  op_type->uv.type->is_generic_element_array_type = 1;
  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_unresolved_type(SPVM_COMPILER* compiler, const char* name, int32_t type_dimension, int32_t type_flag, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_unresolved_type(compiler, name, type_dimension, type_flag);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_any_object_array_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  SPVM_TYPE* type = SPVM_TYPE_new_any_object_array_type(compiler);
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);
  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_type(SPVM_COMPILER* compiler, const char* unresolved_basic_type_name, SPVM_BASIC_TYPE* basic_type, int32_t type_dimension, int32_t type_flag, const char* file, int32_t line) {
  
  SPVM_OP* op_type = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_TYPE, file, line);
  SPVM_TYPE* type = SPVM_TYPE_new_uninitialized(compiler);
  
  type->unresolved_basic_type_name = unresolved_basic_type_name;
  type->basic_type = basic_type;
  type->dimension = type_dimension;
  type->flag = type_flag;
  
  op_type->uv.type = type;
  
  SPVM_LIST_push(compiler->op_types, op_type);
  
  return op_type;
}

SPVM_OP* SPVM_OP_new_op_true(SPVM_COMPILER* compiler, SPVM_OP* op) {
  
  SPVM_OP* op_call_method = SPVM_OP_new_op_call_method(compiler, op->file, op->line);
  SPVM_OP* op_name_basic_type = SPVM_OP_new_op_name(compiler, "Bool", op->file, op->line);
  SPVM_OP* op_name_method = SPVM_OP_new_op_name(compiler, "TRUE", op->file, op->line);
  SPVM_OP* op_operators = SPVM_OP_new_op_list(compiler, op->file, op->line);
  SPVM_OP* op_type = SPVM_OP_build_basic_type(compiler, op_name_basic_type);
  
  op_call_method = SPVM_OP_build_call_method(compiler, op_call_method, op_type, op_name_method, op_operators);
  
  return op_call_method;
}

SPVM_OP* SPVM_OP_new_op_false(SPVM_COMPILER* compiler, SPVM_OP* op) {
  
  SPVM_OP* op_call_method = SPVM_OP_new_op_call_method(compiler, op->file, op->line);
  SPVM_OP* op_name_basic_type = SPVM_OP_new_op_name(compiler, "Bool", op->file, op->line);
  SPVM_OP* op_name_method = SPVM_OP_new_op_name(compiler, "FALSE", op->file, op->line);
  SPVM_OP* op_operators = SPVM_OP_new_op_list(compiler, op->file, op->line);
  SPVM_OP* op_type = SPVM_OP_build_basic_type(compiler, op_name_basic_type);
  
  op_call_method = SPVM_OP_build_call_method(compiler, op_call_method, op_type, op_name_method, op_operators);
  
  return op_call_method;
}

SPVM_OP* SPVM_OP_new_op_field_access(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  
  SPVM_OP* op_field_access = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_FIELD_ACCESS, file, line);
  
  SPVM_FIELD_ACCESS* field_access = SPVM_FIELD_ACCESS_new(compiler);
  
  op_field_access->uv.field_access = field_access;
  
  return op_field_access;
}

SPVM_OP* SPVM_OP_new_op_call_method(SPVM_COMPILER* compiler, const char* file, int32_t line) {

  SPVM_OP* op_call_method = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CALL_METHOD, file, line);
  
  SPVM_CALL_METHOD* call_method = SPVM_CALL_METHOD_new(compiler);
  
  op_call_method->uv.call_method = call_method;
  
  return op_call_method;
}

SPVM_OP* SPVM_OP_new_op_list(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  
  SPVM_OP* op_pushmark = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_PUSHMARK, file, line);
  
  SPVM_OP* op_list = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_LIST, file, line);
  SPVM_OP_insert_child(compiler, op_list, op_list->last, op_pushmark);
  
  return op_list;
}

SPVM_OP* SPVM_OP_new_op_name_tmp_var(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  
  // Temparary variable name
  char* name = SPVM_ALLOCATOR_alloc_memory_block_permanent(compiler->current_each_compile_allocator, strlen("$.tmp_in_op2147483647") + 1);
  sprintf(name, "$.tmp_in_op%d", compiler->current_tmp_vars_length);
  compiler->current_tmp_vars_length++;
  SPVM_OP* op_name = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_NAME, file, line);
  op_name->uv.name = name;
  
  return op_name;
}

SPVM_OP* SPVM_OP_new_op(SPVM_COMPILER* compiler, int32_t id, const char* file, int32_t line) {

  SPVM_OP *op = SPVM_ALLOCATOR_alloc_memory_block_tmp(compiler->current_each_compile_allocator, sizeof(SPVM_OP));
  
  memset(op, 0, sizeof(SPVM_OP));
  
  op->id = id;
  
  assert(!op->file);
  assert(!op->line);
  
  op->file = file;
  op->line = line;
  
  op->name = SPVM_OP_get_name(compiler, id);
  
  SPVM_LIST_push(compiler->ops, op);
  
  return op;
}

int32_t SPVM_OP_is_allowed(SPVM_COMPILER* compiler, SPVM_BASIC_TYPE* basic_type_current, SPVM_BASIC_TYPE* basic_type_dist, int32_t is_parent_field) {
  
  SPVM_LIST* allows = basic_type_dist->allows;
  
  const char* current_basic_type_name = basic_type_current->name;
  const char* dist_basic_type_name = basic_type_dist->name;
  
  int32_t is_allowed = 0;
  if (!is_parent_field && strcmp(current_basic_type_name, dist_basic_type_name) == 0) {
    is_allowed = 1;
  }
  else {
    for (int32_t i = 0; i < allows->length; i++) {
      SPVM_ALLOW* allow = SPVM_LIST_get(allows, i);
      const char* allow_basic_type_name = allow->basic_type_name;
      
      if (strcmp(current_basic_type_name, allow_basic_type_name) == 0) {
        is_allowed = 1;
        break;
      }
      else if (strstr(current_basic_type_name, "::anon_method::") && strncmp(current_basic_type_name, allow_basic_type_name, strlen(allow_basic_type_name)) == 0) {
        is_allowed = 1;
        break;
      }
    }
  }
  
  return is_allowed;
}

int32_t SPVM_OP_is_mutable(SPVM_COMPILER* compiler, SPVM_OP* op) {
  
  switch (op->id) {
    case SPVM_OP_C_ID_VAR:
    case SPVM_OP_C_ID_CLASS_VAR_ACCESS:
    case SPVM_OP_C_ID_ARRAY_ELEMENT_ACCESS:
    case SPVM_OP_C_ID_FIELD_ACCESS:
    case SPVM_OP_C_ID_HASH_VALUE_ACCESS:
    case SPVM_OP_C_ID_DEREFERENCE:
    case SPVM_OP_C_ID_EXCEPTION_VAR:
    case SPVM_OP_C_ID_ARRAY_FIELD_ACCESS:
      return 1;
  }
  
  return 0;
}

SPVM_OP* SPVM_OP_get_parent(SPVM_COMPILER* compiler, SPVM_OP* op_target) {
  
  SPVM_OP* op_parent;
  SPVM_OP* op_cur = op_target;
  while (1) {
    if (op_cur->moresib) {
      op_cur = op_cur->sibparent;
    }
    else {
      op_parent = op_cur->sibparent;
      break;
    }
  }
  
  return op_parent;
}

void SPVM_OP_get_before(SPVM_COMPILER* compiler, SPVM_OP* op_target, SPVM_OP** op_before_ptr, int32_t* next_is_child_ptr) {

  // Get parent
  SPVM_OP* op_parent = SPVM_OP_get_parent(compiler, op_target);
  
  SPVM_OP* op_before;
  int32_t next_is_child = 0;
  if (op_parent->first == op_target) {
    op_before = op_parent;
    next_is_child = 1;
  }
  else {
    op_before = op_parent->first;
    while (1) {
      if (op_before->sibparent == op_target) {
        break;
      }
      else {
        op_before = op_before->sibparent;
      }
    }
  }
  
  *op_before_ptr = op_before;
  *next_is_child_ptr = next_is_child;
}

// Replace target op with replace op
void SPVM_OP_replace_op(SPVM_COMPILER* compiler, SPVM_OP* op_target, SPVM_OP* op_replace) {
  
  // Get parent op
  SPVM_OP* op_parent = SPVM_OP_get_parent(compiler, op_target);
  
  int32_t op_target_is_last_child = op_parent->last == op_target;

  // Get before op
  int32_t next_is_child;
  SPVM_OP* op_before;
  SPVM_OP_get_before(compiler, op_target, &op_before, &next_is_child);
  
  // Stab
  if (next_is_child) {
    // One child
    if (op_before->first == op_before->last) {
      op_before->first = op_replace;
      op_before->last = op_replace;
    }
    // More
    else {
      op_before->first = op_replace;
    }
  }
  else {
    op_before->sibparent = op_replace;
  }
  op_replace->moresib = op_target->moresib;
  op_replace->sibparent = op_target->sibparent;

  if (op_target_is_last_child) {
    op_parent->last = op_replace;
  }
}

// Cut op and insert stab into original position and return stab
SPVM_OP* SPVM_OP_cut_op(SPVM_COMPILER* compiler, SPVM_OP* op_target) {
  // Get parent op
  SPVM_OP* op_parent = SPVM_OP_get_parent(compiler, op_target);
  
  int32_t op_target_is_last_child = op_parent->last == op_target;

  // Get before op
  int32_t next_is_child;
  SPVM_OP* op_before;
  SPVM_OP_get_before(compiler, op_target, &op_before, &next_is_child);
  
  // Stab
  SPVM_OP* op_stab = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_DO_NOTHING, op_target->file, op_target->line);
  if (next_is_child) {
    
    // One child
    if (op_before->first == op_before->last) {
      op_before->first = op_stab;
      op_before->last = op_stab;
    }
    // More
    else {
      op_before->first = op_stab;
    }
  }
  else {
    op_before->sibparent = op_stab;
  }
  op_stab->moresib = op_target->moresib;
  op_stab->sibparent = op_target->sibparent;

  // Clear target
  op_target->moresib = 0;
  op_target->sibparent = NULL;
  
  if (op_target_is_last_child) {
    op_parent->last = op_stab;
  }
  
  return op_stab;
}

// Insert child. Child must not have sibling.
void SPVM_OP_insert_child(SPVM_COMPILER* compiler, SPVM_OP* parent, SPVM_OP* start, SPVM_OP* insert) {
  
  // del_count not used
  assert(parent);
  assert(insert);
  assert(insert->moresib == 0);
  
  if (start) {
    if (start->moresib) {
      insert->moresib = 1;
      insert->sibparent = start->sibparent;
      
      start->sibparent = insert;
    }
    else {
      parent->last = insert;

      insert->moresib = 0;
      insert->sibparent = parent;
      
      start->moresib = 1;
      start->sibparent = insert;
    }
  }
  else {
    if (parent->first) {
      insert->moresib = 1;
      insert->sibparent = parent->first;
      
      parent->first = insert;
    }
    else {
      insert->moresib = 0;
      insert->sibparent = parent;
      
      parent->first = insert;
      parent->last = insert;
    }
  }
}

SPVM_OP* SPVM_OP_sibling(SPVM_COMPILER* compiler, SPVM_OP* op) {
  
  return op->moresib ? op->sibparent : NULL;
}

const char* SPVM_OP_get_name(SPVM_COMPILER* compiler, int32_t op_id) {
  const char* op_name = (SPVM_OP_C_ID_NAMES())[op_id];
  
  return op_name;
}

SPVM_OP* SPVM_OP_build_anon_class_from_statements(SPVM_COMPILER* compiler, SPVM_OP* op_statements) {
  
  SPVM_OP* op_block = SPVM_OP_new_op_block(compiler, op_statements->file, op_statements->line);
  SPVM_OP_insert_child(compiler, op_block, op_block->last, op_statements);
  
  SPVM_OP* op_attributes = SPVM_OP_new_op_list(compiler, op_statements->file, op_statements->line);
  SPVM_OP* op_attribute = SPVM_OP_new_op_attribute(compiler, SPVM_ATTRIBUTE_C_ID_STATIC, op_statements->file, op_statements->line);
  SPVM_OP_insert_child(compiler, op_attributes, op_attributes->last, op_attribute);
  SPVM_OP* op_method = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_METHOD, op_statements->file, op_statements->line);
  SPVM_OP* op_method_name = SPVM_OP_new_op_name(compiler, "main", op_statements->file, op_statements->line);
  SPVM_OP* op_void_type = SPVM_OP_new_op_void_type(compiler, op_statements->file, op_statements->line);
  SPVM_OP* op_args = SPVM_OP_new_op_list(compiler, op_statements->file, op_statements->line);
  op_method = SPVM_OP_build_method(compiler, op_method, op_method_name, op_void_type, op_args, op_attributes, op_block);
  
  SPVM_OP* op_definitions = SPVM_OP_new_op_list(compiler, op_statements->file, op_statements->line);
  SPVM_OP_insert_child(compiler, op_definitions, op_definitions->last, op_method);
  
  SPVM_OP* op_class_block = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CLASS_BLOCK, op_statements->file, op_statements->line);
  SPVM_OP_insert_child(compiler, op_class_block, op_class_block->last, op_definitions);
  
  SPVM_OP* op_class = SPVM_OP_new_op(compiler, SPVM_OP_C_ID_CLASS, op_statements->file, op_statements->line);
  
  op_class = SPVM_OP_build_class(compiler, op_class, NULL, op_class_block, NULL, NULL);
  
  return op_class;
}

SPVM_OP* SPVM_OP_build_caller(SPVM_COMPILER* compiler, SPVM_OP* op_caller, SPVM_OP* op_level) {
  
  // If level is omitted, use constant 0
  if (!op_level) {
    op_level = SPVM_OP_new_op_constant_int(compiler, 0, op_caller->file, op_caller->line);
  }
  
  SPVM_OP_build_unary_op(compiler, op_caller, op_level);
  
  return op_caller;
}

SPVM_OP* SPVM_OP_new_op_caller_info_type(SPVM_COMPILER* compiler, const char* file, int32_t line) {
  // Create the CallerInfo type
  SPVM_TYPE* type = SPVM_TYPE_new_caller_info_type(compiler);
  
  // Wrap the type into an OP node
  SPVM_OP* op_type = SPVM_OP_new_op_type(compiler, type->unresolved_basic_type_name, type->basic_type, type->dimension, type->flag, file, line);  
  
  return op_type;
}
