class TestCase::Literal::String {
  use TestCase::Minimal;
  
  our $VAR_EXPANSION1 : int;

  static method characters : int () {
    
    # Characters
    {
      my $string = "abc";
      
      unless (length $string == 3) {
        return 0;
      }
      
      unless ($string->[0] == 'a' && $string->[1] == 'b' && $string->[2] == 'c') {
        return 0;
      }
    }

    # Multi lines
    {
      my $foo = 5;
      my $str = 
"ab
cd
$foo
ef"
;
      unless ($str eq "ab\ncd\n5\nef") {
        return 0;
      }
    }
    
    return 1;
  }

  static method escape_character : int () {
    
    # String Literal Escape Chracter 
    
    # \0
    {
      my $string = "\0";
      unless ($string->[0] == 0x00) {
        return 0;
      }
    }
    
    # \a
    {
      my $string = "\a";
      unless ($string->[0] == 0x07) {
        return 0;
      }
    }
    
    # \t
    {
      my $string = "\t";
      unless ($string->[0] == 0x09) {
        return 0;
      }
    }
    
    # \n
    {
      my $string = "\n";
      unless ($string->[0] == 0x0A) {
        return 0;
      }
    }
    
    # \f
    {
      my $string = "\f";
      unless ($string->[0] == 0x0C) {
        return 0;
      }
    }
    
    # \r
    {
      my $string = "\r";
      unless ($string->[0] == 0x0D) {
        return 0;
      }
    }
    
    
    # \"
    {
      my $string = "\"";
      unless ($string->[0] == 0x22) {
        return 0;
      }
    }
    
    # \$
    {
      my $string = "\$";
      unless ($string->[0] == 0x24) {
        return 0;
      }
      unless ($string->[0] == '$') {
        return 0;
      }
    }
    
    # \'
    {
      my $string = "\'";
      unless ($string->[0] == 0x27) {
        return 0;
      }
    }
    
    # \\
    {
      my $string = "\\";
      unless ($string->[0] == 0x5C) {
        return 0;
      }
    }
    
    return 1;
  }

  static method octal_escape_character : int () {
    # Octal escape character \000
    {
      unless ("\0"->[0] == 0) {
        return 0;
      }
      
      unless ("\00"->[0] == 0) {
        return 0;
      }
      
      unless ("\01"->[0] == 1) {
        return 0;
      }
      
      unless ("\02"->[0] == 2) {
        return 0;
      }

      unless ("\03"->[0] == 3) {
        return 0;
      }

      unless ("\04"->[0] == 4) {
        return 0;
      }
      
      unless ("\05"->[0] == 5) {
        return 0;
      }
      
      unless ("\06"->[0] == 6) {
        return 0;
      }

      unless ("\07"->[0] == 7) {
        return 0;
      }

      unless ("\000"->[0] == 0) {
        return 0;
      }
      
      unless ("\001"->[0] == 1) {
        return 0;
      }
      
      unless ("\002"->[0] == 2) {
        return 0;
      }

      unless ("\003"->[0] == 3) {
        return 0;
      }

      unless ("\004"->[0] == 4) {
        return 0;
      }
      
      unless ("\005"->[0] == 5) {
        return 0;
      }
      
      unless ("\006"->[0] == 6) {
        return 0;
      }

      unless ("\007"->[0] == 7) {
        return 0;
      }

      unless ("\077"->[0] == 63) {
        return 0;
      }
      
      unless ("\377"->[0] == -1) {
        return 0;
      }
      
      {
        my $string = "\003\004";
        unless ($string->[0] == 3) {
          return 0;
        }
        unless ($string->[1] == 4) {
          return 0;
        }
      }
      {
        my $string = "\038";
        unless ($string->[0] == 3) {
          return 0;
        }
        unless ($string->[1] == '8') {
          return 0;
        }
      }
    }
    
    # Octal escape character \o{}
    {
      unless ("\o{0}"->[0] == 0) {
        return 0;
      }
      
      unless ("\o{1}"->[0] == 1) {
        return 0;
      }
      
      unless ("\o{2}"->[0] == 2) {
        return 0;
      }

      unless ("\o{3}"->[0] == 3) {
        return 0;
      }

      unless ("\o{4}"->[0] == 4) {
        return 0;
      }
      
      unless ("\o{5}"->[0] == 5) {
        return 0;
      }
      
      unless ("\o{6}"->[0] == 6) {
        return 0;
      }

      unless ("\o{7}"->[0] == 7) {
        return 0;
      }

      unless ("\o{00}"->[0] == 0) {
        return 0;
      }
      
      unless ("\o{01}"->[0] == 1) {
        return 0;
      }
      
      unless ("\o{02}"->[0] == 2) {
        return 0;
      }

      unless ("\o{03}"->[0] == 3) {
        return 0;
      }

      unless ("\o{04}"->[0] == 4) {
        return 0;
      }
      
      unless ("\o{05}"->[0] == 5) {
        return 0;
      }
      
      unless ("\o{06}"->[0] == 6) {
        return 0;
      }

      unless ("\o{07}"->[0] == 7) {
        return 0;
      }

      unless ("\o{77}"->[0] == 63) {
        return 0;
      }
      unless ("\o{377}"->[0] == -1) {
        return 0;
      }
    }
    return 1;
  }
  
