use v6.e.PREVIEW; use Test; plan 53; 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; } # MUST be the first test to handle EVAL_n counter subtest 'Special compiler variable $?FILE' => { my $file := $*CWD.absolute ~ '/EVAL_1'; # $?FILE ast RakuAST::Var::Compiler::File.new($file); is-deeply $deparsed, '$?FILE', 'deparse'; is-deeply $_, $file, @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } my class A { subtest 'Special compiler variable $?FILE' => { # ::$?CLASS ast RakuAST::Var::Lexical::Constant.new('::?CLASS'); is-deeply $deparsed, '::?CLASS', 'deparse'; is-deeply $_, A, @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } } subtest 'Special compiler variable $?LINE' => { # $?LINE # always line 1 inside the EVAL ast RakuAST::Var::Compiler::Line.new(1); is-deeply $deparsed, '$?LINE', 'deparse'; is-deeply $_, 1, @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } package Foo { our $foo = 42; subtest 'Special compiler variable $?PACKAGE' => { # $?PACKAGE ast RakuAST::Var::Compiler::Lookup.new('$?PACKAGE'); is-deeply $deparsed, '$?PACKAGE', 'deparse'; is-deeply $_, Foo, @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } subtest 'Generic package variable lookup' => { ast RakuAST::Var::Package.new( name => RakuAST::Name.from-identifier-parts("Foo","foo"), sigil => '$' ); is-deeply $deparsed, '$Foo::foo', 'deparse'; is-deeply $_, 42, @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } } subtest 'Lexical variable lookup ($ sigil)' => { my $x = 42; # $x ast RakuAST::Var::Lexical.new('$x'); is-deeply $deparsed, '$x', 'deparse'; is-deeply $_, 42, @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } subtest 'Lexical variable lookup (& sigil)' => { # &plan ast RakuAST::Var::Lexical.new('&plan'); is-deeply $deparsed, '&plan', 'deparse'; is-deeply $_, &plan, @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } subtest 'A variable in the setting' => { ast RakuAST::Var::Lexical::Setting.new('&val'); is-deeply $deparsed, 'SETTING::<&val>', 'deparse'; is-deeply $_, &val, @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } subtest 'Positional capture variable lookup works' => { my $/; "abc" ~~ /(.)(.)/; # $0 ast RakuAST::Var::PositionalCapture.new(0); is-deeply $deparsed, '$0', 'deparse'; is-deeply .Str, "a", @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); # $1 ast RakuAST::Var::PositionalCapture.new(1); is-deeply $deparsed, '$1', 'deparse'; is-deeply .Str, "b", @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } subtest 'Named capture variable lookup works' => { my $/; "abc" ~~ /$=(.)$=(.)/; # $ ast RakuAST::Var::NamedCapture.new( RakuAST::QuotedString.new( segments => [RakuAST::StrLiteral.new('y')], processors => ['words','val'] ) ); is-deeply $deparsed, '$', 'deparse'; is-deeply .Str, "b", @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); # $ ast RakuAST::Var::NamedCapture.new( RakuAST::QuotedString.new( segments => [RakuAST::StrLiteral.new('x')], processors => ['words','val'] ) ); is-deeply $deparsed, '$', 'deparse'; is-deeply .Str, "a", @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } subtest 'variable declaration takes scope and name' => { for 'my', 'state' -> $scope { # my|state $foo ast RakuAST::VarDeclaration::Simple.new( scope => $scope, sigil => '$', desigilname => RakuAST::Name.from-identifier('foo'), ); is-deeply $deparsed, $scope ~ ' $foo', 'deparse'; is $ast.scope, $scope, "did we get scope: $scope"; is $ast.name, '$foo', 'did we get the right name'; } } subtest 'Lexical variable my|state declarations work' => { for 'my', 'state' -> $scope { # my|state $foo = 10; $foo ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( scope => $scope, sigil => '$', desigilname => RakuAST::Name.from-identifier('foo'), ) ), RakuAST::Statement::Expression.new( expression => RakuAST::ApplyInfix.new( left => RakuAST::Var::Lexical.new('$foo'), infix => RakuAST::Infix.new('='), right => RakuAST::IntLiteral.new(10) ), ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$foo') ) ); is-deeply $deparsed, $scope ~ q:to/CODE/, 'deparse'; $foo; $foo = 10; $foo CODE is-deeply $_, 10, "@type[$++]: $scope" for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } } subtest 'Defaults of my|state untyped container' => { for 'my', 'state' -> $scope { # my|state $foo; $foo ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( scope => $scope, sigil => '$', desigilname => RakuAST::Name.from-identifier('foo'), ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$foo') ) ); is-deeply $deparsed, $scope ~ q:to/CODE/, 'deparse'; $foo; $foo CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, \cont { is-deeply cont, Any, "$type: $scope default value is Any"; ok cont.VAR.of =:= Mu, "$type: $scope Default constraint is Mu"; } } } subtest 'Typed variable my|state declaration (type matches in assignment)' => { for 'my', 'state' -> $scope { # my|state Int $foo; $foo = 99; $foo ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( scope => $scope, sigil => '$', desigilname => RakuAST::Name.from-identifier('foo'), type => RakuAST::Type::Simple.new( RakuAST::Name.from-identifier('Int') ) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::ApplyInfix.new( left => RakuAST::Var::Lexical.new('$foo'), infix => RakuAST::Infix.new('='), right => RakuAST::IntLiteral.new(99) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$foo') ) ); is-deeply $deparsed, $scope ~ q:to/CODE/, 'deparse'; Int $foo; $foo = 99; $foo CODE is-deeply $_, 99, "@type[$++]: $scope" for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } } subtest 'Typed variable my|state declaration (type mismatch throws)' => { for 'my', 'state' -> $scope { # my|state Int $foo; $foo = 1e5; $foo ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( scope => $scope, sigil => '$', desigilname => RakuAST::Name.from-identifier('foo'), type => RakuAST::Type::Simple.new( RakuAST::Name.from-identifier('Int') ) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::ApplyInfix.new( left => RakuAST::Var::Lexical.new('$foo'), infix => RakuAST::Infix.new('='), right => RakuAST::NumLiteral.new(1e5) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$foo') ) ); is-deeply $deparsed, $scope ~ q:to/CODE/, 'deparse'; Int $foo; $foo = 100000e0; $foo CODE for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { todo 'string EVAL produces different error' if $type eq 'Str'; throws-like { EVAL($it) }, X::TypeCheck::Assignment, expected => Int, got => 1e5 ; } } } subtest 'Lexical variable my|state declaration with assignment initializer' => { for 'my', 'state' -> $scope { # my|state $var = 125; $var ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( scope => $scope, sigil => '$', desigilname => RakuAST::Name.from-identifier('var'), initializer => RakuAST::Initializer::Assign.new( RakuAST::IntLiteral.new(125) ) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$var') ) ); is-deeply $deparsed, $scope ~ q:to/CODE/, 'deparse'; $var = 125; $var CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, \result { is-deeply result, 125, "$type: $scope variable declarations with assignment initializer"; ok result.VAR.isa(Scalar), "$type: $scope was an assignment into a Scalar container"; nok result.VAR.dynamic, "$type: $scope is not dynamic"; lives-ok { result = 42 }, "$type: $scope can update the container that was produced"; } } } subtest 'Lexical my|state array declaration with assignment initializer' => { for 'my', 'state' -> $scope { # my|state @var = 22, 33; @var ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( scope => $scope, sigil => '@', desigilname => RakuAST::Name.from-identifier('var'), initializer => RakuAST::Initializer::Assign.new( RakuAST::ApplyListInfix.new( infix => RakuAST::Infix.new(','), operands => ( RakuAST::IntLiteral.new(22), RakuAST::IntLiteral.new(33) ) ) ) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('@var') ), ); is-deeply $deparsed, $scope ~ q:to/CODE/, 'deparse'; @var = 22, 33; @var CODE is-deeply $_, [22,33], "@type[$++]: $scope" for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } } subtest 'Lexical my|state variable declarations with bind initializer' => { for 'my', 'state' -> $scope { # my|state $var := 225; $var ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( scope => $scope, sigil => '$', desigilname => RakuAST::Name.from-identifier('var'), initializer => RakuAST::Initializer::Bind.new( RakuAST::IntLiteral.new(225) ) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$var') ), ); is-deeply $deparsed, $scope ~ q:to/CODE/, 'deparse'; $var := 225; $var CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, \result { # state var limitation: binding (like "state $var := 225") not supported next if $scope eq 'state'; is-deeply result, 225, "$type: $scope variable declarations with bind initializer"; nok result.VAR.isa(Scalar), "$type: $scope really was bound; no Scalar container"; dies-ok { result = 42 }, "$type: $scope cannot assign as it is not a container"; } } } subtest 'Anonymous state variable declaration' => { # -> { ++state $ } ast RakuAST::PointyBlock.new( body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::ApplyInfix.new( left => RakuAST::ApplyPrefix.new( prefix => RakuAST::Prefix.new('++'), operand => RakuAST::VarDeclaration::Anonymous.new(:sigil('$'), :scope('state')) ), infix => RakuAST::Infix.new('+'), right => RakuAST::ApplyPrefix.new( prefix => RakuAST::Prefix.new('++'), operand => RakuAST::VarDeclaration::Anonymous.new(:sigil('$'), :scope('state')) ) ) ) ) ) ); is-deeply $deparsed, q:to/CODE/.chomp, 'deparse'; -> { ++$ + ++$ } CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, &result { is-deeply result() ~ result() ~ result(), '246', "$type: anonymous state variables works are are distinct"; } } subtest 'Dynamic variable access' => { sub with-dyn(&test) { my $*dyn = 'in'; } my $*dyn = 'out'; # $*dyn ast RakuAST::Var::Dynamic.new('$*dyn'); is-deeply $deparsed, '$*dyn', 'deparse'; is-deeply $_, 'out', "@type[$++] access outside" for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); is-deeply $_, 'in', "@type[$++] access inside" for with-dyn({ EVAL($ast) }), with-dyn({ EVAL($deparsed) }); # $*OUT ast RakuAST::Var::Dynamic.new('$*OUT'); is-deeply $deparsed, '$*OUT', 'deparse'; is-deeply $_, $*OUT, "@type[$++] checking \$*OUT" for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } subtest 'Dynamic variable declaration and assignment, dynamic lookup' => { # my $*var = 360; ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( sigil => '$', twigil => '*', desigilname => RakuAST::Name.from-identifier('var'), initializer => RakuAST::Initializer::Assign.new( RakuAST::IntLiteral.new(360) ) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Dynamic.new('$*var') ), ); is-deeply $deparsed, q:to/CODE/, 'deparse'; my $*var = 360; $*var CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, \result { is-deeply result, 360, "$type: Dynamic variable declaration and assignment, dynamic lookup"; ok result.VAR.isa(Scalar), "$type: Dynamic did an assignment into a Scalar container"; ok result.VAR.dynamic, "$type: Is a dynamic"; lives-ok { result = 99 }, "$type: Can update the container that was produced"; } } subtest '@ sigil my|state var is initialized to Array' => { for 'my', 'state' -> $scope { # my|state @arr; @arr ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( scope => $scope, sigil => '@', desigilname => RakuAST::Name.from-identifier('arr'), ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('@arr') ), ); is-deeply $deparsed, $scope ~ q:to/CODE/, 'deparse'; @arr; @arr CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, \cont { is-deeply cont.WHAT, Array, "$type: $scope @ sigil var is initialized to Array"; is-deeply cont.VAR.WHAT, Array, "$type: $scope @ sigil var not wrapped in Scalar"; ok cont.defined, "$type: $scope it is a defined Array instance"; is cont.elems, 0, "$type: $scope it is empty"; is-deeply cont[0].VAR.WHAT, Scalar, "$type: $scope element is a Scalar"; is-deeply cont[0], Any, "$type: $scope contains an Any by default"; ok cont[0].VAR.of =:= Mu, "$type: $scope constraint is Mu by default"; } } } subtest '% sigil my|state var is initialized to Hash' => { for 'my', 'state' -> $scope { # my|state %hash; %hash ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( scope => $scope, sigil => '%', desigilname => RakuAST::Name.from-identifier('hash'), ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('%hash') ), ); is-deeply $deparsed, $scope ~ q:to/CODE/, 'deparse'; %hash; %hash CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, \cont { is-deeply cont.WHAT, Hash, "$type: $scope % sigil var is initialized to Hash"; is-deeply cont.VAR.WHAT, Hash, "$type: $scope % sigil var not wrapped in Scalar"; ok cont.defined, "$type: $scope it is a defined Hash instance"; is cont.elems, 0, "$type: $scope it is empty"; is-deeply cont.VAR.WHAT, Scalar, "$type: $scope element is a Scalar"; is-deeply cont, Any, "$type: $scope contains an Any by default"; ok cont.VAR.of =:= Mu, "$type: $scope constraint is Mu by default"; } } } subtest '@ sigil my|state var with Int type is an Array' => { for 'my', 'state' -> $scope { # my|state Int @arr; @arr ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( scope => $scope, sigil => '@', desigilname => RakuAST::Name.from-identifier('arr'), type => RakuAST::Type::Simple.new( RakuAST::Name.from-identifier('Int') ) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('@arr') ), ); is-deeply $deparsed, $scope ~ q:to/CODE/, 'deparse'; Int @arr; @arr CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, \cont { ok cont ~~ Array, "$type: $scope @ sigil var with Int type is an Array"; ok cont ~~ Positional[Int], "$type: $scope it does Positional[Int]"; is-deeply cont.of, Int, "$type: $scope .of gives Int"; is cont.elems, 0, "$type: $scope it is empty"; is-deeply cont[0].VAR.WHAT, Scalar, "$type: $scope element is a Scalar"; is-deeply cont[0], Int, "$type: $scope contains an Int"; ok cont[0].VAR.of =:= Int, "$type: $scope constraint is Int"; } } } subtest '% sigil my|state var with Int type is a Hash' => { for 'my', 'state' -> $scope { # my|state Int %hash; %hash ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( scope => $scope, sigil => '%', desigilname => RakuAST::Name.from-identifier('hash'), type => RakuAST::Type::Simple.new( RakuAST::Name.from-identifier('Int') ) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('%hash') ) ); is-deeply $deparsed, $scope ~ q:to/CODE/, 'deparse'; Int %hash; %hash CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, \cont { ok cont ~~ Hash, "$type: $scope % sigil var with Int type is a Hash"; ok cont ~~ Associative[Int], "$type: $scope it does Associative[Int]"; is-deeply cont.of, Int, "$type: $scope .of gives Int"; is cont.elems, 0, "$type: $scope it is empty"; is-deeply cont.VAR.WHAT, Scalar, "$type: $scope element is a Scalar"; is-deeply cont, Int, "$type: $scope contains an Int"; ok cont.VAR.of =:= Int, "$type: $scope constraint is Int"; } } } subtest 'Can access external native int var' => { my int $x = 42; # $x ast RakuAST::Var::Lexical.new('$x'); is-deeply $deparsed, '$x', 'deparse'; is-deeply $_, 42, @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } subtest 'Can access external native num var' => { my num $x = 4e2; # $x ast RakuAST::Var::Lexical.new('$x'); is-deeply $deparsed, '$x', 'deparse'; is-deeply $_, 4e2, @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } subtest 'Can access external native str var' => { my str $x = 'answer'; # $x ast RakuAST::Var::Lexical.new('$x'); is-deeply $deparsed, '$x', 'deparse'; is-deeply $_, 'answer', @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } subtest 'int declaration creates a native int container' => { # my int $native-int; $native-int ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( sigil => '$', desigilname => RakuAST::Name.from-identifier('native-int'), type => RakuAST::Type::Simple.new( RakuAST::Name.from-identifier('int') ) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$native-int') ) ); is-deeply $deparsed, q:to/CODE/, 'deparse'; my int $native-int; $native-int CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, \cont { my $desc = "$type: int declaration creates a native int container"; multi check(int $x) { pass $desc } multi check($x) { flunk $desc } check(cont); is-deeply cont, 0, "$type: Native int initialized to 0 by default"; } } subtest 'num declaration creates a native num container' => { # my num $native-num; $native-num ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( sigil => '$', desigilname => RakuAST::Name.from-identifier('native-num'), type => RakuAST::Type::Simple.new( RakuAST::Name.from-identifier('num') ) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$native-num') ) ); is-deeply $deparsed, q:to/CODE/, 'deparse'; my num $native-num; $native-num CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, \cont { my $desc = "$type: num declaration creates a native num container"; multi check(num $x) { pass $desc } multi check($x) { flunk $desc } check(cont); is-deeply cont, 0e0, "$type: Native num initialized to 0e0 by default"; } } subtest 'str declaration creates a native str container' => { # my str $native-str; $native-str ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( sigil => '$', desigilname => RakuAST::Name.from-identifier('native-str'), type => RakuAST::Type::Simple.new( RakuAST::Name.from-identifier('str') ) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$native-str') ) ); is-deeply $deparsed, q:to/CODE/, 'deparse'; my str $native-str; $native-str CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, \cont { my $desc = "$type: str declaration creates a native str container"; multi check(str $x) { pass $desc } multi check($x) { flunk $desc } check(cont); is-deeply cont, '', "$type: Native str initialized to empty string by default"; } } subtest 'Native int assign initializer works' => { # my int $native-int = 963; $native-int ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( sigil => '$', desigilname => RakuAST::Name.from-identifier('native-int'), type => RakuAST::Type::Simple.new( RakuAST::Name.from-identifier('int') ), initializer => RakuAST::Initializer::Assign.new( RakuAST::IntLiteral.new(963) ) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$native-int') ) ); is-deeply $deparsed, q:to/CODE/, 'deparse'; my int $native-int = 963; $native-int CODE is-deeply $_, 963, @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } subtest 'Native num assign initializer works' => { # my num $native-num = 96e3; $native-num ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( sigil => '$', desigilname => RakuAST::Name.from-identifier('native-num'), type => RakuAST::Type::Simple.new( RakuAST::Name.from-identifier('num') ), initializer => RakuAST::Initializer::Assign.new( RakuAST::NumLiteral.new(96e3) ) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$native-num') ) ); is-deeply $deparsed, q:to/CODE/, 'deparse'; my num $native-num = 96000e0; $native-num CODE is-deeply $_, 96e3, @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } subtest 'Native str assign initializer works' => { # my str $native-str = 'nine six three'; $native-str ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( sigil => '$', desigilname => RakuAST::Name.from-identifier('native-str'), type => RakuAST::Type::Simple.new( RakuAST::Name.from-identifier('str') ), initializer => RakuAST::Initializer::Assign.new( RakuAST::StrLiteral.new('nine six three') ) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$native-str') ) ); is-deeply $deparsed, q:to/CODE/, 'deparse'; my str $native-str = "nine six three"; $native-str CODE is-deeply $_, 'nine six three', @type[$++] for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } { my module M { our $var; # our $var; $var ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( scope => 'our', sigil => '$', desigilname => RakuAST::Name.from-identifier('var'), ), ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$var') ), ); is-deeply $deparsed, q:to/CODE/, 'deparse'; our $var; $var CODE subtest 'AST: our-scoped variable declaration without initializer takes current value (eval mode)' => { $var = 66; is-deeply EVAL($ast), 66, 'eval AST'; is-deeply $var, 66, 'Value intact after eval'; } subtest 'Str: our-scoped variable declaration without initializer takes current value (eval mode)' => { $var = 99; is-deeply EVAL($ast), 99, 'eval Str'; is-deeply $var, 99, 'Value intact after eval'; } subtest 'Raku: our-scoped variable declaration without initializer takes current value (eval mode)' => { $var = 137; is-deeply EVAL($ast), 137, 'eval Str'; is-deeply $var, 137, 'Value intact after eval'; } # our $x = 42; $x ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( scope => 'our', sigil => '$', desigilname => RakuAST::Name.from-identifier('x'), initializer => RakuAST::Initializer::Assign.new( RakuAST::IntLiteral.new(42) ) ), ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$x') ), ); is-deeply $deparsed, q:to/CODE/, 'deparse'; our $x = 42; $x CODE subtest 'our-scoped variable declaration with initializer works (eval mode)' => { is-deeply $_, 42 for EVAL($ast), EVAL($deparsed), EVAL(EVAL $raku); } # our $y = 99; $y ast RakuAST::CompUnit.new( :!eval, :comp-unit-name('TEST_1'), :statement-list(RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( scope => 'our', sigil => '$', desigilname => RakuAST::Name.from-identifier('y'), initializer => RakuAST::Initializer::Assign.new( RakuAST::IntLiteral.new(99) ) ), ), RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$y') ) )) ); is-deeply $deparsed, q:to/CODE/, 'deparse'; our $y = 99; $y CODE # There is no equivalent string representation of a compilation unit # so we cannot actually test the DEPARSE of the ast, as that would # interfere with other tests. is-deeply EVAL($ast), 99, 'our-scoped variable declaration with initializer works (top-level mode)'; } is-deeply $M::x, 42, 'our variable set in eval mode is installed into the current package'; ok $M::x.VAR ~~ Scalar, 'It is a bound scalar'; todo "NYI"; nok M.WHO<$y>:exists, 'our-scoped variable declaration in top-level comp unit does not leak out'; } subtest 'A pointy block node with a state variable' => { # -> { state $foo = 42; $foo++ } ast RakuAST::PointyBlock.new( signature => RakuAST::Signature.new( parameters => () ), body => RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( scope => 'state', sigil => '$', desigilname => RakuAST::Name.from-identifier('foo'), initializer => RakuAST::Initializer::Assign.new( RakuAST::IntLiteral.new(42) ) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::ApplyPostfix.new( postfix => RakuAST::Postfix.new(:operator<++>), operand => RakuAST::Var::Lexical.new('$foo') ) ) ) ) ) ) ); is-deeply $deparsed, q:to/CODE/, 'deparse'; -> { state $foo = 42; $foo++ } CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, $block { is $block(), 42, "$type: state variable initialized"; is $block(), 43, "$type: state variable kept value"; } } subtest 'Term (sigilless) variable declaration' => { # my \foo = 111; foo ast RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Term.new( scope => 'my', name => RakuAST::Name.from-identifier('foo'), initializer => RakuAST::Initializer::Assign.new( RakuAST::IntLiteral.new(111) ) ) ), RakuAST::Statement::Expression.new( expression => RakuAST::Term::Name.new(RakuAST::Name.from-identifier('foo')) ) ); is-deeply $deparsed, q:to/CODE/, 'deparse'; my \foo = 111; foo CODE for 'AST', EVAL($ast), 'Str', EVAL($deparsed), 'Raku', EVAL(EVAL $raku) -> $type, \value { is-deeply value, 111, "$type: sigilless variable initialized with correct value"; ok value.VAR =:= value, "$type: no container produced"; } } subtest 'Lexical constant' => { # constant foo = "bar" ast RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Constant.new( name => "foo", initializer => RakuAST::Initializer::Assign.new( RakuAST::StrLiteral.new("bar") ) ) ); is-deeply $deparsed, 'constant foo = "bar"', 'deparse'; my package one { is-deeply EVAL($ast), "bar", "AST: did it produce 'bar'"; is-deeply OUR::, "bar", "AST: was the constant installed"; } my package two { is-deeply EVAL($deparsed), "bar", "Str: did it produce 'bar'"; is-deeply OUR::, "bar", "Str: was the constant installed"; } my package three { is-deeply EVAL(EVAL $raku), "bar", "Raku: did it produce 'bar'"; is-deeply OUR::, "bar", "Raku: was the constant installed"; } } subtest 'Lexical my constant' => { # my constant foo = "bar" ast RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Constant.new( scope => "my", name => "foo", initializer => RakuAST::Initializer::Assign.new( RakuAST::StrLiteral.new("bar") ) ) ); is-deeply $deparsed, 'my constant foo = "bar"', 'deparse'; for "AST", $ast, "Str", $deparsed, 'Raku', EVAL($raku) -> $type, $it { is-deeply EVAL($it), "bar", "$type: did it produce 'bar'"; nok OUR:::exists, "$type: was the constant *not* installed"; } } subtest 'Lexical my constant with expression' => { # my constant foo = now ast RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Constant.new( scope => "my", name => "foo", initializer => RakuAST::Initializer::Assign.new( RakuAST::Term::Named.new("now") ) ) ); is-deeply $deparsed, 'my constant foo = now', 'deparse'; for 'AST', (try EVAL $ast), 'Str', EVAL($deparsed), 'Raku', (try EVAL(EVAL $raku)) -> $type, $it { todo "Unknown compilation input 'qast'" if $type eq 'AST' | 'Raku' && !%*ENV; isa-ok $it, Instant, "$type: did it produce an Instant"; nok $it == now, "$type: check that time has passed"; nok OUR:::exists, "$type: was the constant *not* installed"; } } subtest 'Special compiler variable $=pod' => { # $=pod ast RakuAST::Var::Doc.new('pod'); is-deeply $deparsed, '$=pod', 'deparse'; for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { is-deeply EVAL($it), [], "$type: EVAL"; } } subtest 'Special compiler variable $=finish' => { # $=finish ast RakuAST::Var::Doc.new('finish'); is-deeply $deparsed, '$=finish', 'deparse'; for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { is-deeply EVAL($it), Mu, "$type: EVAL"; } } subtest 'variable with a trait' => { # my $a is default(42) ast RakuAST::VarDeclaration::Simple.new( scope => "my", sigil => "\$", desigilname => RakuAST::Name.from-identifier("a"), traits => ( RakuAST::Trait::Is.new( name => RakuAST::Name.from-identifier("default"), argument => RakuAST::Circumfix::Parentheses.new( RakuAST::SemiList.new( RakuAST::Statement::Expression.new( expression => RakuAST::IntLiteral.new(42) ) ) ) ), ) ); is-deeply $deparsed, 'my $a is default(42)', 'deparse'; for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { is-deeply EVAL($it), 42, "$type: EVAL"; } } # vim: expandtab shiftwidth=4