Skip to content

Commit

Permalink
Implement macros && || // andthen and orelse or. Special block behavi…
Browse files Browse the repository at this point in the history
…or of andthen/orelse NYI.
  • Loading branch information
sorear committed Jul 19, 2010
1 parent 0924b28 commit 9fe76f1
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 8 deletions.
20 changes: 16 additions & 4 deletions Niecza/Actions.pm
Expand Up @@ -244,13 +244,25 @@ sub CHAIN { my ($cl, $M) = @_;
positionals => [ $M->{chain}[0]{_ast}, $M->{chain}[2]{_ast} ]);
}

my %loose2tight = (
'&&' => '&&', '||' => '||', '//' => '//', 'andthen' => 'andthen',
'orelse' => '//', 'and' => '&&', 'or' => '||',
);
sub LIST { my ($cl, $M) = @_;
# STD guarantees that all elements of delims have the same sym
# the last item may have an ast of undef due to nulltermish
$M->{_ast} = Op::CallSub->new(
invocant => Op::Lexical->new(name => '&infix:<' . $M->{delims}[0]{sym} . '>'),
positionals => [ grep { defined } map { $_->{_ast} } @{ $M->{list} } ],
splittable_parcel => ($M->{delims}[0]{sym} eq ','));
my $op = $M->{delims}[0]{sym};
my @pos = grep { defined } map { $_->{_ast} } @{ $M->{list} };

if ($loose2tight{$op}) {
$M->{_ast} = Op::ShortCircuit->new(kind => $loose2tight{$op},
args => \@pos);
} else {
$M->{_ast} = Op::CallSub->new(
invocant => Op::Lexical->new(name => "&infix:<$op>"),
positionals => \@pos,
splittable_parcel => ($op eq ','));
}
}

sub POSTFIX { my ($cl, $M) = @_;
Expand Down
48 changes: 48 additions & 0 deletions Op.pm
Expand Up @@ -189,6 +189,54 @@ use CgOp;
has kind => (isa => 'Str', is => 'ro', required => 1);
}

{
package Op::ShortCircuit;
use Moose;
extends 'Op';

has kind => (isa => 'Str', is => 'ro', required => 1);
has args => (isa => 'ArrayRef', is => 'ro', required => 1);

sub red2 {
my ($self, $sym, $o2) = @_;
given ($self->kind) {
when ("&&") {
return CgOp::ternary(CgOp::unbox('Boolean', CgOp::fetch(
CgOp::methodcall($sym, 'Bool'))), $o2, $sym);
}
when ("||") {
return CgOp::ternary(CgOp::unbox('Boolean', CgOp::fetch(
CgOp::methodcall($sym, 'Bool'))), $sym, $o2);
}
when ("andthen") {
return CgOp::ternary(CgOp::unbox('Boolean', CgOp::fetch(
CgOp::methodcall($sym, 'defined'))), $o2, $sym);
}
when ("//") {
return CgOp::ternary(CgOp::unbox('Boolean', CgOp::fetch(
CgOp::methodcall($sym, 'defined'))), $sym, $o2);
}
default {
die "That's not a sensible short circuit, now is it?";
}
}
}

sub code {
my ($self, $body) = @_;

my @r = reverse @{ $self->args };
my $acc = (shift @r)->code($body);

for (@r) {
$acc = CgOp::let($_->code($body), 'Variable',
sub { $self->red2($_[0], $acc) });
}

$acc;
}
}

{
package Op::StringLiteral;
use Moose;
Expand Down
3 changes: 0 additions & 3 deletions setting
Expand Up @@ -323,9 +323,6 @@ PRE-INIT {
anon method does($obj, $role) { self.isa($obj, $role) }); #no roles yet
}

# I think this really wants to be a macro
sub infix:<&&>($, $) { }

# boxes a List<Variable>
my class LLArray {
method push($x) { $x }
Expand Down
17 changes: 16 additions & 1 deletion test.pl
Expand Up @@ -10,7 +10,7 @@ ($num)
say ("1.." ~ $num);
}

plan 72;
plan 82;

ok 1, "one is true";
ok 2, "two is also true";
Expand Down Expand Up @@ -250,3 +250,18 @@ ($num)
ok "Foo".^isa(Str), "strings are Str";
ok (?1).^isa(Bool), "booleans are Bool";
ok (1.HOW).^isa(ClassHOW), "class objects are ClassHOW";

{
my $canary = 1;

ok 1 || ($canary = 0), "1 || ? returns true";
ok $canary, "without touching the rhs";
ok !(0 && ($canary = 0)), "0 && ? returns false";
ok $canary, "without touching the rhs";
ok (0 // ($canary = 0)) eq '0', "0 // ? returns 0";
ok $canary, "without touching the rhs";
ok (12 && 34) == 34, "12 && 34 -> 34";
ok (2 andthen "three") eq "three", '2 andthen three -> three';
ok (12 || 34) == 12, '12 || 34 -> 34';
ok (0 || 34) == 34, '0 || 34 -> 34';
}

0 comments on commit 9fe76f1

Please sign in to comment.