The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!perl -w

use strict;
use Test::More;

use Text::Xslate;
use Text::Xslate::Compiler;

my $tx = Text::Xslate->new();

my @set = (
    [ <<'T', { data => [[qw(Perl)]] }, <<'X' ],
: macro foo ->($x) {
:   for $x -> ($item) {
        Hello, <: $item :> world!
:   }
: }
: for $data -> ($item) {
:   foo($item)
: }
T
        Hello, Perl world!
X

    [ <<'T', { data => [[qw(Perl Xslate)]] }, <<'X' ],
: macro foo ->($x) {
:   for $x -> ($item) {
        Hello, <: $item :> world!
:   }
: }
: for $data -> ($item) {
:   foo($item)
: }
T
        Hello, Perl world!
        Hello, Xslate world!
X

    [ <<'T', { data => [[qw(Perl Xslate)]] }, <<'X' ],
: macro foo ->($x) {
:   for $x -> ($item) {
        Hello, <: $item :> world!
:   }
: }
: for $data -> ($item) {
:   foo($item)
:   foo($item)
: }
T
        Hello, Perl world!
        Hello, Xslate world!
        Hello, Perl world!
        Hello, Xslate world!
X

    [ <<'T', { }, <<'X' ],
: macro foo ->($x) {
    <strong><:$x:></strong>
: }
: macro bar ->($x) {
:   foo($x)
: }
: foo("FOO")
: bar("BAR")
T
    <strong>FOO</strong>
    <strong>BAR</strong>
X

    [ <<'T', { }, <<'X', "nested call" ],
: macro foo ->($x) {
:   "[" ~ $x  ~ "]"
: }
<: foo(foo("FOO")) :>
T
[[FOO]]
X

    [ <<'T', { }, <<'X', "nested (arithmatic)" ],
: macro add ->($x, $y) { $x + $y }
<: add(1, 2) + add(10, 20) :>
T
33
X

    [ <<'T', { }, <<'X', "multi call" ],
: macro foo ->($x) {
:   "[" ~ $x  ~ "]"
: }
<: foo("FOO") ~ foo("BAR") ~ foo("BAZ") :>
T
[FOO][BAR][BAZ]
X

    [ <<'T', { }, <<'X', "nested multi call" ],
: macro foo ->($x) {
:   "[" ~ $x  ~ "]"
: }
<: foo(foo("FOO") ~ foo("BAR")) :>
T
[[FOO][BAR]]
X

    [ <<'T', { }, <<'X', "nested multi call (arithmatic)" ],
: macro add ->($x, $y) { $x + $y }
<: add(add(1, 2) + add(10, 20) + add(100, 200), 1000) :>
T
1333
X

    [ <<'T', { }, <<'X', "recursion" ],
    : macro factorial ->($x) {
    :   $x == 0 ? 1 : $x * factorial($x-1)
    : }
    <: factorial(0) :>
    <: factorial(1) :>
    <: factorial(2) :>
    <: factorial(3) :>
T
    1
    1
    2
    6
X

    [ <<'T', { }, <<'X', "filter operator" ],
    : macro factorial ->($x) {
    :   $x == 0 ? 1 : $x * factorial($x-1)
    : }
    <: 0 | factorial :>
    <: 1 | factorial :>
    <: 2 | factorial :>
    <: 3 | factorial :>
T
    1
    1
    2
    6
X

    [ <<'T', { }, <<'X', "a macro returns escaped string" ],
<: macro em ->($x) { :><em><: $x :></em><: } -:>
    <: "foo" | em        :>
    <: "bar" | em | raw  :>
    <: "baz" | em | html :>
T
    <em>foo</em>
    <em>bar</em>
    <em>baz</em>
X

    [ <<'T', { }, <<'X', "save macro" ],
<: macro em ->($x) { :><em><: $x :></em><: } -:>
: for [em] -> $m {
    <: $m("foo") :>
: }
T
    <em>foo</em>
X


    [<<'T', { value10 => 10 }, '100'],
: macro foo ->($x) { $x.foo }
: foo( { foo => $value10 == 10 ? 100 : $value10 == 20 ? 200 : 300 } )
T

    [<<'T', { value20 => 20 }, '200'],
: macro foo ->($x) { $x.foo }
: foo( { foo => $value20 == 10 ? 100 : $value20 == 20 ? 200 : 300 } )
T
);

foreach my $d(@set) {
    my($in, $vars, $out, $msg) = @$d;
    is eval { $tx->render_string($in, $vars) }, $out, $msg or diag $in;
    diag $@ if $@;
}

eval {
    $tx->render_string('<: foo("x") :>', {});
};
like $@, qr/\b foo \b/xms, "don't affect the parser";

eval {
    $tx->render_string(<<'T', {});
    : macro foo{
    :   foo()
    : }
    : foo()
T
};
like $@, qr/too deep/, 'deep recursion';
like $@, qr/\b foo \b/xms;

done_testing;