  static method hexadecimal_escape_character : int () {
    
    # 0-9a-fA-F
    {
      my $string = "\x00\x11\x22\x33\x44\x55\x66\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x7A\x7B\x7C\x7D\x7E\x7Fa";

      unless ($string->[0] == 0) {
        return 0;
      }
      
      unless ($string->[1] == 17) {
        return 0;
      }
      
      unless ($string->[2] == 34) {
        return 0;
      }
      
      unless ($string->[3] == 51) {
        return 0;
      }

      unless ($string->[4] == 68) {
        return 0;
      }

      unless ($string->[5] == 85) {
        return 0;
      }
      
      unless ($string->[6] == 102) {
        return 0;
      }
      
      unless ($string->[7] == 119) {
        return 0;
      }

      unless ($string->[8] == 120) {
        return 0;
      }

      unless ($string->[9] == 121) {
        return 0;
      }

      unless ($string->[10] == 122) {
        return 0;
      }

      unless ($string->[11] == 123) {
        return 0;
      }

      unless ($string->[12] == 124) {
        return 0;
      }

      unless ($string->[13] == 125) {
        return 0;
      }

      unless ($string->[14] == 126) {
        return 0;
      }

      unless ($string->[15] == 127) {
        return 0;
      }

      unless ($string->[16] == 122) {
        return 0;
      }

      unless ($string->[17] == 123) {
        return 0;
      }

      unless ($string->[18] == 124) {
        return 0;
      }

      unless ($string->[19] == 125) {
        return 0;
      }

      unless ($string->[20] == 126) {
        return 0;
      }

      unless ($string->[21] == 127) {
        return 0;
      }

      unless ($string->[22] == 'a') {
        return 0;
      }
    }

    # Brace "{" and "}"
    {
      my $string = "\x{7a}\x{7b}";
      
      unless ($string eq "\x7a\x7b") {
        return 0;
      }
    }
    
    # Single hexadecimal number
    {
      my $string = "\xag";
      
      unless ($string eq "\x{0a}g") {
        return 0;
      }
    }
    
    # Tow hexadecimal numbers
    {
      my $string = "\x61g";
      
      unless ($string eq "ag") {
        return 0;
      }
    }
    
    # any byte type
    {
      my $bytes = "\xFF\xFE\x03";
      unless ($bytes->[0] == -1) {
        return 0;
      }
      unless ($bytes->[1] == -2) {
        return 0;
      }
      unless ($bytes->[2] == 3) {
        return 0;
      }

      unless ($bytes->[0] == (byte)0xFF) {
        return 0;
      }
      unless ($bytes->[1] == (byte)0xFE) {
        return 0;
      }
      unless ($bytes->[2] == (byte)0x03) {
        return 0;
      }
    }
    
    return 1;
  }

  static method unicode_escape_character : int () {
    my $string = "\N{U+3042}\N{U+3044}\N{U+3046}";
    
    unless ($string eq "あいう") {
      return 0;
    }
    
    {
      my $string = "\N{U+1F9E6}";
      
      my $utf8_char = [(byte)0xF0, (byte)0x9F, (byte)0xA7, (byte)0xB0];
    }
    
    return 1;
  }

