1. my class X::Hash::Store::OddNumber { ... }
  2. my class Map does Iterable does Associative { # declared in BOOTSTRAP
  3. # my class Map is Iterable is Cool
  4. # has Mu $!storage;
  5. multi method WHICH(Map:D:) {
  6. (nqp::istype(self.WHAT,Map) ?? 'Map|' !! (self.^name ~ '|'))
  7. ~ self.keys.sort.map( { $_.WHICH ~ '(' ~ self.AT-KEY($_) ~ ')' } )
  8. }
  9. method new(*@args) {
  10. @args
  11. ?? nqp::create(self).STORE(@args)
  12. !! nqp::create(self)
  13. }
  14. multi method Map(Map:) { self }
  15. multi method Hash(Map:U:) { Hash }
  16. multi method Hash(Map:D:) {
  17. if nqp::defined($!storage) && nqp::elems($!storage) {
  18. my $hash := nqp::create(Hash);
  19. my $storage := nqp::bindattr($hash,Map,'$!storage',nqp::hash);
  20. my $descriptor := nqp::null;
  21. my $iter := nqp::iterator(nqp::getattr(self,Map,'$!storage'));
  22. nqp::while(
  23. $iter,
  24. nqp::stmts(
  25. nqp::shift($iter),
  26. nqp::bindkey($storage,nqp::iterkey_s($iter),
  27. nqp::p6scalarfromdesc($descriptor) =
  28. nqp::decont(nqp::iterval($iter)))
  29. )
  30. );
  31. $hash
  32. }
  33. else {
  34. nqp::create(Hash)
  35. }
  36. }
  37. multi method Bool(Map:D:) {
  38. nqp::p6bool(nqp::defined($!storage) && nqp::elems($!storage));
  39. }
  40. method elems(Map:D:) {
  41. nqp::p6box_i(nqp::defined($!storage) && nqp::elems($!storage));
  42. }
  43. multi method Int(Map:D:) { self.elems }
  44. multi method Numeric(Map:D:) { self.elems }
  45. multi method Str(Map:D:) { self.sort.join("\n") }
  46. method IterationBuffer() {
  47. nqp::stmts(
  48. (my $buffer := nqp::create(IterationBuffer)),
  49. nqp::if(
  50. nqp::defined($!storage) && nqp::elems($!storage),
  51. nqp::stmts(
  52. (my $iterator := nqp::iterator($!storage)),
  53. nqp::setelems($buffer,nqp::elems($!storage)),
  54. (my int $i = -1),
  55. nqp::while(
  56. $iterator,
  57. nqp::bindpos($buffer,($i = nqp::add_i($i,1)),
  58. Pair.new(
  59. nqp::iterkey_s(nqp::shift($iterator)),
  60. nqp::iterval($iterator)
  61. )
  62. )
  63. )
  64. )
  65. ),
  66. $buffer
  67. )
  68. }
  69. method List() {
  70. nqp::p6bindattrinvres(
  71. nqp::create(List),List,'$!reified',self.IterationBuffer)
  72. }
  73. multi method sort(Map:D:) {
  74. Seq.new(
  75. Rakudo::Iterator.ReifiedList(
  76. Rakudo::Internals.MERGESORT-REIFIED-LIST-AS(
  77. nqp::p6bindattrinvres(
  78. nqp::create(List),List,'$!reified',self.IterationBuffer
  79. ),
  80. { nqp::getattr(nqp::decont($^a),Pair,'$!key') }
  81. )
  82. )
  83. )
  84. }
  85. multi method ACCEPTS(Map:D: Any $topic) {
  86. self.EXISTS-KEY($topic.any);
  87. }
  88. multi method ACCEPTS(Map:D: Cool:D $topic) {
  89. self.EXISTS-KEY($topic);
  90. }
  91. multi method ACCEPTS(Map:D: Positional $topic) {
  92. self.EXISTS-KEY($topic.any);
  93. }
  94. multi method ACCEPTS(Map:D: Regex $topic) {
  95. so self.keys.any.match($topic);
  96. }
  97. multi method EXISTS-KEY(Map:D: Str:D \key) {
  98. nqp::p6bool(
  99. nqp::defined($!storage)
  100. && nqp::existskey($!storage, nqp::unbox_s(key))
  101. )
  102. }
  103. multi method EXISTS-KEY(Map:D: \key) {
  104. nqp::p6bool(
  105. nqp::defined($!storage)
  106. && nqp::existskey($!storage, nqp::unbox_s(key.Str))
  107. )
  108. }
  109. multi method perl(Map:D:) {
  110. self.^name
  111. ~ '.new(('
  112. ~ self.sort.map({.perl}).join(',')
  113. ~ '))';
  114. }
  115. method iterator(Map:D:) {
  116. class :: does Rakudo::Iterator::Mappy {
  117. method pull-one() {
  118. nqp::if(
  119. $!iter,
  120. nqp::stmts(
  121. nqp::shift($!iter),
  122. Pair.new(nqp::iterkey_s($!iter), nqp::iterval($!iter))
  123. ),
  124. IterationEnd
  125. )
  126. }
  127. method push-all($target --> IterationEnd) {
  128. nqp::while(
  129. $!iter,
  130. nqp::stmts( # doesn't sink
  131. nqp::shift($!iter),
  132. $target.push(
  133. Pair.new(nqp::iterkey_s($!iter), nqp::iterval($!iter)))
  134. )
  135. )
  136. }
  137. }.new(self)
  138. }
  139. method list(Map:D:) { Seq.new(self.iterator) }
  140. multi method pairs(Map:D:) { Seq.new(self.iterator) }
  141. multi method keys(Map:D:) { Seq.new(Rakudo::Iterator.Mappy-keys(self)) }
  142. multi method values(Map:D:) { Seq.new(Rakudo::Iterator.Mappy-values(self)) }
  143. multi method kv(Map:D:) {
  144. Seq.new(class :: does Rakudo::Iterator::Mappy {
  145. has int $!on-value;
  146. method pull-one() is raw {
  147. nqp::if(
  148. $!on-value,
  149. nqp::stmts(
  150. ($!on-value = 0),
  151. nqp::iterval($!iter)
  152. ),
  153. nqp::if(
  154. $!iter,
  155. nqp::stmts(
  156. ($!on-value = 1),
  157. nqp::iterkey_s(nqp::shift($!iter))
  158. ),
  159. IterationEnd
  160. )
  161. )
  162. }
  163. method skip-one() {
  164. nqp::if(
  165. $!on-value,
  166. nqp::not_i($!on-value = 0), # skipped a value
  167. nqp::if(
  168. $!iter, # if false, we didn't skip
  169. nqp::stmts( # skipped a key
  170. nqp::shift($!iter),
  171. ($!on-value = 1)
  172. )
  173. )
  174. )
  175. }
  176. method push-all($target --> IterationEnd) {
  177. nqp::while( # doesn't sink
  178. $!iter,
  179. nqp::stmts(
  180. $target.push(nqp::iterkey_s(nqp::shift($!iter))),
  181. $target.push(nqp::iterval($!iter))
  182. )
  183. )
  184. }
  185. method count-only() {
  186. nqp::p6box_i(
  187. nqp::add_i(nqp::elems($!storage),nqp::elems($!storage))
  188. )
  189. }
  190. }.new(self))
  191. }
  192. multi method antipairs(Map:D:) {
  193. Seq.new(class :: does Rakudo::Iterator::Mappy {
  194. method pull-one() {
  195. nqp::if(
  196. $!iter,
  197. nqp::stmts(
  198. nqp::shift($!iter),
  199. Pair.new( nqp::iterval($!iter), nqp::iterkey_s($!iter) )
  200. ),
  201. IterationEnd
  202. );
  203. }
  204. method push-all($target --> IterationEnd) {
  205. nqp::while(
  206. $!iter,
  207. nqp::stmts( # doesn't sink
  208. nqp::shift($!iter),
  209. $target.push(
  210. Pair.new( nqp::iterval($!iter), nqp::iterkey_s($!iter) ))
  211. )
  212. )
  213. }
  214. }.new(self))
  215. }
  216. multi method invert(Map:D:) {
  217. Seq.new(Rakudo::Iterator.Invert(self.iterator))
  218. }
  219. multi method AT-KEY(Map:D: Str:D \key) is raw {
  220. nqp::defined($!storage)
  221. ?? nqp::ifnull(nqp::atkey($!storage,nqp::unbox_s(key)),Nil)
  222. !! Nil
  223. }
  224. multi method AT-KEY(Map:D: \key) is raw {
  225. nqp::defined($!storage)
  226. ?? nqp::ifnull(nqp::atkey($!storage,nqp::unbox_s(key.Str)),Nil)
  227. !! Nil
  228. }
  229. method !STORE_MAP(\map --> Nil) {
  230. nqp::if(
  231. nqp::defined(my $other := nqp::getattr(map,Map,'$!storage')),
  232. nqp::stmts(
  233. (my $iter := nqp::iterator($other)),
  234. nqp::while(
  235. $iter,
  236. nqp::stmts(
  237. nqp::shift($iter),
  238. self.STORE_AT_KEY(
  239. nqp::iterkey_s($iter),nqp::iterval($iter)
  240. )
  241. )
  242. )
  243. )
  244. )
  245. }
  246. method STORE(\to_store) {
  247. my $temp := nqp::p6bindattrinvres(
  248. nqp::clone(self), # make sure we get a possible descriptor as well
  249. Map,
  250. '$!storage',
  251. my $storage := nqp::hash
  252. );
  253. my $iter := to_store.iterator;
  254. my Mu $x;
  255. my Mu $y;
  256. nqp::until(
  257. nqp::eqaddr(($x := $iter.pull-one),IterationEnd),
  258. nqp::if(
  259. nqp::istype($x,Pair),
  260. $temp.STORE_AT_KEY(
  261. nqp::getattr(nqp::decont($x),Pair,'$!key'),
  262. nqp::getattr(nqp::decont($x),Pair,'$!value')
  263. ),
  264. nqp::if(
  265. (nqp::istype($x,Map) && nqp::not_i(nqp::iscont($x))),
  266. $temp!STORE_MAP($x),
  267. nqp::if(
  268. nqp::eqaddr(($y := $iter.pull-one),IterationEnd),
  269. nqp::if(
  270. nqp::istype($x,Failure),
  271. $x.throw,
  272. X::Hash::Store::OddNumber.new(
  273. found => nqp::add_i(nqp::mul_i(nqp::elems($storage),2),1),
  274. last => $x
  275. ).throw
  276. ),
  277. $temp.STORE_AT_KEY($x,$y)
  278. )
  279. )
  280. )
  281. );
  282. nqp::p6bindattrinvres(self,Map,'$!storage',$storage)
  283. }
  284. proto method STORE_AT_KEY(|) { * }
  285. multi method STORE_AT_KEY(Str:D \key, Mu \value --> Nil) {
  286. nqp::bindkey($!storage, nqp::unbox_s(key), value)
  287. }
  288. multi method STORE_AT_KEY(\key, Mu \value --> Nil) {
  289. nqp::bindkey($!storage, nqp::unbox_s(key.Str), value)
  290. }
  291. method Capture(Map:D:) {
  292. nqp::defined($!storage)
  293. ?? nqp::p6bindattrinvres(
  294. nqp::create(Capture),Capture,'%!hash',$!storage)
  295. !! nqp::create(Capture)
  296. }
  297. method FLATTENABLE_LIST() { nqp::list() }
  298. method FLATTENABLE_HASH() {
  299. nqp::defined($!storage)
  300. ?? $!storage
  301. !! nqp::bindattr(self,Map,'$!storage',nqp::hash)
  302. }
  303. method fmt(Map: Cool $format = "%s\t\%s", $sep = "\n") {
  304. nqp::iseq_i(nqp::sprintfdirectives( nqp::unbox_s($format.Stringy)),1)
  305. ?? self.keys.fmt($format, $sep)
  306. !! self.pairs.fmt($format, $sep)
  307. }
  308. method hash() { self }
  309. method clone(Map:D:) is raw { self }
  310. }
  311. multi sub infix:<eqv>(Map:D \a, Map:D \b) {
  312. nqp::p6bool(
  313. nqp::unless(
  314. nqp::eqaddr(a,b),
  315. nqp::if(
  316. nqp::eqaddr(a.WHAT,b.WHAT),
  317. nqp::if(
  318. nqp::iseq_i((my int $elems = a.elems),b.elems),
  319. nqp::unless(
  320. nqp::iseq_i($elems,0),
  321. nqp::stmts(
  322. (my $amap := nqp::getattr(nqp::decont(a),Map,'$!storage')),
  323. (my $bmap := nqp::getattr(nqp::decont(b),Map,'$!storage')),
  324. (my $iter := nqp::iterator($amap)),
  325. nqp::while(
  326. $iter
  327. && nqp::existskey($bmap,
  328. my str $key = nqp::iterkey_s(nqp::shift($iter)))
  329. && nqp::atkey($amap,$key) eqv nqp::atkey($bmap,$key),
  330. ($elems = nqp::sub_i($elems,1))
  331. ),
  332. nqp::iseq_i($elems,0) # checked all, so ok
  333. )
  334. )
  335. )
  336. )
  337. )
  338. )
  339. }