1. # This class contains methods for generating callables to be used
  2. # in metaoperators. There are two reasons for having this in a
  3. # separate class:
  4. #
  5. # 1. It needs to know about all possible builtin operators. If it
  6. # would be part of Rakudo::Internals, it would be too early in
  7. # building the settings. Augmenting Rakudo::Internals at the
  8. # the end of building the settings would also have been an option,
  9. # but that would probably slow down settings building significantly.
  10. # And since the class name is really not that important, this seemed
  11. # like a good solution.
  12. # 2. Nice to have a separate file for similar stuff. Rakudo::Internals
  13. # has become a hodgepodge of stuff of late.
  14. class Rakudo::Metaops {
  15. my $mappers := nqp::hash(
  16. nqp::tostr_I(&infix:<+>.WHERE), # optimized version for &[+]
  17. -> \list {
  18. nqp::if(
  19. nqp::iseq_i(nqp::elems(list),2),
  20. (nqp::atpos(list,0) + nqp::atpos(list,1)),
  21. nqp::if(
  22. nqp::elems(list),
  23. nqp::stmts(
  24. (my $result := nqp::shift(list)),
  25. nqp::while(
  26. nqp::elems(list),
  27. ($result := $result + nqp::shift(list))
  28. ),
  29. $result
  30. ),
  31. 0
  32. )
  33. )
  34. },
  35. nqp::tostr_I(&infix:<~>.WHERE), # optimized version for &[~]
  36. -> \list {
  37. nqp::if(
  38. nqp::iseq_i(nqp::elems(list),2),
  39. (nqp::atpos(list,0) ~ nqp::atpos(list,1)),
  40. nqp::if(
  41. nqp::elems(list),
  42. nqp::stmts( # could possibly be done smarter
  43. (my $result := nqp::shift(list)),
  44. nqp::while(
  45. nqp::elems(list),
  46. ($result := $result ~ nqp::shift(list))
  47. ),
  48. $result
  49. ),
  50. ''
  51. )
  52. )
  53. },
  54. nqp::tostr_I(&infix:<< => >>.WHERE), # optimized version for &[=>]
  55. -> \list {
  56. nqp::if(
  57. nqp::iseq_i(nqp::elems(list),2),
  58. Pair.new(nqp::atpos(list,0),nqp::atpos(list,1)),
  59. nqp::if(
  60. nqp::isgt_i(nqp::elems(list),2),
  61. nqp::stmts(
  62. (my $result := nqp::pop(list)),
  63. nqp::while(
  64. nqp::elems(list),
  65. ($result := Pair.new(nqp::pop(list),$result))
  66. ),
  67. $result
  68. ),
  69. (die "Too few positionals passed; expected 2 arguments but got {nqp::elems(list)}")
  70. )
  71. )
  72. },
  73. nqp::tostr_I(&infix:<,>.WHERE), # optimized version for &[,]
  74. -> \list {
  75. nqp::p6bindattrinvres(nqp::create(List),List,'$!reified',list)
  76. }
  77. );
  78. method MapperForOp(&op) is raw {
  79. nqp::if(
  80. nqp::existskey($mappers,(my str $where = nqp::tostr_I(&op.WHERE))),
  81. nqp::atkey($mappers,$where),
  82. nqp::if(
  83. nqp::iseq_i(nqp::chars(my str $assoc = &op.prec("assoc")),0)
  84. || nqp::iseq_s($assoc,'left'),
  85. -> \list { # generic left-assoc op
  86. nqp::if(
  87. nqp::iseq_i(nqp::elems(list),2),
  88. op(nqp::atpos(list,0),nqp::atpos(list,1)),
  89. nqp::if(
  90. nqp::elems(list),
  91. nqp::stmts(
  92. (my $result := nqp::shift(list)),
  93. nqp::while(
  94. nqp::elems(list),
  95. ($result := op($result,nqp::shift(list)))
  96. ),
  97. $result
  98. ),
  99. op()
  100. )
  101. )
  102. },
  103. nqp::if(
  104. nqp::iseq_s($assoc,"chain"),
  105. -> \list { # generic chain-assoc op
  106. nqp::if(
  107. nqp::iseq_i(nqp::elems(list),2),
  108. op(nqp::atpos(list,0),nqp::atpos(list,1)),
  109. nqp::if(
  110. nqp::elems(list),
  111. nqp::stmts(
  112. (my $state = True),
  113. (my $current := nqp::shift(list)),
  114. nqp::while(
  115. nqp::elems(list)
  116. && ($state := op(
  117. $current,
  118. (my $next := nqp::shift(list))
  119. )),
  120. ($current := $next)
  121. ),
  122. $state
  123. ),
  124. op()
  125. )
  126. )
  127. },
  128. nqp::if(
  129. nqp::iseq_s($assoc,'right'),
  130. -> \list { # generic right-assoc op
  131. nqp::if(
  132. nqp::iseq_i(nqp::elems(list),2),
  133. op(nqp::atpos(list,0),nqp::atpos(list,1)),
  134. nqp::if(
  135. nqp::elems(list),
  136. nqp::stmts(
  137. (my $result := nqp::pop(list)),
  138. nqp::while(
  139. nqp::elems(list),
  140. ($result := op(nqp::pop(list),$result))
  141. ),
  142. $result
  143. ),
  144. op()
  145. )
  146. )
  147. },
  148. nqp::if(
  149. nqp::iseq_s($assoc,'non'),
  150. -> \list { # generic non-assoc op
  151. nqp::if(
  152. nqp::iseq_i(nqp::elems(list),2),
  153. op(nqp::atpos(list,0),nqp::atpos(list,1)),
  154. (die "Incorrect number of elements for non-associative operator: expected 2, got {nqp::elems(list)}")
  155. )
  156. },
  157. nqp::if(
  158. nqp::iseq_s($assoc,"list"),
  159. -> \list { # generic list/listinfix op
  160. op(
  161. nqp::p6bindattrinvres(
  162. nqp::create(List),List,'$!reified',list)
  163. )
  164. },
  165. (die "Don't know how to process '$assoc' associativity")
  166. )
  167. )
  168. )
  169. )
  170. )
  171. )
  172. }
  173. }