  static method raw_escape_character : int () {
    
    # Raw escape - Character and number
    {
      my $string = "\w\W\s\S\d\D\p\P\X\g\k\K\v\V\h\H\R\b\B\A\Z\z\G\N\C\E\Q";
      
      unless ($string->[0] == '\\') { return 0; }
      unless ($string->[1] == 'w') { return 0; }
      unless ($string->[2] == '\\') { return 0; }
      unless ($string->[3] == 'W') { return 0; }
      unless ($string->[4] == '\\') { return 0; }
      unless ($string->[5] == 's') { return 0; }
      unless ($string->[6] == '\\') { return 0; }
      unless ($string->[7] == 'S') { return 0; }
      unless ($string->[8] == '\\') { return 0; }
      unless ($string->[9] == 'd') { return 0; }
      unless ($string->[10] == '\\') { return 0; }
      unless ($string->[11] == 'D') { return 0; }
      unless ($string->[12] == '\\') { return 0; }
      unless ($string->[13] == 'p') { return 0; }
      unless ($string->[14] == '\\') { return 0; }
      unless ($string->[15] == 'P') { return 0; }
      unless ($string->[16] == '\\') { return 0; }
      unless ($string->[17] == 'X') { return 0; }
      unless ($string->[18] == '\\') { return 0; }
      unless ($string->[19] == 'g') { return 0; }
      unless ($string->[20] == '\\') { return 0; }
      unless ($string->[21] == 'k') { return 0; }
      unless ($string->[22] == '\\') { return 0; }
      unless ($string->[23] == 'K') { return 0; }
      unless ($string->[24] == '\\') { return 0; }
      unless ($string->[25] == 'v') { return 0; }
      unless ($string->[26] == '\\') { return 0; }
      unless ($string->[27] == 'V') { return 0; }
      unless ($string->[28] == '\\') { return 0; }
      unless ($string->[29] == 'h') { return 0; }
      unless ($string->[30] == '\\') { return 0; }
      unless ($string->[31] == 'H') { return 0; }
      unless ($string->[32] == '\\') { return 0; }
      unless ($string->[33] == 'R') { return 0; }
      unless ($string->[34] == '\\') { return 0; }
      unless ($string->[35] == 'b') { return 0; }
      unless ($string->[36] == '\\') { return 0; }
      unless ($string->[37] == 'B') { return 0; }
      unless ($string->[38] == '\\') { return 0; }
      unless ($string->[39] == 'A') { return 0; }
      unless ($string->[40] == '\\') { return 0; }
      unless ($string->[41] == 'Z') { return 0; }
      unless ($string->[42] == '\\') { return 0; }
      unless ($string->[43] == 'z') { return 0; }
      unless ($string->[44] == '\\') { return 0; }
      unless ($string->[45] == 'G') { return 0; }
      unless ($string->[46] == '\\') { return 0; }
      unless ($string->[47] == 'N') { return 0; }
      unless ($string->[48] == '\\') { return 0; }
      unless ($string->[49] == 'C') { return 0; }
      unless ($string->[50] == '\\') { return 0; }
      unless ($string->[51] == 'E') { return 0; }
      unless ($string->[52] == '\\') { return 0; }
      unless ($string->[53] == 'Q') { return 0; }
    }

    # Raw escape - punct characters
    {
      my $string = "\!\#\@\%\&\(\)\*\+\,\-\.\/\:\;\<\=\>\?\[\]\^\_\`\{\|\}\~";
      unless ($string->[0] == '\\') { return 0; }
      unless ($string->[1] == '!') { return 0; }
      unless ($string->[2] == '\\') { return 0; }
      unless ($string->[3] == '#') { return 0; }
      unless ($string->[4] == '\\') { return 0; }
      unless ($string->[5] == '@') { return 0; }
      unless ($string->[6] == '\\') { return 0; }
      unless ($string->[7] == '%') { return 0; }
      unless ($string->[8] == '\\') { return 0; }
      unless ($string->[9] == '&') { return 0; }
      unless ($string->[10] == '\\') { return 0; }
      unless ($string->[11] == '(') { return 0; }
      unless ($string->[12] == '\\') { return 0; }
      unless ($string->[13] == ')') { return 0; }
      unless ($string->[14] == '\\') { return 0; }
      unless ($string->[15] == '*') { return 0; }
      unless ($string->[16] == '\\') { return 0; }
      unless ($string->[17] == '+') { return 0; }
      unless ($string->[18] == '\\') { return 0; }
      unless ($string->[19] == ',') { return 0; }
      unless ($string->[20] == '\\') { return 0; }
      unless ($string->[21] == '-') { return 0; }
      unless ($string->[22] == '\\') { return 0; }
      unless ($string->[23] == '.') { return 0; }
      unless ($string->[24] == '\\') { return 0; }
      unless ($string->[25] == '/') { return 0; }
      unless ($string->[26] == '\\') { return 0; }
      unless ($string->[27] == ':') { return 0; }
      unless ($string->[28] == '\\') { return 0; }
      unless ($string->[29] == ';') { return 0; }
      unless ($string->[30] == '\\') { return 0; }
      unless ($string->[31] == '<') { return 0; }
      unless ($string->[32] == '\\') { return 0; }
      unless ($string->[33] == '=') { return 0; }
      unless ($string->[34] == '\\') { return 0; }
      unless ($string->[35] == '>') { return 0; }
      unless ($string->[36] == '\\') { return 0; }
      unless ($string->[37] == '?') { return 0; }
      unless ($string->[38] == '\\') { return 0; }
      unless ($string->[39] == '[') { return 0; }
      unless ($string->[40] == '\\') { return 0; }
      unless ($string->[41] == ']') { return 0; }
      unless ($string->[42] == '\\') { return 0; }
      unless ($string->[43] == '^') { return 0; }
      unless ($string->[44] == '\\') { return 0; }
      unless ($string->[45] == '_') { return 0; }
      unless ($string->[46] == '\\') { return 0; }
      unless ($string->[47] == '`') { return 0; }
      unless ($string->[48] == '\\') { return 0; }
      unless ($string->[49] == '{') { return 0; }
      unless ($string->[50] == '\\') { return 0; }
      unless ($string->[51] == '|') { return 0; }
      unless ($string->[52] == '\\') { return 0; }
      unless ($string->[53] == '}') { return 0; }
    }
    
    return 1;
  }
  
