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 ACCEPTS(Map:D: Map:D \m --> Bool) {
  96. self eqv m;
  97. }
  98. multi method EXISTS-KEY(Map:D: Str:D \key) {
  99. nqp::p6bool(
  100. nqp::defined($!storage) && nqp::existskey($!storage,key)
  101. )
  102. }
  103. multi method EXISTS-KEY(Map:D: \key) {
  104. nqp::p6bool(
  105. nqp::defined($!storage) && nqp::existskey($!storage,key.Str)
  106. )
  107. }
  108. multi method gist(Map:D:) {
  109. self.^name ~ '.new((' ~ self.sort.map({
  110. state $i = 0;
  111. ++$i == 101 ?? '...'
  112. !! $i == 102 ?? last()
  113. !! .gist
  114. }).join(', ') ~ '))'
  115. }
  116. multi method perl(Map:D \SELF:) {
  117. my $p = self.^name ~ '.new((' ~ self.sort.map({.perl}).join(',') ~ '))';
  118. nqp::iscont(SELF) ?? '$(' ~ $p ~ ')' !! $p
  119. }
  120. method iterator(Map:D:) {
  121. class :: does Rakudo::Iterator::Mappy {
  122. method pull-one() {
  123. nqp::if(
  124. $!iter,
  125. nqp::stmts(
  126. nqp::shift($!iter),
  127. Pair.new(nqp::iterkey_s($!iter), nqp::iterval($!iter))
  128. ),
  129. IterationEnd
  130. )
  131. }
  132. method push-all($target --> IterationEnd) {
  133. nqp::while(
  134. $!iter,
  135. nqp::stmts( # doesn't sink
  136. nqp::shift($!iter),
  137. $target.push(
  138. Pair.new(nqp::iterkey_s($!iter), nqp::iterval($!iter)))
  139. )
  140. )
  141. }
  142. }.new(self)
  143. }
  144. method list(Map:D:) { Seq.new(self.iterator) }
  145. multi method pairs(Map:D:) { Seq.new(self.iterator) }
  146. multi method keys(Map:D:) { Seq.new(Rakudo::Iterator.Mappy-keys(self)) }
  147. multi method values(Map:D:) { Seq.new(Rakudo::Iterator.Mappy-values(self)) }
  148. multi method kv(Map:D:) {
  149. Seq.new(class :: does Rakudo::Iterator::Mappy {
  150. has int $!on-value;
  151. method pull-one() is raw {
  152. nqp::if(
  153. $!on-value,
  154. nqp::stmts(
  155. ($!on-value = 0),
  156. nqp::iterval($!iter)
  157. ),
  158. nqp::if(
  159. $!iter,
  160. nqp::stmts(
  161. ($!on-value = 1),
  162. nqp::iterkey_s(nqp::shift($!iter))
  163. ),
  164. IterationEnd
  165. )
  166. )
  167. }
  168. method skip-one() {
  169. nqp::if(
  170. $!on-value,
  171. nqp::not_i($!on-value = 0), # skipped a value
  172. nqp::if(
  173. $!iter, # if false, we didn't skip
  174. nqp::stmts( # skipped a key
  175. nqp::shift($!iter),
  176. ($!on-value = 1)
  177. )
  178. )
  179. )
  180. }
  181. method push-all($target --> IterationEnd) {
  182. nqp::while( # doesn't sink
  183. $!iter,
  184. nqp::stmts(
  185. $target.push(nqp::iterkey_s(nqp::shift($!iter))),
  186. $target.push(nqp::iterval($!iter))
  187. )
  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. self.STORE_AT_KEY(
  237. nqp::iterkey_s(nqp::shift($iter)),nqp::iterval($iter)
  238. )
  239. )
  240. )
  241. )
  242. }
  243. method STORE(\to_store) {
  244. my $temp := nqp::p6bindattrinvres(
  245. nqp::clone(self), # make sure we get a possible descriptor as well
  246. Map,
  247. '$!storage',
  248. my $storage := nqp::hash
  249. );
  250. my $iter := to_store.iterator;
  251. my Mu $x;
  252. my Mu $y;
  253. nqp::until(
  254. nqp::eqaddr(($x := $iter.pull-one),IterationEnd),
  255. nqp::if(
  256. nqp::istype($x,Pair),
  257. $temp.STORE_AT_KEY(
  258. nqp::getattr(nqp::decont($x),Pair,'$!key'),
  259. nqp::getattr(nqp::decont($x),Pair,'$!value')
  260. ),
  261. nqp::if(
  262. (nqp::istype($x,Map) && nqp::not_i(nqp::iscont($x))),
  263. $temp!STORE_MAP($x),
  264. nqp::if(
  265. nqp::eqaddr(($y := $iter.pull-one),IterationEnd),
  266. nqp::if(
  267. nqp::istype($x,Failure),
  268. $x.throw,
  269. X::Hash::Store::OddNumber.new(
  270. found => nqp::add_i(nqp::mul_i(nqp::elems($storage),2),1),
  271. last => $x
  272. ).throw
  273. ),
  274. $temp.STORE_AT_KEY($x,$y)
  275. )
  276. )
  277. )
  278. );
  279. nqp::p6bindattrinvres(self,Map,'$!storage',$storage)
  280. }
  281. proto method STORE_AT_KEY(|) {*}
  282. multi method STORE_AT_KEY(Str:D \key, Mu \value --> Nil) {
  283. nqp::bindkey($!storage, nqp::unbox_s(key), nqp::decont(value))
  284. }
  285. multi method STORE_AT_KEY(\key, Mu \value --> Nil) {
  286. nqp::bindkey($!storage, nqp::unbox_s(key.Str), nqp::decont(value))
  287. }
  288. method Capture(Map:D:) {
  289. nqp::defined($!storage)
  290. ?? nqp::p6bindattrinvres(
  291. nqp::create(Capture),Capture,'%!hash',$!storage)
  292. !! nqp::create(Capture)
  293. }
  294. method FLATTENABLE_LIST() { nqp::list() }
  295. method FLATTENABLE_HASH() {
  296. nqp::defined($!storage)
  297. ?? $!storage
  298. !! nqp::bindattr(self,Map,'$!storage',nqp::hash)
  299. }
  300. method fmt(Map: Cool $format = "%s\t\%s", $sep = "\n") {
  301. nqp::iseq_i(nqp::sprintfdirectives( nqp::unbox_s($format.Stringy)),1)
  302. ?? self.keys.fmt($format, $sep)
  303. !! self.pairs.fmt($format, $sep)
  304. }
  305. method hash() { self }
  306. method clone(Map:D:) is raw { self }
  307. multi method roll(Map:D:) {
  308. nqp::if(
  309. $!storage && nqp::elems($!storage),
  310. nqp::stmts(
  311. (my int $i = nqp::add_i(nqp::elems($!storage).rand.floor,1)),
  312. (my $iter := nqp::iterator($!storage)),
  313. nqp::while(
  314. nqp::shift($iter) && ($i = nqp::sub_i($i,1)),
  315. nqp::null
  316. ),
  317. Pair.new(nqp::iterkey_s($iter),nqp::iterval($iter))
  318. ),
  319. Nil
  320. )
  321. }
  322. multi method roll(Map:D: Callable:D $calculate) {
  323. self.roll( $calculate(self.elems) )
  324. }
  325. multi method roll(Map:D: Whatever $) { self.roll(Inf) }
  326. multi method roll(Map:D: $count) {
  327. Seq.new(nqp::if(
  328. $!storage && nqp::elems($!storage) && $count > 0,
  329. class :: does Iterator {
  330. has $!storage;
  331. has $!keys;
  332. has $!pairs;
  333. has $!count;
  334. method !SET-SELF(\hash,\count) {
  335. nqp::stmts(
  336. ($!storage := nqp::getattr(hash,Map,'$!storage')),
  337. ($!count = $count),
  338. (my int $i = nqp::elems($!storage)),
  339. (my $iter := nqp::iterator($!storage)),
  340. ($!keys := nqp::setelems(nqp::list_s,$i)),
  341. ($!pairs := nqp::setelems(nqp::list,$i)),
  342. nqp::while(
  343. nqp::isge_i(($i = nqp::sub_i($i,1)),0),
  344. nqp::bindpos_s($!keys,$i,
  345. nqp::iterkey_s(nqp::shift($iter)))
  346. ),
  347. self
  348. )
  349. }
  350. method new(\h,\c) { nqp::create(self)!SET-SELF(h,c) }
  351. method pull-one() {
  352. nqp::if(
  353. $!count,
  354. nqp::stmts(
  355. --$!count, # must be HLL to handle Inf
  356. nqp::ifnull(
  357. nqp::atpos(
  358. $!pairs,
  359. (my int $i = nqp::elems($!keys).rand.floor)
  360. ),
  361. nqp::bindpos($!pairs,$i,
  362. Pair.new(
  363. nqp::atpos_s($!keys,$i),
  364. nqp::atkey($!storage,nqp::atpos_s($!keys,$i))
  365. )
  366. )
  367. )
  368. ),
  369. IterationEnd
  370. )
  371. }
  372. method is-lazy() { $!count == Inf }
  373. }.new(self,$count),
  374. Rakudo::Iterator.Empty
  375. ))
  376. }
  377. multi method pick(Map:D:) { self.roll }
  378. multi method Set(Map:D:) {
  379. nqp::create(Set).SET-SELF(Rakudo::QuantHash.COERCE-MAP-TO-SET(self))
  380. }
  381. multi method SetHash(Map:D:) {
  382. nqp::create(SetHash).SET-SELF(Rakudo::QuantHash.COERCE-MAP-TO-SET(self))
  383. }
  384. multi method Bag(Map:D:) {
  385. nqp::create(Bag).SET-SELF(Rakudo::QuantHash.COERCE-MAP-TO-BAG(self))
  386. }
  387. multi method BagHash(Map:D:) {
  388. nqp::create(BagHash).SET-SELF(Rakudo::QuantHash.COERCE-MAP-TO-BAG(self))
  389. }
  390. multi method Mix(Map:D:) {
  391. nqp::create(Mix).SET-SELF(Rakudo::QuantHash.COERCE-MAP-TO-MIX(self))
  392. }
  393. multi method MixHash(Map:D:) {
  394. nqp::create(MixHash).SET-SELF(Rakudo::QuantHash.COERCE-MAP-TO-MIX(self))
  395. }
  396. }
  397. multi sub infix:<eqv>(Map:D \a, Map:D \b) {
  398. class NotEQV { }
  399. nqp::p6bool(
  400. nqp::unless(
  401. nqp::eqaddr(a,b),
  402. nqp::if( # not comparing with self
  403. nqp::eqaddr(a.WHAT,b.WHAT),
  404. nqp::if( # same types
  405. (my $amap := nqp::getattr(nqp::decont(a),Map,'$!storage'))
  406. && (my int $elems = nqp::elems($amap)),
  407. nqp::if( # elems on left
  408. (my $bmap := nqp::getattr(nqp::decont(b),Map,'$!storage'))
  409. && nqp::iseq_i($elems,nqp::elems($bmap)),
  410. nqp::stmts( # same elems on right
  411. (my $iter := nqp::iterator($amap)),
  412. nqp::while(
  413. $iter && infix:<eqv>(
  414. nqp::iterval(nqp::shift($iter)),
  415. nqp::ifnull(nqp::atkey($bmap,nqp::iterkey_s($iter)),NotEQV)
  416. ),
  417. ($elems = nqp::sub_i($elems,1))
  418. ),
  419. nqp::not_i($elems) # ok if none left
  420. )
  421. ),
  422. nqp::isfalse( # nothing on left
  423. ($bmap := nqp::getattr(nqp::decont(b),Map,'$!storage'))
  424. && nqp::elems($bmap) # something on right: fail
  425. )
  426. )
  427. )
  428. )
  429. )
  430. }