use v6.e.PREVIEW; use Test; plan 6; my $ast; my $deparsed; my $raku; sub ast(RakuAST::Node:D $node --> Nil) { $ast := $node; $deparsed := $node.DEPARSE; $raku := 'use experimental :rakuast; ' ~ $node.raku; diag $deparsed.chomp; } subtest 'creating an empty role' => { # role Frobnicator { } ast RakuAST::Role.new( scope => 'my', name => RakuAST::Name.from-identifier('Frobnicator'), ); is-deeply $deparsed, 'my role Frobnicator { }', 'deparse'; for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { my $role := EVAL($it); is $role.^name, 'Frobnicator', "$type: role gets correct name"; isa-ok $role.new, $role, "$type: does the role auto-pun"; my $class := RakuAST::Class.new( scope => 'my', name => RakuAST::Name.from-identifier('Zippo'), traits => ( RakuAST::Trait::Does.new($ast), ) ).EVAL; is $class.^name, 'Zippo', "$type: class gets correct name"; my $object := $class.new; isa-ok $object, $class, "$type: can it be instantiated"; todo "needs work on consuming the role" if $type eq 'Str' | 'Raku'; does-ok $class, $role, "$type: did the role get consumed"; } } subtest 'creating a role with an attribute' => { # my role A { has $.a = 42 } ast RakuAST::Role.new( scope => 'my', name => RakuAST::Name.from-identifier('A'), body => RakuAST::Block.new( body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::VarDeclaration::Simple.new( scope => "has", sigil => '$', twigil => '.', desigilname => RakuAST::Name.from-identifier('a'), initializer => RakuAST::Initializer::Assign.new( RakuAST::IntLiteral.new(42) ) ) ) ) ) ) ); is-deeply $deparsed, q:to/CODE/.chomp, 'deparse'; my role A { has $.a = 42 } CODE for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { my $role := EVAL($it); is $role.^name, 'A', "$type: role gets correct name"; my $none := $role.new; isa-ok $none, $role, "$type: does the role auto-pun (1)"; is-deeply $none.a, 42, "$type: did the attribute get initialized (1)"; my $one := $role.new(a => 666); isa-ok $one, $role, "$type: does the role auto-pun (2)"; is-deeply $one.a, 666, "$type: did the attribute get initialized (2)"; } } subtest 'creating a role that does another role' => { # my role B does Positional { } ast RakuAST::Role.new( scope => 'my', name => RakuAST::Name.from-identifier('B'), traits => ( RakuAST::Trait::Does.new( RakuAST::Type::Simple.new( RakuAST::Name.from-identifier("Positional") ) ), ) ); is-deeply $deparsed, 'my role B does Positional { }', 'deparse'; for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { my $role := EVAL($it); is $role.^name, 'B', "$type: role gets correct name"; is-deeply $role.^roles, (Positional,), "$type: can we see the roles it does"; my $none := $role.new; isa-ok $none, $role, "$type: does the role auto-pun"; } } subtest 'creating a role that does a parameterized role' => { # my role C does Rational[Int,Int] { } ast RakuAST::Role.new( scope => 'my', name => RakuAST::Name.from-identifier('C'), traits => ( RakuAST::Trait::Does.new( RakuAST::Type::Parameterized.new( base-type => RakuAST::Type::Simple.new( RakuAST::Name.from-identifier("Rational") ), args => RakuAST::ArgList.new( RakuAST::Type::Simple.new( RakuAST::Name.from-identifier("Int") ), RakuAST::Type::Simple.new( RakuAST::Name.from-identifier("Int") ) ) ) ), ) ); is-deeply $deparsed, 'my role C does Rational[Int, Int] { }', 'deparse'; for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { my $role := EVAL($it); is $role.^name, 'C', "$type: role gets correct name"; is-deeply $role.^roles, (Rational[Int,Int],Real,Numeric), "$type: can we see the roles it does"; my $zero := $role.new; isa-ok $zero, $role, "$type: does the role auto-pun"; ok $zero == 0, "$type: did we get a rational 0"; ok $role.new(1,1) == 1, "$type: did we get a rational 1"; ok $role.new(1,3) == 1/3, "$type: did we get a rational 1/3"; } } subtest 'creating a parameterized role' => { # my role D[$a = 42] { method a { $a } } ast RakuAST::Role.new( scope => 'my', name => RakuAST::Name.from-identifier('D'), body => RakuAST::Block.new( body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::Method.new( name => RakuAST::Name.from-identifier("a"), body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new('$a') ) ) ) ) ) ) ) ), parameterization => RakuAST::Signature.new( parameters => ( RakuAST::Parameter.new( target => RakuAST::ParameterTarget::Var.new("\$a"), default => RakuAST::IntLiteral.new(42) ), ) ) ); is-deeply $deparsed, q:to/CODE/.chomp, 'deparse'; my role D[$a = 42] { method a { $a } } CODE for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { my $role := EVAL($it); is $role.^name, 'D', "$type: role gets correct name"; is-deeply $role.^roles, (), "$type: can we see the roles it does"; my $none := $role.new; isa-ok $none, $role, "$type: does the role auto-pun (1)"; is-deeply $none.a, 42, "$type: did the default value get set"; my $one := $role.^parameterize(666).new; isa-ok $one, $role, "$type: does the role auto-pun (2)"; is-deeply $one.a, 666, "$type: did the value get set"; } } subtest 'creating a parameterized role with a type capture' => { # my role E[::T] { method a (T:D $a) { $a } } ast RakuAST::Role.new( scope => 'my', name => RakuAST::Name.from-identifier('E'), body => RakuAST::Block.new( body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::Method.new( name => RakuAST::Name.from-identifier("a"), signature => RakuAST::Signature.new( parameters => ( RakuAST::Parameter.new( type => RakuAST::Type::Definedness.new( base-type => RakuAST::Type::Simple.new( RakuAST::Name.from-identifier("T") ), definite => True ), target => RakuAST::ParameterTarget::Var.new("\$a") ), ) ), body => RakuAST::Blockoid.new( RakuAST::StatementList.new( RakuAST::Statement::Expression.new( expression => RakuAST::Var::Lexical.new("\$a") ) ) ) ) ) ) ) ), parameterization => RakuAST::Signature.new( parameters => ( RakuAST::Parameter.new( type-captures => ( RakuAST::Type::Capture.new( RakuAST::Name.from-identifier("T") ), ), default => RakuAST::Type::Simple.new( RakuAST::Name.from-identifier("Str") ) ), ) ) ); is-deeply $deparsed, q:to/CODE/.chomp, 'deparse'; my role E[::T = Str] { method a (T:D $a) { $a } } CODE for 'AST', $ast, 'Str', $deparsed, 'Raku', EVAL($raku) -> $type, $it { my $role := EVAL($it); is $role.^name, 'E', "$type: role gets correct name"; is-deeply $role.^roles, (), "$type: can we see the roles it does"; my $none := $role.new; isa-ok $none, $role, "$type: does the role auto-pun (1)"; is-deeply $none.a("foo"), "foo", "$type: value returned (1)"; todo ":D not yet being applied in type check"; dies-ok { $none.a(Str) }, "$type: does uninstantiated type die (1)"; dies-ok { $none.a(666) }, "$type: does incorrect type die (1)"; my $one := $role.^parameterize(Int).new; isa-ok $one, $role, "$type: does the role auto-pun (2)"; is-deeply $one.a(42), 42, "$type: value returned (2)"; todo ":D not yet being applied in type check"; dies-ok { $one.a(Int) }, "$type: does uninstantiated type die (2)"; dies-ok { $one.a("foo") }, "$type: does incorrect type die (2)"; } } # vim: expandtab shiftwidth=4