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::bindkey($storage,nqp::iterkey_s(nqp::shift($iter)),
  25. nqp::p6scalarfromdesc($descriptor) =
  26. nqp::decont(nqp::iterval($iter))
  27. )
  28. );
  29. $hash
  30. }
  31. else {
  32. nqp::create(Hash)
  33. }
  34. }
  35. multi method Bool(Map:D:) {
  36. nqp::p6bool(nqp::defined($!storage) && nqp::elems($!storage));
  37. }
  38. method elems(Map:D:) {
  39. nqp::p6box_i(nqp::defined($!storage) && nqp::elems($!storage));
  40. }
  41. multi method Int(Map:D:) { self.elems }
  42. multi method Numeric(Map:D:) { self.elems }
  43. multi method Str(Map:D:) { self.sort.join("\n") }
  44. method IterationBuffer() {
  45. nqp::stmts(
  46. (my $buffer := nqp::create(IterationBuffer)),
  47. nqp::if(
  48. nqp::defined($!storage) && nqp::elems($!storage),
  49. nqp::stmts(
  50. (my $iterator := nqp::iterator($!storage)),
  51. nqp::setelems($buffer,nqp::elems($!storage)),
  52. (my int $i = -1),
  53. nqp::while(
  54. $iterator,
  55. nqp::bindpos($buffer,($i = nqp::add_i($i,1)),
  56. Pair.new(
  57. nqp::iterkey_s(nqp::shift($iterator)),
  58. nqp::iterval($iterator)
  59. )
  60. )
  61. )
  62. )
  63. ),
  64. $buffer
  65. )
  66. }
  67. method List() {
  68. nqp::p6bindattrinvres(
  69. nqp::create(List),List,'$!reified',self.IterationBuffer)
  70. }
  71. multi method sort(Map:D:) {
  72. Seq.new(
  73. Rakudo::Iterator.ReifiedList(
  74. Rakudo::Sorting.MERGESORT-REIFIED-LIST-AS(
  75. nqp::p6bindattrinvres(
  76. nqp::create(List),List,'$!reified',self.IterationBuffer
  77. ),
  78. { nqp::getattr(nqp::decont($^a),Pair,'$!key') }
  79. )
  80. )
  81. )
  82. }
  83. multi method ACCEPTS(Map:D: Any $topic) {
  84. self.EXISTS-KEY($topic.any);
  85. }
  86. multi method ACCEPTS(Map:D: Cool:D $topic) {
  87. self.EXISTS-KEY($topic);
  88. }
  89. multi method ACCEPTS(Map:D: Positional $topic) {
  90. self.EXISTS-KEY($topic.any);
  91. }
  92. multi method ACCEPTS(Map:D: Regex $topic) {
  93. so self.keys.any.match($topic);
  94. }
  95. multi method EXISTS-KEY(Map:D: Str:D \key) {
  96. nqp::p6bool(
  97. nqp::defined($!storage) && nqp::existskey($!storage,key)
  98. )
  99. }
  100. multi method EXISTS-KEY(Map:D: \key) {
  101. nqp::p6bool(
  102. nqp::defined($!storage) && nqp::existskey($!storage,key.Str)
  103. )
  104. }
  105. multi method perl(Map:D:) {
  106. self.^name
  107. ~ '.new(('
  108. ~ self.sort.map({.perl}).join(',')
  109. ~ '))';
  110. }
  111. method iterator(Map:D:) {
  112. class :: does Rakudo::Iterator::Mappy {
  113. method pull-one() {
  114. nqp::if(
  115. $!iter,
  116. nqp::stmts(
  117. nqp::shift($!iter),
  118. Pair.new(nqp::iterkey_s($!iter), nqp::iterval($!iter))
  119. ),
  120. IterationEnd
  121. )
  122. }
  123. method push-all($target --> IterationEnd) {
  124. nqp::while(
  125. $!iter,
  126. nqp::stmts( # doesn't sink
  127. nqp::shift($!iter),
  128. $target.push(
  129. Pair.new(nqp::iterkey_s($!iter), nqp::iterval($!iter)))
  130. )
  131. )
  132. }
  133. }.new(self)
  134. }
  135. method list(Map:D:) { Seq.new(self.iterator) }
  136. multi method pairs(Map:D:) { Seq.new(self.iterator) }
  137. multi method keys(Map:D:) { Seq.new(Rakudo::Iterator.Mappy-keys(self)) }
  138. multi method values(Map:D:) { Seq.new(Rakudo::Iterator.Mappy-values(self)) }
  139. multi method kv(Map:D:) {
  140. Seq.new(class :: does Rakudo::Iterator::Mappy {
  141. has int $!on-value;
  142. method pull-one() is raw {
  143. nqp::if(
  144. $!on-value,
  145. nqp::stmts(
  146. ($!on-value = 0),
  147. nqp::iterval($!iter)
  148. ),
  149. nqp::if(
  150. $!iter,
  151. nqp::stmts(
  152. ($!on-value = 1),
  153. nqp::iterkey_s(nqp::shift($!iter))
  154. ),
  155. IterationEnd
  156. )
  157. )
  158. }
  159. method skip-one() {
  160. nqp::if(
  161. $!on-value,
  162. nqp::not_i($!on-value = 0), # skipped a value
  163. nqp::if(
  164. $!iter, # if false, we didn't skip
  165. nqp::stmts( # skipped a key
  166. nqp::shift($!iter),
  167. ($!on-value = 1)
  168. )
  169. )
  170. )
  171. }
  172. method push-all($target --> IterationEnd) {
  173. nqp::while( # doesn't sink
  174. $!iter,
  175. nqp::stmts(
  176. $target.push(nqp::iterkey_s(nqp::shift($!iter))),
  177. $target.push(nqp::iterval($!iter))
  178. )
  179. )
  180. }
  181. method count-only() {
  182. nqp::p6box_i(
  183. nqp::add_i(nqp::elems($!storage),nqp::elems($!storage))
  184. )
  185. }
  186. }.new(self))
  187. }
  188. multi method antipairs(Map:D:) {
  189. Seq.new(class :: does Rakudo::Iterator::Mappy {
  190. method pull-one() {
  191. nqp::if(
  192. $!iter,
  193. nqp::stmts(
  194. nqp::shift($!iter),
  195. Pair.new( nqp::iterval($!iter), nqp::iterkey_s($!iter) )
  196. ),
  197. IterationEnd
  198. );
  199. }
  200. method push-all($target --> IterationEnd) {
  201. nqp::while(
  202. $!iter,
  203. nqp::stmts( # doesn't sink
  204. nqp::shift($!iter),
  205. $target.push(
  206. Pair.new( nqp::iterval($!iter), nqp::iterkey_s($!iter) ))
  207. )
  208. )
  209. }
  210. }.new(self))
  211. }
  212. multi method invert(Map:D:) {
  213. Seq.new(Rakudo::Iterator.Invert(self.iterator))
  214. }
  215. multi method AT-KEY(Map:D: Str:D \key) is raw {
  216. nqp::defined($!storage)
  217. ?? nqp::ifnull(nqp::atkey($!storage,nqp::unbox_s(key)),Nil)
  218. !! Nil
  219. }
  220. multi method AT-KEY(Map:D: \key) is raw {
  221. nqp::defined($!storage)
  222. ?? nqp::ifnull(nqp::atkey($!storage,nqp::unbox_s(key.Str)),Nil)
  223. !! Nil
  224. }
  225. method !STORE_MAP(\map --> Nil) {
  226. nqp::if(
  227. nqp::defined(my $other := nqp::getattr(map,Map,'$!storage')),
  228. nqp::stmts(
  229. (my $iter := nqp::iterator($other)),
  230. nqp::while(
  231. $iter,
  232. self.STORE_AT_KEY(
  233. nqp::iterkey_s(nqp::shift($iter)),nqp::iterval($iter)
  234. )
  235. )
  236. )
  237. )
  238. }
  239. method STORE(\to_store) {
  240. my $temp := nqp::p6bindattrinvres(
  241. nqp::clone(self), # make sure we get a possible descriptor as well
  242. Map,
  243. '$!storage',
  244. my $storage := nqp::hash
  245. );
  246. my $iter := to_store.iterator;
  247. my Mu $x;
  248. my Mu $y;
  249. nqp::until(
  250. nqp::eqaddr(($x := $iter.pull-one),IterationEnd),
  251. nqp::if(
  252. nqp::istype($x,Pair),
  253. $temp.STORE_AT_KEY(
  254. nqp::getattr(nqp::decont($x),Pair,'$!key'),
  255. nqp::getattr(nqp::decont($x),Pair,'$!value')
  256. ),
  257. nqp::if(
  258. (nqp::istype($x,Map) && nqp::not_i(nqp::iscont($x))),
  259. $temp!STORE_MAP($x),
  260. nqp::if(
  261. nqp::eqaddr(($y := $iter.pull-one),IterationEnd),
  262. nqp::if(
  263. nqp::istype($x,Failure),
  264. $x.throw,
  265. X::Hash::Store::OddNumber.new(
  266. found => nqp::add_i(nqp::mul_i(nqp::elems($storage),2),1),
  267. last => $x
  268. ).throw
  269. ),
  270. $temp.STORE_AT_KEY($x,$y)
  271. )
  272. )
  273. )
  274. );
  275. nqp::p6bindattrinvres(self,Map,'$!storage',$storage)
  276. }
  277. proto method STORE_AT_KEY(|) { * }
  278. multi method STORE_AT_KEY(Str:D \key, Mu \value --> Nil) {
  279. nqp::bindkey($!storage, nqp::unbox_s(key), value)
  280. }
  281. multi method STORE_AT_KEY(\key, Mu \value --> Nil) {
  282. nqp::bindkey($!storage, nqp::unbox_s(key.Str), value)
  283. }
  284. method Capture(Map:D:) {
  285. nqp::defined($!storage)
  286. ?? nqp::p6bindattrinvres(
  287. nqp::create(Capture),Capture,'%!hash',$!storage)
  288. !! nqp::create(Capture)
  289. }
  290. method FLATTENABLE_LIST() { nqp::list() }
  291. method FLATTENABLE_HASH() {
  292. nqp::defined($!storage)
  293. ?? $!storage
  294. !! nqp::bindattr(self,Map,'$!storage',nqp::hash)
  295. }
  296. method fmt(Map: Cool $format = "%s\t\%s", $sep = "\n") {
  297. nqp::iseq_i(nqp::sprintfdirectives( nqp::unbox_s($format.Stringy)),1)
  298. ?? self.keys.fmt($format, $sep)
  299. !! self.pairs.fmt($format, $sep)
  300. }
  301. method hash() { self }
  302. method clone(Map:D:) is raw { self }
  303. multi method roll(Map:D:) {
  304. nqp::if(
  305. $!storage && nqp::elems($!storage),
  306. nqp::stmts(
  307. (my int $i = nqp::add_i(nqp::elems($!storage).rand.floor,1)),
  308. (my $iter := nqp::iterator($!storage)),
  309. nqp::while(
  310. nqp::shift($iter) && ($i = nqp::sub_i($i,1)),
  311. nqp::null
  312. ),
  313. Pair.new(nqp::iterkey_s($iter),nqp::iterval($iter))
  314. ),
  315. Nil
  316. )
  317. }
  318. multi method roll(Map:D: Callable:D $calculate) {
  319. self.roll( $calculate(self.elems) )
  320. }
  321. multi method roll(Map:D: Whatever $) { self.roll(Inf) }
  322. multi method roll(Map:D: $count) {
  323. Seq.new(nqp::if(
  324. $!storage && nqp::elems($!storage) && $count > 0,
  325. class :: does Iterator {
  326. has $!storage;
  327. has $!keys;
  328. has $!pairs;
  329. has $!count;
  330. method !SET-SELF(\hash,\count) {
  331. nqp::stmts(
  332. ($!storage := nqp::getattr(hash,Map,'$!storage')),
  333. ($!count = $count),
  334. (my int $i = nqp::elems($!storage)),
  335. (my $iter := nqp::iterator($!storage)),
  336. ($!keys := nqp::setelems(nqp::list_s,$i)),
  337. ($!pairs := nqp::setelems(nqp::list,$i)),
  338. nqp::while(
  339. nqp::isge_i(($i = nqp::sub_i($i,1)),0),
  340. nqp::bindpos_s($!keys,$i,
  341. nqp::iterkey_s(nqp::shift($iter)))
  342. ),
  343. self
  344. )
  345. }
  346. method new(\h,\c) { nqp::create(self)!SET-SELF(h,c) }
  347. method pull-one() {
  348. nqp::if(
  349. $!count,
  350. nqp::stmts(
  351. --$!count, # must be HLL to handle Inf
  352. nqp::ifnull(
  353. nqp::atpos(
  354. $!pairs,
  355. (my int $i = nqp::elems($!keys).rand.floor)
  356. ),
  357. nqp::bindpos($!pairs,$i,
  358. Pair.new(
  359. nqp::atpos_s($!keys,$i),
  360. nqp::atkey($!storage,nqp::atpos_s($!keys,$i))
  361. )
  362. )
  363. )
  364. ),
  365. IterationEnd
  366. )
  367. }
  368. method is-lazy() { $!count == Inf }
  369. }.new(self,$count),
  370. Rakudo::Iterator.Empty
  371. ))
  372. }
  373. multi method pick(Map:D:) { self.roll }
  374. method !SETIFY(\type) {
  375. nqp::stmts(
  376. (my $elems := nqp::create(Rakudo::Internals::IterationSet)),
  377. nqp::if(
  378. $!storage,
  379. nqp::stmts(
  380. (my $iter := nqp::iterator($!storage)),
  381. nqp::while(
  382. $iter,
  383. nqp::if(
  384. nqp::iterval(nqp::shift($iter)),
  385. nqp::bindkey(
  386. $elems,
  387. nqp::iterkey_s($iter).WHICH,
  388. nqp::iterkey_s($iter),
  389. )
  390. )
  391. )
  392. )
  393. ),
  394. nqp::if(
  395. nqp::elems($elems),
  396. nqp::create(type).SET-SELF($elems),
  397. nqp::if(
  398. nqp::eqaddr(type,Set),
  399. set(),
  400. nqp::create(type)
  401. )
  402. )
  403. )
  404. }
  405. multi method Set(Map:D:) { self!SETIFY(Set) }
  406. multi method SetHash(Map:D:) { self!SETIFY(SetHash) }
  407. }
  408. multi sub infix:<eqv>(Map:D \a, Map:D \b) {
  409. nqp::p6bool(
  410. nqp::unless(
  411. nqp::eqaddr(a,b),
  412. nqp::if(
  413. nqp::eqaddr(a.WHAT,b.WHAT),
  414. nqp::if(
  415. nqp::iseq_i((my int $elems = a.elems),b.elems),
  416. nqp::unless(
  417. nqp::iseq_i($elems,0),
  418. nqp::stmts(
  419. (my $amap := nqp::getattr(nqp::decont(a),Map,'$!storage')),
  420. (my $bmap := nqp::getattr(nqp::decont(b),Map,'$!storage')),
  421. (my $iter := nqp::iterator($amap)),
  422. nqp::while(
  423. $iter
  424. && nqp::existskey($bmap,
  425. my str $key = nqp::iterkey_s(nqp::shift($iter)))
  426. && nqp::atkey($amap,$key) eqv nqp::atkey($bmap,$key),
  427. ($elems = nqp::sub_i($elems,1))
  428. ),
  429. nqp::iseq_i($elems,0) # checked all, so ok
  430. )
  431. )
  432. )
  433. )
  434. )
  435. )
  436. }