diff --git a/examples/ff.007 b/examples/ff.007 index 98ed515d..3c38a8d9 100644 --- a/examples/ff.007 +++ b/examples/ff.007 @@ -1,4 +1,5 @@ -macro infix:(lhs, rhs) is tighter(infix:<=>) { +@tighter(infix:<=>) +macro infix:(lhs, rhs) { my active = false; return quasi { if {{{lhs}}} { @@ -24,7 +25,8 @@ for values -> v { say(""); -macro infix:(lhs, rhs) is equiv(infix:) { +@equiv(infix:) +macro infix:(lhs, rhs) { my active = false; return quasi { my result = active; diff --git a/examples/nim-addition.007 b/examples/nim-addition.007 index 6a3c5b3a..20425f36 100644 --- a/examples/nim-addition.007 +++ b/examples/nim-addition.007 @@ -22,7 +22,8 @@ func binfmt(number) { return result || "0"; } -func infix:<⊕>(lhs, rhs) is equiv(infix:<+>) { +@equiv(infix:<+>) +func infix:<⊕>(lhs, rhs) { my lb = binfmt(lhs); my rb = binfmt(rhs); lb = padLeft(lb, rb.chars(), "0"); diff --git a/examples/power.007 b/examples/power.007 index e08a9c07..494b0645 100644 --- a/examples/power.007 +++ b/examples/power.007 @@ -1,4 +1,6 @@ -func infix:<**>(x, n) is tighter(infix:<*>) is assoc("right") { +@tighter(infix:<*>) +@assoc("right") +func infix:<**>(x, n) { if n == 0 { return 1; } diff --git a/examples/x-and-xx.007 b/examples/x-and-xx.007 index 56064bbb..24ac2e3b 100644 --- a/examples/x-and-xx.007 +++ b/examples/x-and-xx.007 @@ -1,10 +1,12 @@ -macro infix:(left, right) is tighter(infix:<~>) { +@tighter(infix:<~>) +macro infix:(left, right) { return quasi { (^{{{right}}}).flatMap(func(_) { {{{left}}} }) } } -func infix:(left, right) is equiv(infix:) { +@equiv(infix:) +func infix:(left, right) { return (left xx right).join(""); } diff --git a/lib/_007/Builtins.pm6 b/lib/_007/Builtins.pm6 index a5018b22..9fab8f1b 100644 --- a/lib/_007/Builtins.pm6 +++ b/lib/_007/Builtins.pm6 @@ -74,6 +74,10 @@ my @builtins = assert-type(:value($type), :type(Val::Type), :operation("assertType (checking the Type parameter)")); assert-type(:$value, :type($type.type), :operation); }, + tighter => -> $fn, $op { $fn }, + looser => -> $fn, $op { $fn }, + equiv => -> $fn, $op { $fn }, + assoc => -> $fn, $direction { $fn }, # OPERATORS (from loosest to tightest within each category) diff --git a/lib/_007/OpScope.pm6 b/lib/_007/OpScope.pm6 index 2030ed3f..0fc1c52a 100644 --- a/lib/_007/OpScope.pm6 +++ b/lib/_007/OpScope.pm6 @@ -2,18 +2,18 @@ use _007::Val; use _007::Q; use _007::Precedence; -class X::Trait::IllegalValue is Exception { - has Str $.trait; +class X::Decorator::IllegalValue is Exception { + has Str $.decorator; has Str $.value; - method message { "The value '$.value' is not compatible with the trait '$.trait'" } + method message { "The value '$.value' is not compatible with the trait '$.decorator'" } } -class X::Trait::Conflict is Exception { - has Str $.trait1; - has Str $.trait2; +class X::Decorator::Conflict is Exception { + has Str $.decorator1; + has Str $.decorator2; - method message { "Traits '$.trait1' and '$.trait2' cannot coexist on the same routine" } + method message { "Decorators '$.decorator1' and '$.decorator2' cannot coexist on the same routine" } } class X::Precedence::Incompatible is Exception { @@ -37,7 +37,7 @@ class _007::OpScope { has @.prepostfixprec; has $.prepostfix-boundary = 0; - method maybe-install($identname, @trait) { + method maybe-install($identname, @decorators) { return unless $identname ~~ /^ (\w+) ':' (.+) /; my $category = ~$0; @@ -55,10 +55,14 @@ class _007::OpScope { my %precedence; my @prec-traits = ; my $assoc; - for @trait -> $trait { - my $name = $trait.ast.name; + for @decorators -> $decorator { + # XXX: Shouldn't do lookup by name here, but lexically + my $name = $decorator.identifier.name.value; if $name eq any @prec-traits { - my $identifier = $trait.ast; + my $argcount = $decorator.argumentlist.arguments.elements.elems; + die X::ParameterMismatch.new(:type("Decorator"), :paramcount(1), :$argcount) + unless $argcount == 1; + my $identifier = $decorator.argumentlist.arguments.elements[0]; my $prep = $name eq "equiv" ?? "to" !! "than"; die "The thing your op is $name $prep must be an identifier" unless $identifier ~~ Q::Identifier; @@ -71,22 +75,26 @@ class _007::OpScope { %precedence{$name} = $s; } elsif $name eq "assoc" { - my $string = $trait.ast; + my $argcount = $decorator.argumentlist.arguments.elements.elems; + die X::ParameterMismatch.new(:type("Decorator"), :paramcount(1), :$argcount) + unless $argcount == 1; + my $string = $decorator.argumentlist.arguments.elements[0]; die "The associativity must be a string" unless $string ~~ Q::Literal::Str; my $value = $string.value.value; - die X::Trait::IllegalValue.new(:trait, :$value) + die X::Decorator::IllegalValue.new(:trait, :$value) unless $value eq any "left", "non", "right"; $assoc = $value; } else { - die "Unknown trait '$name'"; + # XXX: should it still do this? we might decorate a function/macro for other reasons than op + die "Unknown decorator '$name'"; } } if %precedence.keys > 1 { my ($t1, $t2) = %precedence.keys.sort; - die X::Trait::Conflict.new(:$t1, :$t2); + die X::Decorator::Conflict.new(:$t1, :$t2); } self.install($category, $op, :%precedence, :$assoc); diff --git a/lib/_007/Parser/Actions.pm6 b/lib/_007/Parser/Actions.pm6 index a0e73381..5af399e8 100644 --- a/lib/_007/Parser/Actions.pm6 +++ b/lib/_007/Parser/Actions.pm6 @@ -11,10 +11,10 @@ class X::PointyBlock::SinkContext is Exception { method message { "Pointy blocks cannot occur on the statement level" } } -class X::Trait::Duplicate is Exception { - has Str $.trait; +class X::Decorator::Duplicate is Exception { + has Str $.decorator; - method message { "Trait '$.trait' is used more than once" } + method message { "Decorator '$.decorator' is used more than once" } } class X::Macro::Postdeclared is Exception { @@ -91,7 +91,11 @@ class _007::Parser::Actions { } method statementlist($/) { - make Q::StatementList.new(:statements(Val::Array.new(:elements($».ast)))); + make Q::StatementList.new(:statements(Val::Array.new(:elements($».ast)))); + } + + method possibly-decorated-statement($/) { + make $.ast; } method statement:expr ($/) { @@ -138,7 +142,6 @@ class _007::Parser::Actions { my $identifier = $.ast; my $name = $identifier.name; my $parameterlist = $.ast; - my $traitlist = $.ast; my $statementlist = $.ast; my $block = Q::Block.new(:$parameterlist, :$statementlist); @@ -148,11 +151,11 @@ class _007::Parser::Actions { my $outer-frame = $*runtime.current-frame; my $val; if $ eq "func" { - make Q::Statement::Func.new(:$identifier, :$traitlist, :$block); + make Q::Statement::Func.new(:$identifier, :$block); $val = Val::Func.new(:$name, :$parameterlist, :$statementlist, :$outer-frame, :$static-lexpad); } elsif $ eq "macro" { - make Q::Statement::Macro.new(:$identifier, :$traitlist, :$block); + make Q::Statement::Macro.new(:$identifier, :$block); $val = Val::Macro.new(:$name, :$parameterlist, :$statementlist, :$outer-frame, :$static-lexpad); } else { @@ -161,7 +164,7 @@ class _007::Parser::Actions { $*runtime.put-var($identifier, $val); - $*parser.opscope.maybe-install($name, $); + $*parser.opscope.maybe-install($name, @*DECORATORS); } method statement:return ($/) { @@ -208,16 +211,16 @@ class _007::Parser::Actions { $*runtime.put-var($identifier, $val); } - method traitlist($/) { - my @traits = $».ast; - if bag( @traits.map: *.identifier.name.value ).grep( *.value > 1 )[0] -> $p { - my $trait = $p.key; - die X::Trait::Duplicate.new(:$trait); - } - make Q::TraitList.new(:traits(Val::Array.new(:elements(@traits)))); - } - method trait($/) { - make Q::Trait.new(:identifier($.ast), :expr($.ast)); + method decorator ($/) { + my $identifier = $.ast; + # XXX: this ought to be not just by string name, but by declaration site of that name + my $name = $identifier.name.value; + die X::Decorator::Duplicate.new(:decorator($name)) + if $name eq any(@*DECORATORS).identifier.name.value; + my $argumentlist = ast-if-any($); + my $decorator = Q::Decorator.new(:$identifier, :$argumentlist); + make $decorator; + @*DECORATORS.push($decorator); } method blockoid ($/) { @@ -608,7 +611,6 @@ class _007::Parser::Actions { method term:func ($/) { my $parameterlist = $.ast; - my $traitlist = $.ast; my $statementlist = $.ast; my $block = Q::Block.new(:$parameterlist, :$statementlist); @@ -623,7 +625,7 @@ class _007::Parser::Actions { my $name = $.ast.name; my $identifier = ast-if-any($); - make Q::Term::Func.new(:$identifier, :$traitlist, :$block); + make Q::Term::Func.new(:$identifier, :$block); } method unquote ($/) { diff --git a/lib/_007/Parser/Syntax.pm6 b/lib/_007/Parser/Syntax.pm6 index 0d42874d..18ad1ebe 100644 --- a/lib/_007/Parser/Syntax.pm6 +++ b/lib/_007/Parser/Syntax.pm6 @@ -28,7 +28,7 @@ grammar _007::Parser::Syntax { } } rule statementlist { - <.semicolon>* [[<.semicolon>+|<.eat_terminator>] ]* + <.semicolon>* [[<.semicolon>+|<.eat_terminator>] ]* } method panic($what) { @@ -48,7 +48,12 @@ grammar _007::Parser::Syntax { @*declstack[*-1]{$symbol} = $decltype; } - proto token statement {*} + rule possibly-decorated-statement { + :my @*DECORATORS; + * + } + + proto rule statement {*} token statement:expr { $=(export \s+)? '{'> # } }}}, you're welcome vim @@ -65,11 +70,10 @@ grammar _007::Parser::Syntax { $.ast.name.value); } <.newpad> - '(' ~ ')' - { - $*parser.opscope.maybe-install($.ast.name, $); + $*parser.opscope.maybe-install($.ast.name, @*DECORATORS); } + '(' ~ ')' [|| <.panic("block")>]:!s <.finishpad> } @@ -108,11 +112,9 @@ grammar _007::Parser::Syntax { } - rule traitlist { - * - } - token trait { - is» <.ws> '(' ')' + rule decorator { + '@' + ['(' ~ ')' ]? } # requires a <.newpad> before invocation @@ -227,8 +229,6 @@ grammar _007::Parser::Syntax { || "<" <.ws> $=["Q.Term.Array"] ">" <.ws> '{' <.ws> <.ws> '}' || "<" <.ws> $=["Q.Term.Dict"] ">" <.ws> '{' <.ws> <.ws> '}' || "<" <.ws> $=["Q.Term.Quasi"] ">" <.ws> '{' <.ws> <.ws> '}' - || "<" <.ws> $=["Q.Trait"] ">" <.ws> '{' <.ws> <.ws> '}' - || "<" <.ws> $=["Q.TraitList"] ">" <.ws> '{' <.ws> <.ws> '}' || "<" <.ws> $=["Q.Statement"] ">" <.ws> || "<" <.ws> $=["Q.StatementList"] ">" <.ws> || "<" <.ws> $=["Q.Parameter"] ">" <.ws> '{' <.ws> <.ws> '}' @@ -274,7 +274,7 @@ grammar _007::Parser::Syntax { } } '(' ~ ')' - + <.ws> :!s <.finishpad> } @@ -304,7 +304,6 @@ grammar _007::Parser::Syntax { <.newpad> ] - * :!s <.finishpad> } diff --git a/lib/_007/Q.pm6 b/lib/_007/Q.pm6 index 5db1beb9..20d3503c 100644 --- a/lib/_007/Q.pm6 +++ b/lib/_007/Q.pm6 @@ -368,38 +368,15 @@ role Q::Declaration { method is-assignable { False } } -### ### Q::Trait -### -### A trait; a piece of metadata for a routine. A trait consists of an -### identifier and an expression. -### -class Q::Trait does Q { - has $.identifier; - has $.expr; - - method attribute-order { } -} - -### ### Q::TraitList -### -### A list of zero or more traits. Each routine has a traitlist. -### -class Q::TraitList does Q { - has Val::Array $.traits .= new; - - method attribute-order { } -} - ### ### Q::Term::Func ### ### A subroutine. ### class Q::Term::Func does Q::Term does Q::Declaration { has $.identifier; - has $.traitlist = Q::TraitList.new; has $.block; - method attribute-order { } + method attribute-order { } method eval($runtime) { my $name = $.identifier ~~ Val::None @@ -831,6 +808,15 @@ class Q::ArgumentList does Q { has Val::Array $.arguments .= new; } +### ### Q::Decorator +### +### A decorator. +### +class Q::Decorator is Q { + has Q::Identifier $.identifier; + has $.argumentlist; +} + ### ### Q::Statement ### ### A statement. @@ -999,10 +985,9 @@ class Q::Statement::Throw does Q::Statement { ### class Q::Statement::Func does Q::Statement does Q::Declaration { has $.identifier; - has $.traitlist = Q::TraitList.new; has Q::Block $.block; - method attribute-order { } + method attribute-order { } method run($runtime) { } @@ -1014,10 +999,9 @@ class Q::Statement::Func does Q::Statement does Q::Declaration { ### class Q::Statement::Macro does Q::Statement does Q::Declaration { has $.identifier; - has $.traitlist = Q::TraitList.new; has $.block; - method attribute-order { } + method attribute-order { } method run($runtime) { } diff --git a/t/features/custom/ops.t b/t/features/custom/ops.t index 55356ebb..23cca2be 100644 --- a/t/features/custom/ops.t +++ b/t/features/custom/ops.t @@ -117,26 +117,28 @@ use _007::Test; { my $program = q:to/./; - func infix:<~?>(left, right) is looser(infix:<+>) { + @looser(infix:<+>) + func infix:<~?>(left, right) { return 6; } say(1 + 9 ~? 12); . - outputs $program, "6\n", "can specify trait to bind loose"; + outputs $program, "6\n", "can specify decorator to bind loose"; } { my $program = q:to/./; - func infix:<~?>(left, right) is tighter(infix:<+>) { + @tighter(infix:<+>) + func infix:<~?>(left, right) { return 6; } say(1 + 9 ~? 12); . - outputs $program, "7\n", "can specify trait to bind tight"; + outputs $program, "7\n", "can specify decorator to bind tight"; } { @@ -145,7 +147,8 @@ use _007::Test; return 18; } - func infix:<~@>(left, right) is tighter(infix:<+>) { + @tighter(infix:<+>) + func infix:<~@>(left, right) { return 30; } @@ -157,20 +160,24 @@ use _007::Test; { my $program = q:to/./; - func infix:(left, right) is tighter(infix:<+>) is looser(infix:<+>) { + @tighter(infix:<+>) + @looser(infix:<+>) + func infix:(left, right) { } . - parse-error $program, X::Trait::Conflict, "can't have both tighter and looser traits"; + parse-error $program, X::Decorator::Conflict, "can't have both tighter and looser decorators"; } { my $program = q:to/./; - func infix:(left, right) is equiv(infix:<+>) is equiv(infix:<*>) { + @equiv(infix:<+>) + @equiv(infix:<*>) + func infix:(left, right) { } . - parse-error $program, X::Trait::Duplicate, "can't use the same trait more than once"; + parse-error $program, X::Decorator::Duplicate, "can't use the same decorator more than once"; } @@ -180,7 +187,8 @@ use _007::Test; return "@"; } - func infix:(left, right) is equiv(infix:<@>) { + @equiv(infix:<@>) + func infix:(left, right) { return "!"; } @@ -188,30 +196,35 @@ use _007::Test; say(30 ! 2 @ 14); . - outputs $program, "!\n@\n", "can specify trait to bind equal"; + outputs $program, "!\n@\n", "can specify decorator to bind equiv"; } { my $program = q:to/./; - func infix:(left, right) is tighter(infix:<+>) is equiv(infix:<+>) { + @tighter(infix:<+>) + @equiv(infix:<+>) + func infix:(left, right) { } . - parse-error $program, X::Trait::Conflict, "can't have both tighter and equal traits"; + parse-error $program, X::Decorator::Conflict, "can't have both tighter and equiv decorators"; } { my $program = q:to/./; - func infix:(left, right) is looser(infix:<+>) is equiv(infix:<+>) { + @looser(infix:<+>) + @equiv(infix:<+>) + func infix:(left, right) { } . - parse-error $program, X::Trait::Conflict, "can't have both looser and equal traits"; + parse-error $program, X::Decorator::Conflict, "can't have both looser and equiv decorators"; } { my $program = q:to/./; - func infix:<@>(left, right) is assoc("right") { + @assoc("right") + func infix:<@>(left, right) { return "(" ~ left ~ ", " ~ right ~ ")"; } @@ -223,7 +236,8 @@ use _007::Test; { my $program = q:to/./; - func infix:<%>(left, right) is assoc("left") { + @assoc("left") + func infix:<%>(left, right) { return "(" ~ left ~ ", " ~ right ~ ")"; } @@ -247,7 +261,8 @@ use _007::Test; { my $program = q:to/./; - func infix:(left, right) is assoc("non") { + @assoc("non") + func infix:(left, right) { return "oh, James"; } @@ -259,7 +274,8 @@ use _007::Test; { my $program = q:to/./; - func infix:(left, right) is assoc("non") { + @assoc("non") + func infix:(left, right) { return "oh, James"; } @@ -271,54 +287,62 @@ use _007::Test; { my $program = q:to/./; - func infix:<&-&>(left, right) is assoc("salamander") { + @assoc("salamander") + func infix:<&-&>(left, right) { } . - parse-error $program, X::Trait::IllegalValue, "you can't just put any old value in an assoc trait"; + parse-error $program, X::Decorator::IllegalValue, "you can't just put any old value in an assoc decorator"; } { my $program = q:to/./; - func infix:<@>(left, right) is assoc("right") { + @assoc("right") + func infix:<@>(left, right) { } - func infix:<@@>(left, right) is equiv(infix:<@>) { + @equiv(infix:<@>) + func infix:<@@>(left, right) { return "(" ~ left ~ ", " ~ right ~ ")"; } say("A" @@ "B" @@ "C"); . - outputs $program, "(A, (B, C))\n", "right associativity inherits through the 'is equiv' trait"; + outputs $program, "(A, (B, C))\n", "right associativity inherits through the equiv decorator"; } { my $program = q:to/./; - func infix:<@>(left, right) is assoc("non") { + @assoc("non") + func infix:<@>(left, right) { } - func infix:<@@>(left, right) is equiv(infix:<@>) { + @equiv(infix:<@>) + func infix:<@@>(left, right) { return "(" ~ left ~ ", " ~ right ~ ")"; } say("A" @@ "B" @@ "C"); . - parse-error $program, X::Op::Nonassociative, "non-associativity inherits through the 'is equiv' trait"; + parse-error $program, X::Op::Nonassociative, "non-associativity inherits through the equiv decorator"; } { my $program = q:to/./; - func infix:<%>(left, right) is assoc("left") { + @assoc("left") + func infix:<%>(left, right) { } - func infix:<%%>(left, right) is equiv(infix:<%>) is assoc("right") { + @equiv(infix:<%>) + @assoc("right") + func infix:<%%>(left, right) { } . parse-error $program, X::Associativity::Conflict, - "if you're using the 'is equiv' trait, you can't contradict the associativity"; + "if you're using the equiv decorator, you can't contradict the associativity"; } { @@ -376,7 +400,8 @@ use _007::Test; return "prefix is looser"; } - func postfix:(term) is looser(prefix:<¿>) { + @looser(prefix:<¿>) + func postfix:(term) { return "postfix is looser"; } @@ -384,7 +409,8 @@ use _007::Test; return "postfix is looser"; } - func prefix:<%>(term) is tighter(postfix:<$>) { + @tighter(postfix:<$>) + func prefix:<%>(term) { return "prefix is looser"; } @@ -392,7 +418,7 @@ use _007::Test; say(%[]$); . - outputs $program, "postfix is looser\n" x 2, "postfixes can be made looser with traits"; + outputs $program, "postfix is looser\n" x 2, "postfixes can be made looser with decorators"; } { @@ -401,7 +427,8 @@ use _007::Test; return "postfix is looser"; } - func prefix:<¿>(term) is tighter(postfix:) { + @tighter(postfix:) + func prefix:<¿>(term) { return "prefix is looser"; } @@ -409,7 +436,8 @@ use _007::Test; return "prefix is looser"; } - func postfix:<$>(term) is looser(prefix:<%>) { + @looser(prefix:<%>) + func postfix:<$>(term) { return "postfix is looser"; } @@ -417,24 +445,28 @@ use _007::Test; say(%[]$); . - outputs $program, "postfix is looser\n" x 2, "prefixes can be made tighter with traits"; + outputs $program, "postfix is looser\n" x 2, "prefixes can be made tighter with decorators"; } { my $program = q:to/./; - func postfix:<¡>(term) is assoc("right") { + @assoc("right") + func postfix:<¡>(term) { return "postfix is looser"; } - func prefix:<¿>(term) is equiv(postfix:<¡>) { + @equiv(postfix:<¡>) + func prefix:<¿>(term) { return "prefix is looser"; } - func prefix:<%>(term) is assoc("left") { + @assoc("left") + func prefix:<%>(term) { return "prefix is looser"; } - func postfix:<$>(term) is equiv(prefix:<%>) { + @equiv(prefix:<%>) + func postfix:<$>(term) { return "postfix is looser"; } @@ -448,21 +480,24 @@ use _007::Test; { my $program = q:to/./; - func prefix:<¿>(left, right) is assoc("non") { + @assoc("non") + func prefix:<¿>(left, right) { } - func postfix:(left, right) is equiv(prefix:<¿>) { + @equiv(prefix:<¿>) + func postfix:(left, right) { } say(¿0!); . - parse-error $program, X::Op::Nonassociative, "non-associativity inherits through the 'is equiv' trait"; + parse-error $program, X::Op::Nonassociative, "non-associativity inherits through the equiv decorator"; } { my $program = q:to/./; - func postfix:(left, right) is tighter(infix:<+>) { + @tighter(infix:<+>) + func postfix:(left, right) { } . @@ -471,7 +506,8 @@ use _007::Test; { my $program = q:to/./; - func infix:(left, right) is tighter(prefix:<->) { + @tighter(prefix:<->) + func infix:(left, right) { } . @@ -504,7 +540,8 @@ use _007::Test; { my $program = q:to/./; - func postfix:<‡>(x) is looser(prefix:<^>) { + @looser(prefix:<^>) + func postfix:<‡>(x) { return []; } @@ -524,7 +561,8 @@ use _007::Test; return x ~ " prefix:<&>"; } - func postfix:<‡>(x) is looser(prefix:<&>) { + @looser(prefix:<&>) + func postfix:<‡>(x) { return x ~ " postfix:<‡>"; } @@ -665,7 +703,8 @@ use _007::Test; { my $program = q:to/./; - func infix:<@->(a, b) is looser(infix:<+>) { + @looser(infix:<+>) + func infix:<@->(a, b) { if b == 0 { return a; } @@ -714,21 +753,29 @@ use _007::Test; { my $program = q:to/./; { - func postfix:(t) is assoc("right") { + @assoc("right") + func postfix:(t) { "postfix:(" ~ t ~ ")"; } - func prefix:(t) is equiv(postfix:) { + + @equiv(postfix:) + func prefix:(t) { "prefix:(" ~ t ~ ")"; } + say(?"term"!); } { - func prefix:(t) is assoc("right") { + @assoc("right") + func prefix:(t) { "prefix:(" ~ t ~ ")"; } - func postfix:(t) is equiv(prefix:) { + + @equiv(prefix:) + func postfix:(t) { "postfix:(" ~ t ~ ")"; } + say(?"term"!); } . diff --git a/t/features/decorator.t b/t/features/decorator.t new file mode 100644 index 00000000..0d3a1479 --- /dev/null +++ b/t/features/decorator.t @@ -0,0 +1,66 @@ +use v6; +use Test; +use _007::Test; + +{ + my $program = q:to/./; + @constant + my STATE = "alive"; + + say(STATE); + . + + outputs $program, "alive\n", "decorators can decorate 'my' statements"; +} + +{ + my $program = q:to/./; + @equiv(infix:<+>) + func infix:<&>(lhs, rhs) { + return lhs ~ rhs; + } + + say("James" & " " & "Bond"); + . + + outputs $program, "James Bond\n", "decorators can decorate 'func' statements"; +} + +{ + my $program = q:to/./; + @equiv(infix:<+>, 42, "foo") + func infix:<&>(lhs, rhs) { + } + . + + parse-error + $program, + X::ParameterMismatch, + "can't decorate with the wrong number of parameters (equiv)"; +} + +{ + my $program = q:to/./; + @assoc("left", 42, "foo") + func infix:<&>(lhs, rhs) { + } + . + + parse-error + $program, + X::ParameterMismatch, + "can't decorate with the wrong number of parameters (assoc)"; +} + +{ + my $program = q:to/./; + say(tighter ~~ Func); + say(equiv ~~ Func); + say(looser ~~ Func); + say(assoc ~~ Func); + . + + outputs $program, "true\n" x 4, "the custom op decorators are all built-in functions"; +} + +done-testing; diff --git a/t/features/q.t b/t/features/q.t index b307f8e5..693befc9 100644 --- a/t/features/q.t +++ b/t/features/q.t @@ -36,40 +36,4 @@ use _007::Test; "Q.Statement.If can be constructed without an 'else' property (#84)"; } -{ - my $program = q:to/./; - my q = new Q.Statement.Func { - identifier: new Q.Identifier { name: "foo" }, - block: new Q.Block { - parameterlist: new Q.ParameterList { parameters: [] }, - statementlist: new Q.StatementList { statements: [] } - } - }; - say(q.traitlist); - . - - outputs - $program, - "Q.TraitList []\n", - "Q.Statement.Sub can be constructed without a 'traitlist' property (#84)"; -} - -{ - my $program = q:to/./; - my q = new Q.Statement.Macro { - identifier: new Q.Identifier { name: "moo" }, - block: new Q.Block { - parameterlist: new Q.ParameterList { parameters: [] }, - statementlist: new Q.StatementList { statements: [] } - } - }; - say(q.traitlist); - . - - outputs - $program, - "Q.TraitList []\n", - "Q.Statement.Macro can be constructed without a 'traitlist' property (#84)"; -} - done-testing; diff --git a/t/features/quasi.t b/t/features/quasi.t index 081334b1..ed53f1ac 100644 --- a/t/features/quasi.t +++ b/t/features/quasi.t @@ -246,22 +246,6 @@ use _007::Test; outputs $program, "\n", "quasi"; } -{ - my $program = q:to/./; - say(type(quasi { is equiv(infix:<+>) })); - . - - outputs $program, "\n", "quasi"; -} - -{ - my $program = q:to/./; - say(type(quasi { is equiv(infix:<+>) is assoc("right") })); - . - - outputs $program, "\n", "quasi"; -} - { my $program = q:to/./; say(type(quasi { say("james") }));