Skip to content

Commit 57074eb

Browse files
committed
[js] Implement the dynquant regex node
1 parent 626ea02 commit 57074eb

File tree

1 file changed

+99
-0
lines changed

1 file changed

+99
-0
lines changed

src/vm/js/RegexCompiler.nqp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,105 @@ class RegexCompiler {
459459
);
460460
}
461461

462+
method dynquant($node) {
463+
my $backtrack := $node.backtrack || 'g';
464+
my $sep := $node[2];
465+
466+
my str $min := $*BLOCK.add_tmp;
467+
my str $max := $*BLOCK.add_tmp;
468+
469+
my str $tmp_rep := $*BLOCK.add_tmp;
470+
471+
my str $skip_entire := self.new_label;
472+
my str $sep_label := self.new_label;
473+
my str $loop_label := self.new_label;
474+
my str $done_label := self.new_label;
475+
476+
my @chunks;
477+
478+
my $minmax := $!compiler.as_js($node[1], :want($T_OBJ));
479+
@chunks.push($minmax);
480+
@chunks.push(
481+
"$min = {$minmax.expr}.array[0];\n"
482+
~ "$max = {$minmax.expr}.array[1];\n");
483+
484+
@chunks.push("if ($min === 0 && $max === 0) \{{self.goto($skip_entire)}\}\n");
485+
486+
my str $needmark;
487+
if $backtrack eq 'r' {
488+
$needmark := 'true';
489+
} else {
490+
$needmark := $*BLOCK.add_tmp;
491+
@chunks.push("$needmark = $min > 1 || $max > 1;\n");
492+
}
493+
494+
if $backtrack eq 'f' {
495+
@chunks.push("$!rep = 0;\n");
496+
497+
@chunks.push(
498+
"if ($min < 1) \{\n"
499+
~ self.mark($loop_label, $!pos, $!rep)
500+
~ self.goto($done_label)
501+
~ "\}\n"
502+
);
503+
504+
@chunks.push(self.goto($sep_label)) if $sep;
505+
506+
@chunks.push(self.case($loop_label));
507+
508+
@chunks.push("$tmp_rep = $!rep;\n");
509+
510+
if $sep {
511+
@chunks.push(self.compile_rx($sep));
512+
@chunks.push(self.case($sep_label));
513+
}
514+
515+
@chunks.push(self.compile_rx($node[0]));
516+
517+
@chunks.push("$!rep = $tmp_rep+1;\n");
518+
519+
@chunks.push("if ($min > 1 && $!rep < $min) \{{self.goto($loop_label)}\}\n");
520+
521+
522+
@chunks.push("if ($max > 1 && $!rep > $max) \{{self.goto($done_label)}\}\n");
523+
524+
@chunks.push("if ($max !== 1) \{{self.mark($loop_label, $!pos, $!rep)}\}\n");
525+
@chunks.push(self.case($done_label));
526+
}
527+
else {
528+
@chunks.push("if ($min === 0) \{{self.mark($done_label, $!pos, 0)}\}");
529+
@chunks.push("else if ($needmark) \{{self.mark($done_label, -1, 0)}\}\n");
530+
531+
@chunks.push(self.case($loop_label));
532+
533+
@chunks.push(self.compile_rx($node[0]));
534+
535+
@chunks.push("if ($needmark) \{\n"
536+
~ self.peek($done_label, '*', $!rep)
537+
~ ($backtrack eq 'r' ?? self.commit($done_label) !! '')
538+
~ "$!rep++;\n"
539+
~ "if ($max > 1 && $!rep >= $max) \{" ~ self.goto($done_label) ~ "\}\n"
540+
~ "\}\n");
541+
542+
543+
@chunks.push("if ($max === 1) \{{self.goto($done_label)}\}");
544+
545+
@chunks.push(self.mark($done_label, $!pos, $!rep));
546+
547+
@chunks.push(self.compile_rx($sep)) if $sep;
548+
549+
@chunks.push(self.goto($loop_label));
550+
551+
@chunks.push(self.case($done_label));
552+
553+
@chunks.push("if ($min > 1 && $!rep < $min) \{{self.fail}\}");
554+
}
555+
556+
@chunks.push(self.case($skip_entire));
557+
558+
Chunk.new($T_VOID, '', @chunks);
559+
}
560+
462561
method quant($node) {
463562
my int $min := $node.min;
464563
my int $max := $node.max;

0 commit comments

Comments
 (0)