1. # Method that we have on enumeration types.
  2. my role Enumeration {
  3. has $.key;
  4. has $.value;
  5. method enums() { self.^enum_values.Map }
  6. multi method kv(::?CLASS:D:) { ($!key, $!value) }
  7. method pair(::?CLASS:D:) { $!key => $!value }
  8. multi method gist(::?CLASS:D:) { $!key }
  9. multi method perl(::?CLASS:D:) { self.^name ~ '::' ~ $!key }
  10. multi method pick(::?CLASS:U:) { self.^enum_value_list.pick }
  11. multi method pick(::?CLASS:U: \n) { self.^enum_value_list.pick(n) }
  12. multi method pick(::?CLASS:D: *@pos) { self xx +?( @pos[0] // 1 ) }
  13. multi method roll(::?CLASS:U:) { self.^enum_value_list.roll }
  14. multi method roll(::?CLASS:U: \n) { self.^enum_value_list.roll(n) }
  15. multi method roll(::?CLASS:D: *@pos) { self xx +?( @pos[0] // 1 ) }
  16. multi method Numeric(::?CLASS:D:) { $!value.Numeric }
  17. multi method Int(::?CLASS:D:) { $!value.Int }
  18. # Make sure we always accept any element of the enumeration
  19. multi method ACCEPTS(::?CLASS:D: ::?CLASS:U $ --> True) { }
  20. method CALL-ME(|) {
  21. my $x := nqp::atpos(nqp::p6argvmarray(), 1).AT-POS(0);
  22. nqp::istype($x, ::?CLASS)
  23. ?? $x
  24. !! self.^enum_from_value($x)
  25. }
  26. }
  27. # Methods that we also have if the base type of an enumeration is
  28. # Numeric.
  29. my role NumericEnumeration {
  30. multi method Str(::?CLASS:D:) {
  31. self.key
  32. }
  33. }
  34. my role StringyEnumeration {
  35. multi method Str(::?CLASS:D:) {
  36. self.value
  37. }
  38. }
  39. sub ENUM_VALUES(*@args) {
  40. my Mu $prev = -1;
  41. my %res;
  42. for @args {
  43. if .^isa(Pair) {
  44. %res{.key} = $prev = .value;
  45. }
  46. else {
  47. %res{$_} = $prev.=succ;
  48. }
  49. }
  50. nqp::p6bindattrinvres(
  51. nqp::create(Map),Map,'$!storage',nqp::getattr(%res,Map,'$!storage')
  52. )
  53. }
  54. Metamodel::EnumHOW.set_composalizer(-> $type, $name, %enum_values {
  55. my Mu $r := Metamodel::ParametricRoleHOW.new_type(:name($name));
  56. $r.^add_attribute(Attribute.new(
  57. :name('$!' ~ $name), :type(nqp::decont($type)),
  58. :has_accessor(1), :package($r)));
  59. for %enum_values.kv -> $key, $value {
  60. my $meth = method () { self."$name"() == $value }
  61. $meth.set_name($key);
  62. $r.^add_method($key, $meth);
  63. }
  64. $r.^set_body_block( -> |c {nqp::list($r,nqp::hash('$?CLASS',c<$?CLASS>))});
  65. $r.^compose;
  66. $r
  67. });