1. my class Match is Capture is Cool does NQPMatchRole {
  2. my Mu $EMPTY_LIST := nqp::list();
  3. my Mu $NO_CAPS := nqp::hash();
  4. my Mu $DID_MATCH := nqp::create(NQPdidMATCH);
  5. # When nothing's `made`, we get an NQPMu that we'd like to replace
  6. # with Nil; all Rakudo objects typecheck as Mu, while NQPMu doesn't
  7. method ast() { nqp::if(nqp::istype($!made, Mu),$!made,Nil) }
  8. method made() { nqp::if(nqp::istype($!made, Mu),$!made,Nil) }
  9. method STR() {
  10. nqp::if(
  11. nqp::istype(nqp::getattr(self,Match,'$!match'), NQPdidMATCH),
  12. self.Str,
  13. self!MATCH.Str
  14. )
  15. }
  16. method MATCH() {
  17. nqp::if(
  18. nqp::istype(nqp::getattr(self,Match,'$!match'), NQPdidMATCH),
  19. self,
  20. self!MATCH
  21. )
  22. }
  23. method !MATCH() {
  24. my int $from = nqp::getattr_i(self, Match, '$!from');
  25. my int $pos = nqp::getattr_i(self, Match, '$!pos');
  26. my Mu $list;
  27. my Mu $hash := nqp::hash();
  28. if nqp::isge_i($pos, $from) {
  29. # For captures with lists, initialize the lists.
  30. my $caplist := $NO_CAPS;
  31. my $rxsub := nqp::getattr(self, Match, '$!regexsub');
  32. my str $onlyname = '';
  33. my int $namecount = 0;
  34. if nqp::not_i(nqp::isnull($rxsub)) {
  35. $caplist := nqp::can($rxsub, 'CAPS') ?? nqp::findmethod($rxsub, 'CAPS')($rxsub) !! nqp::null();
  36. if nqp::not_i(nqp::isnull($caplist)) && nqp::istrue($caplist) {
  37. my $iter := nqp::iterator($caplist);
  38. my str $name;
  39. while $iter {
  40. $namecount = nqp::add_i($namecount, 1);
  41. if nqp::iterval(nqp::shift($iter)) >= 2 {
  42. $name = nqp::iterkey_s($iter);
  43. $onlyname = $name if nqp::iseq_i($namecount, 1);
  44. nqp::iscclass(nqp::const::CCLASS_NUMERIC, $name, 0)
  45. ?? nqp::bindpos(
  46. nqp::if(nqp::isconcrete($list), $list, ($list := nqp::list())),
  47. nqp::fromstr_I($name, Int), [])
  48. !! nqp::bindkey($hash, $name, []);
  49. }
  50. }
  51. }
  52. }
  53. # Walk the capture stack and populate the Match.
  54. my Mu $cs := nqp::getattr(self, Match, '$!cstack');
  55. if nqp::isnull($cs) || nqp::not_i(nqp::istrue($cs)) {}
  56. elsif nqp::not_i(nqp::istrue($caplist)) {}
  57. elsif nqp::iseq_i($namecount, 1) && nqp::isgt_i(nqp::chars($onlyname), 0) && nqp::eqat($onlyname, '$!', 0) {
  58. # If there's only one destination, avoid repeated hash lookups
  59. my int $cselems = nqp::elems($cs);
  60. my int $csi = -1;
  61. my Mu $dest;
  62. # numeric: <= ord("9") so positional capture
  63. $dest := nqp::islt_i(nqp::ord($onlyname),58)
  64. ?? nqp::atpos($list, $onlyname)
  65. !! nqp::atkey($hash, $onlyname);
  66. my $subcur;
  67. my str $name;
  68. while nqp::islt_i(++$csi,$cselems) {
  69. $subcur := nqp::atpos($cs, $csi);
  70. $name = nqp::getattr_s($subcur, $?CLASS, '$!name');
  71. nqp::push($dest,$subcur.MATCH())
  72. if nqp::not_i(nqp::isnull_s($name));
  73. }
  74. }
  75. else {
  76. my int $cselems = nqp::elems($cs);
  77. my int $csi = -1;
  78. my $subcur;
  79. my str $name;
  80. while nqp::islt_i(++$csi,$cselems) {
  81. $subcur := nqp::atpos($cs, $csi);
  82. $name = nqp::getattr_s($subcur, $?CLASS, '$!name');
  83. if nqp::not_i(nqp::isnull_s($name)) && nqp::isgt_i(nqp::chars($name), 0) {
  84. my Mu $submatch := $subcur.MATCH;
  85. if nqp::eqat($name, '$', 0) && (nqp::iseq_s($name, '$!from') || nqp::iseq_s($name, '$!to')) {
  86. nqp::bindattr_i(self, Match, $name, $submatch.from);
  87. }
  88. elsif nqp::islt_i(nqp::index($name, '='), 0) {
  89. my Mu $capval := nqp::atkey($caplist, $name);
  90. my int $needs_list = nqp::isconcrete($capval) && $capval >= 2;
  91. if nqp::iscclass(nqp::const::CCLASS_NUMERIC, $name, 0) {
  92. $list := nqp::list() unless nqp::isconcrete($list);
  93. $needs_list
  94. ?? nqp::atpos($list, nqp::fromstr_I($name, Int)).append($submatch)
  95. !! nqp::bindpos($list, nqp::fromstr_I($name, Int), $submatch);
  96. }
  97. else {
  98. $needs_list
  99. ?? nqp::atkey($hash, $name).append($submatch)
  100. !! nqp::bindkey($hash, $name, $submatch);
  101. }
  102. }
  103. else {
  104. my $names := nqp::split('=', $name);
  105. my $iter := nqp::iterator($names);
  106. my Mu $capval;
  107. my int $needs_list;
  108. while $iter {
  109. $name = nqp::shift($iter);
  110. $capval := nqp::atkey($caplist, $name);
  111. $needs_list = nqp::isconcrete($capval) && $capval >= 2;
  112. if nqp::iscclass(nqp::const::CCLASS_NUMERIC, $name, 0) {
  113. $list := nqp::list() unless nqp::isconcrete($list);
  114. $needs_list
  115. ?? nqp::atpos($list, nqp::fromstr_I($name, Int)).append($submatch)
  116. !! nqp::bindpos($list, nqp::fromstr_I($name, Int), $submatch);
  117. }
  118. else {
  119. $needs_list
  120. ?? nqp::atkey($hash, $name).append($submatch)
  121. !! nqp::bindkey($hash, $name, $submatch);
  122. }
  123. }
  124. }
  125. }
  126. }
  127. }
  128. }
  129. nqp::bindattr(self, Capture, '@!list', nqp::isconcrete($list) ?? $list !! $EMPTY_LIST);
  130. nqp::bindattr(self, Capture, '%!hash', $hash);
  131. nqp::bindattr(self, Match, '$!match', $DID_MATCH);
  132. # Once we've produced the captures, and if we know we're finished and
  133. # will never be backtracked into, we can release cstack and regexsub.
  134. unless nqp::defined(nqp::getattr(self, Match, '$!bstack')) {
  135. nqp::bindattr(self, Match, '$!cstack', nqp::null());
  136. nqp::bindattr(self, Match, '$!regexsub', nqp::null());
  137. }
  138. self;
  139. }
  140. method CURSOR_NEXT() { # from !cursor_next in nqp
  141. nqp::if(
  142. nqp::defined($!restart),
  143. $!restart(self),
  144. nqp::stmts(
  145. (my $cur := self."!cursor_start_cur"()),
  146. $cur."!cursor_fail"(),
  147. $cur
  148. )
  149. )
  150. }
  151. method CURSOR_OVERLAP() { # adapted from !cursor_more in nqp
  152. nqp::stmts(
  153. (my $new := nqp::create(self)),
  154. nqp::bindattr( $new,$?CLASS,'$!shared',$!shared),
  155. nqp::bindattr( $new,$?CLASS,'$!braid',$!braid),
  156. nqp::bindattr_i($new,$?CLASS,'$!from',-1),
  157. nqp::bindattr_i($new,$?CLASS,'$!pos',nqp::add_i($!from,1)),
  158. nqp::bindattr_i($new,$?CLASS,'$!to',-1),
  159. $!regexsub($new)
  160. )
  161. }
  162. method CURSOR_MORE() { # adapted from !cursor_more in nqp
  163. nqp::stmts(
  164. (my $new := nqp::create(self)),
  165. nqp::bindattr( $new,$?CLASS,'$!shared',$!shared),
  166. nqp::bindattr( $new,$?CLASS,'$!braid',$!braid),
  167. nqp::bindattr_i($new,$?CLASS,'$!from',-1),
  168. nqp::bindattr_i($new,$?CLASS,'$!pos',
  169. nqp::if(
  170. nqp::isge_i($!from,$!pos),
  171. nqp::add_i($!from,1),
  172. $!pos
  173. )
  174. ),
  175. nqp::bindattr_i($new,$?CLASS,'$!to',-1),
  176. $!regexsub($new)
  177. )
  178. }
  179. # INTERPOLATE will iterate over the string $tgt beginning at position 0.
  180. # If it can't match against pattern var (or any element of var if it is an array)
  181. # it will increment $pos and try again. Therefore it is important to only match
  182. # against the current position.
  183. # $i is case insensitive flag
  184. # $m is ignore accent marks flag
  185. # $s is for sequential matching instead of junctive
  186. # $a is true if we are in an assertion
  187. # INTERPOLATE's parameters are non-optional since the ops for optional params
  188. # aren't currently JITted on MoarVM
  189. proto method INTERPOLATE(|) {*}
  190. multi method INTERPOLATE(Callable:D \var, $, $, $, $, $) {
  191. # Call it if it is a routine. This will capture if requested.
  192. (var)(self)
  193. }
  194. multi method INTERPOLATE(Iterable:D \var, int \im, int \monkey, int \s, $, \context) {
  195. my $maxmatch;
  196. my \cur := self.'!cursor_start_cur'();
  197. my str $tgt = cur.target;
  198. my int $eos = nqp::chars($tgt);
  199. my int $maxlen = -1;
  200. my int $pos = nqp::getattr_i(cur, $?CLASS, '$!from');
  201. my int $start = 1;
  202. my int $nomod = im == 0;
  203. my Mu $order;
  204. X::Syntax::Reserved.new(
  205. reserved => "use of hashes in regexes",
  206. ).throw if nqp::istype(var,Hash);
  207. # Looks something we need to loop over
  208. if !nqp::iscont(var) {
  209. my \varlist := var.list;
  210. my int $elems = varlist.elems; # reifies
  211. my \list := nqp::getattr(varlist,List,'$!reified');
  212. # Order matters for sequential matching, so no NFA involved.
  213. if s {
  214. $order := list;
  215. }
  216. # prepare to run the NFA if var is array-ish.
  217. else {
  218. my Mu \nfa := QRegex::NFA.new;
  219. my Mu \alts := nqp::setelems(nqp::list,$elems);
  220. my int $fate = 0;
  221. my int $j = -1;
  222. while nqp::islt_i(++$j,$elems) {
  223. my Mu $topic := nqp::atpos(list,$j);
  224. nqp::bindpos(alts,$j,$topic);
  225. # A Regex already.
  226. if nqp::istype($topic,Regex) {
  227. nfa.mergesubstates($start,0,nqp::decont($fate),
  228. nqp::findmethod($topic,'NFA')($topic),
  229. Mu);
  230. }
  231. # The pattern is a string.
  232. else {
  233. my Mu \lit := QAST::Regex.new(
  234. :rxtype<literal>, $topic,
  235. :subtype( $nomod
  236. ?? ''
  237. !! im == 2
  238. ?? im == 1
  239. ?? 'ignorecase+ignoremark'
  240. !! 'ignoremark'
  241. !! 'ignorecase')
  242. );
  243. my Mu \nfa2 := QRegex::NFA.new;
  244. my Mu \node := nqp::findmethod(nfa2,'addnode')(nfa2,lit);
  245. nfa.mergesubstates($start,0,nqp::decont($fate),
  246. nqp::findmethod(node,'save')(node,:non_empty(1)),
  247. Mu);
  248. }
  249. ++$fate;
  250. }
  251. # Now run the NFA
  252. my Mu \fates := nqp::findmethod(nfa,'run')(nfa,$tgt,$pos);
  253. my int $count = nqp::elems(fates);
  254. nqp::setelems(($order := nqp::list),$count);
  255. $j = -1;
  256. nqp::bindpos($order,$j,
  257. nqp::atpos(alts,nqp::atpos_i(fates,$j)))
  258. while nqp::islt_i(++$j,$count);
  259. }
  260. }
  261. # Use the var as it is if it's not array-ish.
  262. else {
  263. $order := nqp::list(var);
  264. }
  265. my str $topic_str;
  266. my int $omax = nqp::elems($order);
  267. my int $o = -1;
  268. while nqp::islt_i(++$o,$omax) {
  269. my Mu $topic := nqp::atpos($order,$o);
  270. my $match;
  271. my int $len;
  272. # A Regex already.
  273. if nqp::istype($topic,Regex) {
  274. $match := self.$topic;
  275. $len = $match.pos - $match.from;
  276. }
  277. # The pattern is a string. $len and and $topic_str are used
  278. # later on if this condition does not hold.
  279. elsif nqp::iseq_i(($len = nqp::chars($topic_str = $topic.Str)),0) {
  280. $match = 1;
  281. }
  282. # no modifier, match literally
  283. elsif $nomod {
  284. $match = nqp::eqat($tgt, $topic_str, $pos);
  285. }
  286. # ignoremark+ignorecase
  287. elsif im == 3 {
  288. $match = nqp::eqaticim($tgt, $topic_str, $pos);
  289. }
  290. # ignoremark
  291. elsif im == 2 {
  292. $match = nqp::eqatim($tgt, $topic_str, $pos);
  293. }
  294. # ignorecase
  295. elsif im == 1 {
  296. $match = nqp::eqatic($tgt, $topic_str, $pos);
  297. }
  298. if $match
  299. && nqp::isgt_i($len,$maxlen)
  300. && nqp::isle_i(nqp::add_i($pos,$len),$eos) {
  301. $maxlen = $len;
  302. $maxmatch := $match;
  303. last if s; # stop here for sequential alternation
  304. }
  305. }
  306. nqp::istype($maxmatch, Match)
  307. ?? $maxmatch
  308. !! nqp::isge_i($maxlen,0)
  309. ?? cur.'!cursor_pass'(nqp::add_i($pos,$maxlen), '')
  310. !! cur
  311. }
  312. multi method INTERPOLATE(Associative:D \var, int \im, $, $, $, \context) {
  313. my \cur := self.'!cursor_start_cur'();
  314. my $maxmatch;
  315. my str $tgt = cur.target;
  316. my int $maxlen = -1;
  317. my int $pos = nqp::getattr_i(cur, $?CLASS, '$!from');
  318. my str $topic_str;
  319. my $match;
  320. my int $len;
  321. # The pattern is a string. $len and and $topic_str are used
  322. # later on if this condition does not hold.
  323. if nqp::iseq_i(($len = nqp::chars($topic_str = var.Str)),0) {
  324. $match = 1;
  325. }
  326. # no modifier, match literally
  327. elsif im == 0 {
  328. $match = nqp::eqat($tgt, $topic_str, $pos);
  329. }
  330. # ignoremark+ignorecase
  331. elsif im == 3 {
  332. $match = nqp::eqaticim($tgt, $topic_str, $pos);
  333. }
  334. # ignoremark
  335. elsif im == 2 {
  336. $match = nqp::eqatim($tgt, $topic_str, $pos);
  337. }
  338. # ignorecase
  339. elsif im == 1 {
  340. $match = nqp::eqatic($tgt, $topic_str, $pos);
  341. }
  342. if $match
  343. && nqp::isgt_i($len,$maxlen)
  344. && nqp::isle_i(nqp::add_i($pos,$len),nqp::chars($tgt)) {
  345. $maxlen = $len;
  346. $maxmatch := $match;
  347. }
  348. nqp::istype($maxmatch, Match)
  349. ?? $maxmatch
  350. !! nqp::isge_i($maxlen,0)
  351. ?? cur.'!cursor_pass'(nqp::add_i($pos,$maxlen), '')
  352. !! cur
  353. }
  354. multi method INTERPOLATE(Regex:D \var, int \im, int \monkey, $, $, $) {
  355. my $maxmatch;
  356. my \cur := self.'!cursor_start_cur'();
  357. my int $maxlen = -1;
  358. my int $pos = nqp::getattr_i(cur, $?CLASS, '$!from');
  359. my Mu $topic := var;
  360. my $match := self.$topic;
  361. if $match {
  362. my int $len = $match.pos - $match.from;
  363. if nqp::isgt_i($len,$maxlen)
  364. && nqp::isle_i(nqp::add_i($pos,$len),nqp::chars(cur.target)) {
  365. $maxlen = $len;
  366. $maxmatch := $match;
  367. }
  368. }
  369. nqp::istype($maxmatch, Match)
  370. ?? $maxmatch
  371. !! nqp::isge_i($maxlen,0)
  372. ?? cur.'!cursor_pass'(nqp::add_i($pos,$maxlen), '')
  373. !! cur
  374. }
  375. multi method INTERPOLATE(Mu:D \var, int \im, int \monkey, $, $, \context) {
  376. my \cur = self.'!cursor_start_cur'();
  377. my str $tgt = cur.target;
  378. my int $maxlen = -1;
  379. my int $pos = nqp::getattr_i(cur, $?CLASS, '$!from');
  380. my str $topic_str;
  381. my $match;
  382. my int $len;
  383. # The pattern is a zero length string. $len and and $topic_str
  384. # are used later on if this condition does not hold.
  385. if nqp::iseq_i(($len = nqp::chars($topic_str = var.Str)),0) {
  386. $match = 1;
  387. }
  388. # no modifier, match literally
  389. elsif im == 0 {
  390. $match = nqp::eqat($tgt, $topic_str, $pos);
  391. }
  392. # ignoremark+ignorecase
  393. elsif im == 3 {
  394. $match = nqp::eqaticim($tgt, $topic_str, $pos);
  395. }
  396. # ignoremark
  397. elsif im == 2 {
  398. $match = nqp::eqatim($tgt, $topic_str, $pos);
  399. }
  400. # ignorecase
  401. elsif im == 1 {
  402. $match = nqp::eqatic($tgt, $topic_str, $pos);
  403. }
  404. if $match
  405. && nqp::isgt_i($len,$maxlen)
  406. && nqp::isle_i(nqp::add_i($pos,$len),nqp::chars($tgt)) {
  407. $maxlen = $len;
  408. }
  409. nqp::isge_i($maxlen,0)
  410. ?? cur.'!cursor_pass'(nqp::add_i($pos,$maxlen), '')
  411. !! cur
  412. }
  413. multi method INTERPOLATE(Mu:U \var, $, $, $, $, $) {
  414. self."!cursor_start_cur"()
  415. }
  416. proto method INTERPOLATE_ASSERTION(|) {*}
  417. multi method INTERPOLATE_ASSERTION(Associative:D $, $, $, $, $, $) {
  418. return self.'!cursor_start_cur'().'!cursor_start_cur'()
  419. }
  420. multi method INTERPOLATE_ASSERTION(Iterable:D \var, int \im, int \monkey, int \s, $, \context) {
  421. my $maxmatch;
  422. my \cur := self.'!cursor_start_cur'();
  423. my str $tgt = cur.target;
  424. my int $eos = nqp::chars($tgt);
  425. my int $maxlen = -1;
  426. my int $pos = nqp::getattr_i(cur, $?CLASS, '$!from');
  427. my int $start = 1;
  428. my int $nomod = im == 0;
  429. my Mu $order := nqp::list();
  430. # Looks something we need to loop over
  431. if !nqp::iscont(var) {
  432. my \varlist := var.list;
  433. my int $elems = varlist.elems; # reifies
  434. my \list := nqp::getattr(varlist,List,'$!reified');
  435. # Order matters for sequential matching, so no NFA involved.
  436. if s {
  437. $order := list;
  438. }
  439. # prepare to run the NFA if var is array-ish.
  440. else {
  441. my Mu \nfa := QRegex::NFA.new;
  442. my Mu \alts := nqp::setelems(nqp::list,$elems);
  443. my int $fate = 0;
  444. my int $j = -1;
  445. while nqp::islt_i(++$j,$elems) {
  446. my Mu $topic := nqp::atpos(list,$j);
  447. nqp::bindpos(alts,$j,$topic);
  448. # We are in a regex assertion, the strings we get will
  449. # be treated as regex rules.
  450. return cur.'!cursor_start_cur'() if nqp::istype($topic,Associative);
  451. my $rx := MAKE_REGEX($topic,im == 1 || im == 3,im == 2 || im == 3,monkey,context);
  452. nfa.mergesubstates($start,0,nqp::decont($fate),nqp::findmethod($rx,'NFA')($rx),Mu);
  453. ++$fate;
  454. }
  455. # Now run the NFA
  456. my Mu \fates := nqp::findmethod(nfa,'run')(nfa,$tgt,$pos);
  457. my int $count = nqp::elems(fates);
  458. nqp::setelems($order,$count);
  459. $j = -1;
  460. nqp::bindpos($order,$j,nqp::atpos(alts,nqp::atpos_i(fates,$j)))
  461. while nqp::islt_i(++$j,$count);
  462. }
  463. }
  464. # Use the var as it is if it's not array-ish.
  465. else {
  466. nqp::push($order, var);
  467. }
  468. my str $topic_str;
  469. my int $omax = nqp::elems($order);
  470. my int $o = -1;
  471. while nqp::islt_i(++$o,$omax) {
  472. my Mu $topic := nqp::atpos($order,$o);
  473. my $match;
  474. my int $len;
  475. # We are in a regex assertion, the strings we get will be
  476. # treated as regex rules.
  477. return cur.'!cursor_start_cur'()
  478. if nqp::istype($topic,Associative);
  479. my $rx := MAKE_REGEX($topic,im == 1 || im == 3,im == 2 || im == 3,monkey,context);
  480. $match := self.$rx;
  481. $len = $match.pos - $match.from;
  482. if $match
  483. && nqp::isgt_i($len,$maxlen)
  484. && nqp::isle_i(nqp::add_i($pos,$len),$eos) {
  485. $maxlen = $len;
  486. $maxmatch := $match;
  487. last if s; # stop here for sequential alternation
  488. }
  489. }
  490. nqp::istype($maxmatch, Match)
  491. ?? $maxmatch
  492. !! nqp::isge_i($maxlen,0)
  493. ?? cur.'!cursor_pass'(nqp::add_i($pos,$maxlen), '')
  494. !! cur
  495. }
  496. multi method INTERPOLATE_ASSERTION(Mu:D \var, int \im, int \monkey, $, $, \context) {
  497. my \cur = self.'!cursor_start_cur'();
  498. # We are in a regex assertion, the strings we get will be
  499. # treated as regex rules.
  500. my $rx := MAKE_REGEX(var,im == 1 || im == 3,im == 2 || im == 3,monkey,context);
  501. my Match \match := self.$rx;
  502. my int $len = match.pos - match.from;
  503. match.Bool
  504. && nqp::isgt_i($len,-1)
  505. && nqp::isle_i(nqp::add_i(nqp::getattr_i(cur, $?CLASS, '$!from'),$len),nqp::chars(cur.target))
  506. ?? match
  507. !! cur
  508. }
  509. method CALL_SUBRULE($rule, |c) {
  510. $rule(self, |c)
  511. }
  512. method DYNQUANT_LIMITS($mm) {
  513. # Treat non-Range values as range with that value on both end points
  514. # Throw for non-Numeric or NaN Ranges, or if minimum limit is +Inf
  515. # Convert endpoints that are less than 0 to 0, then,
  516. # throw if Range is empty.
  517. nqp::if(
  518. nqp::istype($mm,Range),
  519. nqp::if(
  520. nqp::isfalse(nqp::istype((my $min := $mm.min),Numeric))
  521. || nqp::isfalse(nqp::istype((my $max := $mm.max),Numeric))
  522. || $min.isNaN || $max.isNaN,
  523. X::Syntax::Regex::QuantifierValue.new(:non-numeric-range).throw,
  524. nqp::if(
  525. $min == Inf,
  526. X::Syntax::Regex::QuantifierValue.new(:inf).throw,
  527. nqp::stmts(
  528. nqp::if(
  529. nqp::islt_i(
  530. ($min := nqp::add_i($min == -Inf ?? -1 !! $min.Int,
  531. $mm.excludes-min)),
  532. 0),
  533. $min := 0),
  534. nqp::if(
  535. $max == Inf,
  536. nqp::list_i($min,-1),
  537. nqp::stmts(
  538. nqp::if(
  539. $max == -Inf || nqp::islt_i(
  540. ($max := nqp::sub_i($max.Int,$mm.excludes-max)),0),
  541. $max := 0),
  542. nqp::if(
  543. nqp::islt_i($max, $min),
  544. X::Syntax::Regex::QuantifierValue.new(:empty-range).throw,
  545. nqp::list_i($min,$max))))))),
  546. nqp::if(
  547. nqp::istype((my $v := $mm.Int), Failure),
  548. nqp::stmts(
  549. ($v.so), # handle Failure
  550. nqp::if(
  551. nqp::istype($mm,Numeric) && nqp::isfalse($mm.isNaN),
  552. nqp::if(
  553. $mm == Inf,
  554. X::Syntax::Regex::QuantifierValue.new(:inf).throw,
  555. nqp::list_i(0,0)), # if we got here, $mm is -Inf, treat as zero
  556. X::Syntax::Regex::QuantifierValue.new(:non-numeric).throw)),
  557. nqp::if(
  558. nqp::islt_i($v,0),
  559. nqp::list_i(0,0),
  560. nqp::list_i($v,$v))))
  561. }
  562. method OTHERGRAMMAR($grammar, $name, |) {
  563. my $lang_cursor := $grammar.'!cursor_init'(self.target(), :p(self.pos()));
  564. $lang_cursor.clone_braid_from(self);
  565. $lang_cursor."$name"();
  566. }
  567. method INDMETHOD($name, |c) {
  568. self."$name"(|c);
  569. }
  570. method INDRULE($rule, |c) {
  571. $rule(self, |c)
  572. }
  573. method RECURSE() {
  574. nqp::getlexdyn('$?REGEX')(self)
  575. }
  576. sub MAKE_REGEX($arg, int $i, int $m, int $monkey, $context) {
  577. my role CachedCompiledRegex {
  578. has $.regex;
  579. }
  580. if nqp::istype($arg,Regex) {
  581. $arg
  582. }
  583. elsif nqp::istype($arg, CachedCompiledRegex) {
  584. $arg.regex
  585. }
  586. else {
  587. my $*RESTRICTED = "Prohibited regex interpolation"
  588. unless $monkey; # Comes from when regex was originally compiled.
  589. my $rx := $i
  590. ?? $m
  591. ?? EVAL("anon regex \{ :i :m $arg\}", :$context)
  592. !! EVAL("anon regex \{ :i $arg\}", :$context)
  593. !! $m
  594. ?? EVAL("anon regex \{ :m $arg\}", :$context)
  595. !! EVAL("anon regex \{ $arg\}", :$context);
  596. $arg does CachedCompiledRegex($rx);
  597. $rx
  598. }
  599. }
  600. submethod BUILD(
  601. :$orig = '',
  602. :$from = 0,
  603. :to(:$pos),
  604. :ast(:$made),
  605. :$shared,
  606. :$braid,
  607. :$list,
  608. :$hash)
  609. {
  610. # :build tells !cursor_init that it's too late to do a CREATE
  611. self.'!cursor_init'($orig, :build, :p($pos), :$shared, :$braid);
  612. nqp::bindattr_i(self, Match, '$!from', $from);
  613. nqp::bindattr( self, Match, '$!made', nqp::decont($made)) if $made.defined;
  614. }
  615. method clone() {
  616. my $new := nqp::clone(self);
  617. $new;
  618. }
  619. multi method WHICH (Match:D:) {
  620. self.Mu::WHICH # skip Capture's as Match is not a value type
  621. }
  622. proto method Bool(|) {*}
  623. multi method Bool(Match:U:) { False }
  624. multi method Bool(Match:D:) { nqp::p6bool($!pos >= $!from) }
  625. multi method Numeric(Match:D:) {
  626. self.Str.Numeric
  627. }
  628. multi method ACCEPTS(Match:D: Any $) { self }
  629. method prematch(Match:D:) {
  630. nqp::substr(self.target,0,$!from)
  631. }
  632. method postmatch(Match:D:) {
  633. nqp::substr(self.target,self.to)
  634. }
  635. method caps(Match:D:) {
  636. my @caps;
  637. for self.pairs -> $p {
  638. if nqp::istype($p.value,Array) {
  639. @caps.push: $p.key => $_ for $p.value.list
  640. } elsif $p.value.DEFINITE {
  641. @caps.push: $p
  642. }
  643. }
  644. @caps.sort: -> $a { $a.value.from +< 32 + $a.value.pos }
  645. }
  646. method chunks(Match:D:) {
  647. my $prev = $!from;
  648. my $target := self.target;
  649. gather {
  650. for self.caps {
  651. if .value.from > $prev {
  652. take '~' => substr($target,$prev, .value.from - $prev)
  653. }
  654. take $_;
  655. $prev = .value.pos;
  656. }
  657. take '~' => substr($target,$prev, $!pos - $prev) if $prev < $!pos;
  658. }
  659. }
  660. multi method perl(Match:D:) {
  661. my %attrs;
  662. %attrs.ASSIGN-KEY("orig", (self.orig // '' ).perl);
  663. %attrs.ASSIGN-KEY("from", (self.from // 0 ).perl);
  664. %attrs.ASSIGN-KEY("pos", (self.pos // 0 ).perl);
  665. %attrs.ASSIGN-KEY("made", (self.made // Any).perl);
  666. %attrs.ASSIGN-KEY("list", (self.Capture::list // [] ).perl);
  667. %attrs.ASSIGN-KEY("hash", (self.Capture::hash // {} ).perl);
  668. 'Match.new('
  669. ~ %attrs.fmt('%s => %s', ', ')
  670. ~ ')'
  671. }
  672. multi method gist (Match:D: $d = 0) {
  673. return "#<failed match>" unless self;
  674. my $s = ' ' x ($d + 1);
  675. my $r = ("=> " if $d) ~ "\x[FF62]{self}\x[FF63]\n";
  676. for @.caps {
  677. $r ~= $s ~ (.key // '?') ~ ' ' ~ .value.gist($d + 1)
  678. }
  679. $d == 0 ?? $r.chomp !! $r;
  680. }
  681. }
  682. multi sub infix:<eqv>(Match:D \a, Match:D \b) {
  683. a =:= b
  684. ||
  685. [&&] (
  686. a.pos eqv b.pos,
  687. a.from eqv b.from,
  688. a.orig eqv b.orig,
  689. (a.made // Any) eqv (b.made // Any),
  690. (a.Capture::list // nqp::list ) eqv (b.Capture::list // nqp::list ),
  691. (a.Capture::hash // nqp::hash ) eqv (b.Capture::hash // nqp::hash )
  692. );
  693. }
  694. proto sub make(|) { $/ := nqp::getlexcaller('$/'); {*} }
  695. multi sub make(Mu \made) {
  696. nqp::bindattr(nqp::decont(nqp::getlexcaller('$/')),Match,'$!made',made)
  697. }