use v6.e.PREVIEW; use Test; plan 18; my $ast; my $deparsed; my $raku; my @type = ; sub ast(RakuAST::Node:D $node --> Nil) { $ast := $node; $deparsed := $node.DEPARSE; $raku := 'use experimental :rakuast; ' ~ $node.raku; diag $deparsed.chomp; } subtest 'The do statement prefix works with a statement' => { # do 137 ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::StatementPrefix::Do.new( RakuAST::IntLiteral.new(137) ) ) ); is-deeply $deparsed, "do 137\n", 'deparse'; is-deeply $_, 137, @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } subtest 'The do statement prefix works with a block' => { # do { 199 } ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::StatementPrefix::Do.new( RakuAST::Block.new( body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::IntLiteral.new(199) ) ) ) ) ) ) ); is-deeply $deparsed, q:to/CODE/, 'deparse'; do { 199 } CODE is-deeply $_, 199, @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } subtest 'The quietly statement prefix works' => { my $warned; CONTROL { default { $warned = True; .resume; } } sub do-warning() { warn "oops"; "survived" } # quietly do-warning() ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::StatementPrefix::Quietly.new( RakuAST::Call::Name.new( name => RakuAST::Name.from-identifier('do-warning') ) ) ) ); is-deeply $deparsed, "quietly do-warning()\n", 'deparse'; for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { $warned = False; is-deeply EVAL($it), "survived", "$type: with a statement"; nok $warned, "$type: the warning was suppressed"; } # quietly { do-warning() } ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::StatementPrefix::Quietly.new( RakuAST::Block.new( body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::Call::Name.new( name => RakuAST::Name.from-identifier('do-warning') ) ) ) ) ) ) ) ); is-deeply $deparsed, q:to/CODE/, 'deparse'; quietly { do-warning() } CODE for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { $warned = False; is-deeply EVAL($it), "survived", "$type: with a block"; nok $warned, "$type: the warning was suppressed"; } } subtest 'The gather statement prefix works on a statement' => { my $done; sub do-takes() { $done = True; take 111; take 222; } # gather do-takes() ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::StatementPrefix::Gather.new( RakuAST::Call::Name.new( name => RakuAST::Name.from-identifier('do-takes') ) ) ) ); is-deeply $deparsed, "gather do-takes()\n", 'deparse'; $done = False; for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, \result { isa-ok result, Seq, "$type: Got a Seq back from gather (expression form)"; is-deeply $done, False, 'The gather is lazy'; my @elems = result; is-deeply $done, True, 'Gathered the takes'; is-deeply @elems, [111, 222], 'Got correct result from the gather expression'; $done = False; } } subtest 'The gather statement prefix works on a block' => { my $done; sub do-takes() { $done = True; take 333; take 444; } # gather { do-takes() } ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::StatementPrefix::Gather.new( RakuAST::Block.new( body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::Call::Name.new( name => RakuAST::Name.from-identifier('do-takes') ) ) ) ) ) ) ) ); is-deeply $deparsed, q:to/CODE/, 'deparse'; gather { do-takes() } CODE $done = False; for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, \result { isa-ok result, Seq, "$type: Got a Seq back from gather (block form)"; is-deeply $done, False, 'The gather is lazy'; my @elems = result; is-deeply $done, True, 'Gathered the takes'; is-deeply @elems, [333, 444], 'Got correct result from the gather expression'; $done = False; } } subtest "The race / hyper / lazy / eager statement prefixes work" => { my class ContextMe { has @.called; method race() { @!called.push('race'); 'result' } method hyper() { @!called.push('hyper'); 'result' } method lazy() { @!called.push('lazy'); 'result' } method eager() { @!called.push('eager'); 'result' } } for -> $context { my $c; my $result; # race|hyper|lazy|eager $c ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::StatementPrefix::{tclc $context}.new( RakuAST::Var::Lexical.new('$c') ) ) ); is-deeply $deparsed, $context ~ ' $c' ~ "\n", 'deparse'; for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { $c = ContextMe.new; is-deeply EVAL($it), 'result', "$type: $context works with statement"; is-deeply $c.called, [$context], "$type: context method was called"; } } for -> $context { my $c; my $result; # race|hyper|lazy|eager { $c } ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::StatementPrefix::{tclc $context}.new( RakuAST::Block.new( body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$c') ) ) ) ) ) ) ); is-deeply $deparsed, qq:!c:to/CODE/, 'deparse'; $context { \$c } CODE for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { $c = ContextMe.new; is-deeply EVAL($it), 'result', "$type: $context works with block"; is-deeply $c.called, [$context], "$type: context method was called"; } } } subtest 'try statement prefix with expression producing value results' => { # try 99 ast RakuAST::StatementPrefix::Try.new( RakuAST::Statement::Expression.new( expression => RakuAST::IntLiteral.new(99) ) ); is-deeply $deparsed, 'try 99', 'deparse'; for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { is-deeply EVAL($it), 99, "$type: correct result"; todo "$type: string eval does not set $!, also in main" if $type eq 'Str'; is-deeply $!, Nil, "$type: \$! is Nil when not exception"; } } subtest 'try statement prefix with throwing expression handles the exception' => { # try die("hard") ast RakuAST::StatementPrefix::Try.new( RakuAST::Statement::Expression.new( expression => RakuAST::Call::Name.new( name => RakuAST::Name.from-identifier('die'), args => RakuAST::ArgList.new(RakuAST::StrLiteral.new('hard')) ) ) ); is-deeply $deparsed, 'try die("hard")', 'deparse'; for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { $! = 42; is-deeply EVAL($it), Nil, "$type: did we get Nil"; is-deeply $!.Str, 'hard', "$type: \$! is populated with exception"; } } subtest 'try statement prefix with block producing value results' => { # try { 999 } ast RakuAST::StatementPrefix::Try.new( RakuAST::Block.new( body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::IntLiteral.new(999) ) ) ) ) ); is-deeply $deparsed, q:to/CODE/.chomp, 'deparse'; try { 999 } CODE for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { is-deeply EVAL($it), 999, "$type: correct result"; todo "$type: string eval does not set \$!, also in main" if $type eq 'Str'; is-deeply $!, Nil, "$type: \$! is Nil when not exception"; } } subtest 'try statement prefix with throwing block handles the exception' => { # try { die("another day") } ast RakuAST::StatementPrefix::Try.new( RakuAST::Block.new( body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::Call::Name.new( name => RakuAST::Name.from-identifier('die'), args => RakuAST::ArgList.new(RakuAST::StrLiteral.new('another day')) ) ) ) ) ) ); is-deeply $deparsed, q:to/CODE/.chomp, 'deparse'; try { die("another day") } CODE for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { $! = 42; is-deeply EVAL($ast), Nil, "$type: did we get Nil"; is-deeply $!.Str, 'another day', "$type: \$! is populated with exception"; } } subtest 'start statement prefix with expression evaluates to Promise' => { # start 111 ast RakuAST::StatementPrefix::Start.new( RakuAST::Statement::Expression.new( expression => RakuAST::IntLiteral.new(111) ) ); is-deeply $deparsed, 'start 111', 'deparse'; for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, $promise { isa-ok $promise, Promise, $type; is-deeply await($promise), 111, 'Correct result from Promise'; } } subtest 'start statement prefix with block evaluates to Promise' => { # start { 137 } ast RakuAST::StatementPrefix::Start.new( RakuAST::Block.new( body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::IntLiteral.new(137) ) ) ) ) ); is-deeply $deparsed, q:to/CODE/.chomp, 'deparse'; start { 137 } CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, $promise { isa-ok $promise, Promise, $type; is-deeply await($promise), 137, 'Correct result from Promise'; } } subtest 'A start has a fresh $/' => { # start $/ ast RakuAST::StatementPrefix::Start.new( RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$/') ) ); is-deeply $deparsed, 'start $/', 'deparse'; { my $/ = 42; nok await(EVAL($ast)) ~~ 42, 'AST: A start has a fresh $/'; } { my $/ = 666; nok await(EVAL($deparsed)) ~~ 666, 'Str: A start has a fresh $/'; } { my $/ = 137; nok await(EVAL(EVAL $raku)) ~~ 137, 'Raku: A start has a fresh $/'; } } subtest 'A start has a fresh $!' => { # start $! ast RakuAST::StatementPrefix::Start.new( RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$!') ) ); is-deeply $deparsed, 'start $!', 'deparse'; { my $! = 42; nok await(EVAL($ast)) ~~ 42, 'AST: A start has a fresh $!'; } { my $! = 666; nok await(EVAL($deparsed)) ~~ 666, 'Str: A start has a fresh $!'; } { my $! = 137; nok await(EVAL(EVAL $raku)) ~~ 137, 'Raku: A start has a fresh $!'; } } subtest 'supply statement prefix with expression evaluates to Supply' => { # supply whenever 42 { .emit } ast RakuAST::StatementPrefix::Supply.new( RakuAST::Statement::Whenever.new( trigger => RakuAST::IntLiteral.new(42), body => RakuAST::Block.new( implicit-topic => True, required-topic => 1, body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::Term::TopicCall.new( RakuAST::Call::Method.new( name => RakuAST::Name.from-identifier("emit") ) ) ) ) ) ) ) ); # must be this number of tests plan 7; is-deeply $deparsed, q:to/CODE/.chomp, 'deparse'; supply whenever 42 { .emit } CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, $supply { isa-ok $supply, Supply, $type; $supply.tap({ is $_, 42, 'emitted value ok' }); } } subtest 'supply statement prefix with block evaluates to Supply' => { # supply { whenever 42 { .emit } } ast RakuAST::StatementPrefix::Supply.new( RakuAST::Block.new( body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Whenever.new( trigger => RakuAST::IntLiteral.new(42), body => RakuAST::Block.new( implicit-topic => True, required-topic => 1, body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::Term::TopicCall.new( RakuAST::Call::Method.new( name => RakuAST::Name.from-identifier("emit") ) ) ) ) ) ) ) ) ) ) ); # must be this number of tests plan 7; is-deeply $deparsed, q:to/CODE/.chomp, 'deparse'; supply { whenever 42 { .emit } } CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, $supply { isa-ok $supply, Supply, $type; $supply.tap({ is $_, 42, 'emitted value ok' }); } } subtest 'react statement prefix with expression evaluates to Nil' => { # react whenever 42 { $a = $_ } ast RakuAST::StatementPrefix::React.new( RakuAST::Statement::Whenever.new( trigger => RakuAST::IntLiteral.new(42), body => RakuAST::Block.new( implicit-topic => True, required-topic => 1, body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::ApplyInfix.new( left => RakuAST::Var::Lexical.new("\$a"), infix => RakuAST::Infix.new("="), right => RakuAST::Var::Lexical.new("\$_") ) ) ) ) ) ) ); is-deeply $deparsed, q:to/CODE/.chomp, 'deparse'; react whenever 42 { $a = $_ } CODE for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL $raku -> $type, $it { my $a; is-deeply EVAL($it), Nil, "$type: is the result Nil"; is-deeply $a, 42, "$type: did the code get run"; } } subtest 'react statement block with expression evaluates to Nil' => { # react { whenever 42 { $a = $_ } } ast RakuAST::StatementPrefix::React.new( RakuAST::Block.new( body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Whenever.new( trigger => RakuAST::IntLiteral.new(42), body => RakuAST::Block.new( implicit-topic => True, required-topic => 1, body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::ApplyInfix.new( left => RakuAST::Var::Lexical.new("\$a"), infix => RakuAST::Infix.new("="), right => RakuAST::Var::Lexical.new("\$_") ) ) ) ) ) ) ) ) ) ); is-deeply $deparsed, q:to/CODE/.chomp, 'deparse'; react { whenever 42 { $a = $_ } } CODE for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL $raku -> $type, $it { my $a; is-deeply EVAL($it), Nil, "$type: is the result Nil"; is-deeply $a, 42, "$type: did the code get run"; } } # vim: expandtab shiftwidth=4