view src/core.c/Enumeration.pm6 @ 0:c341f82e7ad7 default tip

Rakudo branch in cr.ie.u-ryukyu.ac.jp
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Thu, 26 Dec 2019 16:50:27 +0900
parents
children
line wrap: on
line source

# Method that we have on enumeration types.
my role Enumeration {
    has $.key;
    has $.value;
    has int $!index;

    method enums() { self.^enum_values.Map }

    multi method kv(::?CLASS:D:) { ($!key, $!value) }
    method pair(::?CLASS:D:) { $!key => $!value }

    multi method gist(::?CLASS:D:) { $!key                     }
    multi method perl(::?CLASS:D:) { self.^name ~ '::' ~ $!key }

    multi method pick(::?CLASS:U:)       { self.^enum_value_list.pick     }
    multi method pick(::?CLASS:U: \n)    { self.^enum_value_list.pick(n)  }
    multi method pick(::?CLASS:D: *@pos) { self xx +?( @pos[0] // 1 )     }
    multi method roll(::?CLASS:U:)       { self.^enum_value_list.roll     }
    multi method roll(::?CLASS:U: \n)    { self.^enum_value_list.roll(n)  }
    multi method roll(::?CLASS:D: *@pos) { self xx +?( @pos[0] // 1 )     }

    multi method Numeric(::?CLASS:D:) { $!value.Numeric }
    multi method Int(::?CLASS:D:)     { $!value.Int }
    multi method Real(::?CLASS:D:)    { $!value.Real }

    multi method WHICH(::?CLASS:D: --> ValueObjAt:D) {
        nqp::box_s(
          nqp::concat(self.^name,nqp::concat("|",$!index)),
          ValueObjAt
        )
    }

    multi method ACCEPTS(::?CLASS:D: ::?CLASS:D \v) { self === v }

    proto method CALL-ME(|) {*}
    multi method CALL-ME(|) {
        my $x := nqp::atpos(nqp::p6argvmarray(), 1).AT-POS(0);
        nqp::istype($x, ::?CLASS)
            ?? $x
            !! self.^enum_from_value($x)
    }

    method pred(::?CLASS:D:) {
        nqp::if(
          nqp::getattr_i(self,::?CLASS,'$!index'),
          nqp::atpos(
            nqp::getattr(self.^enum_value_list,List,'$!reified'),
            nqp::sub_i(nqp::getattr_i(self,::?CLASS,'$!index'),1)
          ),
          self
        )
    }
    method succ(::?CLASS:D:) {
        nqp::stmts(
          (my $values := nqp::getattr(self.^enum_value_list,List,'$!reified')),
          nqp::if(
            nqp::islt_i(
              nqp::getattr_i(self,::?CLASS,'$!index'),
              nqp::sub_i(nqp::elems($values),1),
            ),
            nqp::atpos(
               $values,
               nqp::add_i(nqp::getattr_i(self,::?CLASS,'$!index'),1)
            ),
            self
          )
        )
    }
}

# Methods that we also have if the base type of an enumeration is
# Numeric.
my role NumericEnumeration {
    multi method Str(::?CLASS:D:) {
        self.key
    }
}
my role StringyEnumeration {
    multi method Str(::?CLASS:D:) {
        self.value
    }
}
my role NumericStringyEnumeration {
    multi method Str(::?CLASS:D:) {
        self.key
    }
}

sub ENUM_VALUES(*@args --> Map:D) {
    my Mu $prev = -1;
    my $res := nqp::hash;

    nqp::istype($_,Pair)
      ?? nqp::bindkey($res, .key, nqp::decont($prev = .value))
      !! nqp::bindkey($res,   $_, nqp::decont($prev = $prev.succ))
      for @args;

    nqp::p6bindattrinvres(nqp::create(Map),Map,'$!storage',$res)
}

Metamodel::EnumHOW.set_composalizer(-> $type, $name, @enum_values {
    my Mu $r := Metamodel::ParametricRoleHOW.new_type(:name($name));
    $r.^add_attribute(Attribute.new(
        :name('$!' ~ $name), :type(nqp::decont($type)),
        :has_accessor(1), :package($r)));
    for @enum_values {
        my $key   = $_.key;
        my $value = $_.value;
        my $meth = method () { self."$name"() == $value }
        $meth.set_name($key);
        $r.^add_method($key, $meth);
    }
    $r.^set_body_block( -> |c {nqp::list($r,nqp::hash('$?CLASS',c<$?CLASS>))});
    $r.^compose;
    $r
});

# We use this one because, for example, Int:D === Int:D, has an optimization
# that simply unboxes the values. That's no good for us, since two different
# Enumertaion:Ds could have the same Int:D value.
multi infix:<===> (Enumeration:D \a, Enumeration:D \b) {
    nqp::hllbool(nqp::eqaddr(nqp::decont(a), nqp::decont(b)))
}

# vim: ft=perl6 expandtab sw=4