# NOTE: if you're adding / adapting tests here, you probably want to do
#       the same in t/12-rakuast/strings.t

use v6.e.PREVIEW;
use Test;

plan 12;

my $ast;
my $deparsed;
my $raku;
my @type = <AST Str Raku>;
sub ast(RakuAST::Node:D $node --> Nil) {
    $ast      := $node;
    $deparsed := $ast.DEPARSE;
    $raku     := 'use experimental :rakuast; ' ~ $ast.raku;
    diag $deparsed.chomp;
}

subtest 'One-part heredoc with literal piece' => {
    # "hello\n\ngoodbye\n"
    ast RakuAST::Heredoc.new(
#      :segments[RakuAST::StrLiteral.new("    hello\n\n    goodbye\n")],  # XX enable after trimfix
      :segments[RakuAST::StrLiteral.new("hello\n\ngoodbye\n")],
      :stop("    CODE\n")
    );

    is-deeply $deparsed,
      "qq:to/CODE/\n    hello\n\n    goodbye\n    CODE\n",
      'deparse';

    is-deeply $_, "hello\n\ngoodbye\n", @type[$++]
      for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku);
}

subtest 'Heredoc with interpolated string variable works' => {
    my $str = 'hello,';

    # "$str world\n"
    ast RakuAST::Heredoc.new(
      :segments[
        RakuAST::Var::Lexical.new('$str'),
        RakuAST::StrLiteral.new(" world\n")
      ],
      :stop("MESSAGE\n")
    );

    is-deeply $deparsed,
      qq:!s|qq:to/MESSAGE/\n$str world\nMESSAGE\n|,
      'deparse';

    is-deeply $_, "hello, world\n", @type[$++]
      for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku);
}

subtest 'Heredoc with interpolated integer variable' => {
    my $int = 42;

    # "$int is the answer\n"
    ast RakuAST::Heredoc.new(
      :segments[
#        RakuAST::StrLiteral.new("  "),  #  XXX enable after trim fix
        RakuAST::Var::Lexical.new('$int'),
        RakuAST::StrLiteral.new(" is the answer\n")
    ],
      :stop("  INTY\n")
    );

    is-deeply $deparsed,
      qq:!s|qq:to/INTY/\n  $int is the answer\n  INTY\n|,
      'deparse';

    is-deeply $_, "42 is the answer\n", @type[$++]
      for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku);
}

subtest 'Heredoc with 3 parts works' => {
    my $int = 42;

    # "The answer is $int of course"
    ast RakuAST::Heredoc.new(
      :segments[
#        RakuAST::StrLiteral.new('    The answer is '),  # XXX enable after trim fix
        RakuAST::StrLiteral.new('The answer is '),
        RakuAST::Var::Lexical.new('$int'),
        RakuAST::StrLiteral.new(" of course!\n")
      ],
      :stop("    MORE\n")
    );

    is-deeply $deparsed,
      qq:!s|qq:to/MORE/\n    The answer is $int of course!\n    MORE\n|,
      'deparse';

    is-deeply $_, "The answer is 42 of course!\n", @type[$++]
      for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku);
}

subtest 'Heredoc involving an interpolated block' => {
    my $bv = 'interpolated';

    # "An { $bv } block\n"
    ast RakuAST::Heredoc.new(
      :segments[
        RakuAST::StrLiteral.new('An '),
        RakuAST::Block.new(
          body => RakuAST::Blockoid.new(
            RakuAST::StatementList.new(
              RakuAST::Statement::Expression.new(
                expression => RakuAST::Var::Lexical.new('$bv')
              )
            )
          )
        ),
        RakuAST::StrLiteral.new(" block\n")
      ],
      :stop("\n")
    );

    is-deeply $deparsed,
      qq:!s:!c|qq:to//\nAn {\n    $bv\n} block\n\n|,
      'deparse';

    is-deeply $_, "An interpolated block\n", @type[$++]
      for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku);
}

