Skip to content
Permalink
Browse files

Add a Hyper class

- inactive for now, re-configure of Rakudo is needed
- contains the initial version of the HYPER sub refactor
- the basic gain is that operator/dwim info doesn't need to be passed
- also fixes use of object hashes / QuantHashes in infix hypers
- should allow for easier optimizations in the future

   HYPER(&[+],:dwim-left,:dwim-right,a,b)

becomes:

   Hyper.new(:operator(&[+]), :dwim-left, :dwim-right).infix(a,b)
  • Loading branch information...
lizmat committed Nov 8, 2018
1 parent cbbe16e commit 0bc72e0504ade2fbf8a24420332309c37edf8363
Showing with 313 additions and 0 deletions.
  1. +310 −0 src/core/Hyper.pm6
  2. +1 −0 tools/build/js_core_sources
  3. +1 −0 tools/build/jvm_core_sources
  4. +1 −0 tools/build/moar_core_sources
@@ -0,0 +1,310 @@
# A class to perform hyper operations of the form left op right

class Hyper {
has &.operator;
has int $.dwim-left;
has int $.dwim-right;

proto method infix(|) {*}

# x >>op<< y
multi method infix(\left, \right) is raw { &!operator(left,right) }

# %x >>op<< %y
multi method infix(
Associative:D \left,
Associative:D \right
--> Associative:D
) is default {
nqp::eqaddr(left.keyof,Str(Any))
&& nqp::eqaddr(right.keyof,Str(Any))
?? self!str-associatives(left,right)
!! self!obj-associatives(left,right)
}

# %x >>op<< ...
multi method infix(Associative:D \left, Iterable:D \right) {
die "{left.^name} {&!operator.name} {right.^name} can never work
reliably: the order of keys in {left.^name} is indeterminate"
}

# %x >>op<< y
multi method infix(Associative:D \left, \right --> Associative:D) {
my @keys = left.keys;
my \result := left.WHAT.new.STORE(
Rakudo::Iterator.RoundrobinIterablesFlat(
(@keys, self.infix(left{@keys}, right))
)
);
nqp::iscont(left) ?? result.item !! result;
}

# ... >>op<< %y
multi method infix(Iterable:D \left, Associative:D \right) {
die "{left.^name} {&!operator.name} {right.^name} can never work
reliably: the order of keys in {right.^name} is indeterminate"
}

# x >>op<< %y
multi method infix(\left, Associative:D \right --> Associative:D) {
my @keys = right.keys;
my \result := right.WHAT.new.STORE(
Rakudo::Iterator.RoundrobinIterablesFlat(
(@keys, self.infix(left, right{@keys}))
)
);
nqp::iscont(left) ?? result.item !! result;
}

# [x] >>op<< y
multi method infix(Positional:D \left, \right --> Positional:D) {
X::HyperOp::Infinite.new(:side<left>, :&!operator).throw
if left.is-lazy;

my int $left-elems = left.elems;
X::HyperOp::NonDWIM.new(
:&!operator, :$left-elems, :right-elems(1), :recursing
).throw
unless $left-elems == 1
or $left-elems > 1 and $!dwim-right
or $left-elems == 0 and $!dwim-left || $!dwim-right;

my \values := nqp::create(IterationBuffer);
my \iterator := left.iterator;

nqp::until(
nqp::eqaddr((my \value := iterator.pull-one),IterationEnd),
nqp::push(values, self.op(value,right))
);

my \result := nqp::p6bindattrinvres(
nqp::create(
nqp::istype(left,List) ?? left.WHAT !! List # keep subtype
),
List, '$!reified', values
);
nqp::iscont(result) ?? result.item !! result
}

# x >>op<< [y]
multi method infix(\left, Positional:D \right --> Positional:D) {
X::HyperOp::Infinite.new(:side<right>, :&!operator).throw
if right.is-lazy;

my int $right-elems = right.elems;
X::HyperOp::NonDWIM.new(
:&!operator, :left-elems(1), :$right-elems, :recursing
).throw
unless $right-elems == 1
or $right-elems > 1 and $!dwim-left
or $right-elems == 0 and $!dwim-left || $!dwim-right;

my \values := nqp::create(IterationBuffer);
my \iterator := right.iterator;

nqp::until(
nqp::eqaddr((my \value := iterator.pull-one),IterationEnd),
nqp::push(values, self.op(left,value))
);

my \result := nqp::p6bindattrinvres(
nqp::create(
nqp::istype(left,List) ?? left.WHAT !! List # keep subtype
),
List, '$!reified', values
);
nqp::iscont(result) ?? result.item !! result
}

# ... >>op<< ...
multi method infix(
Iterable:D \left,
Iterable:D \right
--> Iterable:D
) {
my \left-iterator = left.iterator;
my \right-iterator = right.iterator;

# Check whether any side is lazy. They must not be to proceed.
if left-iterator.is-lazy {
X::HyperOp::Infinite.new(:side<both>, :&!operator).throw
if right-iterator.is-lazy;
X::HyperOp::Infinite.new(:side<left>, :&!operator).throw
if nqp::not_i($!dwim-left) || $!dwim-right;
}
X::HyperOp::Infinite.new(:side<right>, :&!operator).throw
if right-iterator.is-lazy
and (nqp::not_i($!dwim-right) || $!dwim-left);

my \retval := (
$!dwim-left
?? $!dwim-right
?? self!iterables-left-right(left-iterator,right-iterator)
!! self!iterables-left(left-iterator,right-iterator)
!! $!dwim-right
?? self!iterables-right(left-iterator,right-iterator)
!! self!iterables(left-iterator,right-iterator)
).List;
nqp::iscont(left) ?? retval.item !! retval;
}

# using an infix on a one element list in a meta op
multi method infix(\object) {
nqp::if(
nqp::can(&!operator,"nodal"),
nodemap(&!operator,object),
deepmap(&!operator,object)
)
}

#--- Private helper methods ----------------------------------------------------

# ... >>op<< ...
method !iterables(Iterator:D \left, Iterator:D \right) {
my \result := nqp::create(IterationBuffer);

nqp::until(
nqp::eqaddr((my \leftv := left.pull-one),IterationEnd)
|| nqp::eqaddr((my \rightv := right.pull-one),IterationEnd),
nqp::push(result, self.infix(leftv, rightv))
);

my int $left-elems = my int $right-elems = nqp::elems(result);
nqp::if(
nqp::eqaddr(rightv,IterationEnd),
self!right-exhausted(left,nqp::elems(result)),
nqp::unless(
nqp::eqaddr(right.pull-one,IterationEnd),
self!left-exhausted(right,nqp::elems(result))
)
);

result
}

# ... <<op<< ...
method !iterables-left(Iterator:D \left, Iterator:D \right) {
my \righti := Rakudo::Iterator.DWIM(right);
my \result := nqp::create(IterationBuffer);

nqp::until(
nqp::eqaddr((my \leftv := left.pull-one),IterationEnd),
nqp::push(result, self.infix(leftv,righti.pull-one))
);

result
}

# ... >>op>> ...
method !iterables-right(Iterator:D \left, Iterator:D \right) {
my \lefti := Rakudo::Iterator.DWIM(left);
my \result := nqp::create(IterationBuffer);

nqp::until(
nqp::eqaddr((my \rightv := right.pull-one),IterationEnd),
nqp::push(result, self.infix(lefti.pull-one,rightv))
);

result
}

# ... <<op>> ...
method !iterables-left-right(Iterator:D \left, Iterator:D \right) {
my \lefti := Rakudo::Iterator.DWIM(left);
my \righti := Rakudo::Iterator.DWIM(right);
my \result := nqp::create(IterationBuffer);

my \leftv := lefti.pull-one;
my \rightv := righti.pull-one;
nqp::until(
lefti.ended && righti.ended,
nqp::stmts(
nqp::push(result, self.infix(leftv,rightv)),
nqp::bind(leftv, lefti.pull-one),
nqp::bind(rightv,righti.pull-one)
)
);

result
}

# handle normal hashes
method !str-associatives(\left, \right) {
my $keys := nqp::hash;
if $!dwim-left {
nqp::bindkey($keys,$_,1) if right.EXISTS-KEY($_) for left.keys;
}
else {
nqp::bindkey($keys,$_,1) for left.keys;
}
if nqp::not_i($!dwim-right) {
nqp::bindkey($keys,$_,1) for right.keys;
}

# create HLL version of keys
my @keys =
nqp::p6bindattrinvres(nqp::create(Map),Map,'$!storage',$keys).keys;

# run with the left/right values
my \result := left.WHAT.new.STORE(
Seq.new(
Rakudo::Iterator.RoundrobinIterablesFlat(
(@keys, quietly self.infix(left{@keys}, right{@keys}))
)
)
);
nqp::iscont(left) ?? result.item !! result;
}

# handle object hashes / QuantHashes
method !obj-associatives(\left, \right) {
my $keys := nqp::hash;
if $!dwim-left {
nqp::bindkey($keys,.WHICH,$_) if right.EXISTS-KEY($_) for left.keys;
}
else {
nqp::bindkey($keys,.WHICH,$_) for left.keys;
}
if nqp::not_i($!dwim-right) {
nqp::bindkey($keys,.WHICH,$_) for right.keys;
}

# create HLL version of keys
my @keys =
nqp::p6bindattrinvres(nqp::create(Map),Map,'$!storage',$keys).values;

# run with the left/right values
my \result := left.WHAT.new.STORE(
Rakudo::Iterator.RoundrobinIterablesFlat(
(@keys, quietly self.infix(left{@keys}, right{$keys}))
)
);
nqp::iscont(left) ?? result.item !! result;
}

# error when left side of non-DWIM exhausted
method !left-exhausted(Iterator:D \iterator, int $left-elems) {
my int $right-elems = $left-elems + 1;
nqp::until(
nqp::eqaddr(iterator.pull-one,IterationEnd),
++$right-elems
);
X::HyperOp::NonDWIM.new(
:&!operator, :$left-elems, :$right-elems, :recursing
).throw;
}

# error when right side of non-DWIM exhausted
method !right-exhausted(Iterator:D \iterator, int $right-elems) {
my int $left-elems = $right-elems + 1;
nqp::until(
nqp::eqaddr(iterator.pull-one,IterationEnd),
++$left-elems
);
X::HyperOp::NonDWIM.new(
:&!operator, :$left-elems, :$right-elems, :recursing
).throw;
}
}

# vim: ft=perl6 expandtab sw=4
@@ -150,6 +150,7 @@ src/core/ObjAt.pm6
src/core/Version.pm6
src/core/ForeignCode.pm6
src/core/operators.pm6
src/core/Hyper.pm6
src/core/metaops.pm6
src/core/precedence.pm6
src/core/Deprecations.pm6
@@ -148,6 +148,7 @@ src/core/ObjAt.pm6
src/core/Version.pm6
src/core/ForeignCode.pm6
src/core/operators.pm6
src/core/Hyper.pm6
src/core/metaops.pm6
src/core/precedence.pm6
src/core/Deprecations.pm6
@@ -150,6 +150,7 @@ src/core/ObjAt.pm6
src/core/Version.pm6
src/core/ForeignCode.pm6
src/core/operators.pm6
src/core/Hyper.pm6
src/core/metaops.pm6
src/core/precedence.pm6
src/core/Deprecations.pm6

0 comments on commit 0bc72e0

Please sign in to comment.
You can’t perform that action at this time.