The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
=pod

ABCDE

=cut ppppp

use Test::EnumA;
use Test::EnumB;
use Test::EnumC;
use Test::EnumD;
use Test::Simple;
use Test::Minimal;
use Test::Template_int_long;
use Test::Destructor;

=head1 NAME

SPVM::Test - SPVM test module

=cut

package Test {
  has next_test : Test;
  has minimal : Test::Minimal;
  has x_byte : byte;
  has x_short : short;
  has x_int : int;
  has x_long : long;
  has x_float : float;
  has x_double : double;

  enum {
    BYTE_MAX = 127b,
    BYTE_MIN = -128b,
    SHORT_MAX = 32767s,
    SHORT_MIN = -32768s,
    INT_MAX = 2147483647,
    INT_MIN = -2147483648,
    LONG_MAX = 9223372036854775807L,
    LONG_MIN = -9223372036854775808L,
    FLOAT_PRECICE = 32767f,
    DOUBLE_PRECICE = 2147483647.0,
  }
  
  sub main($mvar : int) : int {
    {
      my $num = 1;
      
      $num += 4;
      
      std::println_int($num);
    }
    
    {
      error_eval_call();
    }
    
    {
      # error_call_stack();
    }
    
    {
      my $ret = error_eval();
      
      std::println_int($ret);
    }
    
    error_eval();
    
    my $dest = new Test::Destructor;
    
    # enum
    {
      BYTE_MAX();
      BYTE_MIN();
      SHORT_MAX();
      SHORT_MIN();
      INT_MAX();
      INT_MIN();
      LONG_MAX();
      LONG_MIN();
    }
    # Field set and get
    {
      my $test = new Test;
      
      $test->{x_byte} = Test->BYTE_MAX;
      $test->{x_short} = Test->SHORT_MAX;
      $test->{x_int} = Test->INT_MAX;
      $test->{x_long} = Test->LONG_MAX;
      $test->{x_float} = Test->FLOAT_PRECICE;
      $test->{x_double} = Test->DOUBLE_PRECICE;
    }
    
    # Weaken
    {
      my $num = weaken_reference_count1_object();
    }
    
    # Weaken
    {
      my $test = new Test;
      
      $test->{next_test} = $test;
      
      weaken $test->{next_test};
    }
    
    call_void();
    
    std::println("a");
    
    # Default return value object
    if (default_return_value_object_sub() == undef) {
      if (default_return_value_object_sub_empty() == undef) {
        1;
      }
    }

    # Use template package
    {
      my $num = Test::Template_int_long::get(100, 200, 300L,);
      std::println_int($num);
    }

    # Get object from freelist
    {
       check_freelist();
       
       my $result = new int[65];
       $result->[64] = 0;
    }
    
    my $ppp = [1, 2, 3];
    std::println_float(Test->ONE);
    
    # Logical or
    {
      if (1 || 0) {
        std::println("Logical or");
      }
    }

    # Logical and
    {
      if (1 && 1) {
        std::println("Logical and");
      }
    }
    
    # Constant e
    {
      my $num = 0.25E+3;
      std::println_double($num);
    }

    # Constant e
    {
      my $num = 0.25E-3f;
      std::println_float($num);
    }
    # Convert to string to byte[]
    {
      my $string = "abc";
      my $byte = $string->[0];
    }
    
    # MIN long constant;
    {
      my $num = -9223372036854775808L;
      std::println_long($num);
    }
    {
      my $var = 3;
      while (my $var = 964) {
        std::println_int($var);
        last;
      }
    }
    
    {
      my $error = "First";
      $@ = "Error";
      if (my $error = $@) {
        std::println($error);
      }
    }
    
    # Check for third part bug
    {
      for (my $i = 0; $i < 260; $i++) {
      }
    }
    
    # Exception variable
    {
      $@ = "Exception";
      std::println($@);
    }
    
    # $array->[0]{x}
    {
      my $objs = new Test::Minimal[3];
      $objs->[0] = new Test::Minimal;
      $objs->[0]{x} = 111;
      std::println_int($objs->[0]{x});
    }
    
    # $obj->{x}[0]
    {
      my $obj = new Test::Simple;
      $obj->{values1} = new int[5];
      $obj->{values1}[0] = 221;
      std::println_int($obj->{values1}[0]);
    }
    
    # Call new subroutine without variable
    {
      Test::Minimal::new();
      sum0(1, 3);
    }
    
    # Subroutine call from native
    {
      my $total = std::test1(3, 11);
      std::println_int($total);
    }
    
    # Create temporary varialbe
    {
      new Test::Minimal;
      new Test::Minimal;
      (new Test::Minimal)->{x};
    }
    
    my $obj1 = new Test::Minimal;
    
    # last
    {
      while (1) {
        my $obj1 = new Test::Minimal;
        my $obj2 = new Test::Minimal;
        {
          my $obj3 = new Test::Minimal;
          last;
          next;
        }
      }
    }
    # String escape character
    {
      my $string : string = "abc\"\'\\\n\t\b\r\fdef";
     
      std::println($string);
    }
    
    # Escape sequence
    {
      my $ch1 = '\"';
      my $ch2 = '\'';
      my $ch3 = '\\';
      my $ch4 = '\n';
      my $ch5 = '\t';
      my $ch6 = '\b';
      my $ch7 = '\r';
      my $ch8 = '\f';
    }
    
    # char null string
    {
      my $ch : byte = (byte)0x04;
    }
    
    # char
    {
      my $ch : byte = 'a';
    }
    
    # Malloc mutil array
    {
      my $nums : int[][] = new int[][3];
      $nums->[0] = new int[1];
      $nums->[0][0] = 178;
      std::println_int($nums->[0][0]);
    }
    
    # Declare mutil array
    {
      my $nums : int[][];
    }
    
    std::println_int(Test::EnumD->THREE);
    std::println_int(Test::EnumD->FORE);
    
    # Core functions
    {
      std::print_byte((byte)1);
      std::print_short((short)1);
      std::print_int(1);
      std::print_long(1L);
      std::print_float(1f);
      std::print_double(1.0);
      std::println("end");
    }

    {
      my $nums = new int[258];
      my $len = @$nums;
      for (my $i = 0; $i < $len; $i++) {
        $nums->[$i] = $i;
      }

      for (my $i = 0; $i < @$nums; $i++) {
        $nums->[$i] = $i;
      }
    }
    
    # length with {} Too many elements over 4G
    {
      my $nums = new int[150000000];
      my $len = @{$nums};
      $nums->[149999999] = 764;
      std::println_int($nums->[149999999]);
    }
    
    {
      my $nums = new int[2000000];
      my $len = len $nums;
      my $i = 0;
      for ($i = 0; $i < $len; $i = $i + 1) {
        $nums->[$i] = $i;
      }
      std::println_int($i);
      std::println_int($nums->[$i - 1]);
    }
    
    # Object to get long field is undef
    #{
    #  my $obj : Test::Minimal;
    #  $obj{x} = 1L;
    #}

    # Object to get long field is undef
    #{
    #  my $obj : Test::Minimal;
    #  $obj{x};
    #}

    # Index is out of range
    #{
    #  my $nums = new int[3];
    #  $nums->[3] = 1;
    #}
    
    # Index is out of range
    #{
    #  my $nums = new int[3];
    #  $nums->[-1] = 3;
    #}

    # Array is undef
    #{
    #  my $nums : int[];
    #  $nums->[0] = 1;
    #}

    # Index is out of range
    #{
    #  my $nums = new int[3];
    #  $nums->[3];
    #}
    
    # Index is out of range
    #{
    #  my $nums = new int[3];
    #  $nums->[-1];
    #}
    
    # Array is undef
    #{
    #  my $nums : int[];
    #  $nums->[0];
    #}
    
    {
      new Test::Minimal;
    }
    
    {
      my $obj1 = new Test::Minimal;
      
      my $obj2 : Test::Minimal;
      
      my $obj3 : Test::Minimal = undef;
    }
    
    # Increment byte
    {
      my $num = (byte)1;
      $num++;
      std::println_byte($num);
    }
    
    # Increment short 
    {
      my $num = (short)1;
      $num++;
      std::println_short($num);
    }
    
    # increment int
    {
      my $var = 4;
      $var++;
      $var--;
      --$var;
      ++$var;
    }
    
    # Increment long
    {
      my $var = (long)4;
      $var++;
      $var--;
      --$var;
      ++$var;
    }
    
    eval {
      sum0(1, 2);
      
      3;
      
      sum0(3, 4);
      
      die "Catch error";
      
      1;
    };
    
    {
      sum0(5, 6);
      2;
      std::println("Error");
    }
    
    {
      my $nums = new int[3];
      $nums->[0] = 1;
      $nums->[1] = 2;
      $nums->[2] = 3;
      
      my $total = std::sum_int($nums);
      std::println_int($total);
    }
    
    {
      my $string = "ace";
     
      std::println($string);
    }

    {
      my $string : string = "ace";
     
      std::println($string);
    }
    
    {
      my $num = (byte)3;
      std::println_byte($num);
      
      my $num2 = (long)1 + (long)$num;
      
      std::println_long($num2);
    }
    
    # get and set field
    {
      my $m = new Test::Minimal;
      
      $m->{x} = 99;
      $m->{y} = 100;
      
      std::println_int($m->{x});
      std::println_int($m->{y});
    }

    # Free when assignment
    {
      my $m = new Test::Minimal;
      $m = new Test::Minimal;
    }

    # left is object, right is undef
    {
      my $obj : Test::Minimal = undef;
    }
    
    if (1) {
      2;
      if (3) {
        4;
      }
      elsif (8) {
        9;
      }
      else {
        5;
      }
    }
    else {
      6;
    }
    7;
    
    std::println_int(sum0(1, 1));
    std::println_int(sum2(1, 2));

    # Constant float
    std::println_float(0.3f);
    std::println_float(1f);
    std::println_float(2f);
    std::println_float(1.2f);
    
    # Constant double
    std::println_double(0d);
    std::println_double(1d);
    std::println_double(1.2);

    # Constant int
    std::println_int(-2147483648);
    std::println_int(-32769);
    std::println_int(-32768);
    std::println_int(-129);
    std::println_int(-128);
    std::println_int(-2);
    std::println_int(-1);
    std::println_int(0);
    std::println_int(1);
    std::println_int(2);
    std::println_int(3);
    std::println_int(4);
    std::println_int(5);
    std::println_int(6);
    std::println_int(127);
    std::println_int(128);
    std::println_int(255);
    std::println_int(256);
    std::println_int(32767);
    std::println_int(32768);
    std::println_int(65535);
    std::println_int(65536);
    std::println_int(2147483647);
    
    # Constant long
    std::println_long(-1L);
    std::println_long(0L);
    std::println_long(1L);
    std::println_long(2L);
    std::println_long(9223372036854775807L);
    std::println_long(-9223372036854775807L);
    std::println_long(-2147483648L);
    std::println_long(-32769L);
    std::println_long(-32768L);
    std::println_long(-129L);
    std::println_long(-128L);
    std::println_long(-2L);
    std::println_long(-1L);
    std::println_long(0L);
    std::println_long(1L);
    std::println_long(2L);
    std::println_long(3L);
    std::println_long(4L);
    std::println_long(5L);
    std::println_long(6L);
    std::println_long(127L);
    std::println_long(128L);
    std::println_long(255L);
    std::println_long(256L);
    std::println_long(32767L);
    std::println_long(32768L);
    std::println_long(65535L);
    std::println_long(65536L);
    std::println_long(2147483647L);
    std::println_long(0xFFL);
    
    "abc";
    
    # Table switch int
    {
      my $num = 3;
      
      switch($num) {
        case Test::EnumD->THREE:
          std::println_int(1);
          last;
        case Test::EnumD->FORE:
          std::println_int(2);
          last;
        case 5:
          std::println_int(3);
          last;
        default:
          std::println_int(5);
      }
    }

    # Lookup switch int
    {
      my $num = 3;
      switch ($num) {
        case 1:
          std::println_int(1);
          last;
        case 3:
          std::println_int(2);
          last;
        case 10000:
          std::println_int(2);
          last;
        default:
          std::println_int(5);
      }
    }
    
    # {
    #   my $num = 5;
    #   switch($num) {
    #     default:
    #       std::println_int(5);
    #   }
    # }
    
    # my $num;
    # my $num1 = undef;
    
    if (1) {
      3;
      if (2) {
        4;
      }
      5;
    }
    6;

    my $simple3 : Test::Simple = new Test::Simple;
    
    std::println_int($simple3->get_x());
    $simple3->get_x;
    
    $simple3->{y} = 2;
    $simple3->{x};
    $simple3->{y};

    my $simple2 : Test::Simple = new Test::Simple;
    
    if (1) { }
    
    if (1 == 1) {
    
    }

    if (1 != 1) {
    
    }

    if (1 <= 1) {
    
    }

    if (1 < 1) {
    
    }

    if (1 >= 1) {
    
    }

    if (1 > 1) {
    
    }

    if (!1) { }
    
    if (1L) { }
    if (1.5) { }
    if ($simple2) { }
    
    if (undef) {
    
    }
    
    if ($simple2 == undef) {
      
    }

    if (undef == $simple2) {
      
    }
    
    if (undef == undef) {
    
    }
    if (undef != undef) {
    
    }
    
    if (5L || 6L) {
    
    }

    if (5L && 6L) {
    
    }
    if (!1L) {
    
    }
    
    if (1L > 2L) {
      3L;
      4L;
    };
    5L;

    if (1.2 > 2.0) {};
    if (1.2 >= 2.0) {};
    if (1.2 < 2.0) {};
    if (1.2 <= 2.0) {};

    if (1.2 == 1.0) { }
    if (1.2 != 2.0) { };

    if (1 > 2) {};
    if (1 >= 2) {};
    if (1 < 2) {};
    if (1 <= 2) {};

    if (1 == 1) { }
    if (1 != 2) { };

    {
      my $nums = new int[3];
      $nums->[0] = 13;
      $nums->[1] = 14;
      std::println_int($nums->[0]);
      std::println_int($nums->[1]);
    }
    
    {
      my $nums : long[] = new long[3];
      $nums->[0] = 11L;
      $nums->[1] = 12L;
      std::println_long($nums->[0]);
      std::println_long($nums->[1]);
      std::println_int(@$nums);
      my $nums_length : int = @$nums;
    }
    
    my $simple : Test::Simple = new Test::Simple;
    
    
    my $v1 : int;
    my $v2 : int;
    my $v3 : int;
    
    $v3 = $v2 = $v1 = 543;
    std::println_int($v3);
    
    100;
    1000;
    1 << 2;
    1 >> 2;
    1 >>> 2;
    
    Test::EnumA::ONE();
    Test::EnumA::TWO();
    
    Test::EnumA->ONE();
    Test::EnumA->ONE;
    
    # Basic operation byte
    {
    
    }
    
    # Basic operation short
    {
    
    }
    
    # Basic operation int
    {
      1 ^ 4;
      1 & 2;
      1 | 2;
      -3 + +2;
      3 - (1 + 2);
      5 + 19;
      1 + 2;
      1 - 2;
      1 * 2;
      1 / 3;
      4 % 6;
    }
    
    # Basic operation long
    {
      1L ^ 4L;
      1L & 2L;
      1L | 2L;
      -3L + +2L;
      3L - (1L + 2L);
      5L + 19L;
      1L + 2L;
      1L - 2L;
      1L * 2L;
      1L / 3L;
      4L % 6L;
    }
    
    1.2 / 3.0;
    1.2f / 3.0f;
    1.2 * 4.0;
    1.2f * 4.0f;
    1.2 + 3.0;
    1.2f + 3.0f;
    1.2 - 3.0;
    1.2f - 3.0f;
    
    # Compare long
    {
      if (1L > 2L) {};
      if (1L >= 2L) {};
      if (1L < 2L) {};
      if (1L <= 2L) {};
      if (1L == 1L) { }
      if (1L != 2L) { };
    }
    
    my $bar : double = (double)1;
    undef;
    
    Test::sum0(1, 2);
    sum0(1, 2);
    
    test1();
    
    while (1) {
      1;
      last;
    }
    
    # for (my $i : int = 0; $i < 5; $i = $i + 1) {
    #   1;
    #   last;
    #   next;
    # }

    {
      my $num0 = (byte)0;
      my $num1 = (byte)1;
      my $num2 = (byte)2;
      my $num3 = $num0 + $num1 + $num2;
      std::println_byte($num3);
    }

    {
      my $num0 = (short)0;
      my $num1 = (short)1;
      my $num2 = (short)2;
      my $num3 = $num0 + $num1 + $num2;
      std::println_short($num3);
    }

    # my $error : byte[] = "Error";
    # die $error;
    
    return $mvar + 3;
  }

  enum {
    ONE = 0.25f
  }

  # Call void function
  sub call_void_sub($nums : int[]) : void {
    $nums->[0] = 5;
  }
  sub call_void() : int {
    my $nums = [1];
    
    call_void_sub($nums);
    
    if ($nums->[0] == 5) {
      return 1;
    }
    return 0;
  }
  
  sub test1 () : int {
    my $num0 = 1;
    my $num1 = 2;
    my $num2 = 3;
    my $num3 = 4;
    my $num4 = 5;
    
    return 0;
  }
  
  sub sum4($num1 : long, $num2 : long) : long {
    return $num1 + $num2;
  }
  
  sub sum3 ($simple : Test::Simple, $foo : long, $bar : float) : int {
    
    if (3) {
    
    }
    
    if (3) {
      1;
    }
    elsif (4) {
      4;
    }
    else {
      
    }

    if (3) {
      1;
    }
    elsif (4) {
      4;
    }
    elsif (5) {
    
    }
    else {
      
    }
    
    if (1) {
      
    }
    else {
      
    }
    
    array_new();
    
    return 2;
  }
  
  sub sum1 ($num1 : long, $num2 : long) : long {
    return $num1 + $num2;
  }
  sub sum0($num1 : int, $num2 : int) : int {
    return $num1 + $num2;
  }
  
  sub sum2 ($num1 : int, $num2 : int) : int {
    # die "Error";
    
    my $num3 = sum0($num1, $num2);
    
    return $num3 + 3;
  }

  sub increfcount($test : Test::Minimal, $num : int) : int {
    
    my $aaa = new Test::Minimal;
  }

  sub decinctest ($num1 : Test::Minimal, $num2 : int, $num3 : Test::Minimal) : int {
    {
      my $num4 = new Test::Minimal;
      my $num5 = new Test::Minimal;
    }
    return 2;
  }

  sub return_object() : Test::Minimal {
    my $obj0 = new Test::Minimal;
    {
      my $obj1 = new Test::Minimal;
      
      my $obj2 : Test::Minimal;
      
      my $obj3 : Test::Minimal = undef;

      return $obj2;
    }
  }

  sub eval_block() : void {
    my $obj0 = new Test::Minimal;
    
    eval {
      my $obj1 = new Test::Minimal;
      
      my $obj2 : Test::Minimal;
      
      my $obj3 : Test::Minimal = undef;
      
      my $obj_error1 = "Error1";
      
      die $obj_error1;
    };
    
    {
      my $obj4 = new Test::Minimal;
      
      my $obj5 : Test::Minimal;
      
      my $obj6 : Test::Minimal = undef;
      
      my $obj_error2 = "Error2";
      
      die $obj_error2;
    }
  }
  sub array_new() : int {
    
    my $nums = new int[3];
    
    return @$nums;
  }
  sub check_freelist () : int[] {
    my $result = new int[63];
    my $true_result = new int[1];
    return $true_result;
  }
  sub default_return_value_object_sub() : Test {
    1;
  }
  sub default_return_value_object_sub_empty() : Test {
    
  }
  sub weaken_reference_count1_object() : int {
    my $minimal = new Test::Minimal;
    my $test = new Test;
    
    $test->{minimal} = $minimal;
    $minimal = undef;
    weaken $test->{minimal};
    
    if ($test->{minimal} == undef) {
      return 1;
    }
    
    return 0;
  }
  
  sub error() : int {
    die "ERROR";
  }
  
  sub error_call_stack() : int {
    error();
  }

  sub error_eval() : int {
    eval {
      die "ERROR";
    };
    
    if ($@) {
      return 1;
    }
    else {
      return 0;
    }
  }

  sub error_eval_call() : int {
    eval {
      error();
    };
    
    if ($@) {
      return 1;
    }
    else {
      return 0;
    }
  }

}

=head1 NAME

SPVM::Test - SPVM test module