subtest 'words processor splits a heredoc into words' => {
    # qqw/ foo bar 42 /
    ast RakuAST::Heredoc.new(
      :segments[RakuAST::StrLiteral.new("foo bar 42\n")],
      :processors['words'],
      :stop("WORDS\n")
    );

    is-deeply $deparsed,
      qq|qq:w:to/WORDS/\nfoo bar 42\nWORDS\n|,
      'deparse';

    is-deeply $_, ("foo", "bar", "42"), @type[$++]
      for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku);
}

subtest 'Words processor applied to a heredoc with interpolation' => {
    my $stuff = 'r baz';

    # qqw/ ba$stuff 66 /
    ast RakuAST::Heredoc.new(
      :segments[
        RakuAST::StrLiteral.new('ba'),
        RakuAST::Var::Lexical.new('$stuff'),
        RakuAST::StrLiteral.new(" 66\n")
      ],
      :processors['words'],
      :stop("WORDS\n")
    );

    is-deeply $deparsed,
      qq:!s|qq:w:to/WORDS/\nba$stuff 66\nWORDS\n|,
      'deparse';

    is-deeply $_, ("bar", "baz", "66"), @type[$++]
      for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku);
}

subtest 'Using the val processor alone on a heredoc' => {
    # qq:v/42/
    ast RakuAST::Heredoc.new(
      :segments[RakuAST::StrLiteral.new("42\n")],
      :processors['val'],
      :stop("VAL\n")
    );

    is-deeply $deparsed,
      qq|qq:v:to/VAL/\n42\nVAL\n|,
      'deparse';

    for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it {
        todo("problem in parsing static heredocs and :v")
          if $type eq 'Str' | 'Raku';
        is-deeply EVAL($it), IntStr.new(42,"42\n"), $type;
    }
}

subtest 'Using the val processor in heredoc with interpolation' => {
    my $end = '6';

    # qq:v/4$end/
    ast RakuAST::Heredoc.new(
      :segments[
        RakuAST::StrLiteral.new('4'),
        RakuAST::Var::Lexical.new('$end'),
        RakuAST::StrLiteral.new("\n")
      ],
      :processors['val'],
      :stop("VAL\n")
    );

    is-deeply $deparsed,
      qq:!s|qq:v:to/VAL/\n4$end\nVAL\n|,
      'deparse';

    is-deeply $_, IntStr.new(46,"46\n"), @type[$++]
      for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku);
}

subtest 'Using the words and val processor in a heredoc' => {
    # <foo bar 42>
    ast RakuAST::Heredoc.new(
      :segments[RakuAST::StrLiteral.new("foo bar 42\n")],
      :processors['words', 'val'],
      :stop("VALS\n")
    );

    is-deeply $deparsed,
      qq|qq:w:v:to/VALS/\nfoo bar 42\nVALS\n|,
      'deparse';

    is-deeply $_, ("foo", "bar", IntStr.new(42,"42")), @type[$++]
      for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku);
}

subtest 'Words processor applied to a heredoc with interpolation' => {
    my $stuff = 'r baz';

    # qq:w:v/ba$stuff 66/
    ast RakuAST::Heredoc.new(
      :segments[
        RakuAST::StrLiteral.new('ba'),
        RakuAST::Var::Lexical.new('$stuff'),
        RakuAST::StrLiteral.new(" 66\n")
      ],
      :processors['words', 'val'],
      :stop("HUH\n")
    );

    is-deeply $deparsed,
      qq:!s|qq:w:v:to/HUH/\nba$stuff 66\nHUH\n|,
      'deparse';

    is-deeply $_, ('bar', 'baz', IntStr.new(66,"66")), @type[$++]
      for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku);
}

subtest 'Using the exec processor alone gives expected result' => {
    # qx/ echo 123 /
    ast RakuAST::Heredoc.new(
      :segments[RakuAST::StrLiteral.new('echo 123')],
      :processors['exec'],
      :stop("GO\n")
    );

    is-deeply $deparsed,
      qq|qq:x:to/GO/\necho 123\nGO\n|,
      'deparse';

    is-deeply $_, "123\n", @type[$++]
      for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku);
}

# vim: expandtab shiftwidth=4
