# This file is an unfinished sketch of :perl5 regex support. sub JS::Root::rxbare_(Str $re) is primitive { my %h; rx_helper_(%h, $re, "/", "/"); } sub JS::Root::rx_(%mods, Str $re, Str $qo, Str $qc) is primitive { rx_helper_(%mods, $re, $qo, $qc); } sub JS::Root::m_(%mods, Str $re, Str $qo, Str $qc) is primitive { rx_helper_(%mods, $re, $qo, $qc); } our(%modifiers_known, %modifiers_supported_p6, %modifiers_supported_p5); our(%rx_helper_cache); sub JS::Root::rx_helper_(%mods0, Str $pat0, Str $qo, Str $qc) is primitive { if !%modifiers_known.keys { %modifiers_known = map {;($_ => 1)}, ; %modifiers_supported_p6 = map {;($_ => 1)}, ; %modifiers_supported_p5 = map {;($_ => 1)}, ; %rx_helper_cache{'re_x'} = rx_core_({perl5=>1},'^(\d+)x$','/','/'); %rx_helper_cache{'re_nth'} = rx_core_({perl5=>1},'^(\d+)(?:th|st|nd|rd)$','/','/'); } my $pat = $pat0; my %mods = %mods0; my $p5 = %mods{"perl5"} || %mods{"Perl5"} || %mods{"P5"}; my sub warning($e){warn("Warning: $e")}; for %mods.keys -> $k { if %modifiers_known{$k} { if $p5 && !%modifiers_supported_p5{$k} { warning "Modifier :$k is not (yet?) supported by :perl5 regexps."; } elsif !$p5 && !%modifiers_supported_p6{$k} { warning "Modifier :$k is not yet supported by PGE/pugs."; } } elsif (($k.chars > 1) && (substr($k,-1,1) eq "x") # XXX - excess parens required && ($k ~~ %rx_helper_cache{'re_x'})) { my $n = 0+ ~$0; # +$0 XXX %mods.delete($k); %mods{'x'} = $n; } elsif (($k.chars > 2) && (substr($k,-2,2) eq ("th"|"st"|"nd"|"rd")) && ($k ~~ %rx_helper_cache{'re_nth'})) { my $n = 0+ ~$0; # +$0 XXX %mods.delete($k); %mods{'nth'} = $n; } else { my $msg = "Unknown modifier :$k will probably be ignored."; $msg ~= " Perhaps you meant :i:s ?" if $k eq ("is"|"si"); warning $msg; } } if !$p5 { die "Perl 6 Rules are not supported." if !$perl5; } rx_core_(%mods,$pat,$qo,$qc); } sub JS::Root::rx_core_(%mods, Str $pat, Str $qo, Str $qc) is primitive { my $global = %mods{'g'} || %mods{'global'}; my $overlap = %mods{'ov'} || %mods{'overlap'}; my $exhaustive = %mods{'ex'} || %mods{'exhaustive'}; my $ignore = %mods{'i'} || %mods{'ignore'}; my $nth = %mods.exists('nth'); my $multiline; my $new_Match = JS::inline(' PIL2JS.toPIL2JSBox(function (as_b, from, to, as_s, as_a, as_h) { return new PIL2JS.Match(as_b, from, to, as_s, as_a, as_h); })'); my $matcher; if $global { my %mods_altered = %mods; %mods_altered.delete('g'); %mods_altered.delete('global'); my $rx = rx_core_(%mods_altered,$pat,$qo,$qc); $matcher = sub ($string) { my $s = ~$string; my $offset = 0; my @a; while 1 { my $m = $s ~~ $rx; last if !$m; push(@a,$m); my $off = $m.to +1; # $m.from += $offset; # XXX - should be writable, but isnt. # $m.to += $offset; # XXX - should be writable, but isnt. last if $s eq ''; $offset += $off; $s = substr($s,$off); } my $match; if !@a { $match = $new_Match(?0, undef, undef, "", [], ()); } else { my $from = @a[0].from; my $to = $offset -1; # @a[-1].to; # XXX - .to is incorrect - see above my $str = substr($string,$from,$to-$from+1); $match = $new_Match(?1, $from, $to, $str, @a, ()); } $/ := $match; }; } elsif $nth { my %mods_altered = %mods; %mods_altered.delete('nth'); %mods_altered{'global'} = 1; my $rx = rx_core_(%mods_altered,$pat,$qo,$qc); my $nth_keys = %mods{'nth'}; $matcher = sub ($string) { my $m = $string ~~ $rx; return $m if !$m; my @a = @$m; # XXX Error: Can't use "[object Object]" as a generic reference! @a = map {$_[0]}, @$m if @(@a[0]); unshift(@a,undef); # 1-based. @a[$nth_keys]; }; } elsif $overlap { my %mods_altered = %mods; %mods_altered.delete('ov'); %mods_altered.delete('overlap'); my $rx = rx_core_(%mods_altered,$pat,$qo,$qc); $matcher = sub ($string) { my $pos = 0; my $a = []; my $prev = -1; my $m; while 1 { my $s = substr($string,$pos) // last; $m = $s ~~ $rx or last; last if !$m; # XXX - should be caught by or last; my $m0 = $m[0] // $m; my $at = $pos + $m0.from; $a.push($m0) if $at > $prev; $prev = $at; $pos += $m.from + 1; } # 0 ?? ([|] @$a) !! $a; # XXX - 0 should be want.Item $a; }; } else { my $pattern = $pat; if (substr($pattern,0,2) eq '(?') { if substr($pattern,0,4) eq '(?i)' { # XXX - kludge - use regex $pattern = substr($pattern,4); $ignore = 1; } if substr($pattern,0,4) eq '(?m)' { $pattern = substr($pattern,4); $multiline = 1; } # TODO - x } # TODO - \A \Z \z my $flags = ""; $flags ~= "i" if $ignore; $flags ~= "m" if $multiline; $matcher = sub ($string) { # XXX - Are things getting boxed? my $ret = JS::inline('( function (pattern,flags,string) { var mkMatch = function (ok, from, to, s, a, h) { return new PIL2JS.Match(ok, from, to, s, a, h); }; var unpack_match = function (match) { var from = match.index; var str = match[0]; var to = from + str.length -1; var arr = []; for(var i = 1; i < match.length; i++) { var s = match[i]; var tf = s != null; if (!tf) { s = "" } arr.push( mkMatch(tf, null,null, s, null,null)); } var m = mkMatch(true, from, to, str, arr, null); return m; }; var regexp; try { // Guard against malformed patterns. regexp = new RegExp(pattern,flags); } catch (e) { return null; } var match = regexp.exec(string); if (!match) { return mkMatch(false, null,null, null, null,null); } var m = unpack_match(match); return m; } )')(~$pattern,$flags,~$string); if !defined $ret { # XXX - should really be die, not warn. warn "JavaScript RegExp syntax error in /$pattern/.\n"; $ret = $new_Match(?0, undef, undef, "", [], ()); } $/ := $ret; }; } JS::inline('(function (matcher) { return new PIL2JS.Rul(matcher) })')($matcher); } method JS::Root::matcher (Rul $rule:) { JS::inline('(function (rul) { return rul.matcher })')($rule); } for -> $attr { Pugs::Internals::eval_perl6 "method JS::Root::$attr (Match \$match:) \{ JS::inline('(function (match) \{ return match.$attr \})')(\$match); \}"; } # Needs PIL2 and MMD to be done without hacks sub PIL2JS::Internals::Hacks::postcircumfix_for_match_objects ( Match $match, Int *@idxs ) is primitive { $match.subpos[@idxs]; } sub PIL2JS::Internals::Hacks::init_match_postcircumfix_method () is primitive { JS::inline('(function () { PIL2JS.addmethod( _3aMain_3a_3aMatch, "postcircumfix:[]", _26PIL2JS_3a_3aInternals_3a_3aHacks_3a_3apostcircumfix_for_match_objects ); })')(); } # $0 etc. are aliases for $/[0] etc. # Here is an old JS version of :global. Fyi. # var regexp = new RegExp(pattern,flags + "g"); # var match = regexp.exec(string); # if (!match) { # return mkMatch(false, null,null, null, null,null); # } # var g_from = match.index; # var g_array = []; # var g_to; # while (match) { # var m = unpack_match(match); # g_array.push(m); # g_to = regexp.lastIndex - 1; # match = regexp.exec(string); # } # var g_str = string.substring(g_from,g_to+1); # var m = mkMatch(true, g_from, g_to, g_str, g_array, null); # return m;