  static method var_expansion : int () {
    
    # Variable access
    {
      my $var1 = 1;
      my $var2 = 2;
      
      my $string = "a $var1 b ${var2}c ${var1}->2";
      
      unless ($string eq "a 1 b 2c 1->2") {
        return 0;
      }
    }
    
    # Exception variable
    {
      $@ = "Error";
      
      my $string = "a $@b ${@}c";
      
      unless ($string eq "a Errorb Errorc") {
        return 0;
      }
      $@ = undef;
    }

    # Dereference variable
    {
      my $num = 5;
      my $num_ref = \$num;
      
      my $string = "a $$num_ref b $${num_ref}c";
      
      unless ($string eq "a 5 b 5c") {
        return 0;
      }
    }
    
    # Class variable
    {
      $VAR_EXPANSION1 = 6;
      
      my $string = "a $TestCase::Literal::String::VAR_EXPANSION1 b";
      
      unless ($string eq "a 6 b") {
        return 0;
      }
    }

    # Getting array element
    {
      my $nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
      
      my $string = "a $nums->[1]b$nums->[10]c$nums->[1_0]d$nums->[0xA]";
      
      unless ($string eq "a 2b11c11d11") {
        return 0;
      }
    }

    # Getting field
    {
      my $minimal = TestCase::Minimal->new;
      $minimal->{x} = 5;
      
      my $string = "b $minimal->{x}c";
      
      unless ($string eq "b 5c") {
        return 0;
      }
    }

    # Multiple field and element access
    {
      my $minimals = new TestCase::Minimal[11];
      $minimals->[1] = new TestCase::Minimal;
      $minimals->[1]{x} = 5;

      $minimals->[10] = new TestCase::Minimal;
      $minimals->[10]{x} = 11;
      
      my $string = "a $minimals->[1]{x}b $minimals->[1_0]{x} c $minimals->[1]->{x}";
      
      unless ($string eq "a 5b 11 c 5") {
        return 0;
      }
    }

    # String literal last allow $
    {
      my $str = "aaaa$";
      
      unless ($str->[4] == '$') {
        return 0;
      }
    }

    # Single ":" is not the part of variable
    {
      my $foo = 1;
      my $str = "$foo:bar";
      
      unless ($str eq "1:bar") {
        return 0;
      }
    }

    # Single "-" is not the part of variable
    {
      my $foo = 1;
      my $str = "$foo-bar";
      
      unless ($str eq "1-bar") {
        return 0;
      }
    }

    # Single ">" is not the part of variable
    {
      my $foo = 1;
      my $str = "$foo>bar";
      
      unless ($str eq "1>bar") {
        return 0;
      }
    }
    
    return 1;
  }

  static method heredoc_like_lf : int () {
    
    my $string =
"ab
cd
ef";

    unless ($string eq "ab\ncd\nef") {
      return 0;
    }
    
    return 1;
  }
  
  static method string_literal_extra : int () {

    # String literal last character is \\
    {
      my $str = "aaaa\\";
      
      unless ($str->[4] == '\\') {
        return 0;
      }
    }
    
    {
      # Escape chracter
      my $string = "a\0\a \t\n\f\r\"\'\\z";
      
      # \0
      unless ($string->[1] == 0) {
        return 0;
      }
      
      # \a
      unless ($string->[2] == 7) {
        return 0;
      }
      
      # \t
      unless ($string->[4] == 9) {
        return 0;
      }
      
      # \n
      unless ($string->[5] == 10) {
        return 0;
      }
      
      # \f
      unless ($string->[6] == 12) {
        return 0;
      }
      
      # \r
      unless ($string->[7] == 13) {
        return 0;
      }
      
      # \"
      unless ($string->[8] == 34) {
        return 0;
      }
      
      # \'
      unless ($string->[9] == 39) {
        return 0;
      }
      
      # \\
      unless ($string->[10] == 92) {
        return 0;
      }
    }
    
    return 1;
  }
}
